User:XLinkBot/Code/XLinkBot.pl

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;
 * 1) First, let's declare our modules.

my $quicklist = ;

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

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;

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' );
 * 1) Declare all sorts of IRC-related goodness

my %page_locks; my %user_locks;
 * 1) Set up various data structures

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 media file (e.g. an image or a sound or video file) on an external server, then note that linking to such files may be subject to Wikipedia's copyright policy and therefore probably should not be linked to. Please consider using our 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 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 copyright (see Linking to copyrighted works), or they are not written by a recognised, reliable source.  Linking to sites that you are involved with is also strongly discouraged (see 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 advertising service. Linking to sites that you are involved with is also strongly discouraged (see 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/ (.*?)<\/div><\/td>/sg; @removedPre=$diffContent=~m/ (.*?)<\/div><\/td>/sg;

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

$addedTotal  =~ s/ //g; $addedTotal  =~ s/<\/span>//g; $removedTotal =~ s/ //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/\.+?$rule.+?\<\/ref\>/ |                  $pagecontents=~m/\.+?$rule.+?\<\/ref\>/ |                  $pagecontents=~m/\$rule.+?\<\/ref\>/ |                  $pagecontents=~m/\$rule.+?\<\/ref\>/ |                  $pagecontents=~m/\.+?$rule\<\/ref\>/ |                  $pagecontents=~m/\.+?$rule\<\/ref\>/ |                  $pagecontents=~m/\$rule\<\/ref\>/ |                  $pagecontents=~m/\$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 $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' && $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);
 * 1)               if( $safety_status eq 'Reversion' && $override ) {
 * 2)                   $safety_status = 'Pass';
 * 3)               }

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 = "" }   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="$pagename\n\n"; }   elsif ($warninglevel==3) { $talk_page="$pagename\n\n"; }   elsif ($warninglevel==4) { $talk_page="$pagename\n\n"; }   elsif ($warninglevel==5) { $talk_page="$pagename\n\n"; }   if (($warninglevel < 6) && ($warninglevel >0)) { $talk_page.="Your edit [$diffurl here] was reverted by an automated bot that attempts to remove unwanted links and 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 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 media file (e.g. an image or a sound or video file) on an external server, then note that linking to such files may be subject to Wikipedia's copyright policy and therefore probably should not be linked to. Please consider using our 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 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 copyright (see Linking to copyrighted works), or they are not written by a recognised, reliable source. Linking to sites that you are involved with is also strongly discouraged (see 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 advertising service. Linking to sites that you are involved with is also strongly discouraged (see 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 external link that does comply with our policies and guidelines, then please accept my creator's apologies and feel free to undo the bot's revert. Please read Wikipedia's external links guideline for more information, and consult my list of frequently-reverted sites. For more information about me, see my FAQ page. Thanks! --~"; }          $talk_page.="\n\n If this is a shared IP address, and you didn't make the edit, please ignore this notice. $warningleveltext"; } else { if (($warninglevel < 6) && ($warninglevel >0)) { $talk_page.="\n\nIf you were trying to insert an external link that does comply with our policies and guidelines, then please accept my creator's apologies and feel free to undo the bot's revert. Please read Wikipedia's external links guideline for more information, and consult my list of frequently-reverted sites. For more information about me, see 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=" - Keeps adding external links after final warning. ~"; } else { $message=" - 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 uw-spam1.\n" if $settings{'debug'}; } elsif ($warninglevel == 3) { $editsummary .= " (second warning)"; print "Warned $user with uw-spam2.\n" if $settings{'debug'}; } elsif ($warninglevel == 4) { $editsummary .= " (third warning)"; print "Warned $user with uw-spam3.\n" if $settings{'debug'}; } elsif ($warninglevel == 4) { $editsummary .= " (fourth warning)"; print "Warned $user with 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\>/) { $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'}; }       }    } }