User:XLinkBot/Code/XLinkBot.pl

#First, let's declare our modules.
use POE qw(Component::IRC Component::IRC::Plugin::BotAddressed);
use perlwikipedia;
use DBI;
use HTML::Entities;
use LWP::UserAgent;
use Date::Parse qw(str2time);
use XML::Simple;

my $quicklist = ();

#Now we'll set up the mechanism we'll use for editing/retrieving pages
my $username = "XLinkBot";
my $editor;
eval {
    $editor=Perlwikipedia->new($username,$username);
};

my $diffFetcher=LWP::UserAgent->new;
$diffFetcher->agent("LinkParser/2.0");

my %info;
my $number_of_jobs=0;
$info{timeon}=localtime();
$info{users}=0;
$info{average_time}=0;
my @revert_times;

open(PASS,'xlinkbot-mysql-password');
sysread(PASS, my $mysql_password, -s(PASS));
close(PASS);
$mysql_password=~s/\n//;

open(PASS,'xlinkbot-wiki-password');
sysread(PASS, my $wikipedia_password, -s(PASS));
close(PASS);
$wikipedia_password=~s/\n//;

open(PASS,'xlinkbot-nickserv-password');
sysread(PASS, my $nickserv_password, -s(PASS));
close(PASS);
$nickserv_password=~s/\n//;

eval {
    $editor->login($username,$wikipedia_password);
};

my $login_test_page;
eval {
    $login_test_page=$editor->_get("Main_Page");
};
if ($login_test_page->decoded_content=~m/var wgUserName = "$username"/) {
    print "Logged into Wikipedia.\n" if $settings{'debug'};
} else {
    die "Not logged into Wikipedia.\n";
}

my $mysql_handle=DBI->connect("dbi:mysql:squelchbot;localhost","squelchbot",$mysql_password);
$mysql_handle->{mysql_auto_reconnect}=1;

#Declare all sorts of IRC-related goodness
my $nickname   = 'XLinkBot';
my $ircname    = 'anti-spam bot (Beetstra, Versageek)';
my $ircserver  = 'kornbluth.freenode.net';
my $port       = 8001;
my @channels   = ( '#wikipedia-en-spam', '#beetstra-spam-bot-channel', '#cvn-sw-spam', '#wikipedia-spam-t' );

#Set up various data structures
my %page_locks;
my %user_locks;

my %settings;

$settings{'settingspage'}="User:XLinkBot/Settings";

$settings{'AntiVandalismBots'}          = "VoABot II|ClueBot";
$settings{'RevertList'}                 = "User:XLinkBot/RevertList";
$settings{'OverrideList'}               = "User:XLinkBot/OverrideList";
$settings{'RevertListSource'}           = "Wiki";
$settings{'reverting_on'}               = 1;
$settings{'revert_once'}                = 1;
$settings{'detect_refs'}                = 1;
$settings{'size_limit'}                 = 1500;
$settings{'autoconfirm_limit'}          = 604800;
$settings{'3RR_Reverttime'}             = 108000;
$settings{'3RR_Reverts'}                = 2;
$settings{'Forget'}                     = 14400;
$settings{'revertoneedit'}              = 0;
$settings{'NoRevertNameSpace'}          = "Talk:|User:|User talk:|Wikipedia:|Wikipedia talk:|Image:|Image talk:|MediaWiki:|MediaWiki talk:|Template:|Template talk:|Help:|Help talk:|Category:|Category talk:|Portal:|Portal talk:";
$settings{'PageLocks'}                  = "User:XLinkBot/PageLocks";
$settings{'UserLocks'}                  = "User:XLinkBot/UserLocks";
$settings{'DontRevertSameUserTwice'}    = 1;
$settings{'WithinReversionTime'}        = "indef";

$settings{'debug'} = 1;

$settings{'EL1'}="@";
$settings{'EL2'}="@";
$settings{'EL3'}="@";
$settings{'EL4'}="@";
$settings{'EL5'}="@";
$settings{'EL6'}="@";
$settings{'EL7'}="@";
$settings{'EL8'}="@";
$settings{'EL9'}="@";
$settings{'EL10'}="@";
$settings{'EL11'}="@";
$settings{'EL12'}="@";
$settings{'EL13'}="@";
$settings{'EL14'}="@";
$settings{'EL15'}="@";
$settings{'X1'}="jpg|gif|jpeg|pic";
$settings{'X1Warning'}="If the external link you inserted or changed was to a [[Wikipedia:Media|media]] file (e.g. an [[Wikipedia:Images|image]] or a [[Wikipedia:Media help|sound or video]] file) on an external server, then note that linking to such files may be subject to Wikipedia's [[WP:COPYRIGHT|copyright policy]] and therefore probably should not be linked to.  Please consider using our [[Wikipedia:Upload|upload]] facility to upload a suitable media file.";
$settings{'X2'}="petitiononline";
$settings{'X2Warning'}="If the external link you inserted or changed was to a [[petition]] site then please note that wikipedia is [[WP:SOAPBOX|not a soapbox]], and that such links generally should not be included.";
$settings{'X3'}="blog|forum";
$settings{'X3Warning'}="If the external link you inserted or changed was to a [[blog]], [[forum]], [[free web hosting service]], or similar site, then please check the information on the external site thorougly.  Note that such sites should probably not be linked to if they contain information that is in violation of the creators [[Wikipedia:COPYRIGHT|copyright]] (see [[Wikipedia:COPYRIGHT#Linking to copyrighted works|Linking to copyrighted works]]), or they are not written by a recognised, [[Wikipedia:Reliable sources|reliable source]].  Linking to sites that you are involved with is also strongly discouraged (see [[Wikipedia:Conflict of interest|conflict of interest]]).";
$settings{'X4'}="ehow";
$settings{'X4Warning'}="If the external link you inserted or changed was to a site that provides payment for people visiting the that page, then note that Wikipedia is not an [[Wikipedia:Spam|advertising service]].  Linking to sites that you are involved with is also strongly discouraged (see [[Wikipedia:Conflict of interest|conflict of interest]]).";

&readsettings;

my $irc=POE::Component::IRC->spawn;
$irc->plugin_add( 'BotAddressed', POE::Component::IRC::Plugin::BotAddressed->new(eat=>1) );

POE::Session->create(
    inline_states => {
        _start     => \&bot_start,
        irc_001    => \&on_connect,
        irc_public => \&on_public,
    irc_bot_addressed => \&irc_bot_addressed,
    irc_msg    => \&irc_msg,
    },
);

$poe_kernel->run();
exit 0;

sub bot_start {
    my $kernel  = $_[KERNEL];
    my $heap    = $_[HEAP];
    my $session = $_[SESSION];

    $irc->yield( register => "all" );

    $irc->yield( connect =>
          { Nick => $nickname,
            Username => $username,
            Ircname  => $ircname,
            Server   => $ircserver,
            Port     => $port,
          }
    );
}

sub on_connect {
    print "Connected to $ircserver.\n" if $settings{'debug'};
    $irc->yield(privmsg=>"NickServ","identify $nickserv_password");
    sleep 5;
    foreach $channel (@channels) {
        $irc->yield(join=>$channel);
    }
}

sub on_public {
    my ($who,$where,$message) = @_[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 $lcmessage = lc($message);
    if ( $channel eq '#beetstra-spam-bot-channel' ) { #Are we talking in the bot channel?
            if (check_mysql("authorized_bots",$nick)) { #Yep, it's a bot
                $number_of_jobs++;
                processalert($message); #Process the message.
                --$number_of_jobs;
                return;
            } #It's a bot
    } #It's a bot channel
    if ( $lcmessage =~ m/^aspf-en link bl add (.+)/ || $message =~ m/^enlinkwatcher2 link bl add (.+)/ || $message =~ m/^intlinkwatcher2 link bl add (.+)/ || $message =~ m/^delinkwatcher2 link bl add (.+)/ || $message =~ m/^bigwikilw2 link bl add (.+)/ | $lcmessage =~ m/^aspf-en: link bl add (.+)/ || $message =~ m/^enlinkwatcher2: link bl add (.+)/ || $message =~ m/^intlinkwatcher2: link bl add (.+)/ || $message =~ m/^delinkwatcher2: link bl add (.+)/ || $message =~ m/^bigwikilw2: link bl add (.+)/ || $message =~ m/^link! bl add (.+)/) { #adding to linkwatcher revertlist?
        my ($toadd,$reason) = split(/ /,$1,2);
        insert_mysql("quarantine",$toadd);#Initialize the key with a blank def.
        $irc->yield(privmsg=>$channel,"Item \"$toadd\" has been added to my quarantine list." ); #Report back
    } #adding to lw revertlist
    if ( $lcmessage =~ m/^aspf-en link bl del (.+)/ || $message =~ m/^enlinkwatcher2 link bl del (.+)/ || $message =~ m/^intlinkwatcher2 link bl del (.+)/ || $message =~ m/^delinkwatcher2 link bl del (.+)/ || $message =~ m/^bigwikilw2 link bl del (.+)/ | $lcmessage =~ m/^aspf-en: link bl del (.+)/ || $message =~ m/^enlinkwatcher2: link bl del (.+)/ || $message =~ m/^intlinkwatcher2: link bl del (.+)/ || $message =~ m/^delinkwatcher2: link bl del (.+)/ || $message =~ m/^bigwikilw2: link bl del (.+)/ || $message =~ m/^link! bl del (.+)/) { #deling to linkwatcher revertlist?
        my ($toadd,$reason) = split(/ /,$1,2);
        my $todel = $1;
        my $sql="DELETE FROM quarantine WHERE rule=" . $mysql_handle->quote($todel);
        query_mysql($sql);
        $irc->yield(privmsg=>$channel,"Item \"$todel\" has been removed from my quarantine list." );
    }#deleting from lw revertlist

    if ($message=~m/^!info/) {
        my $message_to_send="I've been active since $info{timeon}. I've reverted $info{users} times. ";
        if ($settings{'revert_once'}) {
            $message_to_send.="I'm currently in calm mode. ";
        }
        else {
            $message_to_send.="I'm currently in angry mode. ";
        }
        $message_to_send.="Autoconfirm limit is $settings{'autoconfirm_limit'} seconds.";
        my $average_time;
        foreach $time (@revert_times) {
            $average_time+=$time;
        }
        if (scalar @revert_times) {
            $info{average_time}=$average_time / (scalar @revert_times);
        }
        $message_to_send.=" Average reversion time is " . $info{average_time} . " seconds.";
        $irc->yield(privmsg=>$channel,$message_to_send);
    }       


}

sub irc_bot_addressed {
    my ($who,$where,$message) = @_[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

    if ( $channel eq '#wikipedia-en-spam' | $channel eq '#wikipedia-spam-t') { #Command channel?
        my $verified=0;
        $verified=check_mysql("trusted_users",$cloak);
        if ($message=~m/^lock page (.+?) (.+)/) {
            my $rule=$1;
            my $article=$2;
            if ($verified) {
                $page_locks{$rule}=$article;
                $irc->yield(privmsg=>$channel,"Rule $rule now locked to [[:en:$article]].");
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }
        }
        if ($message=~m/^lock user (.+?) (.+)/) {
            my $rule=$1;
            my $touser=$2;
            if ($verified) {
                $page_locks{$rule}=$touser;
                $irc->yield(privmsg=>$channel,"Rule $rule now locked to [[:en:User:$touser]].");
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }
        }
        if ($message=~m/^unlock (.+)/) {
            my $rule=$1;
            if ($verified) {
                delete $page_locks{$rule};
                $irc->yield(privmsg=>$channel,"Rule $rule unlocked.");
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }
        }
        if ($message=~m/^autoconfirm (\d+)/) {
            my $age=$1;
            if ($verified) {
                $age*=24 * 60 * 60;
                $settings{'autoconfirm_limit'}=$age;
                $irc->yield(privmsg=>$channel,"Autoconfirm limit set to $settings{'autoconfirm_limit'}.");
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }
        }
        if ($message=~m/^add watch (.+)/) {
            my $rule=$1;
            if ($verified) {
                insert_mysql("watched_rules",$rule);
                $irc->yield(privmsg=>$channel,"Now watching for $rule");
            }
            else {
                $irc->yield(privmsg=>$channel,"$nick is not in my list of trusted users.");
            }
        }
        if ($message=~m/^remove watch (.+)/) {
                        my $rule=$1;
                        if ($verified) {
                my $sql="DELETE FROM watched_rules WHERE rule=" . $mysql_handle->quote($rule);
                                query_mysql($sql);
                                $irc->yield(privmsg=>$channel,"Stopped watching for $rule");
                        }
                        else {
                                $irc->yield(privmsg=>$channel,"$nick is not in my list of trusted users");
                        }
                }
    
        if ($message=~m/^activate override (.+)/) {
            my $rule=$1;
            if ($verified) {
                set_override($rule,1);
                $irc->yield(privmsg=>$channel,"$rule override turned on.");
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }
        }
        if ($message=~m/^deactivate override (.+)/) {
            my $rule=$1;
            if ($verified) {
                set_override($rule,0);
                $irc->yield(privmsg=>$channel,"$rule override turned off.");
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }
        }
        if ($message=~m/^activate hard override (.+)/) {
            my $rule = $1;
            if ($verified) {
                my $temp = $mysql_handle->quote($rule);
                query_mysql("INSERT INTO hard_overrides (rule) VALUES ($temp)");
                $irc->yield(privmsg=>$channel,"$rule hard override turned on");
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }
        }
        if ($message=~m/^deactivate hard override (.+)/) {
            my $rule = $1;
            if ($verified) {
                my $temp = $mysql_handle->quote($rule);
                query_mysql("DELETE FROM hard_overrides WHERE rule=$temp");
                $irc->yield(privmsg=>$channel,"$rule hard override turned off");
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }
        }
        if ($message=~m/^status/) {
            my $message_to_send="I've been active since $info{timeon}. I've reverted $info{users} times. ";
            if ($settings{'revert_once'}) {
                $message_to_send.="I'm currently in calm mode. ";
            }
            else {
                $message_to_send.="I'm currently in angry mode. ";
            }
            $message_to_send.="Autoconfirm limit is $settings{'autoconfirm_limit'} seconds.";
            my $average_time;
            foreach $time (@revert_times) {
                $average_time+=$time;
            }
            if (scalar @revert_times) {
                $info{average_time}=$average_time / (scalar @revert_times);
            }
            $message_to_send.=" Average reversion time is " . $info{average_time} . " seconds.";
            $irc->yield(privmsg=>$channel,$message_to_send);
        }       
        if ($message=~m/^mode (.+)/) {
            my $mode=$1;
            if ($verified) {
                if ($mode=~/angry/) {
                    $settings{'revert_once'}=0;
                    $irc->yield(privmsg=>$channel,"Safety check disabled.");
                }
                elsif($mode=~/calm/) {
                    $settings{'revert_once'}=1;
                    $irc->yield(privmsg=>$channel,"Safety check enabled.");
                }
                else {
                    $irc->yield(privmsg=>$channel,"Mode $mode not recognized.");
                }
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }
        }
        
        if ($message=~m/^quit$/) {
            if (lc($cloak) eq "wikimedia/beetstra" || lc($cloak) eq "wikimedia/versageek") {
                $kernel->signal($kernel, 'POCOIRC_SHUTDOWN', "Mayday! Mayday! .. going down!");
            } else {
                $kernel->post( $sender => privmsg => $channel => "Only Dirk Beetstra and Versageek can tell me to quit." );
            }
        }

        if ($message=~m/^size (\d+)/) { #It's a size command
            my $newsize=$1;         
            if ($verified) { #Are they supposed to do this?
                $settings{'size_limit'}=$newsize; #Change the size
                $irc->yield(privmsg=>$channel,"Size limit changed to $settings{'size_limit'}."); #Report back
            } #They're supposed to do this
            else { #They're not supposed to do this
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users."); #Tell them they've been naughty
            } #They're not supposed to do this
        }       
        if ( $message =~ m/^references/ ) { #Checking refs?
            if ($verified) { #Authed?
                $settings{'detect_refs'}=1;
                $irc->yield(privmsg=>$channel,"Now detecting references.");
            }#Authed
            else { #Not authed?
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }#Not authed
        }#References command
        if ( $message =~ m/^noreferences/ ) { #Checking refs?
            if ($verified) { #Authed?
                $settings{'detect_refs'}=0;
                $irc->yield(privmsg=>$channel,"References detection disabled.");
            }#Authed
            else { #Not authed?
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }#Not authed
        }#NoReferences command      
        if ($message=~m/^add authorized (.+)/) { #Adding a bot
            my $botname=$1;
            if ($verified) {
                $exists=check_mysql("authorized_bots",$botname);
                unless ($exists) { #Make sure it doesn't exist
                    insert_mysql("authorized_bots",$botname);
                    $irc->yield(privmsg=>$channel,"New authorized bot \"$botname\" added.");
                    return;
                }
                if ($exists) {
                    $irc->yield(privmsg=>$channel,"Bot already exists in my database.");
                    return;
                }
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }
        }
        if ($message=~m/^remove authorized (.+)/) {
            my $botname=$1;
            if ($verified) {
                $exists=check_mysql("authorized_bots",$botname);
                unless ($exists) {
                    $irc->yield(privmsg=>$channel,"Bot does not exist in my database.");
                    return;
                }               
                if ($exists) {
                    my $sql="DELETE FROM authorized_bots WHERE bot_name=" . $mysql_handle->quote($botname);
                    query_mysql($sql);
                    $irc->yield(privmsg=>$channel,"Authorized bot \"$botname\" deleted.");
                    return;
                }
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }
        }
        if ($message=~m/^depth (.+)/) { #It's a depth command
            my $newdepth=$1;            
            if ($verified) { #Are they supposed to do this?
                $searchdepth=$newdepth; #Change the depth
                $irc->yield(privmsg=>$channel,"Search depth changed to $searchdepth."); #Report back
            } #They're supposed to do this
            else { #They're not supposed to do this
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users."); #Tell them they've been naughty
            } #They're not supposed to do this
        } #It's a depth command 

        if ( $message =~ m/^revert on/ ) { #Are we going to go into revert mode?
            if ($verified) { #Verified?
                $irc->yield(privmsg=>$channel,"Reverting enabled.");
                $settings{'reverting_on'}=1; #Activate reverting
            } #Verified?
            else { #Not verified.
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            } #Not verified     
        } #Revert command

        if ( $message =~ m/^norevert/ || $message =~ m/^revert off/ ) { #Turning off reversion?
            if ($verified) { #Authed?
                $settings{'reverting_on'}=0;
                $irc->yield(privmsg=>$channel,"Reverting disabled.");
            }#Authed
            else { #Not authed?
                $irc->yield(privmsg=>$channel,"User $nick not in my list of trusted users.");
            }#Not authed
        }#Norevert command          

        if ( $message =~ m/^blacklist search (.+)/ || $message =~ m/^revertlist search (.+)/ ) { #Search the revertlist
            my $search = $1;
            my $exists=check_mysql("blacklist",$search);
            unless($exists) { #Darn, didn't find it
                $irc->yield(privmsg=>$channel,"$search was not found in my revertlist." );
            } #Didn't find string
            if ($exists) { #Hoorah, we found it
                $irc->yield(privmsg=>$channel,"$search was found in my revertlist." );
            } #Found string
        } #Searching revertlist

        
        if ( $message =~ m/^approve (.+)/ ) {
            my ($toapprove,$reason) = split(/ /,$1,2);
            if ($verified) {
                my $exists=check_mysql("quarantine",$toapprove);
                unless ($exists) {
                    $irc->yield(privmsg=>$channel,"Item $toapprove does not exist in my quarantine list."
                    );
                }
                if ($exists) {
                    insert_mysql("blacklist",'DEFAULT',$toapprove,'NULL',$cloak,$reason);
                    my $sql="DELETE FROM quarantine WHERE rule=" . $mysql_handle->quote($toapprove);
                    query_mysql($sql);
                    $irc->yield(privmsg=>$channel,"Item $toapprove has been approved and added to my revertlist ('$reason')."
                    );
                    logit("$nick added revertlist rule $toapprove");
                }
            }
            elsif (!$verified) {
                $irc->yield(privmsg=>$channel,"User $nick is not in my list of trusted users." );
            }
        }
        if ( $message =~ m/^deny (.+)/ ) {
            my $todeny = $1;
            if ($verified) {
                my $exists=check_mysql("quarantine",$todeny);
                unless ($exists) {
                    $irc->yield(privmsg=>$channel,"Item $todeny does not exist in my quarantine list.");
                }
                if ($exists) {
                    my $sql="DELETE FROM quarantine WHERE rule=" . $mysql_handle->quote($todeny);   
                    query_mysql($sql);
                    $irc->yield(privmsg=>$channel,"Item $todeny has been denied and has been removed from my quarantine list.");
                }
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick is not in my list of trusted users." );
            }
        }

        if ( $message =~ m/^blacklist add (.+?) (.+)/ ) {
            my ($toapprove,$reason) = split(/ /,$1,2);
            if ($verified) {
                my $exists=check_mysql("quarantine",$toapprove);
                unless ($exists) {
                    $irc->yield(privmsg=>$channel,"Item $toapprove does not exist in my quarantine list."
                    );
                }
                if ($exists) {
                    if ($reason eq "") {
                        $irc->yield(privmsg=>$channel,"You have not provided a reason for rule '$toapprove'.");
                    } else {
                        insert_mysql("blacklist",'DEFAULT',$toapprove,'NULL',$cloak,$reason);
                        my $sql="DELETE FROM quarantine WHERE rule=" . $mysql_handle->quote($toapprove);
                        query_mysql($sql);
                        $irc->yield(privmsg=>$channel,"Item $toapprove has been approved and added to my revertlist ('$reason')."
                        );
                        logit("$nick added revertlist rule $toapprove");
                    }
                }
            }
            elsif (!$verified) {
                $irc->yield(privmsg=>$channel,"User $nick is not in my list of trusted users." );
            }
        }   

        if ( $message =~ m/^quicklist add (.+?) (.+)/ ) {
            my $editor = $1;
            my $toapprove = lc($2);
            if ($toapprove =~ m/(resolve \d+\.\d+\.\d+\.\d+)/) {
                $toapprove = $1;
            } else {
                $toapprove .= " ";
                ($toapprove,$garbage) = split(/\s/,$toapprove);
            }
            if ($verified) {
                push(@quicklist,(1=>$toapprove,2=>$editor,3=>time(),4=>$nick));
                $irc->yield(privmsg=>$channel,"Item $toapprove has been approved and added to my revertlist.  Rule is valid for 1 hour, please add it to [[User:XLinkBot/RevertList]] before it expires.");
                logit("$nick added quicklist rule $toapprove");
            }
            elsif (!$verified) {
                $irc->yield(privmsg=>$channel,"User $nick is not in my list of trusted users." );
            }
        }   


        if ( $message =~ m/^blacklist remove (.+)/ ) {
            my $toremove = $1;
            if ($verified) {
                my $exists=check_mysql("blacklist",$toremove);
                unless ($exists) {
                    $irc->yield(privmsg=>$channel,"Item $toremove does not exist in my revertlist."
                    );
                }
                if ($exists) {
                    my $sql="DELETE FROM blacklist WHERE rule=" . $mysql_handle->quote($toremove);
                    query_mysql($sql);
                    $irc->yield(privmsg=>$channel,"Item $toremove has been removed from my revertlist.");
                }
            }
            else {
                $irc->yield(privmsg=>$channel,"User $nick is not in my list of trusted users");
            }
        }   

        if ( $message =~ m/^add media (.+)/ ) {
            my $totrust = $1;
            if ($verified) {
                my $exists=check_mysql("media",$totrust);
                if ($exists) {
                    $irc->yield(privmsg=> $channel => "Media $totrust already in list.");
                }
                unless ($exists) {
                    insert_mysql("media",$totrust);
                    $irc->yield(privmsg=> $channel => "Media: '$totrust' added, users adding links matching a rule that includes '$totrust' will now recieve a customised warning.");
                }
            }
            else {
                $irc->yield(privmsg=> $channel => "User $nick not in my list of trusted users.");
            }       
        }
        if ( $message =~ m/^remove media (.+)/ ) {
            my $todelete = $1;
            if ($verified) {
                my $sql="DELETE FROM media WHERE rule=" . $mysql_handle->quote($todelete);
                query_mysql($sql);
                $irc->yield(privmsg=> $channel => "Media $todelete has been removed.");  
            }
            else {
                $irc->yield(privmsg=> $channel => "User $nick not in my list of trusted users.");
            }
        }   

        if ( $message =~ m/^add freehost (.+)/ ) {
            my $totrust = $1;
            if ($verified) {
                my $exists=check_mysql("freehost",$totrust);
                if ($exists) {
                    $irc->yield(privmsg=> $channel => "Free host $totrust already in list.");
                }
                unless ($exists) {
                    insert_mysql("freehost",$totrust);
                    $irc->yield(privmsg=> $channel => "Free host: '$totrust' added, users adding links matching a rule that includes '$totrust' will now recieve a customised warning.");
                }
            }
            else {
                $irc->yield(privmsg=> $channel => "User $nick not in my list of trusted users.");
            }       
        }
        if ( $message =~ m/^remove freehost (.+)/ ) {
            my $todelete = $1;
            if ($verified) {
                my $sql="DELETE FROM freehost WHERE rule=" . $mysql_handle->quote($todelete);
                query_mysql($sql);
                $irc->yield(privmsg=> $channel => "Free host $todelete has been removed.");  
            }
            else {
                $irc->yield(privmsg=> $channel => "User $nick not in my list of trusted users.");
            }
        }   

        if ( $message =~ m/^add petition (.+)/ ) {
            my $totrust = $1;
            if ($verified) {
                my $exists=check_mysql("petition",$totrust);
                if ($exists) {
                    $irc->yield(privmsg=> $channel => "Petition site $totrust already in list.");
                }
                unless ($exists) {
                    insert_mysql("petition",$totrust);
                    $irc->yield(privmsg=> $channel => "Petition site: '$totrust' added, users adding links matching a rule that includes '$totrust' will now recieve a customised warning.");
                }
            }
            else {
                $irc->yield(privmsg=> $channel => "User $nick not in my list of trusted users.");
            }       
        }
        if ( $message =~ m/^remove petition (.+)/ ) {
            my $todelete = $1;
            if ($verified) {
                my $sql="DELETE FROM petition WHERE rule=" . $mysql_handle->quote($todelete);
                query_mysql($sql);
                $irc->yield(privmsg=> $channel => "Petition site $todelete has been removed.");  
            }
            else {
                $irc->yield(privmsg=> $channel => "User $nick not in my list of trusted users.");
            }
        }   

        if ( $message =~ m/^add payperview (.+)/ ) {
            my $totrust = $1;
            if ($verified) {
                my $exists=check_mysql("payperview",$totrust);
                if ($exists) {
                    $irc->yield(privmsg=> $channel => "Pay per view site $totrust already in list.");
                }
                unless ($exists) {
                    insert_mysql("payperview",$totrust);
                    $irc->yield(privmsg=> $channel => "Pay per view: '$totrust' added, users adding links matching a rule that includes '$totrust' will now recieve a customised warning.");
                }            
            }
            else {
                $irc->yield(privmsg=> $channel => "User $nick not in my list of trusted users.");
            }       
        }
        if ( $message =~ m/^remove payperview (.+)/ ) {
            my $todelete = $1;
            if ($verified) {
                my $sql="DELETE FROM payperview WHERE rule=" . $mysql_handle->quote($todelete);
                query_mysql($sql);
                $irc->yield(privmsg=> $channel => "Pay per view site $todelete has been removed.");  
            }
            else {
                $irc->yield(privmsg=> $channel => "User $nick not in my list of trusted users.");
            }
        }   

        if ( $message =~ m/^source (.+)/ ) {
            my $revertfrom = $1;
            if (lc($cloak) eq "wikimedia/versageek" || lc($cloak) eq "wikimedia/beetstra") {
                if ($revertfrom =~ m/(SQL|Wiki)/) {
                    $settings{'RevertListSource'} = $revertfrom;
                    $irc->yield(privmsg=> $channel => "Revertlist source is now $revertfrom.");  
                } else {
                    $irc->yield(privmsg=> $channel => "'$revertfrom' is not a valid revertlist source.");  
                }
            }
            else {
                $irc->yield(privmsg=> $channel => "I only trust Versageek and Beetstra to issue this command.");
            }
        }   


        if ( $message =~ m/^add trusted (.+)/ ) {
            my $totrust = $1;
            if (lc($cloak) eq "wikimedia/versageek" || lc($cloak) eq "wikimedia/beetstra") {
                my $exists=check_mysql("trusted_users",$totrust);
                if ($exists) {
                    $irc->yield(privmsg=> $channel => "User cloak $totrust is already in my list of trusted users.");
                }
                unless ($exists) {
                insert_mysql("trusted_users",$totrust);
                $irc->yield(privmsg=> $channel => "User cloak $totrust has been added to my list of trusted users.");
                
            }
            else {
                $irc->yield(privmsg=> $channel => "I only trust Versageek and Beetstra to issue this command.");
            }       
        }
        if ( $message =~ m/^remove trusted (.+)/ ) {
            my $todelete = $1;
            if (lc($cloak) eq "wikimedia/versageek" || lc($cloak) eq "wikimedia/beetstra") {
                my $sql="DELETE FROM trusted_users WHERE user_cloak=" . $mysql_handle->quote($todelete);
                query_mysql($sql);
                $irc->yield(privmsg=> $channel => "User cloak $todelete has been removed from my list of trusted users.");  
            }
            else {
                $irc->yield(privmsg=> $channel => "I only trust Versageek and Beetstra to issue this command.");
            }
        }   
        if ($message=~m/add exemption (.+?) (.+)/) {
            my $rule=$1;
            my $exemption_rule=$2;
            if ($verified) {
                update_mysql("blacklist","rule=$rule","exempt=$exemption_rule");
                $irc->yield(privmsg=>$channel=>"Changed exemption for $rule to \"$exemption_rule\".");
            }
            else {
                $irc->yield(privmsg=>$channel=>"User $nick is not in my list of trusted users.");
            }
        }
        if ($message=~m/^remove exemption "(.+?)"/) {
            my $rule=$1;
            if ($verified) {
                update_mysql("blacklist","rule=$rule","exempt=");
                $irc->yield(privmsg=>$channel=>"Removed exemption for $rule.");
            }
            else {
                $irc->yield(privmsg=>$channel=>"User $nick is not in my list of trusted users.");
            }
        }
    }
}
}
sub parsealert {
    my $alert    = shift;
    my $pagename;
    my $user;
    my $diffurl;
    my $size;
    my @rules=();
        if ( $alert =~ m/diff=<(.+?)> user=<(.+?)> title=<(.+?)>/ ) {
            $diffurl =$1;
            $user    =$2;
            $pagename=$3;
            
        }
        if ($alert=~m/size=<(.+?)>/) {
             $size=$1;
        }   
        while ($alert=~m/rule=<(.+?)>/) {
            my $rule=$1;        
            push(@rules,$rule);
            $alert=~s/rule=<.+>//;
        }
        if ($size=~m/-(\d+)/) {
            $size=-$1;
        }
        elsif ($size=~m/(\d+)/) {
            $size=$1;
        }
        print "Received an alert for $user on $pagename ($diffurl, $size).\n" if $settings{'debug'};
        return $pagename, $user, $diffurl, $size, @rules;
}

sub authenticate {
    my $cloak = shift;
    my $exists=check_mysql("trusted_users",$cloak);
    if ($exists) {
        return 1;
    }
    else { return 0; }
}

sub processalert {

    my $message=shift;  
    my $start_time=time();
    my ( $pagename, $user, $diff, $size, @rules ) = parsealert($message);
    my $revisionid;
    if ($settings{'revertoneedit'}) {
        $revisionid = $diff =~ m/oldid=(\d+)/;  # previous edit
    } else {
        eval {
            $revisionid=$editor->get_last($pagename,$user);  # last edit not by this user
        };
        if ($@) {
            $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to retrieve the last revisionid not by [[user:$user]] on [[$pagename]] ($diff, $rules[0]).");
            logit("ERROR: Failed to retrieve the last revisionid not by [[user:$user]] on [[$pagename]] ($rule).");
            return;
        }
    }
    print ("Processing [$pagename] - [$user] - [$diff] - [$size].\n") if $settings{'debug'};
    my $pagecontents;
    my $thispage = $pagename;
    $thispage =~ s/\s/_/;
    eval {
        $pagecontents=$editor->get_text($thispage) . "    ";
    };
    if ($@) {
        $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to retrieve $pagename: ([[:en:user:$user]], $diff ; $rules[0] ).");
        logit("ERROR: Failed to retrieve $pagename: ([[:en:user:$user]], $diff ; $rules[0] ).");
        return;
    }

    &readsettings;
    my $override=0;
    my $watched=0;
    my $hard_override=0;
    my $resolve_blacklisted;
    my $link;
    my $rule;
    my $rules;
    my @links;
    my @links2;
    my $link2;
    my $links;
    my $DiffUrl= $diff . "&diffonly=1&action=render";
    my $diffContent=$diffFetcher->get($DiffUrl)->content;
    my @addedPre = ();
    my @removedPre = ();
    my @addedlinks = ();
    my @removedlinks = ();
    my $addedTotal = "";
    my $removedTotal = "";

    @addedPre=$diffContent=~m/<td class="diff-addedline"><div>(.*?)<\/div><\/td>/sg;
    @removedPre=$diffContent=~m/<td class="diff-deletedline"><div>(.*?)<\/div><\/td>/sg;    

    $addedTotal=join(' ', @addedPre) . "  ";
    $removedTotal=join(' ', @removedPre) . "  ";

    $addedTotal   =~ s/<span class="diffchange">//g;
    $addedTotal   =~ s/<\/span>//g;
    $removedTotal =~ s/<span class="diffchange">//g;
    $removedTotal =~ s/<\/span>//g;

    decode_entities( $addedTotal );
    decode_entities( $removedTotal );

    @addedlinks=$addedTotal=~m{(https?://[^\s\]\[\{\}\\\|^~`<>]+)}sgi;
    @removedlinks=$removedTotal=~m{(https?://[^\s\]\[\{\}\\\|\)\(^~`<>]+)}sgi;
    my @really_added_links = ();
    my @really_removed_links = ();
    my $links_added;
    my $links_removed;
    
    if (@addedlinks) {
        if (@removedlinks) {
            foreach $links_added(@addedlinks) {
                my $found = 0;
                foreach $links_removed(@removedlinks) {
                    if ($links_removed eq $links_added) {
                        $found = 1;
                    }
                }
                unless ($found) {
                    push(@really_added_links,$links_added);
                }
            }
        } else {
            @really_added_links = @addedlinks;
        }
    }
    $links = join ("   ",@really_added_links);
    $links .= "    ";
    print "[$links]\n" if $settings{'debug'};
    foreach $rule (@rules) {
        @links = $links =~ m/(https?[^\s]+$rule[^\s]+)\s/sgi;
        if (@links) {
            $link .= "rule: '$rule' (link(s): ". join(" ",@links) . ") ";
        } else {
            $link .= "rule: '$rule' ";
        }
    }
    my $test_page;
    eval {
        $test_page=$editor->{mech}->get("http://en.wikipedia.org/wiki/Main_Page")->decoded_content;
    };
    if ($test_page !~m/var wgUserName = "$username"/) {
        eval {
            $editor->login($username,$wikipedia_password);
        };
        eval {
            $test_page=$editor->{mech}->get("http://en.wikipedia.org/wiki/Main_Page")->decoded_content;
        };
        if ($test_page !~m/var wgUserName = "$username"/) {
            $settings{'reverting_on'}=0;     
            $irc->yield(privmsg=>"#wikipedia-spam-t"=>"I've become logged-out on Wikipedia, reverting disabled ( $diff ; $rules[0]).");
            logit("ERROR: I've become logged-out on Wikipedia, reverting disabled ($diff not reverted ) [$link].");
        } else {
            if ($settings{'reverting_on'} == 0) {
                logit("Login refreshed.");
                $settings{'reverting_on'}=1;
            }
        }
    }

    foreach $rule ( @rules ) {
        if( $rule =~ m/^resolve/ ) {
            $resolve_blacklisted = 1;
            $pagecontents .= $rule . " ";
        }
    }

    if ($settings{'RevertListSource'} eq "SQL") {
        foreach $rule (@rules) {
            $override=check_mysql("overrides",$rule);
            $hard_override=check_mysql("hard_overrides",$rule);
        }
    } elsif ($settings{'RevertListSource'} eq "Wiki") {
        foreach $rule (@rules) {
            my $rulespage;
            eval {
                $rulespage=$editor->get_text($settings{'OverrideList'});
            };
            my @blacklist = split(/\n/,$rulespage);
            $override = 0;
            foreach $blacklistrule (@blacklist) {
                $blacklistrule = lc($blacklistrule);
                $blacklistrule .= "#";
                ($blacklistrule,$garbage) = split(/#/,$blacklistrule);
                $blacklistrule .= " ";
                if ($blacklistrule =~ m/(resolve \d+\.\d+\.\d+\.\d+)/) {
                    $blacklistrule = $1;
                } else {
                    ($blacklistrule,$garbage) = split(/\s/,$blacklistrule);
                }
                if ($rule eq $blacklistrule) {
                    $override = 1;
                }
            }
            my $rulespage;
            eval {
                $rulespage=$editor->get_text($settings{'OverrideList'});
            };
            my @blacklist = split(/\n/,$rulespage);
            $hard_override = 0;
            foreach $blacklistrule (@blacklist) {
                $blacklistrule = lc($blacklistrule);
                $blacklistrule .= "#";
                ($blacklistrule,$garbage) = split(/#/,$blacklistrule);
                $blacklistrule .= " ";
                if ($blacklistrule =~ m/(resolve \d+\.\d+\.\d+\.\d+)/) {
                    $blacklistrule = $1;
                } else {
                    ($blacklistrule,$garbage) = split(/\s/,$blacklistrule);
                }
                if ($rule eq $blacklistrule) {
                    $hard_override = 1;
                }
            }
        }
    }

    if ($override) {
        $irc->yield(privmsg=>"#wikipedia-en-spam","User [[:en:user:$user]] added rule $rule (on override list) on $diff.");
        $irc->yield(privmsg=>"#wikipedia-spam-t","User [[:en:user:$user]] added rule $rule (on override list) on $diff.");
        logit("User $user added rule $rule (on override list) on $diff ($link).");
    }

    if ($hard_override) {
        $irc->yield(privmsg=>"#wikipedia-en-spam","User [[:en:user:$user]] added rule $rule (on hard override list) on $diff.");
        $irc->yield(privmsg=>"#wikipedia-spam-t","User [[:en:user:$user]] added rule $rule (on hard override list) on $diff.");
        logit("User $user added rule $rule (on hard override list) on $diff ($link).");
    }

    if ($pagename =~ m/$settings{'NoRevertNameSpace'}/ && $pagename != "User:Beetstra/Sandbox" ) {
        $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Page $pagename is outside of allowed namespace, edit $diff by [[:en:user:$user]] not reverted ($rules[0]).");
        $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: Page $pagename is outside of allowed namespace, edit $diff by [[:en:user:$user]] not reverted ($rules[0]).");  
        logit("ERROR: Page $pagename is outside of allowed namespace, $diff not reverted; $link.");
        return;
    }

    unless ($user=~m/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) {
        my $autoconfirm_user=&autoconfirm($user);
        print "$user is $autoconfirm_user seconds old, limit is $settings{'autoconfirm_limit'}.\n" if $settings{'debug'};
        if ($autoconfirm_user>$settings{'autoconfirm_limit'}) { 
            unless ($hard_override) {
                $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to revert to revision $revisionid on $pagename: User [[:en:user:$user]] does not satisfy autoconfirm ( $diff ; $rules[0] ).");
                logit("ERROR: Failed to revert to $revisionid on $pagename: User [[:en:user:$user]] doesn't meet autoconfirm $settings{'autoconfirm_limit'} ($diff, $link).");
                return;
            }
        }
    }

    if ($size>$settings{'size_limit'}) {
        unless ($override) {
            $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to revert [[:en:user:$user]] to $revisionid on $pagename: Edit larger than $settings{'size_limit'} ( $diff ; $rules[0]).");
            logit("ERROR: Failed to revert [[:en:user:$user]] to $revisionid on $pagename: Edit larger than $settings{'size_limit'} ($diff; $link).");
            return;
        }
    }

    foreach $rule (@rules) {
        if ($settings{'detect_refs'}==1) {
            if ( $pagecontents=~m/\{\{[^\}]*?$rule.*?\}\}/ | 
                 $pagecontents=~m/\{\{[^\}]*?$rule\}\}/) {
                unless($override) {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: [[:en:user:$user]] added \"$rule\" inside of a template, please manually check $diff .");
                    $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: [[:en:user:$user]] added \"$rule\" inside of a template, please manually check $diff .");
                    logit("ERROR: [[:en:user:$user]] added \"$rule\" inside of a reference, please manually check ($diff) [$link].");
                }
                return;
            }       
            if ( $pagecontents=~m/\<ref.*?\>.+?$rule.+?\<\/ref\>/ | 
                 $pagecontents=~m/\<ref\>.+?$rule.+?\<\/ref\>/ | 
                 $pagecontents=~m/\<ref.*?\>$rule.+?\<\/ref\>/ | 
                 $pagecontents=~m/\<ref\>$rule.+?\<\/ref\>/ | 
                 $pagecontents=~m/\<ref.*?\>.+?$rule\<\/ref\>/ | 
                 $pagecontents=~m/\<ref\>.+?$rule\<\/ref\>/ | 
                 $pagecontents=~m/\<ref.*?\>$rule\<\/ref\>/ | 
                 $pagecontents=~m/\<ref\>$rule\<\/ref\>/ ) {
                unless($override) {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: [[:en:user:$user]] added \"$rule\" inside of a reference, please manually check $diff .");
                    $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: [[:en:user:$user]] added \"$rule\" inside of a reference, please manually check $diff .");
                    logit("ERROR: [[:en:user:$user]] added \"$rule\" inside of a reference, please manually check ($diff) [$link].");
                }
                return;
            }       
            if ( $pagecontents=~m/\<\!--[^\<]*?$rule.+?--\>/ | 
                 $pagecontents=~m/\<\!--[^\<]*?$rule--\>/ | 
                 $pagecontents=~m/\<\!--$rule.+?--\>/ | 
                 $pagecontents=~m/\<\!--$rule--\>/) {
                $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: [[:en:user:$user]] added \"$rule\" inside of a remark, please manually check $diff .");
                logit("ERROR: [[:en:user:$user]] added \"$rule\" inside of a remark, please manually check ($diff) [$link].");
                return;
            }       
        }
    }

    if ($settings{'RevertListSource'} eq "SQL") {
        foreach $rule (@rules) {
            my $exists=check_mysql("blacklist",$rule);
            unless ($exists) {
                $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Rule \"$rule\" was not found in my blacklist, so I can't revert [[:en:user:$user]] on diff $diff .");
                logit("ERROR: Rule \"$rule\" was not found in my blacklist, so I can't revert [[:en:user:$user]] on diff \"$diff\" ($link).");
                return;
            }
        }
    } elsif ($settings{'RevertListSource'} eq "Wiki") {
        foreach $rule (@rules) {
            my $rulespage;
            eval {
                $rulespage=$editor->get_text($settings{'RevertList'});
            };
            my @blacklist = split(/\n/,$rulespage);
            $exists = 0;
            
            foreach my $item (@blacklist) {
                $item = lc($item);
                $item .= "#";
                ($item,$garbage) = split(/#/,$item);
                $item .= " ";
                if ($item =~ m/(resolve \d+\.\d+\.\d+\.\d+)/) {
                    $item = $1;
                } else {
                    ($item,$garbage) = split(/\s/,$item);
                }

                if ($rule eq $item) {
                    $exists = 1;
                }
            }
            foreach my $item (@quicklist) {
                if ($rule eq $item->{1}) {
                    if ($user eq $item->{2}) {
                        if (time() < ($item->{3} + 3600)) {
                            $exists = 1;
                        }
                    }
                }
            }
            unless ($exists) {
                $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Rule \"$rule\" was not found in my blacklist, so I can't revert [[:en:user:$user]] on diff $diff .");
                logit("ERROR: Rule \"$rule\" was not found in my blacklist, so I can't revert [[:en:user:$user]] on diff \"$diff\" ($link).");
                return;
            }
        }
    } else {
        $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Error in setting for RevertListSource ('$settings{'RevertListSource'}' is not valid), see [[:en:User:XLinkBot/Settings]]. , Can't revert [[:en:user:$user]] on diff $diff ");
        $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: Error in setting for RevertListSource ('$settings{'RevertListSource'}' is not valid), see [[:en:User:XLinkBot/Settings]]. , Can't revert [[:en:user:$user]] on diff $diff ");
        logit("ERROR: Error in setting for RevertListSource ($settings{'RevertListSource'} is not valid), see [[:en:User:XLinkBot/Settings]].");
        return;
    }
    foreach $rule (@rules) {
        unless ($pagecontents=~m/$rule/i) {
            $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Rule \"$rule\" was not found in the on-top version of $pagename ([[:en:user:$user]], $diff ).");
            logit("ERROR: Rule \"$rule\" was not found in the on-top version of $pagename ([[:en:user:$user]], $diff; $link).");
            return;
        }
    }
    
    if ($settings{'RevertListSource'} eq "SQL") {
        foreach $rule (@rules) {
            if (exists $page_locks{$rule}) {
                unless ($pagename eq $page_locks{$rule}) {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Rule $rule is locked to page $page_locks{$rule} ([[:en:user:$user]], $diff ).");   
                    logit("ERROR: Rule $rule is locked to page $page_locks{$rule} ([[:en:user:$user]], $diff ) [$link].");
                    return;
                }
            }
        }
    } elsif ($settings{'RevertListSource'} eq "Wiki") {
        foreach $rule (@rules) {
            my $rulespage;
            eval {
                $rulespage=$editor->get_text($settings{'PageLocks'});
            };
            my @blacklist = split(/\n/,$rulespage);
            foreach $blacklistrule (@blacklist) {
                $blacklistrule .= "#";
                ($blacklistrule,$garbage) = split(/#/,$blacklistrule);
                $blacklistrule .= " ";
                ($blacklistrule,$garbage) = split(/\s/,$blacklistrule);
                if( $blacklistrule =~ m/(.+?)=(.+)/) {
                    my ($pagelock,$rulelock) = split(/=/,$blacklistrule);
                    unless ($pagename eq $pagelock && $rulelock eq $rule) {
                        $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Rule $rule is locked to page $pagelock ([[:en:user:$user]], $diff ).");   
                        logit("ERROR: Rule $rule is locked to page $pagelock ($diff) [$link].");
                        return;
                    }
                }
            }
        }
    }

    if ($settings{'RevertListSource'} eq "SQL") {
        foreach $rule (@rules) {
            if (exists $user_locks{$rule}) {
                unless ($pagename eq $user_locks{$rule}) {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Rule $rule is locked to user $user_locks{$rule} ([[:en:user:$user]], $diff ).");   
                    logit("ERROR: Rule $rule is locked to user $user_locks{$rule} ($diff) [$link].");
                    return;
                }
            }
        }
    } elsif ($settings{'RevertListSource'} eq "Wiki") {
        foreach $rule (@rules) {
            my $rulespage;
            eval {
                $rulespage=$editor->get_text($settings{'UserLocks'});
            };
            my @blacklist = split(/\n/,$rulespage);
            foreach $blacklistrule (@blacklist) {
                $blacklistrule .= "#";
                ($blacklistrule,$garbage) = split(/#/,$blacklistrule);
                $blacklistrule .= " ";
                ($blacklistrule,$garbage) = split(/\s/,$blacklistrule);
                if( $blacklistrule =~ m/(.+?)=(.+)/) {
                    my ($pagelock,$rulelock) = split(/=/,$blacklistrule);
                    unless ($pagename eq $pagelock && $rulelock eq $rule) {
                        $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Rule $rule is locked to user $pagelock ([[:en:user:$user]], $diff ).");   
                        logit("ERROR: Rule $rule is locked to user $pagelock ($diff) [$link].");
                        return;
                    }
                }
            }
        }
    }
    
        if (($settings{'reverting_on'}==1) || ($pagename eq "User:Beetstra/Sandbox")) {
            my $rule=$rules[0];
            my $edit_summary="BOT--Reverting edits by [[:en:Special:Contributions/$user|$user]] to revision $revisionid ($rule)";
            print "Preparing to revert to $revisionid on \"$pagename\". hard_override=$hard_override,override=$override [$link].\n" if $settings{'debug'};
            unless ($revisionid==0) {
                my $safety_status=check_if_safe($pagename,$user,$override,$hard_override,$revisionid,$rule);
#               if( $safety_status eq 'Reversion' && $override ) {
#                   $safety_status = 'Pass';
#               }
                if( $safety_status eq 'Reversion' && $hard_override ) {
                    $safety_status = 'Pass';
                }
                if ($safety_status eq 'Pass') {                 
                    my $revert_response;
                    my $oldtext="";
                    eval {
                        $oldtext=$editor->get_text($pagename,$revisionid);
                    };
                    if ($oldtext eq "1") {
                        if ($settings{'revertoneedit'}) {
                            $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: Unknown error retrieving the previous page by [[user:$user]] on [[$pagename]] ($rule).");
                            $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Unknown error retrieving the previous page by [[user:$user]] on [[$pagename]] ($rule).");
                            print("ERROR: Unknown error retrieving the previous page by [[user:$user]] on [[$pagename]] ($rule).") if $settings{'debug'};
                            logit("ERROR: Unknown error retrieving the previous page by [[user:$user]] on [[$pagename]] ($rule).");
                        } else {
                            $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: Unknown error retrieving the last page not by [[user:$user]] on [[$pagename]] ($rule).");
                            $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Unknown error retrieving the last page not by [[user:$user]] on [[$pagename]] ($rule).");
                            print("ERROR: Unknown error retrieving the last page not by [[user:$user]] on [[$pagename]] ($rule).") if $settings{'debug'};
                            logit("ERROR: Unknown error retrieving the last page not by [[user:$user]] on [[$pagename]] ($rule).");
                        }
                        return;
                    } else {
                        eval {
                            $revert_response=$editor->edit($pagename,$oldtext,$edit_summary)->decoded_content;
                        };
                    }
                    my $end_time=time();
                    print ("\n\nRevert response: [$revert_response]\n\n") if $settings{'debug'};
                    if (($revert_response)=~m/Someone else has changed this page since you started editing it/) {
                        $irc->yield(privmsg=>"#wikipedia-spam-t"=>"Edit-conflicted while reverting [[:en:user:$user]] to $revisionid on $pagename ( $diff ; $rule).");
                        $irc->yield(privmsg=>"#wikipedia-en-spam"=>"Edit-conflicted while reverting [[:en:user:$user]] to $revisionid on $pagename ( $diff ; $rule).");
                        logit("ERROR: Edit-conflicted while reverting [[:en:user:$user]] to $revisionid on $pagename [$diff; $link].");
                        print "Edit-conflicted on $pagename [$link].\n" if $settings{'debug'};
                    } else {
                        sleep 5;
                        my @history;
                        eval {
                            @history=$editor->get_history($pagename,2);  
                        };
                        if ($@) {
                            $irc->yield(privmsg=>"#wikipedia-en-spam","Could not get page history after reverting [[:en:user:$user]] on $pagename ( $diff ; $rule) - please check if reversion was OK.");
                            $irc->yield(privmsg=>"#wikipedia-spam-t","Could not get page history after reverting [[:en:user:$user]] on $pagename ( $diff ; $rule) - please check if reversion was OK.");
                            print "An unknown error occured while reverting [[:en:user:$user]] on $pagename ( $diff ; $link).\n" if $settings{'debug'};
                            logit("An unknown error occured while reverting [[:en:user:$user]] on $pagename ( $diff ; $rule).");
                        } else {
                            my $reverter = @history[0]->{user};
                            if ($reverter eq $username) {
                                $irc->yield(privmsg=>"#wikipedia-en-spam","Reverted [[:en:user:$user]] to $revisionid on $pagename ( $diff ; $rule).");
                                print "Reverted to $revisionid on $pagename [$link].\n" if $settings{'debug'};
                                logit("Reverted to $revisionid on $pagename [$link].");
                                warn_user($user,$diff,$pagename,$link);
                                $info{users}++;
                                update_rules_stats(@rules);

                                my $revid = 0;
                                if ($diffurl =~ m/diff=(\d+)/) {
                                    $revid = $1;
                                }
                                eval {
                                    $sql = $editor->_get_api("action=query&prop=info&revids=$revid&format=xml")->decoded_content;
                                };
                                my $xml;
                                eval {
                                    $xml = XMLin( $sql );
                                };
                                my $wikitimestamp;
                                eval{
                                    $wikitimestamp = $xml->{query}->{pages}->{page}->{touched};
                                };
                                my $mnth;
                                my $timestamp;
                                if ($wikitimestamp =~ m/(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d:\d\d:\d\d)Z/) {
                                    $mnth = $months[$2-1];
                                    $start_time = str2time("$4, $3 $mnth $1");
                                } else {
                                    $edit_id = -1;
                                }

                                push (@revert_times,($end_time-$start_time));
                            } elsif ($reverter eq $user) {
                                $irc->yield(privmsg=>"#wikipedia-en-spam","An unknown error occured while reverting [[:en:user:$user]] on $pagename ( $diff ; $rule) - last editor on page appears to be $user.");
                                $irc->yield(privmsg=>"#wikipedia-spam-t","An unknown error occured while reverting [[:en:user:$user]] on $pagename ( $diff ; $rule) - last editor on page appears to be $user.");
                                print "An unknown error occured while reverting [[:en:user:$user]] on $pagename ( $diff ; $link) - last editor on page appears to be $user.\n" if $settings{'debug'};
                                logit("An unknown error occured while reverting [[:en:user:$user]] on $pagename ( $diff ; $rule) - last editor on page appears to be $user.");
                            } else {
                                $irc->yield(privmsg=>"#wikipedia-en-spam","User $reverter beat me to reverting [[:en:user:$user]] on $pagename ( $diff ; $rule).");
                                print "User $reverter beat me to reverting [[:en:user:$user]] on $pagename ( $diff ; $link).\n" if $settings{'debug'};
                                logit("User $reverter beat me to reverting [[:en:user:$user]] on $pagename ( $diff ; $rule).");
                            }
                        }
                    }                
                    return;
                }
                elsif ($safety_status eq 'Fail') {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to revert to $revisionid on $pagename: Someone has edited since [[:en:user:$user]] ( $diff ; $rule).");
                    logit("ERROR: Failed to revert to $revisionid on $pagename: Someone has edited since the spammer [$link].");
                    return;         
                }
                elsif ($safety_status eq 'Reversion') {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to revert [[:en:user:$user]] to $revisionid on $pagename: Detected reversion ( $diff ; $rule].");
                    logit("ERROR: Failed to revert [[:en:user:$user]] to $revisionid on $pagename: Detected reversion [$link].");
                }
                elsif ($safety_status eq 'Alreadydid') {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I've already reverted ( $diff ; $rule].");
                    $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I've already reverted ( $diff ; $rule].");
                    logit("ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I've already reverted [$link].");
                }
                elsif ($safety_status eq 'AlreadydidUser') {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I've already reverted this user ( $diff ; $rule].");
                    $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I've already reverted this user ( $diff ; $rule].");
                    logit("ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I've already reverted this user [$link].");
                }
                elsif ($safety_status eq 'AlreadydidTime') {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I've already reverted this user in the last $settings{'WithinReversiontime'} seconds ( $diff ; $rule].");
                    $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I've already reverted this user in the last $settings{'WithinReversiontime'} seconds ( $diff ; $rule].");
                    logit("ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I've already reverted this user in the last $settings{'WithinReversiontime'} seconds  [$link].");
                }
                elsif ($safety_status eq 'AntiVandalismBot') {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: Previous revert was by another AntiVandalismBot ( $diff ; $rule].");
                    $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: Previous revert was by another AntiVandalismBot ( $diff ; $rule].");
                    logit("ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: Previous revert was by another AntiVandalismBot [$link].");
                }
                elsif ($safety_status eq '3RR') {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I already reverted the page 3 times in the last 30 hours, please check ( $diff ; $rule].");
                    $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I already reverted the page 3 times in the last 30 hours, please check ( $diff ; $rule].");
                    logit("ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: I already reverted the page 3 times in the last 30 hours, please check [$link].");
                }
                elsif ($safety_status eq 'HeavyEdit') {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: Page is heavily edited in the last 30 hours, please check manualy ( $diff ; $rule].");
                    $irc->yield(privmsg=>"#wikipedia-spam-t","ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: Page is heavily edited in the last 30 hours, please check manualy ( $diff ; $rule].");
                    logit("ERROR: Failed to revert edits by [[:en:user:$user]] to $revisionid on $pagename: Page is heavily edited in the last 30 hours, please check [$link].");
                }
            }   
            if ($revisionid==0) {
                $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: An unknown error occurred in my revid retrieval function while reverting [[:en:user:$user]] ( $diff ; $rule].");
                logit("ERROR: An unknown error occurred in my revid retrieval function while reverting [[:en:user:$user]] [$link].");
                return;     
            }
        }
        elsif ($settings{'reverting_on'}==0) {
            my $rule=$rules[0];
            unless ($revisionid==0) {
                my $safety_status=check_if_safe($pagename,$user);
                if ($safety_status eq 'Pass') {                 
                    $irc->yield(privmsg=>"#wikipedia-en-spam","Fake-reverted [[:en:user:$user]] to $revisionid on $pagename ( $diff ; $rule].");
                    logit("Fake-reverted [[:en:user:$user]] to $revisionid on $pagename ($diff) [$link].");
                    return;
                }
                elsif ($safety_status eq 'Fail') {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to fake-revert [[:en:user:$user]] to $revisionid on $pagename: Someone has edited since the spammer ( $diff ; $rule].");
                    logit("ERROR: Failed to [[:en:user:$user]] fake-revert to $revisionid on $pagename: Someone has edited since the spammer ($diff) [$link].");
                    return;         
                }
                elsif ($safety_status eq 'Alreadydid') {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to fake-revert [[:en:user:$user]] to $revisionid on $pagename: I've already reverted ( $diff ; $rule].");
                    logit("ERROR: Failed to fake-revert [[:en:user:$user]] to $revisionid on $pagename: I've already reverted ($diff) [$link].");
                }
                elsif ($safety_status eq 'Reversion') {
                    $irc->yield(privmsg=>"#wikipedia-en-spam","ERROR: Failed to fake-revert [[:en:user:$user]] to $revisionid on $pagename: Detected reversion ( $diff ; $rule].");
                    logit("ERROR: Failed to fake-revert [[:en:user:$user]] to $revisionid on $pagename: Detected reversion ($diff) [$link].");
                }
            }
        }   
    }   

sub check_if_safe {
    my $pagename = shift;
    my $user     = shift;
    my $override = shift;
    my $hard_override = shift;
    my $revisionid = shift;
    my $rule = shift;

    my @history;
    eval {
        @history=$editor->get_history($pagename,50);  
    };
    print "Last user on $pagename is " . $history[0]->{user} . "\n" if $settings{'debug'};
    unless (($history[0]->{user}) eq $user) {       
        return 'Fail';
    }
    if (($history[0]->{comment})=~m/\b(rvv|rv|revert|reverting|VP2|rvt|reverted|reversion|robot|bot|rollback|roll back|rolling back|rolled back|undid|undo)\b/i) {
        unless($settings{'revert_once'}==0 || $override==1 || $hard_override==1) {
            return 'Reversion';
        }
    } 

    if (($history[1]->{user}) eq $username) {
        unless ($settings{'revert_once'}==0 || $override==1 || $hard_override==1) {
            return 'Alreadydid';
        } else {
            if (($history[2]->{user}) eq $user) {
                unless (($settings{'DontRevertSameUserTwice'}==0 && $settings{'WithinReversionTime'} eq "indef") || $override==1 || $hard_override==1) {
                    return 'AlreadydidUser';
                }
                my $reverttime=str2time("$history[2]->{timestamp_date} $history[2]->{timestamp_time}");
                if ((time() - $lasttime) > $settings{'WithinReversiontime'}) {
                    unless ($settings{'DontRevertSameUserTwice'}==0 || $override==1 || $hard_override==1) {
                        return 'AlreadydidTime';
                    }
                }
            }
        }
    } 
    if (($history[1]->{user}) =~m/($settings{'AntiVandalismBots'})/) {
        unless($settings{'revert_once'}==0 || $override==1 || $hard_override==1) {
            return 'AntiVandalismBot';
        }
    }
    my $count = 0;
    my $reverttime;
    my $timediff;
    foreach my $hist (@history) {
        if ($hist->{user} eq $username) {
            $reverttime=str2time("$hist->{timestamp_date} $hist->{timestamp_time}");
            $timediff = time() - $reverttime;
            print ("I already reverted today, U: $username, W: $hist->{user}, TS: $hist->{timestamp_date} $hist->{timestamp_time}, RT: $reverttime, TD: $timediff.\n") if $settings{'debug'};
            if ($timediff < $settings{'3RR_Reverttime'}) {
                $count = $count + 1;
            }
        }
    }
    $reverttime=str2time($hist->{timestamp});
    $timediff = time() - $reverttime;
    if ($count >$settings{'3RR_Reverts'} ) {
        unless($settings{'revert_once'}==0 || $hard_override==1) {
            return '3RR';
        }
    } elsif (($timediff < (60 * 60 * 30)) && ($count > 0)) {
        unless($settings{'revert_once'}==0 || $hard_override==1) {
            return 'HeavyEdit';
        }
    }
    return 'Pass';
}

sub warn_user {
    my $user=shift;
    my $diffurl=shift;
    my $pagename=shift;
    my $rule=shift;
    my $time=time();
    my $summary='';
    my $exists=check_mysql("users",$user);
    my $talk_page="";
    unless ($exists) {
        insert_mysql("users",$user,$time,"1");
    }
    print ("Warning user $user for $diffurl.\n") if $settings{'debug'};
    my $sql="SELECT * FROM users WHERE user_name=" . $mysql_handle->quote($user);
    my $sql_results=select_mysql($sql);
    
    my $lasttime=@{$sql_results}[0]->{user_time};
    my $warninglevel=@{$sql_results}[0]->{user_level};

    if (($time-$lasttime)>$settings{'Forget'}) {
        $warninglevel=1;
    }
    $outputwarninglevel=$warninglevel-1;
    if ($outputwarninglevel > 0) {
        $warningleveltext = "<!-- Template:uw-spam$outputwarninglevel -->"        
    }
    if ($warninglevel==1) {
        $talk_page="Welcome to Wikipedia. Although everyone is welcome to contribute constructively to the encyclopedia, your addition of one or more external links to the page [[:$pagename]] has been reverted. ";
    }
    if ($warninglevel==2) {
        $talk_page="{{subst:uw-spam1|$pagename| }}\n\n";
    }
    elsif ($warninglevel==3) {
        $talk_page="{{subst:uw-spam2|$pagename| }}\n\n";
    }
    elsif ($warninglevel==4) {
        $talk_page="{{subst:uw-spam3|$pagename| }}\n\n";
    }
    elsif ($warninglevel==5) {
        $talk_page="{{subst:uw-spam4|$pagename| }}\n\n";
    }
    if (($warninglevel < 6) && ($warninglevel >0)) {
        $talk_page.="Your edit [$diffurl here] was reverted by an automated bot that attempts to remove [[WP:NOT#REPOSITORY|unwanted links]] and [[WP:SPAM|spam]] from Wikipedia.  The external link you added or changed is on my list of links to remove and probably shouldn't be included in Wikipedia.  The external links I reverted were matching the following [[regex|regex rule(s)]]: $rule.";
    }   
    my $found = 0;
    my $query_handle=$mysql_handle->prepare("SELECT rule FROM media");
    $query_handle->execute;
    while (my $data=$query_handle->fetchrow_array) {
        if($rule=~m/$data/) {
            $found = 1;
        }
    }
    if (($found) && (($warninglevel < 6) && ($warninglevel >0))) {
        $talk_page.=" If the external link you inserted or changed was to a [[Wikipedia:Media|media]] file (e.g. an [[Wikipedia:Images|image]] or a [[Wikipedia:Media help|sound or video]] file) on an external server, then note that linking to such files may be subject to Wikipedia's [[WP:COPYRIGHT|copyright policy]] and therefore probably should not be linked to.  Please consider using our [[Wikipedia:Upload|upload]] facility to upload a suitable media file.";
    }   
    my $found = 0;
    my $query_handle=$mysql_handle->prepare("SELECT rule FROM petition");
    $query_handle->execute;
    while (my $data=$query_handle->fetchrow_array) {
        if($rule=~m/$data/) {
            $found = 1;
        }
    }
    if (($found) && (($warninglevel < 6) && ($warninglevel >0))) {
        $talk_page.=" If the external link you inserted or changed was to a [[petition]] site then please note that wikipedia is [[WP:SOAPBOX|not a soapbox]], and that such links generally should not be included.";
    }   
    my $found = 0;
    my $query_handle=$mysql_handle->prepare("SELECT rule FROM freehost");
    $query_handle->execute;
    while (my $data=$query_handle->fetchrow_array) {
        if($rule=~m/$data/) {
            $found = 1;
        }
    }
    if (($found) && (($warninglevel < 6) && ($warninglevel >0))) {
        $talk_page.=" If the external link you inserted or changed was to a [[blog]], [[forum]], [[free web hosting service]], or similar site, then please check the information on the external site thorougly. Note that such sites should probably not be linked to if they contain information that is in violation of the creators [[Wikipedia:COPYRIGHT|copyright]] (see [[Wikipedia:COPYRIGHT#Linking to copyrighted works|Linking to copyrighted works]]), or they are not written by a recognised, [[Wikipedia:Reliable sources|reliable source]]. Linking to sites that you are involved with is also strongly discouraged (see [[Wikipedia:Conflict of interest|conflict of interest]]).";
    }   
    my $found = 0;
    my $query_handle=$mysql_handle->prepare("SELECT rule FROM payperview");
    $query_handle->execute;
    while (my $data=$query_handle->fetchrow_array) {
        if($rule=~m/$data/) {
            $found = 1;
        }
    }
    if (($found) && (($warninglevel < 6) && ($warninglevel >0))) {
        $talk_page.=" If the external link you inserted or changed was to a site that provides payment for people visiting the that page, then note that Wikipedia is not an [[Wikipedia:Spam|advertising service]]. Linking to sites that you are involved with is also strongly discouraged (see [[Wikipedia:Conflict of interest|conflict of interest]]).";
    }   
    if (($user =~ m/\d+\.\d+\.\d+\.\d+/) && (($warninglevel < 5) && ($warninglevel >0))) {
        if (($warninglevel < 6) && ($warninglevel >0)) {
            $talk_page.="\n\nIf you were trying to insert an [[Wikipedia:External links|external link]] that does comply with our [[Wikipedia:List of policies|policies]] and [[Wikipedia:List of guidelines|guidelines]], then please accept my creator's apologies and feel free to undo the bot's revert.  Please read Wikipedia's [[WP:EL|external links guideline]] for more information, and consult my [[User:$username/Reversion reasons|list of frequently-reverted sites]]. For more information about me, see [[User:$username/FAQ|my FAQ page]]. Thanks! --~~~~";
        }   
        $talk_page.="\n\n<small>If this is a shared [[IP address]], and you didn't make the edit, please ignore this notice.</small>$warningleveltext";
    } else {
        if (($warninglevel < 6) && ($warninglevel >0)) {
            $talk_page.="\n\nIf you were trying to insert an [[Wikipedia:External links|external link]] that does comply with our [[Wikipedia:List of policies|policies]] and [[Wikipedia:List of guidelines|guidelines]], then please accept my creator's apologies and feel free to undo the bot's revert.  Please read Wikipedia's [[WP:EL|external links guideline]] for more information, and consult my [[User:$username/Reversion reasons|list of frequently-reverted sites]]. For more information about me, see [[User:$username/FAQ|my FAQ page]].$warningleveltext Thanks! ~~~~";
        }   
    }
    if ($warninglevel==6) {
        my $message;
        $irc->yield(privmsg=>"#wikipedia-en-spam"=>"!admin ALERT: [[:en:User:$user]]/[[:en:Special:Contributions/$user]] has spammed past their final warning (level $warninglevel).");
        $irc->yield(privmsg=>"#wikipedia-spam-t"=>"!admin ALERT: [[:en:User:$user]]/[[:en:Special:Contributions/$user]] has spammed past their final warning (level $warninglevel).");
        if ($user =~ m/\d+\.\d+\.\d+\.\d+/) {
            $message="{{IPvandal|$user}} - Keeps adding external links after final warning. ~~~~";
        } else { 
            $message="{{vandal|$user}} - Keeps adding external links after final warning. ~~~~";
        }

        my $text;
        eval {
            $text=$editor->get_text("Wikipedia:Administrator_intervention_against_vandalism/TB2");
        };
        $text .="\n$message";
        eval {
            $editor->edit("Wikipedia:Administrator_intervention_against_vandalism/TB2",$text,"Reporting editor who persistently adds external links");
        };
        print "$user posted to AIV.\n" if $settings{'debug'};
        $irc->yield(ctcp=>"#wikipedia-spam-t"=>"ACTION dances.");
    }

    if ($warninglevel<6) {
        print ("Putting warning on talkpage user $user for $diffurl.\n") if $settings{'debug'};
        my $user_talkpage;
        eval {
            $user_talkpage=$editor->get_text("User_talk:$user");
        };
        my ($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear, $daylightSavings) = gmtime(time());
        my @months = qw(January February March April May June July August September October November December);
        $year = $yearOffset + 1900;
        my $date = "$months[$month] $year";
        unless (($user_talkpage =~ m/==$date/) || ($user_talkpage =~ m/== $date/)) {
            $user_talkpage .= "\n== $date ==";
        }
        $user_talkpage.="\n$talk_page";
        my $editsummary = "BOT - Notifying $user of reverted link additions (matching '$rule') to [[$pagename]]";
        if ($warninglevel == 1) {
            $editsummary .= " (good faith remark)";
            print "Notified $user with good faith remark.\n" if $settings{'debug'};
        } elsif ($warninglevel == 2) {
            $editsummary .= " (first warning)";
            print "Warned $user with {{tl|uw-spam1}}.\n" if $settings{'debug'};
        } elsif ($warninglevel == 3) {
            $editsummary .= " (second warning)";
            print "Warned $user with {{tl|uw-spam2}}.\n" if $settings{'debug'};
        } elsif ($warninglevel == 4) {
            $editsummary .= " (third warning)";
            print "Warned $user with {{tl|uw-spam3}}.\n" if $settings{'debug'};
            } elsif ($warninglevel == 4) {
            $editsummary .= " (fourth warning)";
            print "Warned $user with {{tl|uw-spam4}}.\n" if $settings{'debug'};
        }
        eval {
            $editor->edit("User_talk:$user",$user_talkpage,$editsummary);
        };
    } 
    if ($warninglevel == 1) {
        $irc->yield(privmsg=>"#wikipedia-en-spam"=>"Warned user [[:en:User:$user]] (good faith notification of reversion).");
    } elsif ($warninglevel == 6) {
    } else {
        $irc->yield(privmsg=>"#wikipedia-en-spam"=>"Warned user [[:en:User:$user]] (warninglevel $outputwarninglevel).");
    }

    $warninglevel++;
    update_mysql("users","user_name=$user","user_time=$time","user_level=$warninglevel");
}

sub select_mysql {
    my $query=shift;

    my $query_handle=$mysql_handle->prepare($query);
    $query_handle->execute;

    my $results=$query_handle->fetchall_arrayref({});

    $query_handle->finish;
    return $results;
}

sub insert_mysql {
    my $table=shift;
    my @values = @_;

    my $query="INSERT INTO $table VALUES (";

    foreach $value (@values) {
        $value=$mysql_handle->quote($value);
        $query.="$value,";
    }

    $query=~s/,$//;
    $query.=")";

    my $query_handle=$mysql_handle->prepare($query);
    $query_handle->execute;
    $query_handle->finish;
}

sub query_mysql {
    my $query=shift;
    my $query_handle=$mysql_handle->prepare($query);
    $query_handle->execute;
    $query_handle->finish;
}

sub update_mysql {
    my $table=shift;
    my $where=shift;
    my @updates=@_;
    my $updates;
    foreach $entry (@updates) {
        my ($field,$new_value)=split(/=/,$entry);
        $new_value=$mysql_handle->quote($new_value);
        $updates.="$field=$new_value,";
    }
    $updates=~s/,$//;
    my ($where_field,$where_value)=split(/=/,$where);
    $where_value=$mysql_handle->quote($where_value);
    my $query="UPDATE $table SET $updates WHERE $where_field=$where_value";
    my $query_handle=$mysql_handle->prepare($query);
    $query_handle->execute;
    $query_handle->finish;
}

sub check_mysql {
    my $table=shift;
    my $value=shift;
    my $query="SELECT * FROM $table WHERE ";
    if ($table eq 'authorized_bots') {
        $query.="bot_name=";
    }
    elsif ($table eq 'trusted_users') {
        $query.="user_cloak=";
    }
    elsif ($table eq 'blacklist' || $table eq 'quarantine' || $table eq 'overrides' || $table eq 'rules_stats' | $table eq 'watched_rules' | $table eq 'autoconfirm' || $table eq 'hard_overrides') {
        $query.="rule=";
    }
    elsif ($table eq 'users') {
        $query.="user_name=";
    }
    $value=$mysql_handle->quote($value);
    $query.="$value";
    my $query_handle=$mysql_handle->prepare($query);
    $query_handle->execute;
    my $result=undef;
    if ($query_handle->rows > 0) {  
        $result=1;
    }
    return $result;
}

sub set_override {
    my $rule=shift;
    my $option=shift;
    $rule=$mysql_handle->quote($rule);
    my $query;
    if ($option==1) { $query="INSERT INTO overrides VALUES($rule)"; }
    else {$query="DELETE FROM overrides WHERE rule=$rule";}
    my $query_handle=$mysql_handle->prepare($query);
    $query_handle->execute;
    $query_handle->finish;
}

sub update_rules_stats {
    my @rules=shift;
    foreach $rule (@rules) {
        my $query;
        my $exists=check_mysql("rules_stats",$rule);
        $rule=$mysql_handle->quote($rule);
        if (!$exists) {
            $query="INSERT INTO rules_stats VALUES($rule,1)";
        }
        elsif ($exists) {
            $query="UPDATE rules_stats SET count=count + 1 WHERE rule=$rule";
        }
        my $query_handle=$mysql_handle->prepare($query);
        $query_handle->execute;
        $query_handle->finish;
    }
}

sub logit {
    my $message = shift;
    eval {
        $message=$mysql_handle->quote($message);
    };
    my $query="INSERT INTO log VALUES(DEFAULT,$message)";
    eval {
        my $query_handle=$mysql_handle->prepare($query);
    };
    eval {
        $query_handle->execute;
    };
    eval {
        $query_handle->finish;
    };
}

sub extract_mysql {
    my $table=shift;
    my $criteria=shift;
    my @results;
    my $query_handle=$mysql_handle->prepare("SELECT * FROM $table WHERE $criteria");
    $query_handle->execute;
    my $data=$query_handle->fetchall_arrayref({});
    foreach my $row (@$data) {
        push (@results,$row);
    }
    $query_handle->finish;
    return @results;
}

sub irc_msg {
    my ($kernel,$sender,$nick,$message)=@_[KERNEL,SENDER,ARG0,ARG2];
    my $cloak=(split /@/, $nick)[1];
    if ($cloak eq 'wikimedia/shadow42') {
    print "Received private message from user cloak $cloak.\n" if $settings{'debug'};
        if ($message=~m/(.+?) (.+)/) {
            my $channel=$1;
            my $to_send=$2;
            $irc->yield(privmsg=>$channel=>$to_send);
            print "Sending text \"$to_send\" to channel $channel\n" if $settings{'debug'};
        }
    }
}

sub autoconfirm {
    my $user=shift;
    my $user_log;
    eval {
        my $thisuser = $user;
        $thisuser =~ s/\s/_/;
        $user_log=$editor->{mech}->get("http://en.wikipedia.org/w/api.php?action=query&list=allusers&aufrom=$thisuser&aulimit=1&auprop=registration&format=xml")->decoded_content;
    };
    
    
    my $age="00:00, 1 January 1970";
    print ("XML: $user_log\n") if $settings{'debug'};
    if ($user_log=~m/\<allusers\>\<u name=\"$user\" registration=\"(.*?)\" \/\>\<\/allusers\>/) {
        $age=$1;
    }
    my $current_time=time();
    my $myage=str2time($age);
    print ("XML: $age - $myage - $current_time\n") if $settings{'debug'};
    $myage=$current_time-$myage;
    return $myage;
}

sub readsettings{
    my $settingspage;
    eval {
        $settingspage=$editor->get_text($settings{'settingspage'});
    };
    my @settinglist = split(/\n/,$settingspage);
    foreach my $setting (@settinglist) {
        unless ($setting =~ m/#/) {
            if( $setting =~ m/(.+?)=(.+)/) {
                $settings{$1}=$2;
                print ("Setting $1 to $2\n") if $settings{'debug'};
            }
        }
    }
}