2010-03-23 19:24:02 +01:00
# File: Quotegrabs.pm
2010-03-17 07:36:54 +01:00
#
2021-06-19 06:23:34 +02:00
# Purpose: Allows users to "grab" quotes from message history and store them
# for later retrieval. Can grab a quote from any point in the message history,
# not just the most recent message. Can grab multiple distinct messages with
# one `grab` command.
2010-03-17 07:36:54 +01:00
2021-07-11 00:00:22 +02:00
# SPDX-FileCopyrightText: 2021 Pragmatic Software <pragma78@gmail.com>
# SPDX-License-Identifier: MIT
2017-03-05 22:33:31 +01:00
2021-07-14 04:45:56 +02:00
package PBot::Plugin::Quotegrabs ;
use parent 'PBot::Plugin::Base' ;
2010-03-17 07:36:54 +01:00
2021-06-19 06:23:34 +02:00
use PBot::Imports ;
2019-07-11 03:40:53 +02:00
2010-03-23 19:24:02 +01:00
use HTML::Entities ;
2011-12-04 02:13:21 +01:00
use Time::Duration ;
use Time::HiRes qw( gettimeofday ) ;
2019-07-25 03:03:38 +02:00
use Getopt::Long qw( GetOptionsFromArray ) ;
2010-03-23 19:24:02 +01:00
2021-07-21 06:38:07 +02:00
use PBot::Plugin::Quotegrabs::Storage::SQLite ; # use SQLite backend for quotegrabs database
#use PBot::Plugin::Quotegrabs::Storage::Hashtable; # use Perl hashtable backend for quotegrabs database
2017-11-24 00:19:28 +01:00
use PBot::Utils::ValidateString ;
2014-05-06 07:15:27 +02:00
2013-10-11 20:17:07 +02:00
use POSIX qw( strftime ) ;
2010-03-23 19:24:02 +01:00
sub initialize {
2020-02-15 23:38:32 +01:00
my ( $ self , % conf ) = @ _ ;
$ self - > { filename } = $ conf { quotegrabs_file } // $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . '/quotegrabs.sqlite3' ;
2021-07-21 06:38:07 +02:00
$ self - > { database } = PBot::Plugin::Quotegrabs::Storage::SQLite - > new ( pbot = > $ self - > { pbot } , filename = > $ self - > { filename } ) ;
#$self->{database} = PBot::Plugin::Quotegrabs::Storage::Hashtable->new(pbot => $self->{pbot}, filename => $self->{filename});
2020-02-15 23:38:32 +01:00
$ self - > { database } - > begin ( ) ;
2010-03-23 19:24:02 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { atexit } - > register ( sub { $ self - > { database } - > end ( ) ; return ; } ) ;
2014-05-17 00:11:31 +02:00
2020-05-04 22:21:35 +02:00
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_grab_quotegrab ( @ _ ) } , 'grab' , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_show_quotegrab ( @ _ ) } , 'getq' , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_delete_quotegrab ( @ _ ) } , 'delq' , 0 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_show_random_quotegrab ( @ _ ) } , 'rq' , 0 ) ;
2019-09-01 20:01:18 +02:00
}
sub unload {
2020-02-15 23:38:32 +01:00
my ( $ self ) = @ _ ;
$ self - > { pbot } - > { commands } - > unregister ( 'grab' ) ;
$ self - > { pbot } - > { commands } - > unregister ( 'getq' ) ;
$ self - > { pbot } - > { commands } - > unregister ( 'delq' ) ;
$ self - > { pbot } - > { commands } - > unregister ( 'rq' ) ;
2010-03-23 19:24:02 +01:00
}
2013-10-12 13:00:29 +02:00
sub uniq { my % seen ; grep ! $ seen { $ _ } + + , @ _ }
2019-06-26 18:34:19 +02:00
sub export_quotegrabs {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
$ self - > { export_path } = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . '/quotegrabs.html' ;
2020-05-04 22:21:35 +02:00
my $ quotegrabs = $ self - > { database } - > get_all_quotegrabs ;
2020-02-15 23:38:32 +01:00
my $ text ;
my $ table_id = 1 ;
my $ had_table = 0 ;
open FILE , "> $self->{export_path}" or return "Could not open export path." ;
my $ time = localtime ;
my $ botnick = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'botnick' ) ;
print FILE "<html>\n<head><link href=\"css/blue.css\" rel=\"stylesheet\" type=\"text/css\">\n" ;
print FILE '<script type="text/javascript" src="js/jquery-latest.js"></script>' . "\n" ;
print FILE '<script type="text/javascript" src="js/jquery.tablesorter.js"></script>' . "\n" ;
print FILE '<script type="text/javascript" src="js/picnet.table.filter.min.js"></script>' . "\n" ;
print FILE "</head>\n<body><i>Generated at $time</i><hr><h2>$botnick\'s Quotegrabs</h2>\n" ;
my $ i = 0 ;
my $ last_channel = "" ;
foreach my $ quotegrab ( sort { $$ a { channel } cmp $$ b { channel } or $$ a { nick } cmp $$ b { nick } } @$ quotegrabs ) {
if ( not $ quotegrab - > { channel } =~ /^$last_channel$/i ) {
print FILE "<a href='#" . encode_entities ( $ quotegrab - > { channel } ) . "'>" . encode_entities ( $ quotegrab - > { channel } ) . "</a><br>\n" ;
$ last_channel = $ quotegrab - > { channel } ;
}
2013-10-13 12:23:20 +02:00
}
2010-03-23 19:24:02 +01:00
2020-02-15 23:38:32 +01:00
$ last_channel = "" ;
foreach my $ quotegrab ( sort { $$ a { channel } cmp $$ b { channel } or lc $$ a { nick } cmp lc $$ b { nick } } @$ quotegrabs ) {
if ( not $ quotegrab - > { channel } =~ /^$last_channel$/i ) {
print FILE "</tbody>\n</table>\n" if $ had_table ;
print FILE "<a name='" . encode_entities ( $ quotegrab - > { channel } ) . "'></a>\n" ;
print FILE "<hr><h3>" . encode_entities ( $ quotegrab - > { channel } ) . "</h3><hr>\n" ;
print FILE "<table border=\"0\" id=\"table$table_id\" class=\"tablesorter\">\n" ;
print FILE "<thead>\n<tr>\n" ;
print FILE "<th>id </th>\n" ;
print FILE "<th>author(s)</th>\n" ;
print FILE "<th>quote</th>\n" ;
print FILE "<th>date</th>\n" ;
print FILE "<th>grabbed by</th>\n" ;
print FILE "</tr>\n</thead>\n<tbody>\n" ;
$ had_table = 1 ;
$ table_id + + ;
}
$ last_channel = $ quotegrab - > { channel } ;
$ i + + ;
if ( $ i % 2 ) { print FILE "<tr bgcolor=\"#dddddd\">\n" ; }
else { print FILE "<tr>\n" ; }
print FILE "<td>" . ( $ quotegrab - > { id } ) . "</td>" ;
my @ nicks = split /\+/ , $ quotegrab - > { nick } ;
$ text = join ', ' , uniq ( @ nicks ) ;
print FILE "<td>" . encode_entities ( $ text ) . "</td>" ;
my $ nick ;
$ text = $ quotegrab - > { text } ;
if ( $ text =~ s/^\/me\s+// ) { $ nick = "* $nicks[0]" ; }
else { $ nick = "<$nicks[0]>" ; }
$ text = "<td><b>" . encode_entities ( $ nick ) . "</b> " . encode_entities ( $ text ) . "</td>\n" ;
print FILE $ text ;
print FILE "<td>" . encode_entities ( strftime "%Y/%m/%d %a %H:%M:%S" , localtime $ quotegrab - > { timestamp } ) . "</td>\n" ;
print FILE "<td>" . encode_entities ( $ quotegrab - > { grabbed_by } ) . "</td>\n" ;
print FILE "</tr>\n" ;
2010-03-23 19:24:02 +01:00
}
2013-10-11 20:17:07 +02:00
2020-02-15 23:38:32 +01:00
print FILE "</tbody>\n</table>\n" if $ had_table ;
print FILE "<script type='text/javascript'>\n" ;
$ table_id - - ;
print FILE '$(document).ready(function() {' . "\n" ;
while ( $ table_id > 0 ) {
print FILE '$("#table' . $ table_id . '").tablesorter();' . "\n" ;
print FILE '$("#table' . $ table_id . '").tableFilter();' . "\n" ;
$ table_id - - ;
}
print FILE "});\n" ;
print FILE "</script>\n" ;
print FILE "</body>\n</html>\n" ;
close ( FILE ) ;
return "$i quotegrabs exported." ;
}
2013-10-11 20:17:07 +02:00
2020-05-04 22:21:35 +02:00
sub cmd_grab_quotegrab {
my ( $ self , $ context ) = @ _ ;
2013-10-11 20:17:07 +02:00
2020-05-04 22:21:35 +02:00
if ( not defined $ context - > { from } ) {
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Command missing ~from parameter!\n" ) ;
return "" ;
2013-10-11 20:17:07 +02:00
}
2020-05-04 22:21:35 +02:00
if ( not defined $ context - > { arguments } or not length $ context - > { arguments } ) {
2020-02-15 23:38:32 +01:00
return
"Usage: grab <nick> [history [channel]] [+ <nick> [history [channel]] ...] -- where [history] is an optional regex argument; e.g., to grab a message containing 'pizza', use `grab nick pizza`; you can chain grabs with + to grab multiple messages" ;
}
2019-06-26 18:34:19 +02:00
2020-05-04 22:21:35 +02:00
$ context - > { arguments } = lc $ context - > { arguments } ;
2013-10-11 20:17:07 +02:00
2020-05-04 22:21:35 +02:00
my @ grabs = split /\s\+\s/ , $ context - > { arguments } ;
2010-03-17 07:36:54 +01:00
2020-02-15 23:38:32 +01:00
my ( $ grab_nick , $ grab_history , $ channel , $ grab_nicks , $ grab_text ) ;
2010-03-17 07:36:54 +01:00
2020-02-15 23:38:32 +01:00
foreach my $ grab ( @ grabs ) {
( $ grab_nick , $ grab_history , $ channel ) = $ self - > { pbot } - > { interpreter } - > split_line ( $ grab , strip_quotes = > 1 ) ;
2010-03-17 07:36:54 +01:00
2020-05-04 22:21:35 +02:00
$ grab_history = $ context - > { nick } eq $ grab_nick ? 2 : 1 if not defined $ grab_history ; # skip grab command if grabbing self without arguments
$ channel = $ context - > { from } if not defined $ channel ;
2010-03-17 07:36:54 +01:00
2020-02-15 23:38:32 +01:00
if ( not $ channel =~ m/^#/ ) {
return "'$channel' is not a valid channel; usage: grab <nick> [[history] channel] (you must specify a history parameter before the channel parameter)" ;
}
2010-03-17 07:36:54 +01:00
2020-02-15 23:38:32 +01:00
my ( $ account , $ found_nick ) = $ self - > { pbot } - > { messagehistory } - > { database } - > find_message_account_by_nick ( $ grab_nick ) ;
2011-01-20 01:15:58 +01:00
2020-02-15 23:38:32 +01:00
if ( not defined $ account ) { return "I don't know anybody named $grab_nick" ; }
2010-03-17 07:36:54 +01:00
2020-02-15 23:38:32 +01:00
$ found_nick =~ s/!.*$// ;
2010-03-17 07:36:54 +01:00
2020-02-15 23:38:32 +01:00
$ grab_nick = $ found_nick ; # convert nick to proper casing
2010-03-17 07:36:54 +01:00
2020-02-15 23:38:32 +01:00
my $ message ;
2011-02-11 03:46:35 +01:00
2020-02-15 23:38:32 +01:00
if ( $ grab_history =~ /^\d+$/ ) {
# integral history
my $ max_messages = $ self - > { pbot } - > { messagehistory } - > { database } - > get_max_messages ( $ account , $ channel ) ;
if ( $ grab_history < 1 || $ grab_history > $ max_messages ) { return "Please choose a history between 1 and $max_messages" ; }
2010-03-17 07:36:54 +01:00
2020-02-15 23:38:32 +01:00
$ grab_history - - ;
2012-08-01 11:57:43 +02:00
2020-02-15 23:38:32 +01:00
$ message = $ self - > { pbot } - > { messagehistory } - > { database } - > recall_message_by_count ( $ account , $ channel , $ grab_history , 'grab' ) ;
} else {
# regex history
$ message = $ self - > { pbot } - > { messagehistory } - > { database } - > recall_message_by_text ( $ account , $ channel , $ grab_history , 'grab' ) ;
2010-03-17 07:36:54 +01:00
2020-02-15 23:38:32 +01:00
if ( not defined $ message ) { return "No such message for nick $grab_nick in channel $channel containing text '$grab_history'" ; }
}
2013-10-04 06:55:45 +02:00
2020-05-04 22:21:35 +02:00
$ self - > { pbot } - > { logger } - > log ( "$context->{nick} ($context->{from}) grabbed <$grab_nick/$channel> $message->{msg}\n" ) ;
2013-10-04 06:55:45 +02:00
2020-02-15 23:38:32 +01:00
if ( not defined $ grab_nicks ) { $ grab_nicks = $ grab_nick ; }
else { $ grab_nicks . = "+$grab_nick" ; }
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ text = $ message - > { msg } ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
if ( not defined $ grab_text ) { $ grab_text = $ text ; }
else {
if ( $ text =~ s/^\/me\s+// ) { $ grab_text . = " * $grab_nick $text" ; }
else { $ grab_text . = " <$grab_nick> $text" ; }
}
2013-10-04 06:55:45 +02:00
}
2012-08-01 11:57:43 +02:00
2020-02-15 23:38:32 +01:00
my $ quotegrab = { } ;
$ quotegrab - > { nick } = $ grab_nicks ;
$ quotegrab - > { channel } = $ channel ;
$ quotegrab - > { timestamp } = gettimeofday ;
2020-05-04 22:21:35 +02:00
$ quotegrab - > { grabbed_by } = $ context - > { hostmask } ;
2020-02-15 23:38:32 +01:00
$ quotegrab - > { text } = validate_string ( $ grab_text ) ;
$ quotegrab - > { id } = undef ;
2013-10-04 06:55:45 +02:00
2020-02-15 23:38:32 +01:00
$ quotegrab - > { id } = $ self - > { database } - > add_quotegrab ( $ quotegrab ) ;
if ( not defined $ quotegrab - > { id } ) { return "Failed to grab quote." ; }
2013-10-04 06:55:45 +02:00
2020-02-15 23:38:32 +01:00
$ self - > export_quotegrabs ( ) ;
2013-10-04 06:55:45 +02:00
2020-02-15 23:38:32 +01:00
my $ text = $ quotegrab - > { text } ;
( $ grab_nick ) = split /\+/ , $ grab_nicks , 2 ;
if ( $ text =~ s/^(NICKCHANGE)\b/changed nick to/ or $ text =~ s/^(KICKED|QUIT)\b/lc "$1"/e or $ text =~ s/^(JOIN|PART)\b/lc "$1ed"/e ) {
# fix ugly "[nick] quit Quit: Leaving." messages
$ text =~ s/^(quit) (.*)/$1 ($2)/ ;
return "Quote grabbed: $quotegrab->{id}: $grab_nick $text" ;
} elsif ( $ text =~ s/^\/me\s+// ) {
return "Quote grabbed: $quotegrab->{id}: * $grab_nick $text" ;
2013-10-04 06:55:45 +02:00
} else {
2020-02-15 23:38:32 +01:00
return "Quote grabbed: $quotegrab->{id}: <$grab_nick> $text" ;
2012-08-01 11:57:43 +02:00
}
2010-03-17 07:36:54 +01:00
}
2020-05-04 22:21:35 +02:00
sub cmd_delete_quotegrab {
my ( $ self , $ context ) = @ _ ;
2010-03-23 19:24:02 +01:00
2020-05-04 22:21:35 +02:00
my $ quotegrab = $ self - > { database } - > get_quotegrab ( $ context - > { arguments } ) ;
2010-03-23 19:24:02 +01:00
2020-05-04 22:21:35 +02:00
if ( not defined $ quotegrab ) { return "/msg $context->{nick} No quotegrab matching id $context->{arguments} found." ; }
2013-10-26 04:39:54 +02:00
2020-05-04 22:21:35 +02:00
if ( not $ self - > { pbot } - > { users } - > loggedin_admin ( $ context - > { from } , $ context - > { hostmask } ) and $ quotegrab - > { grabbed_by } ne $ context - > { hostmask } ) {
2020-02-15 23:38:32 +01:00
return "You are not the grabber of this quote." ;
}
2013-10-26 04:39:54 +02:00
2020-05-04 22:21:35 +02:00
$ self - > { database } - > delete_quotegrab ( $ context - > { arguments } ) ;
2020-02-15 23:38:32 +01:00
$ self - > export_quotegrabs ( ) ;
2013-06-04 19:09:30 +02:00
2020-02-15 23:38:32 +01:00
my $ text = $ quotegrab - > { text } ;
2013-06-04 19:09:30 +02:00
2020-02-15 23:38:32 +01:00
my ( $ first_nick ) = split /\+/ , $ quotegrab - > { nick } , 2 ;
2013-10-05 00:14:16 +02:00
2020-05-04 22:21:35 +02:00
if ( $ text =~ s/^\/me\s+// ) { return "Deleted $context->{arguments}: * $first_nick $text" ; }
else { return "Deleted $context->{arguments}: <$first_nick> $text" ; }
2010-03-17 07:36:54 +01:00
}
2020-05-04 22:21:35 +02:00
sub cmd_show_quotegrab {
my ( $ self , $ context ) = @ _ ;
2010-03-17 07:36:54 +01:00
2020-05-04 22:21:35 +02:00
my $ quotegrab = $ self - > { database } - > get_quotegrab ( $ context - > { arguments } ) ;
2014-05-06 07:15:27 +02:00
2020-05-04 22:21:35 +02:00
if ( not defined $ quotegrab ) { return "/msg $context->{nick} No quotegrab matching id $context->{arguments} found." ; }
2010-03-17 07:36:54 +01:00
2020-02-15 23:38:32 +01:00
my $ timestamp = $ quotegrab - > { timestamp } ;
my $ ago = ago ( gettimeofday - $ timestamp ) ;
my $ text = $ quotegrab - > { text } ;
my ( $ first_nick ) = split /\+/ , $ quotegrab - > { nick } , 2 ;
2013-06-04 19:09:30 +02:00
2020-02-15 23:38:32 +01:00
if ( $ text =~ s/^\/me\s+// ) {
2020-05-04 22:21:35 +02:00
return "$context->{arguments}: grabbed by $quotegrab->{grabbed_by} in $quotegrab->{channel} on " . localtime ( $ timestamp ) . " [$ago] * $first_nick $text" ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "$context->{arguments}: grabbed by $quotegrab->{grabbed_by} in $quotegrab->{channel} on " . localtime ( $ timestamp ) . " [$ago] <$first_nick> $text" ;
2020-02-15 23:38:32 +01:00
}
2010-03-17 07:36:54 +01:00
}
2020-05-04 22:21:35 +02:00
sub cmd_show_random_quotegrab {
my ( $ self , $ context ) = @ _ ;
2020-02-15 23:38:32 +01:00
my @ quotes = ( ) ;
my ( $ nick_search , $ channel_search , $ text_search ) ;
2014-04-28 03:52:49 +02:00
2020-05-04 22:21:35 +02:00
if ( not defined $ context - > { from } ) {
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Command missing ~from parameter!\n" ) ;
return "" ;
2010-03-17 07:36:54 +01:00
}
2020-02-15 23:38:32 +01:00
my $ usage = 'Usage: rq [nick [channel [text]]] [-c <channel>] [-t <text>]' ;
2010-03-17 07:36:54 +01:00
2020-05-04 22:21:35 +02:00
if ( defined $ context - > { arguments } ) {
2020-02-15 23:38:32 +01:00
my $ getopt_error ;
local $ SIG { __WARN__ } = sub {
$ getopt_error = shift ;
chomp $ getopt_error ;
} ;
2019-06-26 18:34:19 +02:00
2020-05-04 22:21:35 +02:00
my @ opt_args = $ self - > { pbot } - > { interpreter } - > split_line ( $ context - > { arguments } , preserve_escapes = > 1 , strip_quotes = > 1 ) ;
2020-02-15 23:38:32 +01:00
GetOptionsFromArray (
\ @ opt_args ,
'channel|c=s' = > \ $ channel_search ,
'text|t=s' = > \ $ text_search
) ;
2014-02-10 19:04:05 +01:00
2020-02-15 23:38:32 +01:00
return "$getopt_error -- $usage" if defined $ getopt_error ;
$ nick_search = shift @ opt_args ;
$ channel_search = shift @ opt_args if not defined $ channel_search ;
$ text_search = shift @ opt_args if not defined $ text_search ;
2014-02-10 19:07:34 +01:00
2020-02-15 23:38:32 +01:00
if ( $ nick_search =~ m/^#/ ) {
my $ tmp = $ channel_search ;
$ channel_search = $ nick_search ;
$ nick_search = $ tmp ;
}
2020-05-04 22:21:35 +02:00
if ( not defined $ channel_search ) { $ channel_search = $ context - > { from } ; }
2014-02-10 19:07:34 +01:00
}
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
if ( defined $ channel_search and $ channel_search !~ /^#/ ) {
2020-05-04 22:21:35 +02:00
if ( $ channel_search eq $ context - > { nick } ) { $ channel_search = undef ; }
2020-02-15 23:38:32 +01:00
elsif ( $ channel_search =~ m/^\./ ) {
# do nothing
} else {
return "$channel_search is not a valid channel." ;
}
2014-02-10 19:04:05 +01:00
}
2020-02-15 23:38:32 +01:00
my $ quotegrab = $ self - > { database } - > get_random_quotegrab ( $ nick_search , $ channel_search , $ text_search ) ;
if ( not defined $ quotegrab ) {
my $ result = "No quotes grabbed " ;
2010-03-17 07:36:54 +01:00
2020-02-15 23:38:32 +01:00
if ( defined $ nick_search ) { $ result . = "for nick $nick_search " ; }
2013-06-04 19:09:30 +02:00
2020-02-15 23:38:32 +01:00
if ( defined $ channel_search ) { $ result . = "in channel $channel_search " ; }
if ( defined $ text_search ) { $ result . = "matching text '$text_search' " ; }
return $ result . "yet ($usage)." ;
}
my $ text = $ quotegrab - > { text } ;
my ( $ first_nick ) = split /\+/ , $ quotegrab - > { nick } , 2 ;
if ( $ text =~ s/^\/me\s+// ) {
2020-05-04 22:21:35 +02:00
return "$quotegrab->{id}: " . ( ( $ channel_search eq '.*' or $ quotegrab - > { channel } ne $ context - > { from } ) ? "[$quotegrab->{channel}] " : "" ) . "* $first_nick $text" ;
2020-02-15 23:38:32 +01:00
} else {
2020-05-04 22:21:35 +02:00
return "$quotegrab->{id}: " . ( ( $ channel_search eq '.*' or $ quotegrab - > { channel } ne $ context - > { from } ) ? "[$quotegrab->{channel}] " : "" ) . "<$first_nick> $text" ;
2020-02-15 23:38:32 +01:00
}
2010-03-17 07:36:54 +01:00
}
1 ;