diff --git a/PBot/MessageHistory.pm b/PBot/MessageHistory.pm index dc52105a..b9e8235b 100644 --- a/PBot/MessageHistory.pm +++ b/PBot/MessageHistory.pm @@ -34,7 +34,6 @@ sub initialize { $self->{MSG_NICKCHANGE} = 3; # CHANGED NICK $self->{pbot}->{registry}->add_default('text', 'messagehistory', 'max_recall_time', $conf{max_recall_time} // 0); - $self->{pbot}->{registry}->add_default('text', 'messagehistory', 'max_messages', 32); $self->{pbot}->{commands}->register(sub { $self->cmd_recall_message(@_) }, "recall", 0); $self->{pbot}->{commands}->register(sub { $self->cmd_rebuild_aliases(@_) }, "rebuildaliases", 1); @@ -220,11 +219,6 @@ sub cmd_list_also_known_as { sub cmd_recall_message { my ($self, $context) = @_; - if (not defined $context->{from}) { - $self->{pbot}->{logger}->log("Command missing ~from parameter!\n"); - return ""; - } - my $usage = 'Usage: recall [nick [history [channel]]] [-c ] [-t ] [-b ] [-a ] [-x ] [-n ] [-r raw mode] [+ ...]'; my $arguments = $context->{arguments}; @@ -338,7 +332,9 @@ sub cmd_recall_message { } # skip recall command if recalling self without arguments - $recall_history = $context->{nick} eq $recall_nick ? 2 : 1 if defined $recall_nick and not defined $recall_history; + if (defined $recall_nick and not defined $recall_history) { + $recall_history = $context->{nick} eq $recall_nick ? 2 : 1; + } # set history to most recent message if not specified $recall_history = '1' if not defined $recall_history; @@ -352,26 +348,39 @@ sub cmd_recall_message { $recall_channel = $context->{from}; } - if (not defined $recall_nick and defined $recall_context) { $recall_nick = $recall_context; } + # set nick argument to -x argument if no nick was provided but -x was + if (not defined $recall_nick and defined $recall_context) { + $recall_nick = $recall_context; + } + # message account and stored nickname with proper typographical casing my ($account, $found_nick); + # get message account and found nick if a nick was provided if (defined $recall_nick) { + # account and hostmask ($account, $found_nick) = $self->{database}->find_message_account_by_nick($recall_nick); - if (not defined $account) { return "I don't know anybody named $recall_nick."; } + if (not defined $account) { + return "I don't know anybody named $recall_nick."; + } + # keep only nick portion of hostmask $found_nick =~ s/!.*$//; } + # matching message found in database, if any my $message; if ($random) { - $message = $self->{database}->get_random_message($account, $recall_channel); + # get a random message + $message = $self->{database}->get_random_message($account, $recall_channel, $recall_nick); } elsif ($recall_history =~ /^\d+$/ and not defined $recall_text) { # integral history + + # if a nick was given, ensure requested history is within range of nick's history count if (defined $account) { - my $max_messages = $self->{database}->get_max_messages($account, $recall_channel); + my $max_messages = $self->{database}->get_max_messages($account, $recall_channel, $recall_nick); if ($recall_history < 1 || $recall_history > $max_messages) { if ($max_messages == 0) { return "No messages for $recall_nick in $recall_channel yet."; @@ -382,57 +391,81 @@ sub cmd_recall_message { } $recall_history--; - $message = $self->{database}->recall_message_by_count($account, $recall_channel, $recall_history, '(?:recall|mock|ftfy|fix|clapper)'); - - if (not defined $message) { - return "No message found at index $recall_history in channel $recall_channel."; - } - } else { - # regex history - $message = $self->{database}->recall_message_by_text($account, $recall_channel, $recall_history, '(?:recall|mock|ftfy|fix|clapper)'); + $message = $self->{database}->recall_message_by_count($account, $recall_channel, $recall_history, '(?:recall|mock|ftfy|fix|clapper)', $recall_nick); if (not defined $message) { if (defined $account) { - return "No message for nick $found_nick in channel $recall_channel containing \"$recall_history\""; + return "No message found at index $recall_history for $found_nick in $recall_channel."; } else { - return "No message in channel $recall_channel containing \"$recall_history\"."; + return "No message found at index $recall_history in $recall_channel."; + } + } + } else { + # regex history + $message = $self->{database}->recall_message_by_text($account, $recall_channel, $recall_history, '(?:recall|mock|ftfy|fix|clapper)', $recall_nick); + + if (not defined $message) { + if (defined $account) { + return "No message for $found_nick in $recall_channel containing \"$recall_history\""; + } else { + return "No message in $recall_channel containing \"$recall_history\"."; } } } - my $context_account; + my ($context_account, $context_nick); if (defined $recall_context) { - ($context_account) = $self->{database}->find_message_account_by_nick($recall_context); + ($context_account, $context_nick) = $self->{database}->find_message_account_by_nick($recall_context); - if (not defined $context_account) { return "I don't know anybody named $recall_context."; } + if (not defined $context_account) { + return "I don't know anybody named $recall_context."; + } + + # keep only nick portion of hostmask + $context_nick =~ s/!.*$//; } - my $messages = $self->{database}->get_message_context($message, $recall_before, $recall_after, $recall_count, $recall_history, $context_account); + my $messages = $self->{database}->get_message_context($message, $recall_before, $recall_after, $recall_count, $recall_history, $context_account, $context_nick); my $max_recall_time = $self->{pbot}->{registry}->get_value('messagehistory', 'max_recall_time'); foreach my $msg (@$messages) { - if ($max_recall_time && time - $msg->{timestamp} > $max_recall_time && not $self->{pbot}->{users}->loggedin_admin($context->{from}, $context->{hostmask})) { - $max_recall_time = duration($max_recall_time); + # optionally limit messages by by a maximum recall duration from the current time, for privacy + if ($max_recall_time && time - $msg->{timestamp} > $max_recall_time + && not $self->{pbot}->{users}->loggedin_admin($context->{from}, $context->{hostmask})) + { + $max_recall_time = duration $max_recall_time; $result .= "Sorry, you can not recall messages older than $max_recall_time."; return $result; } my $text = $msg->{msg}; - my $ago = concise ago(time - $msg->{timestamp}); + my $ago = concise ago (time - $msg->{timestamp}); + my $nick; + + if (not $raw) { + if ($msg->{hostmask}) { + ($nick) = $msg->{hostmask} =~ /^([^!]+)!/; + } else { + $nick = $self->{database}->find_most_recent_hostmask($msg->{id}); + ($nick) = $nick =~ m/^([^!]+)/; + } + } if ( $text =~ s/^(NICKCHANGE)\b/changed nick to/ or $text =~ s/^(KICKED|QUIT)\b/lc "$1"/e or $text =~ s/^MODE ([^ ]+) (.*)/set mode $1 on $2/ or $text =~ s/^(JOIN|PART)\b/lc "$1ed"/e) { - $text =~ s/^(quit) (.*)/$1 ($2)/; # fix ugly "[nick] quit Quit: Leaving." - $result .= $raw ? "$text\n" : "[$ago] $msg->{nick} $text\n"; - } elsif ($text =~ s/^\/me\s+//) { - $result .= $raw ? "$text\n" : "[$ago] * $msg->{nick} $text\n"; - } else { - $result .= $raw ? "$text\n" : "[$ago] <$msg->{nick}> $text\n"; + $text =~ s/^(quit) (.*)/$1 ($2)/; # fix ugly "[nick] quit Quit: Leaving." + $result .= $raw ? "$text\n" : "[$ago] $nick $text\n"; + } + elsif ($text =~ s/^\/me\s+//) { + $result .= $raw ? "$text\n" : "[$ago] * $nick $text\n"; + } + else { + $result .= $raw ? "$text\n" : "[$ago] <$nick> $text\n"; } } } diff --git a/PBot/MessageHistory_SQLite.pm b/PBot/MessageHistory_SQLite.pm index 896535c3..1735f1f1 100644 --- a/PBot/MessageHistory_SQLite.pm +++ b/PBot/MessageHistory_SQLite.pm @@ -139,7 +139,8 @@ CREATE TABLE IF NOT EXISTS Messages ( channel TEXT COLLATE NOCASE, msg TEXT COLLATE NOCASE, timestamp NUMERIC, - mode INTEGER + mode INTEGER, + hostmask TEXT COLLATE NOCASE ) SQL @@ -863,23 +864,24 @@ sub get_hostmasks_for_nickserv { } sub add_message { - my ($self, $id, $mask, $channel, $message) = @_; - - #$self->{pbot}->{logger}->log("Adding message [$id][$mask][$channel][$message->{msg}][$message->{timestamp}][$message->{mode}]\n"); + my ($self, $id, $hostmask, $channel, $message) = @_; eval { - my $sth = $self->{dbh}->prepare('INSERT INTO Messages VALUES (?, ?, ?, ?, ?)'); - $sth->execute($id, $channel, $message->{msg}, $message->{timestamp}, $message->{mode}); + my $sth = $self->{dbh}->prepare('INSERT INTO Messages VALUES (?, ?, ?, ?, ?, ?)'); + $sth->execute($id, $channel, $message->{msg}, $message->{timestamp}, $message->{mode}, $hostmask); $self->{new_entries}++; }; + $self->{pbot}->{logger}->log($@) if $@; - $self->update_channel_data($id, $channel, {last_seen => $message->{timestamp}}); - $self->update_hostmask_data($mask, {last_seen => $message->{timestamp}}); + + $self->update_channel_data($id, $channel, { last_seen => $message->{timestamp }}); + $self->update_hostmask_data($hostmask, { last_seen => $message->{timestamp }}); } sub get_recent_messages { my ($self, $id, $channel, $limit, $mode, $nick) = @_; - $limit = 25 if not defined $limit; + + $limit //= 25; $channel = lc $channel; @@ -887,65 +889,55 @@ sub get_recent_messages { $mode_query = "AND mode = $mode" if defined $mode; my $messages = eval { - my $sql = "SELECT msg, mode, timestamp FROM Messages WHERE "; + my $sql = "SELECT * FROM Messages WHERE "; + + my %seen_id; 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}; } + if (defined $mode and $mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}) { + %akas = $self->get_also_known_as($nick); + } else { + $akas{$id} = { + id => $id, + type => $self->{alias_type}->{STRONG}, + nickchange => 0, + }; + } - my $ids; - my %seen_id; - my $or = ''; 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 .= "${or}id = ?"; - $or = ' OR '; + $seen_id{$akas{$aka}->{id}} = 1; } + my $ids = join " OR ", map { "id = ?" } keys %seen_id; + $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}); - } - + map { $sth->bind_param($param++, $_) } keys %seen_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}); - } - + map { $sth->bind_param($param++, $_) } keys %seen_id; $sth->bind_param($param++, $channel); $sth->bind_param($param, $limit); - $sth->execute(); + $sth->execute; return $sth->fetchall_arrayref({}); }; + $self->{pbot}->{logger}->log($@) if $@; return $messages; } sub get_recent_messages_from_channel { my ($self, $channel, $limit, $mode, $direction) = @_; - $limit = 25 if not defined $limit; - $direction = 'ASC' if not defined $direction; + + $limit //= 25; + $direction //= 'ASC'; $channel = lc $channel; @@ -953,7 +945,7 @@ sub get_recent_messages_from_channel { $mode_query = "AND mode = $mode" if defined $mode; my $messages = eval { - my $sql = "SELECT id, msg, mode, timestamp FROM Messages WHERE channel = ? $mode_query ORDER BY timestamp $direction LIMIT ?"; + my $sql = "SELECT * FROM Messages WHERE channel = ? $mode_query ORDER BY timestamp $direction LIMIT ?"; my $sth = $self->{dbh}->prepare($sql); $sth->execute($channel, $limit); return $sth->fetchall_arrayref({}); @@ -963,7 +955,39 @@ sub get_recent_messages_from_channel { } sub get_message_context { - my ($self, $message, $before, $after, $count, $text, $context_id) = @_; + my ($self, $message, $before, $after, $count, $text, $context_id, $context_nick) = @_; + + my %seen_id; + my $ids = ''; + + my $sql = 'SELECT * FROM Messages WHERE channel = ? '; + + if (defined $context_id) { + my %akas; + + if (defined $context_nick) { + %akas = $self->get_also_known_as($context_nick); + } else { + $akas{$context_id} = { + id => $context_id, + type => $self->{alias_type}->{STRONG}, + nickchange => 0, + }; + } + + 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 = join " OR ", map { "id = ?" } keys %seen_id; + $ids = "AND ($ids) "; + } + + $sql .= $ids; my ($messages_before, $messages_after, $messages_count); @@ -973,70 +997,50 @@ sub get_message_context { $search =~ s/\?/_/g; $messages_count = eval { - my $sth; - if (defined $context_id) { - $sth = $self->{dbh}->prepare( - 'SELECT id, msg, mode, timestamp, channel FROM Messages WHERE id = ? AND channel = ? AND msg LIKE ? ESCAPE "\" AND timestamp < ? AND mode = 0 ORDER BY timestamp DESC LIMIT ?'); - $sth->bind_param(1, $context_id); - $sth->bind_param(2, $message->{channel}); - $sth->bind_param(3, $search); - $sth->bind_param(4, $message->{timestamp}); - $sth->bind_param(5, $count - 1); - } else { - $sth = $self->{dbh} - ->prepare('SELECT id, msg, mode, timestamp, channel FROM Messages WHERE channel = ? AND msg LIKE ? ESCAPE "\" AND timestamp < ? AND mode = 0 ORDER BY timestamp DESC LIMIT ?'); - $sth->bind_param(1, $message->{channel}); - $sth->bind_param(2, $search); - $sth->bind_param(3, $message->{timestamp}); - $sth->bind_param(4, $count - 1); - } - $sth->execute(); + $sql .= 'AND msg LIKE ? ESCAPE "\" AND timestamp < ? AND mode = 0 ORDER BY timestamp DESC LIMIT ?'; + my $sth = $self->{dbh}->prepare($sql); + my $param = 1; + $sth->bind_param($param++, $message->{channel}); + map { $sth->bind_param($param++, $_) } keys %seen_id; + $sth->bind_param($param++, $search); + $sth->bind_param($param++, $message->{timestamp}); + $sth->bind_param($param++, $count - 1); + $sth->execute; return [reverse @{$sth->fetchall_arrayref({})}]; }; + $self->{pbot}->{logger}->log($@) if $@; } if (defined $before and $before > 0) { $messages_before = eval { - my $sth; - if (defined $context_id) { - $sth = $self->{dbh} - ->prepare('SELECT id, msg, mode, timestamp, channel FROM Messages WHERE id = ? AND channel = ? AND timestamp < ? AND mode = 0 ORDER BY timestamp DESC LIMIT ?'); - $sth->bind_param(1, $context_id); - $sth->bind_param(2, $message->{channel}); - $sth->bind_param(3, $message->{timestamp}); - $sth->bind_param(4, $before); - } else { - $sth = $self->{dbh}->prepare('SELECT id, msg, mode, timestamp, channel FROM Messages WHERE channel = ? AND timestamp < ? AND mode = 0 ORDER BY timestamp DESC LIMIT ?'); - $sth->bind_param(1, $message->{channel}); - $sth->bind_param(2, $message->{timestamp}); - $sth->bind_param(3, $before); - } - $sth->execute(); + $sql .= ' AND timestamp < ? AND mode = 0 ORDER BY timestamp DESC LIMIT ?'; + my $sth = $self->{dbh}->prepare($sql); + my $param = 1; + $sth->bind_param($param++, $message->{channel}); + map { $sth->bind_param($param++, $_) } keys %seen_id; + $sth->bind_param($param++, $message->{timestamp}); + $sth->bind_param($param++, $before); + $sth->execute; return [reverse @{$sth->fetchall_arrayref({})}]; }; + $self->{pbot}->{logger}->log($@) if $@; } if (defined $after and $after > 0) { $messages_after = eval { - my $sth; - if (defined $context_id) { - $sth = $self->{dbh} - ->prepare('SELECT id, msg, mode, timestamp, channel FROM Messages WHERE id = ? AND channel = ? AND timestamp > ? AND mode = 0 ORDER BY timestamp ASC LIMIT ?'); - $sth->bind_param(1, $context_id); - $sth->bind_param(2, $message->{channel}); - $sth->bind_param(3, $message->{timestamp}); - $sth->bind_param(4, $after); - } else { - $sth = $self->{dbh}->prepare('SELECT id, msg, mode, timestamp, channel FROM Messages WHERE channel = ? AND timestamp > ? AND mode = 0 ORDER BY timestamp ASC LIMIT ?'); - $sth->bind_param(1, $message->{channel}); - $sth->bind_param(2, $message->{timestamp}); - $sth->bind_param(3, $after); - } - $sth->execute(); + $sql .= ' AND timestamp > ? AND mode = 0 ORDER BY timestamp ASC LIMIT ?'; + my $sth = $self->{dbh}->prepare($sql); + my $param = 1; + $sth->bind_param($param++, $message->{channel}); + map { $sth->bind_param($param++, $_) } keys %seen_id; + $sth->bind_param($param++, $message->{timestamp}); + $sth->bind_param($param++, $after); + $sth->execute; return $sth->fetchall_arrayref({}); }; + $self->{pbot}->{logger}->log($@) if $@; } @@ -1046,75 +1050,54 @@ sub get_message_context { push(@messages, $message); push(@messages, @$messages_after) if defined $messages_after; - my %nicks; - foreach my $msg (@messages) { - if (not exists $nicks{$msg->{id}}) { - my $hostmask = $self->find_most_recent_hostmask($msg->{id}); - my ($nick) = $hostmask =~ m/^([^!]+)/; - $nicks{$msg->{id}} = $nick; - } - $msg->{nick} = $nicks{$msg->{id}}; - } - return \@messages; } sub recall_message_by_count { my ($self, $id, $channel, $count, $ignore_command, $use_aliases) = @_; - my $messages; + my $messages = eval { + my $sql = 'SELECT * FROM Messages WHERE '; + + my %seen_id; + + if (defined $id) { + my %akas; - if (defined $id) { - $messages = eval { if (defined $use_aliases) { - my %akas = $self->get_also_known_as($use_aliases); - my %seen_id; - my $ids; - my $or = ''; - 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 .= "${or}id = ?"; - $or = ' OR '; - } - - my $sql = "SELECT id, msg, mode, timestamp, channel FROM Messages WHERE ($ids) AND channel = ? ORDER BY timestamp DESC LIMIT 10 OFFSET ?"; - 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++, $count); - $sth->execute(); - return $sth->fetchall_arrayref({}); + %akas = $self->get_also_known_as($use_aliases); } else { - my $sth = $self->{dbh}->prepare('SELECT id, msg, mode, timestamp, channel FROM Messages WHERE id = ? AND channel = ? ORDER BY timestamp DESC LIMIT 10 OFFSET ?'); - $sth->bind_param(1, $id); - $sth->bind_param(2, $channel); - $sth->bind_param(3, $count); - $sth->execute(); - return $sth->fetchall_arrayref({}); + $akas{$id} = { + id => $id, + type => $self->{alias_type}->{STRONG}, + nickchange => 0, + }; } - }; - } else { - $messages = eval { - my $sth = $self->{dbh}->prepare('SELECT id, msg, mode, timestamp, channel FROM Messages WHERE channel = ? ORDER BY timestamp DESC LIMIT 10 OFFSET ?'); - $sth->execute($channel, $count); - return $sth->fetchall_arrayref({}); - }; - } + + 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; + } + + my $ids = join " OR ", map { "id = ?" } keys %seen_id; + + $sql .= "($ids) AND "; + } + + $sql .= 'channel = ? ORDER BY timestamp DESC LIMIT 10 OFFSET ?'; + + my $sth = $self->{dbh}->prepare($sql); + + my $param = 1; + map { $sth->bind_param($param++, $_) } keys %seen_id; + $sth->bind_param($param++, $channel); + $sth->bind_param($param++, $count); + $sth->execute; + return $sth->fetchall_arrayref({}); + }; $self->{pbot}->{logger}->log($@) if $@; @@ -1125,33 +1108,64 @@ sub recall_message_by_count { next if $message->{msg} =~ m/^$botnick.? $ignore_command/ or $message->{msg} =~ m/^$bot_trigger$ignore_command/; return $message; } + return undef; } + return $messages->[0]; } sub recall_message_by_text { - my ($self, $id, $channel, $text, $ignore_command) = @_; + my ($self, $id, $channel, $text, $ignore_command, $use_aliases) = @_; my $search = "%$text%"; $search =~ s/\*/%/g; $search =~ s/\?/_/g; - my $messages; + my $messages = eval { + my $sql = 'SELECT * FROM Messages WHERE channel = ? AND msg LIKE ? ESCAPE "\" '; - if (defined $id) { - $messages = eval { - my $sth = $self->{dbh}->prepare('SELECT id, msg, mode, timestamp, channel FROM Messages WHERE id = ? AND channel = ? AND msg LIKE ? ESCAPE "\" ORDER BY timestamp DESC LIMIT 10'); - $sth->execute($id, $channel, $search); - return $sth->fetchall_arrayref({}); - }; - } else { - $messages = eval { - my $sth = $self->{dbh}->prepare('SELECT id, msg, mode, timestamp, channel FROM Messages WHERE channel = ? AND msg LIKE ? ESCAPE "\" ORDER BY timestamp DESC LIMIT 10'); - $sth->execute($channel, $search); - return $sth->fetchall_arrayref({}); - }; - } + my %seen_id; + + if (defined $id) { + my %akas; + + if (defined $use_aliases) { + %akas = $self->get_also_known_as($use_aliases); + } else { + $akas{$id} = { + id => $id, + type => $self->{alias_type}->{STRONG}, + nickchange => 0, + }; + } + + 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; + } + + my $ids = join " OR ", map { "id = ?" } keys %seen_id; + + $sql .= "AND ($ids) "; + } + + $sql .= 'ORDER BY timestamp DESC LIMIT 10'; + + my $sth = $self->{dbh}->prepare($sql); + + my $param = 1; + $sth->bind_param($param++, $channel); + $sth->bind_param($param++, $search); + + map { $sth->bind_param($param++, $_) } keys %seen_id; + + $sth->execute; + return $sth->fetchall_arrayref({}); + }; $self->{pbot}->{logger}->log($@) if $@; @@ -1165,29 +1179,61 @@ sub recall_message_by_text { or $message->{msg} =~ m/^\s*$ignore_command.? $botnick$/i; return $message; } + return undef; } + return $messages->[0]; } sub get_random_message { - my ($self, $id, $channel) = @_; + my ($self, $id, $channel, $use_aliases) = @_; - my $message; + my $message = eval { + my $sql = 'SELECT * FROM Messages WHERE channel = ? AND mode = ? '; - if (defined $id) { - $message = eval { - my $sth = $self->{dbh}->prepare('SELECT id, msg, mode, timestamp, channel FROM Messages WHERE id = ? AND channel = ? AND mode = ? ORDER BY RANDOM() LIMIT 1'); - $sth->execute($id, $channel, $self->{pbot}->{messagehistory}->{MSG_CHAT}); - return $sth->fetchrow_hashref; - }; - } else { - $message = eval { - my $sth = $self->{dbh}->prepare('SELECT id, msg, mode, timestamp, channel FROM Messages WHERE channel = ? AND mode = ? ORDER BY RANDOM() LIMIT 1'); - $sth->execute($channel, $self->{pbot}->{messagehistory}->{MSG_CHAT}); - return $sth->fetchrow_hashref; - }; - } + my %seen_id; + + if (defined $id) { + my %akas; + + if (defined $use_aliases) { + %akas = $self->get_also_known_as($use_aliases); + } else { + $akas{$id} = { + id => $id, + type => $self->{alias_type}->{STRONG}, + nickchange => 0, + }; + } + + 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; + } + + my $ids = join " OR ", map { "id = ?" } keys %seen_id; + + $sql .= "AND ($ids) "; + } + + $sql .= 'ORDER BY RANDOM() LIMIT 1'; + + my $sth = $self->{dbh}->prepare($sql); + + my $param = 1; + $sth->bind_param($param++, $channel); + $sth->bind_param($param++, $self->{pbot}->{messagehistory}->{MSG_CHAT}); + + map { $sth->bind_param($param++, $_) } keys %seen_id; + + $sth->execute; + + return $sth->fetchrow_hashref; + }; $self->{pbot}->{logger}->log($@) if $@; @@ -1198,46 +1244,44 @@ sub get_max_messages { my ($self, $id, $channel, $use_aliases) = @_; my $count = eval { - my $sql = "SELECT COUNT(*) FROM Messages WHERE channel = ? AND "; + 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; + if (defined $use_aliases) { + %akas = $self->get_also_known_as($use_aliases); + } else { + $akas{$id} = { + id => $id, + type => $self->{alias_type}->{STRONG}, + nickchange => 0, + }; + } + my %seen_id; - my $or = ''; + 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 .= "${or}id = ?"; - $or = ' OR '; + $seen_id{$akas{$aka}->{id}} = 1; } + my $ids = join " OR ", map { "id = ?" } keys %seen_id; + $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; + map { $sth->bind_param($param++, $_) } keys %seen_id; - $sth->bind_param($param++, $akas{$aka}->{id}); - } - - $sth->execute(); - my $row = $sth->fetchrow_hashref(); - $sth->finish(); - return $row->{'COUNT(*)'}; + $sth->execute; + return $sth->fetchrow_hashref->{'COUNT(*)'}; }; + $self->{pbot}->{logger}->log($@) if $@; $count = 0 if not defined $count; return $count;