User:Beetstra/unblockbot2

From Meta, a Wikimedia project coordination wiki

Code of unblockbot. You need everything between the <pre> and </pre> tags below.

  • Bot is written in perl
  • Uses perlwikipedia -> see [1]
  • You need to create a file with the password for the bot on IRC. The name of the file has to be 'unblockbot-password' and can only contain the password.
# UnBlockBot - a Block reporting bot for mediawiki projects.
# Written by Dirk Beetstra, 2007.

# You need perlwikipedia.pm -> http://code.google.com/p/perlwikipedia/
#Passwords are stored in a separate file, 'unblockbot-password', create this file in the same directory as this file, 
# with only the password in it (no CR or any other thing).

#First, let's declare our modules.
use strict;
#use warnings;
use POE;
use POE::Component::IRC::State;
use POE::Component::IRC::Plugin::BotAddressed;
use POE::Component::IRC::Plugin::Connector;
use perlwikipedia;

my %settings : shared;
$settings{checkevery} = 600;                                                 # number of reported cases in this run
$settings{blocklist} = [()];
$settings{counter} = 50;
$settings{interval} = 50;
$settings{boottime} = time();
$settings{message} = time();
$settings{rcmessage} = time();
$settings{oldcounter} = 0;

#Now we'll set up the mechanism we'll use for editing/retrieving pages
my $editor=Perlwikipedia->new;

#Declare all sorts of IRC-related goodness
my $nickname       = 'UnBlockBot2';                                             # Bots nickname
my $username       = 'UnBlockBot2';                                             # Bots username
open(PASS,'unblockbot-password');                                               # A file with only the password, no carriage return
sysread(PASS, my $password, -s(PASS));                                          # No password in sourcecode.
close(PASS);
$password=~s/\n//;                                                              # IRC password
my $ircname        = 'UnblockBot - block reports (by [[en:User:Beetstra]])';     # IRC name

my @channels       = (                                                          # Listening to channels
                       '#wikipedia-en-unblock', 
                     );         
                     
my @rcchannels       = (
                        '#en.wikipedia',
                     );

my $connections = {  # http://freenode.net/irc_servers.shtml
    'niven.freenode.net' => { port => 8001, channels => [ @channels ], },
    'irc.wikimedia.org' => { port => 6667, channels => [ @rcchannels ], },
};

# We create a new PoCo-IRC objects and components.
foreach my $server ( keys %{ $connections } ) {
    POE::Component::IRC->spawn( 
            alias   => $server, 
            nick    => $nickname,
            ircname => $ircname,  
    );
}

POE::Session->create(
    inline_states => {
        _start             => \&_start,
        irc_registered     => \&irc_registered,
        irc_001            => \&irc_001,
        irc_public         => \&irc_public,
        irc_bot_addressed  => \&irc_bot_addressed,
    },
    heap => { config => $connections },
);

$poe_kernel->run();
exit 0;

sub _start {
    my ($kernel,$session) = @_[KERNEL,SESSION];
    
    # Send a POCOIRC_REGISTER signal to all poco-ircs
    $kernel->signal( $kernel, 'POCOIRC_REGISTER', $session->ID(), 'all' );
    
    undef $kernel;
    undef $session;
}

# We'll get one of these from each PoCo-IRC that we spawned above.
sub irc_registered {
    my ($kernel,$heap,$sender,$irc_object) = @_[KERNEL,HEAP,SENDER,ARG0];
    
    my $alias = $irc_object->session_alias();
    
    my %conn_hash = (
        server => $alias,
        port   => $heap->{config}->{ $alias }->{port},
    );
    
    # In any irc_* events SENDER will be the PoCo-IRC session
    $kernel->post( $sender, 'connect', \%conn_hash ); 
    $irc_object->plugin_add( 'BotAddressed', POE::Component::IRC::Plugin::BotAddressed->new(eat=>1) );    
    $heap->{connector} = POE::Component::IRC::Plugin::Connector->new( delay => 120 );    
    $irc_object->plugin_add( 'Connector' => $heap->{connector} );    
    undef $kernel;
    undef $heap;
    undef $sender;
    undef $irc_object;
    undef $alias;
}

sub irc_001 {
    my ($kernel,$heap,$sender) = @_[KERNEL,HEAP,SENDER];
    
    # Get the component's object at any time by accessing the heap of
    # the SENDER
    my $poco_object = $sender->get_heap();
    print "Connected to ", $poco_object->server_name(), "\n";
    my $alias = $poco_object->session_alias();
    my @channels = @{ $heap->{config}->{ $alias }->{channels} };
    if ( $poco_object->server_name eq 'niven.freenode.net') {
        $kernel->post($sender=>privmsg=>"NickServ","identify $password");
        sleep 5;
        $kernel->post( $sender => join => $_ ) for @channels;
    } else {
        $kernel->post( $sender => join => $_ ) for @channels;
    }
    $heap->{seen_traffic} = 1;
    $kernel->delay( autoping => 300 );
    undef $kernel;
    undef $heap;
    undef $sender;
    undef $poco_object;
    undef $alias;
    undef @channels;
    undef;
}

sub irc_public {
    my ($kernel,$heap,$sender,$who,$where,$message) = @_[KERNEL,HEAP,SENDER,ARG0,ARG1,ARG2];
    my $nick = (split /!/,$who)[0];
    my ($cloak)=( split /@/, $who)[1];
    my $channel   = $where->[0];
    my $jchannel;
    if (lc($message) eq '!unblock') {
        foreach $jchannel(@rcchannels) {
            $kernel->post( 'irc.wikimedia.org' => join => $jchannel );
        }
        foreach $jchannel(@channels) {
            $kernel->post( 'niven.freenode.net' => join => $jchannel );
        }
        readandreport($channel, $kernel, $nick, 0, 0);
        $settings{counter} = 0;
    }
    if (lc($message) eq '!new') {
        $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "Hi $nick.  Parsing unblock requests ..." );
        readandreport($channel, $kernel, $nick, 1, 0);
        $settings{counter} = 0;
    }
    if (($nick == 'rc') && ($message =~ m/User talk:/)) {
        $settings{counter}++;
        if ($message =~ m/block/i) {
            $settings{counter} = 0;
            readandreport($channel, $kernel, $nick, 1, 1);
        }
    }
    if ($settings{counter} > $settings{interval}) {
        foreach $jchannel(@rcchannels) {
            $kernel->post( 'irc.wikimedia.org' => join => $jchannel );
        }
        foreach $jchannel(@channels) {
            $kernel->post( 'niven.freenode.net' => join => $jchannel );
        }
        $settings{counter} = 0;
        readandreport($channel, $kernel, $nick, 1, 1);
    }
    if ($channel = "#en.wikipedia") {$settings{rcmessage} = time(); }
    if ($channel = "#wikipedia-en-unblock") {$settings{message} = time(); }
}

sub irc_bot_addressed {                                                     # Bot commands
    my ($kernel,$heap,$sender,$who,$where,$message) = @_[KERNEL,HEAP,SENDER,ARG0,ARG1,ARG2];
    my $nick = (split /!/,$who)[0];
    my ($cloak)=( split /@/, $who)[1];
    my $channel   = $where->[0];
    $message =~ s/\cC\d{1,2}(?:,\d{1,2})?|[\cC\cB\cI\cU\cR\cO]//g; #Kill any color codes.
    my $minute;
    my $second;
    my $hour;
    my $dayOfYear;
    my $theTime;
    my $uptime;
    my $timedifference;
    my $message_to_send;
    if ((lc($message) eq 'unblock') || (lc($message) eq "")) {
        readandreport($channel, $kernel, $nick, 0, 0);
    }
    if (lc($message) eq 'new') {
        readandreport($channel, $kernel, $nick, 1, 0);
    }
    if (lc($message) eq 'quit' || lc($message) eq 'die') {
        if (lc($cloak) eq "wikimedia/beetstra" || lc($cloak) eq "wikimedia/versageek") {
            $kernel->signal($kernel, 'POCOIRC_SHUTDOWN', "Und sterb' ich denn, so sterb' ich doch ... Durch sie, durch sie, ...");
            die "Asked to quit by $nick.\n";
        } else {
            $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "Only Beetstra and Versageek can tell me to quit." );
        }
    }
    if ($message=~ m/^status/i ) {                                  # ask for status
        $timedifference = time() - $settings{boottime};
        $minute = int($timedifference / 60);
        $second = $timedifference - $minute * 60;
        $hour = int($minute / 60);
        $minute = $minute - $hour * 60;
        $dayOfYear = int($hour / 24);
        $hour = $hour - $dayOfYear * 24;
        if (length("$hour") == 1) {
            $hour = "0$hour";
        }
        if (length("$minute") == 1) {
            $minute = "0$minute";
        }
        if (length("$second") == 1) {
            $second = "0$second";
        }
        if ($dayOfYear > 1) {
            $theTime = "$dayOfYear days, $hour:$minute:$second hours";
        } elsif ($dayOfYear == 1) {
            $theTime = "$dayOfYear day, $hour:$minute:$second hours";
        } else {
            if ($hour > 1) {
                $theTime = "$hour hours $minute:$second minutes";
            } elsif ($hour == 1) {
                $theTime = "$hour hour $minute:$second minutes";
            } else {
                if ($minute > 1) {
                    $theTime = "$minute minutes $second seconds";
                } elsif ($minute == 1) {
                    $theTime = "$minute minute $second seconds";
                } else {
                    $theTime = "$second seconds";
                }
            }
        }
        $uptime = $timedifference/60;
        $message_to_send="$nick:";
        $message_to_send.=" Uptime: $theTime.";
        $timedifference = time() - $settings{rcmessage};
        $minute = int($timedifference / 60);
        $second = $timedifference - $minute * 60;
        $hour = int($minute / 60);
        $minute = $minute - $hour * 60;
        $dayOfYear = int($hour / 24);
        $hour = $hour - $dayOfYear * 24;
        if (length("$hour") == 1) {
            $hour = "0$hour";
        }
        if (length("$minute") == 1) {
            $minute = "0$minute";
        }
        if (length("$second") == 1) {
            $second = "0$second";
        }
        if ($dayOfYear > 1) {
            $theTime = "$dayOfYear days, $hour:$minute:$second hours";
        } elsif ($dayOfYear == 1) {
            $theTime = "$dayOfYear day, $hour:$minute:$second hours";
        } else {
            if ($hour > 1) {
                $theTime = "$hour hours $minute:$second minutes";
            } elsif ($hour == 1) {
                $theTime = "$hour hour $minute:$second minutes";
            } else {
                if ($minute > 1) {
                    $theTime = "$minute minutes $second seconds";
                } elsif ($minute == 1) {
                    $theTime = "$minute minute $second seconds";
                } else {
                    $theTime = "$second seconds";
                }
            }
        }
        $message_to_send.=" Last RC $theTime ago.";
        $kernel->post( $sender => privmsg => '#wikipedia-en-unblock' => $message_to_send );
    }
}

sub readandreport {
    my $channel = shift;
    my $kernel = shift;
    my $nick = shift;
    my $onlyknown = shift;
    my $auto = shift;
    if ($auto) { $onlyknown = 1; }
    my $known;
    my @blockedusers = ();
    my $difference;
    my $blockedusers;
    my @autoblockedusers = ();
    my $autoblockedusers;
    my @usernameblockedusers = ();
    my $usernameblockedusers;
    my $difflist;
    my $add;
    my $userlist;
    my $new;
    my $counter;
    my @blocks = ();
    my $blocks;
    my $page = $editor->{mech}->get("http://en.wikipedia.org/w/index.php?title=Category:Requests_for_unblock&action=render")->decoded_content;
    @blockedusers = $page=~m/>User talk:(.*?)</sg;
    $page = $editor->{mech}->get("http://en.wikipedia.org/w/index.php?title=Category:Requests_for_unblock-auto&action=render")->decoded_content;
    @autoblockedusers = $page=~m/>User talk:(.*?)</sg;
    $page = $editor->{mech}->get("http://en.wikipedia.org/w/index.php?title=Category:Requests_for_username_changes_when_blocked&action=render")->decoded_content;
    @usernameblockedusers = $page=~m/>User talk:(.*?)</sg;
    print("\n");
    print("new command - $onlyknown - $auto - $settings{counter}\n");
    print( @blockedusers . "\n");
    my @blocklist = @{$settings{blocklist}};
    delete $settings{blocklist};
    print( @blocklist . "\n");
    print("end of listing\n");
    if (@blockedusers) {
        $userlist = "";
        $counter = 0;
        my $by;
        my $at;
        my $for;
        $new = 0;
        foreach $blockedusers(@blockedusers) {
            $counter++;
            @blocks = ();
            $userlist = "\x034[[User talk:$blockedusers]]\x03";
            $add = "";
            $page = $editor->{mech}->get("http://en.wikipedia.org/w/index.php?title=Special:Log&type=block&page=User:$blockedusers")->decoded_content;
            @blocks = $page =~ m/(<li>.*?blocked.*?<\/li>)/;
            $known = 0;
            if (@blocklist) {
                $known = 0;
                foreach $blocks(@blocklist) {
                    print(" Compare [$blocks] -> [$blockedusers]");
                    if ($blocks eq $blockedusers) {
                        $known = 1;
                        print(" - Known");
                    }
                    print("\n");
                }
                unless ($known) {
                    $userlist .= " \x034NEW\x03";
                    $new++;
                }
            } else {
                $userlist .= " \x034NEW\x03";
                $new++;
            }
            print (" -> $onlyknown - $known \n");
            if ($onlyknown == 1) {
                if ($known == 0) {
                    print("Unknown found\n");
                    if ((@blocks) && (@blocks[0] =~ m/User talk:$blockedusers/)) {
                        $blocks = @blocks[0];
                        if ($blocks =~ m/Special:Contributions\/(.*?)\"/) {
                            $by = $1;
                            if ($blocks =~ m/<li>(.*?) <a/) {
                                $at = $1;
                                $userlist .= " - Blocked at $at";
                            }   
                            if ($blocks =~ m/\<span class="comment"\>\((.*?)\)\<\/span\>/) {
                                my $reason = $1;
                                $reason =~ s/\<[^\>]*?\>//g;
                                $userlist .= " - Reason: '$reason'";
                            }   
                            $userlist .= ", by [[:en:user:$by]]";
                            if ($blocks =~ m/<span title=(.*?)>(.*?)<\/span>/) {
                                $for = $2;
                                $userlist .= ", for $for";
                            }
                        }
                    } else {
                        $userlist .= " - no blocking admin found";
                    }
                    if (@autoblockedusers) {
                        foreach $autoblockedusers(@autoblockedusers) {
                            if ($autoblockedusers eq $blockedusers) {
                                $userlist .= " - Autoblock removal request";
                            }
                        }                
                    }
                    if (@usernameblockedusers) {
                        foreach $usernameblockedusers(@usernameblockedusers) {
                            if ($usernameblockedusers eq $blockedusers) {
                                $userlist .= " - Username change request";
                            }
                        }                
                    }
                    $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "$new - $userlist" );
                }
            } else {
                if ((@blocks) && (@blocks[0] =~ m/User talk:$blockedusers/)) {
                    $blocks = @blocks[0];
                    if ($blocks =~ m/Special:Contributions\/(.*?)\"/) {
                        $by = $1;
                        if ($blocks =~ m/<li>(.*?) <a/) {
                            $at = $1;
                            $userlist .= " - Blocked at $at";
                        }   
                        if ($blocks =~ m/\<span class="comment"\>\((.*?)\)\<\/span\>/) {
                            my $reason = $1;
                            $reason =~ s/\<[^\>]*?\>//g;
                            $userlist .= " - Reason: '$reason'";
                        }   
                        $userlist .= ", by [[:en:user:$by]]";
                        if ($blocks =~ m/<span title=(.*?)>(.*?)<\/span>/) {
                            $for = $2;
                            $userlist .= ", for $for";
                        }
                    }
                } else {
                    $userlist .= " - no blocking admin found";
                }
                if (@autoblockedusers) {
                    foreach $autoblockedusers(@autoblockedusers) {
                        if ($autoblockedusers eq $blockedusers) {
                            $userlist .= " - Autoblock";
                        }
                    }                
                }
                if (@usernameblockedusers) {
                    foreach $usernameblockedusers(@usernameblockedusers) {
                        if ($autoblockedusers eq $blockedusers) {
                            $userlist .= " - Username change request";
                        }
                    }                
                }
                $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "$counter - $userlist" );
            }
            print ("$counter - $userlist\n");
        }
        $difference = $counter - $settings{oldcounter};
        if ($difference < 0 ) {
            $difflist = "";
            foreach $blocks(@blocklist) {
                $known = 0;
                foreach $blockedusers(@blockedusers) {
                    print ("$blocks -> $blockedusers");
                    if ($blocks eq $blockedusers) {
                        $known = 1;
                        print (" - Still there");
                    }
                    print("\n");
                }
                if ($known == 0) {
                    $difflist .= "[[User talk:$blocks]] ";
                }
            }
            $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "Handled: $difflist" );
        }
        if ($auto) {
            unless (@blocklist eq @blockedusers) {
                if ($counter == 1) {
                    $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "Autoreport: there is one user in [[Category:Requests for unblock]] ($new new)." );
                } else {
                    $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "Autoreport: there are $counter users in [[Category:Requests for unblock]] ($new new)." );
                }
            }
        } else {
            if ($counter == 1) {
                $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "There is one user in [[Category:Requests for unblock]] ($new new)." );
            } else {
                $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "There are $counter users in [[Category:Requests for unblock]] ($new new)." );
            }
        }
    } else {
        if (@blocklist) {
            $difflist = "";
            foreach $blocks(@blocklist) {
                $difflist .= "[[User talk:$blocks]] ";
            }
            $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "Handled: $difflist" );
        }
        if ($auto) {
            unless (@blocklist == @blockedusers) {
                $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "Autoreport: there are currently no users in [[Category:Requests for unblock]]" );
            }
        } else {
            $kernel->post( 'niven.freenode.net' => privmsg => '#wikipedia-en-unblock' => "There are currently no users in [[Category:Requests for unblock]]" );
        }
    }
    $settings{blocklist} = [@blockedusers];
    $settings{oldcounter} = $counter;
    undef $channel;
    undef $kernel;
    undef $nick;
    undef $onlyknown;
    undef $auto;
    undef $known;
    undef @blockedusers;
    undef $difference;
    undef $blockedusers;
    undef @autoblockedusers;
    undef $autoblockedusers;
    undef @usernameblockedusers;
    undef $usernameblockedusers;
    undef $difflist;
    undef $add;
    undef $userlist;
    undef $new;
    undef $counter;
    undef @blocks;
    undef $blocks;
    undef $page;
    undef @blocklist;
}