From d47edc100b3a6808bcc28bae3ee35dd5b56df7b4 Mon Sep 17 00:00:00 2001 From: Pragmatic Software Date: Thu, 1 Dec 2016 04:57:25 -0800 Subject: [PATCH] Improve robustness of message history tracking --- PBot/AntiFlood.pm | 5 +- PBot/IRCHandlers.pm | 3 +- PBot/MessageHistory_SQLite.pm | 114 ++++++++++++++++++++++++++++------ 3 files changed, 100 insertions(+), 22 deletions(-) diff --git a/PBot/AntiFlood.pm b/PBot/AntiFlood.pm index ecfa2c83..d0218180 100644 --- a/PBot/AntiFlood.pm +++ b/PBot/AntiFlood.pm @@ -381,6 +381,7 @@ sub check_flood { } foreach my $channel (@$channels) { + $channel = lc $channel; # do not do flood processing if channel is not in bot's channel list or bot is not set as chanop for the channel next if $channel =~ /^#/ and not $self->{pbot}->{chanops}->can_gain_ops($channel); @@ -453,7 +454,7 @@ sub check_flood { } # check for chat/join/private message flooding - if($max_messages > 0 and $self->{pbot}->{messagehistory}->{database}->get_max_messages($mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE} ? $ancestor : $account, $channel) >= $max_messages) { + if($max_messages > 0 and $self->{pbot}->{messagehistory}->{database}->get_max_messages($account, $channel, $mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE} ? $nick : undef) >= $max_messages) { my $msg; if($mode == $self->{pbot}->{messagehistory}->{MSG_CHAT}) { $msg = $self->{pbot}->{messagehistory}->{database}->recall_message_by_count($account, $channel, $max_messages - 1) @@ -463,7 +464,7 @@ sub check_flood { $msg = $joins->[0]; } elsif($mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}) { - my $nickchanges = $self->{pbot}->{messagehistory}->{database}->get_recent_messages($ancestor, $channel, $max_messages, $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}); + my $nickchanges = $self->{pbot}->{messagehistory}->{database}->get_recent_messages($ancestor, $channel, $max_messages, $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}, $nick); $msg = $nickchanges->[0]; } elsif($mode == $self->{pbot}->{messagehistory}->{MSG_DEPARTURE}) { diff --git a/PBot/IRCHandlers.pm b/PBot/IRCHandlers.pm index 30a6ccab..1a2e6d9c 100644 --- a/PBot/IRCHandlers.pm +++ b/PBot/IRCHandlers.pm @@ -383,11 +383,12 @@ sub on_nickchange { my $message_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($nick, $user, $host); $self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($message_account, $self->{pbot}->{antiflood}->{NEEDS_CHECKBAN}); - my $channels = $self->{pbot}->{nicklist}->get_channels($newnick); + my $channels = $self->{pbot}->{nicklist}->get_channels($nick); foreach my $channel (@$channels) { next if $channel !~ m/^#/; $self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, "NICKCHANGE $newnick", $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}); } + $self->{pbot}->{messagehistory}->{database}->update_hostmask_data("$nick!$user\@$host", { last_seen => scalar gettimeofday }); my $newnick_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($newnick, $user, $host, $nick); $self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($newnick_account, $self->{pbot}->{antiflood}->{NEEDS_CHECKBAN}); diff --git a/PBot/MessageHistory_SQLite.pm b/PBot/MessageHistory_SQLite.pm index 3d61b5b4..630d5cbd 100644 --- a/PBot/MessageHistory_SQLite.pm +++ b/PBot/MessageHistory_SQLite.pm @@ -299,9 +299,10 @@ sub add_message_account { } eval { - my $sth = $self->{dbh}->prepare('INSERT INTO Hostmasks VALUES (?, ?, 0, 0)'); + my $sth = $self->{dbh}->prepare('INSERT INTO Hostmasks VALUES (?, ?, ?, 0)'); $sth->bind_param(1, $mask); $sth->bind_param(2, $id); + $sth->bind_param(3, scalar gettimeofday); $sth->execute(); $self->{new_entries}++; @@ -780,26 +781,66 @@ sub add_message { } sub get_recent_messages { - my ($self, $id, $channel, $limit, $mode) = @_; + my ($self, $id, $channel, $limit, $mode, $nick) = @_; $limit = 25 if not defined $limit; + $channel = lc $channel; + my $mode_query = ''; $mode_query = "AND mode = $mode" if defined $mode; my $messages = eval { - my $sth = $self->{dbh}->prepare(<bind_param(1, $id); - $sth->bind_param(2, $channel); - $sth->bind_param(3, $limit); - $sth->bind_param(4, $id); - $sth->bind_param(5, $channel); - $sth->bind_param(6, $limit); + my $sql = "SELECT msg, mode, timestamp FROM Messages WHERE "; + + my %akas; + if (defined $mode and $mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}) { + %akas = $self->get_also_known_as($nick); + } else { + $akas{'this'} = { id => $id, type => $self->{alias_type}->{STRONG}, nickchange => 0 }; + } + + my $ids; + my %seen_id; + my $and = ''; + foreach my $aka (keys %akas) { + next if $akas{$aka}->{type} == $self->{alias_type}->{WEAK}; + next if $akas{$aka}->{nickchange} == 1; + next if exists $seen_id{$akas{$aka}->{id}}; + $seen_id{$akas{$aka}->{id}} = 1; + + $ids .= "${and}id = ?"; + $and = ' AND '; + } + + $sql .= "$ids AND channel = ? $mode_query ORDER BY timestamp ASC LIMIT ? OFFSET (SELECT COUNT(*) FROM Messages WHERE $ids AND channel = ? $mode_query) - ?"; + my $sth = $self->{dbh}->prepare($sql); + + my $param = 1; + %seen_id = (); + foreach my $aka (keys %akas) { + next if $akas{$aka}->{type} == $self->{alias_type}->{WEAK}; + next if $akas{$aka}->{nickchange} == 1; + next if exists $seen_id{$akas{$aka}->{id}}; + $seen_id{$akas{$aka}->{id}} = 1; + + $sth->bind_param($param++, $akas{$aka}->{id}); + } + + $sth->bind_param($param++, $channel); + $sth->bind_param($param++, $limit); + + %seen_id = (); + foreach my $aka (keys %akas) { + next if $akas{$aka}->{type} == $self->{alias_type}->{WEAK}; + next if $akas{$aka}->{nickchange} == 1; + next if exists $seen_id{$akas{$aka}->{id}}; + $seen_id{$akas{$aka}->{id}} = 1; + + $sth->bind_param($param++, $akas{$aka}->{id}); + } + + $sth->bind_param($param++, $channel); + $sth->bind_param($param, $limit); $sth->execute(); return $sth->fetchall_arrayref({}); }; @@ -985,12 +1026,47 @@ sub recall_message_by_text { } sub get_max_messages { - my ($self, $id, $channel) = @_; + my ($self, $id, $channel, $use_aliases) = @_; my $count = eval { - my $sth = $self->{dbh}->prepare('SELECT COUNT(*) FROM Messages WHERE id = ? AND channel = ?'); - $sth->bind_param(1, $id); - $sth->bind_param(2, $channel); + my $sql = "SELECT COUNT(*) FROM Messages WHERE channel = ? AND "; + + my %akas; + if (defined $use_aliases) { + %akas = $self->get_also_known_as($use_aliases); + } else { + $akas{'this'} = { id => $id, type => $self->{alias_type}->{STRONG}, nickchange => 0 }; + } + + my $ids; + my %seen_id; + my $and = ''; + foreach my $aka (keys %akas) { + next if $akas{$aka}->{type} == $self->{alias_type}->{WEAK}; + next if $akas{$aka}->{nickchange} == 1; + next if exists $seen_id{$akas{$aka}->{id}}; + $seen_id{$akas{$aka}->{id}} = 1; + + $ids .= "${and}id = ?"; + $and = ' AND '; + } + + $sql .= $ids; + + my $sth = $self->{dbh}->prepare($sql); + my $param = 1; + $sth->bind_param($param++, $channel); + + %seen_id = (); + foreach my $aka (keys %akas) { + next if $akas{$aka}->{type} == $self->{alias_type}->{WEAK}; + next if $akas{$aka}->{nickchange} == 1; + next if exists $seen_id{$akas{$aka}->{id}}; + $seen_id{$akas{$aka}->{id}} = 1; + + $sth->bind_param($param++, $akas{$aka}->{id}); + } + $sth->execute(); my $row = $sth->fetchrow_hashref(); $sth->finish();