2014-05-13 12:15:52 +02:00
# File: MessageHistory_SQLite.pm
#
2021-06-19 06:23:34 +02:00
# Purpose: SQLite backend for storing/retreiving a user's message history.
# Peforms intelligent hostmask and nickserv heuristics to link nicknames
# in order to ensure message history is stored in the right user account
# ids. This is also extremely useful for detecting ban-evasions and listing
# also-known-as data for a nickname (see the !aka bot command).
2014-05-13 12:15:52 +02: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
2014-05-13 12:15:52 +02:00
package PBot::MessageHistory_SQLite ;
2020-02-08 20:04:13 +01:00
use parent 'PBot::Class' ;
2014-05-13 12:15:52 +02:00
2021-06-19 06:23:34 +02:00
use PBot::Imports ;
2019-07-11 03:40:53 +02:00
2014-05-13 12:15:52 +02:00
use DBI ;
2021-07-12 02:05:55 +02:00
use Carp qw/shortmess/ ;
use Time::HiRes qw/time/ ;
2016-02-10 16:10:37 +01:00
use Text::CSV ;
2016-08-18 05:34:45 +02:00
use Text::Levenshtein qw/fastdistance/ ;
2017-08-02 06:36:31 +02:00
use Time::Duration ;
2014-05-13 12:15:52 +02:00
sub initialize {
2020-02-15 23:38:32 +01:00
my ( $ self , % conf ) = @ _ ;
$ self - > { filename } = $ conf { filename } // $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'data_dir' ) . '/message_history.sqlite3' ;
$ self - > { new_entries } = 0 ;
$ self - > { pbot } - > { registry } - > add_default ( 'text' , 'messagehistory' , 'debug_link' , 0 ) ;
$ self - > { pbot } - > { registry } - > add_default ( 'text' , 'messagehistory' , 'debug_aka' , 0 ) ;
$ self - > { pbot } - > { registry } - > add_default ( 'text' , 'messagehistory' , 'sqlite_commit_interval' , 30 ) ;
$ self - > { pbot } - > { registry } - > add_default ( 'text' , 'messagehistory' , 'sqlite_debug' , $ conf { sqlite_debug } // 0 ) ;
$ self - > { pbot } - > { registry } - > add_trigger ( 'messagehistory' , 'sqlite_commit_interval' , sub { $ self - > sqlite_commit_interval_trigger ( @ _ ) } ) ;
$ self - > { pbot } - > { registry } - > add_trigger ( 'messagehistory' , 'sqlite_debug' , sub { $ self - > sqlite_debug_trigger ( @ _ ) } ) ;
2021-06-22 02:26:24 +02:00
$ self - > { pbot } - > { event_queue } - > enqueue (
2020-02-15 23:38:32 +01:00
sub { $ self - > commit_message_history } ,
$ self - > { pbot } - > { registry } - > get_value ( 'messagehistory' , 'sqlite_commit_interval' ) ,
2020-03-09 06:46:02 +01:00
'messagehistory commit'
2020-02-15 23:38:32 +01:00
) ;
$ self - > { alias_type } - > { WEAK } = 0 ;
$ self - > { alias_type } - > { STRONG } = 1 ;
2014-05-19 12:30:25 +02:00
}
sub sqlite_commit_interval_trigger {
2020-02-15 23:38:32 +01:00
my ( $ self , $ section , $ item , $ newvalue ) = @ _ ;
2021-06-22 02:26:24 +02:00
$ self - > { pbot } - > { event_queue } - > update_interval ( 'messagehistory commit' , $ newvalue ) ;
2014-05-13 12:15:52 +02:00
}
2014-05-20 12:17:01 +02:00
sub sqlite_debug_trigger {
2020-02-15 23:38:32 +01:00
my ( $ self , $ section , $ item , $ newvalue ) = @ _ ;
2019-06-26 18:34:19 +02:00
2020-09-29 21:29:40 +02:00
if ( $ newvalue ) {
open $ self - > { trace_layer } , '>:via(PBot::SQLiteLoggerLayer)' , PBot::SQLiteLogger - > new ( pbot = > $ self - > { pbot } ) ;
} else {
close $ self - > { trace_layer } if $ self - > { trace_layer } ;
delete $ self - > { trace_layer } ;
}
$ self - > { dbh } - > trace ( $ self - > { dbh } - > parse_trace_flags ( "SQL|$newvalue" ) ) if defined $ self - > { dbh } ;
2014-05-20 12:17:01 +02:00
}
2014-05-13 12:15:52 +02:00
sub begin {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Opening message history SQLite database: $self->{filename}\n" ) ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { dbh } = DBI - > connect ( "dbi:SQLite:dbname=$self->{filename}" , "" , "" , { RaiseError = > 1 , PrintError = > 0 , AutoInactiveDestroy = > 1 , sqlite_unicode = > 1 } )
or die $ DBI:: errstr ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
eval {
my $ sqlite_debug = $ self - > { pbot } - > { registry } - > get_value ( 'messagehistory' , 'sqlite_debug' ) ;
use PBot::SQLiteLoggerLayer ;
use PBot::SQLiteLogger ;
2020-09-29 21:29:40 +02:00
if ( $ sqlite_debug ) {
open $ self - > { trace_layer } , '>:via(PBot::SQLiteLoggerLayer)' , PBot::SQLiteLogger - > new ( pbot = > $ self - > { pbot } ) ;
$ self - > { dbh } - > trace ( $ self - > { dbh } - > parse_trace_flags ( "SQL|$sqlite_debug" ) , $ self - > { trace_layer } ) ;
}
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > do ( << SQL ) ;
2014-05-13 12:15:52 +02:00
CREATE TABLE IF NOT EXISTS Hostmasks (
2017-06-18 12:40:51 +02:00
hostmask TEXT PRIMARY KEY UNIQUE COLLATE NOCASE ,
2014-05-13 12:15:52 +02:00
id INTEGER ,
2016-08-18 05:34:45 +02:00
last_seen NUMERIC ,
2017-06-18 12:40:51 +02:00
nickchange INTEGER ,
nick TEXT COLLATE NOCASE ,
user TEXT COLLATE NOCASE ,
host TEXT COLLATE NOCASE
2014-05-13 12:15:52 +02:00
)
SQL
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > do ( << SQL ) ;
2014-05-13 12:15:52 +02:00
CREATE TABLE IF NOT EXISTS Accounts (
id INTEGER PRIMARY KEY ,
2017-06-18 12:40:51 +02:00
hostmask TEXT UNIQUE COLLATE NOCASE ,
nickserv TEXT COLLATE NOCASE
2014-05-13 12:15:52 +02:00
)
SQL
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > do ( << SQL ) ;
2014-05-13 12:15:52 +02:00
CREATE TABLE IF NOT EXISTS Nickserv (
2019-06-26 18:34:19 +02:00
id INTEGER ,
2017-06-18 12:40:51 +02:00
nickserv TEXT COLLATE NOCASE ,
2017-06-12 08:33:38 +02:00
timestamp NUMERIC ,
UNIQUE ( id , nickserv )
2014-05-13 12:15:52 +02:00
)
2016-02-10 12:42:42 +01:00
SQL
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > do ( << SQL ) ;
2016-02-10 12:42:42 +01:00
CREATE TABLE IF NOT EXISTS Gecos (
id INTEGER ,
2017-06-18 12:40:51 +02:00
gecos TEXT COLLATE NOCASE ,
2017-06-12 08:33:38 +02:00
timestamp NUMERIC ,
UNIQUE ( id , gecos )
2016-02-10 12:42:42 +01:00
)
2014-05-13 12:15:52 +02:00
SQL
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > do ( << SQL ) ;
2014-05-13 12:15:52 +02:00
CREATE TABLE IF NOT EXISTS Channels (
id INTEGER ,
2017-06-18 12:40:51 +02:00
channel TEXT COLLATE NOCASE ,
2014-05-13 12:15:52 +02:00
enter_abuse INTEGER ,
enter_abuses INTEGER ,
offenses INTEGER ,
last_offense NUMERIC ,
last_seen NUMERIC ,
validated INTEGER ,
2016-01-17 01:55:48 +01:00
join_watch INTEGER ,
2017-06-12 08:33:38 +02:00
unbanmes INTEGER ,
UNIQUE ( id , channel )
2014-05-13 12:15:52 +02:00
)
SQL
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > do ( << SQL ) ;
2014-05-13 12:15:52 +02:00
CREATE TABLE IF NOT EXISTS Messages (
id INTEGER ,
2017-06-18 12:40:51 +02:00
channel TEXT COLLATE NOCASE ,
2020-04-19 21:42:44 +02:00
msg TEXT COLLATE NOCASE ,
2014-05-13 12:15:52 +02:00
timestamp NUMERIC ,
mode INTEGER
)
SQL
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > do ( << SQL ) ;
2015-05-12 06:27:22 +02:00
CREATE TABLE IF NOT EXISTS Aliases (
id INTEGER ,
2015-07-15 09:18:57 +02:00
alias INTEGER ,
type INTEGER
2015-05-12 06:27:22 +02:00
)
SQL
2015-02-16 05:30:28 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > do ( 'CREATE INDEX IF NOT EXISTS MsgIdx1 ON Messages(id, channel, mode)' ) ;
$ self - > { dbh } - > do ( 'CREATE INDEX IF NOT EXISTS AliasIdx1 ON Aliases(id, alias, type)' ) ;
$ self - > { dbh } - > do ( 'CREATE INDEX IF NOT EXISTS AliasIdx2 ON Aliases(alias, id, type)' ) ;
2015-02-16 05:30:28 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > do ( 'CREATE INDEX IF NOT EXISTS hostmask_nick_idx on Hostmasks (nick)' ) ;
$ self - > { dbh } - > do ( 'CREATE INDEX IF NOT EXISTS hostmask_host_idx on Hostmasks (host)' ) ;
$ self - > { dbh } - > do ( 'CREATE INDEX IF NOT EXISTS hostmasks_id_idx on Hostmasks (id)' ) ;
$ self - > { dbh } - > do ( 'CREATE INDEX IF NOT EXISTS gecos_id_idx on Gecos (id)' ) ;
$ self - > { dbh } - > do ( 'CREATE INDEX IF NOT EXISTS nickserv_id_idx on Nickserv (id)' ) ;
2017-06-18 12:40:51 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > begin_work ( ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2014-05-13 12:15:52 +02:00
}
sub end {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Closing message history SQLite database\n" ) ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
if ( exists $ self - > { dbh } and defined $ self - > { dbh } ) {
$ self - > { dbh } - > commit ( ) if $ self - > { new_entries } ;
$ self - > { dbh } - > disconnect ( ) ;
2020-09-29 21:29:40 +02:00
close $ self - > { trace_layer } if $ self - > { trace_layer } ;
2020-02-15 23:38:32 +01:00
delete $ self - > { dbh } ;
}
2014-05-13 12:15:52 +02:00
}
2016-02-10 12:42:42 +01:00
sub get_gecos {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id ) = @ _ ;
my $ gecos = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT gecos FROM Gecos WHERE ID = ?' ) ;
$ sth - > execute ( $ id ) ;
return $ sth - > fetchall_arrayref ( ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return map { $ _ - > [ 0 ] } @$ gecos ;
2016-02-10 12:42:42 +01:00
}
2014-05-13 12:15:52 +02:00
sub get_nickserv_accounts {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id ) = @ _ ;
my $ nickserv_accounts = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT nickserv FROM Nickserv WHERE ID = ?' ) ;
$ sth - > execute ( $ id ) ;
return $ sth - > fetchall_arrayref ( ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return map { $ _ - > [ 0 ] } @$ nickserv_accounts ;
2014-05-13 12:15:52 +02:00
}
sub set_current_nickserv_account {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ nickserv ) = @ _ ;
eval {
my $ sth = $ self - > { dbh } - > prepare ( 'UPDATE Accounts SET nickserv = ? WHERE id = ?' ) ;
$ sth - > execute ( $ nickserv , $ id ) ;
$ self - > { new_entries } + + ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2014-05-13 12:15:52 +02:00
}
sub get_current_nickserv_account {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id ) = @ _ ;
my $ nickserv = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT nickserv FROM Accounts WHERE id = ?' ) ;
$ sth - > execute ( $ id ) ;
my $ row = $ sth - > fetchrow_hashref ( ) ;
if ( defined $ row ) { return $ row - > { 'nickserv' } ; }
else { return undef ; }
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return $ nickserv ;
2014-05-13 12:15:52 +02:00
}
sub create_nickserv {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ nickserv ) = @ _ ;
eval {
my $ sth = $ self - > { dbh } - > prepare ( 'INSERT OR IGNORE INTO Nickserv VALUES (?, ?, 0)' ) ;
my $ rv = $ sth - > execute ( $ id , $ nickserv ) ;
$ self - > { new_entries } + + ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2014-05-13 12:15:52 +02:00
}
sub update_nickserv_account {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ nickserv , $ timestamp ) = @ _ ;
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
#$self->{pbot}->{logger}->log("Updating nickserv account for id $id to $nickserv with timestamp [$timestamp]\n");
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ self - > create_nickserv ( $ id , $ nickserv ) ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
eval {
my $ sth = $ self - > { dbh } - > prepare ( 'UPDATE Nickserv SET timestamp = ? WHERE id = ? AND nickserv = ?' ) ;
$ sth - > execute ( $ timestamp , $ id , $ nickserv ) ;
$ self - > { new_entries } + + ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2014-05-13 12:15:52 +02:00
}
2016-02-10 12:42:42 +01:00
sub create_gecos {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ gecos ) = @ _ ;
eval {
my $ sth = $ self - > { dbh } - > prepare ( 'INSERT OR IGNORE INTO Gecos VALUES (?, ?, 0)' ) ;
my $ rv = $ sth - > execute ( $ id , $ gecos ) ;
$ self - > { new_entries } + + if $ sth - > rows ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2016-02-10 12:42:42 +01:00
}
sub update_gecos {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ gecos , $ timestamp ) = @ _ ;
2016-02-10 12:42:42 +01:00
2020-02-15 23:38:32 +01:00
$ self - > create_gecos ( $ id , $ gecos ) ;
2016-02-10 12:42:42 +01:00
2020-02-15 23:38:32 +01:00
eval {
my $ sth = $ self - > { dbh } - > prepare ( 'UPDATE Gecos SET timestamp = ? WHERE id = ? AND gecos = ?' ) ;
$ sth - > execute ( $ timestamp , $ id , $ gecos ) ;
$ self - > { new_entries } + + ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2016-02-10 12:42:42 +01:00
}
2014-05-13 12:15:52 +02:00
sub add_message_account {
2020-02-15 23:38:32 +01:00
my ( $ self , $ mask , $ link_id , $ link_type ) = @ _ ;
my $ id ;
my ( $ nick , $ user , $ host ) = $ mask =~ m/^([^!]+)!([^@]+)@(.*)/ ;
if ( defined $ link_id and $ link_type == $ self - > { alias_type } - > { STRONG } ) { $ id = $ link_id ; }
else {
$ id = $ self - > get_new_account_id ( ) ;
$ self - > { pbot } - > { logger } - > log ( "Got new account id $id\n" ) ;
2014-05-13 12:15:52 +02:00
}
2020-02-15 23:38:32 +01:00
eval {
my $ sth = $ self - > { dbh } - > prepare ( 'INSERT INTO Hostmasks VALUES (?, ?, ?, 0, ?, ?, ?)' ) ;
2021-07-12 02:05:55 +02:00
$ sth - > execute ( $ mask , $ id , scalar time , $ nick , $ user , $ host ) ;
2020-02-15 23:38:32 +01:00
$ self - > { new_entries } + + ;
if ( ( not defined $ link_id ) || ( ( defined $ link_id ) && ( $ link_type == $ self - > { alias_type } - > { WEAK } ) ) ) {
$ sth = $ self - > { dbh } - > prepare ( 'INSERT INTO Accounts VALUES (?, ?, ?)' ) ;
$ sth - > execute ( $ id , $ mask , "" ) ;
$ self - > { new_entries } + + ;
$ self - > { pbot } - > { logger } - > log ( "Added new account $id for mask $mask\n" ) ;
}
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2016-08-18 05:34:45 +02:00
2020-02-15 23:38:32 +01:00
if ( defined $ link_id && $ link_type == $ self - > { alias_type } - > { WEAK } ) {
$ self - > { pbot } - > { logger } - > log ( "Weakly linking $id to $link_id\n" ) ;
$ self - > link_alias ( $ id , $ link_id , $ link_type ) ;
}
2016-08-18 05:34:45 +02:00
2020-02-15 23:38:32 +01:00
return $ id ;
2014-05-13 12:15:52 +02:00
}
2020-01-31 10:56:02 +01:00
sub find_message_account_by_id {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id ) = @ _ ;
2020-01-31 10:56:02 +01:00
2020-02-15 23:38:32 +01:00
my $ hostmask = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT hostmask FROM Hostmasks WHERE id = ? ORDER BY last_seen DESC LIMIT 1' ) ;
$ sth - > execute ( $ id ) ;
my $ row = $ sth - > fetchrow_hashref ( ) ;
return $ row - > { hostmask } ;
} ;
2020-01-31 10:56:02 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return $ hostmask ;
2020-01-31 10:56:02 +01:00
}
2014-05-13 12:15:52 +02:00
sub find_message_account_by_nick {
2020-02-15 23:38:32 +01:00
my ( $ self , $ nick ) = @ _ ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my ( $ id , $ hostmask ) = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask FROM Hostmasks WHERE nick = ? ORDER BY last_seen DESC LIMIT 1' ) ;
$ sth - > execute ( $ nick ) ;
my $ row = $ sth - > fetchrow_hashref ( ) ;
return ( $ row - > { id } , $ row - > { hostmask } ) ;
} ;
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return ( $ id , $ hostmask ) ;
2014-05-13 12:15:52 +02:00
}
sub find_message_accounts_by_nickserv {
2020-02-15 23:38:32 +01:00
my ( $ self , $ nickserv ) = @ _ ;
my $ accounts = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id FROM Nickserv WHERE nickserv = ?' ) ;
$ sth - > execute ( $ nickserv ) ;
return $ sth - > fetchall_arrayref ( ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return map { $ _ - > [ 0 ] } @$ accounts ;
2014-05-13 12:15:52 +02:00
}
sub find_message_accounts_by_mask {
2020-02-15 23:38:32 +01:00
my ( $ self , $ mask ) = @ _ ;
my $ qmask = quotemeta $ mask ;
$ qmask =~ s/_/\\_/g ;
$ qmask =~ s/\\\*/%/g ;
$ qmask =~ s/\\\?/_/g ;
$ qmask =~ s/\\\$.*$// ;
my $ accounts = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id FROM Hostmasks WHERE hostmask LIKE ? ESCAPE "\"' ) ;
$ sth - > execute ( $ qmask ) ;
return $ sth - > fetchall_arrayref ( ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return map { $ _ - > [ 0 ] } @$ accounts ;
2014-05-13 12:15:52 +02:00
}
2019-05-08 10:38:59 +02:00
sub get_message_account_ancestor {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
my $ id = $ self - > get_message_account ( @ _ ) ;
$ id = $ self - > get_ancestor_id ( $ id ) ;
return $ id ;
2019-05-08 10:38:59 +02:00
}
2014-05-13 12:15:52 +02:00
sub get_message_account {
2020-02-15 23:38:32 +01:00
my ( $ self , $ nick , $ user , $ host , $ orig_nick ) = @ _ ;
2016-08-18 05:34:45 +02:00
2020-02-15 23:38:32 +01:00
( $ nick , $ user , $ host ) = $ self - > { pbot } - > { irchandlers } - > normalize_hostmask ( $ nick , $ user , $ host ) ;
2018-08-13 23:24:37 +02:00
2016-08-18 05:34:45 +02:00
= cut
use Devel::StackTrace ;
my $ trace = Devel::StackTrace - > new ( indent = > 1 , ignore_class = > [ 'PBot::PBot' , 'PBot::IRC' ] ) ;
$ self - > { pbot } - > { logger } - > log ( "get_message_account stacktrace: " . $ trace - > as_string ( ) . "\n" ) ;
= cut
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ mask = "$nick!$user\@$host" ;
my $ id = $ self - > get_message_account_id ( $ mask ) ;
return $ id if defined $ id ;
2014-05-13 12:15:52 +02:00
2021-07-12 02:05:55 +02:00
$ self - > { pbot } - > { logger } - > log ( "Getting new message account for $nick!$user\@$host\n" ) ;
$ self - > { pbot } - > { logger } - > log ( "Nick-changing from $orig_nick\n" ) if defined $ orig_nick ;
2016-11-29 10:50:49 +01:00
2020-02-15 23:38:32 +01:00
my $ do_nothing = 0 ;
my $ sth ;
2016-08-18 05:34:45 +02:00
2020-02-15 23:38:32 +01:00
my ( $ rows , $ link_type ) = eval {
my ( $ account1 ) = $ host =~ m {/([^/]+)$} ;
2021-07-12 02:05:55 +02:00
$ account1 // = '' ;
# extract ips from hosts like 75-42-36-105.foobar.com as 75.42.36.105
my $ hostip ;
2016-08-18 05:34:45 +02:00
2020-02-15 23:38:32 +01:00
if ( $ host =~ m/(\d+[[:punct:]]\d+[[:punct:]]\d+[[:punct:]]\d+)\D/ ) {
$ hostip = $ 1 ;
$ hostip =~ s/[[:punct:]]/./g ;
2017-08-02 06:36:31 +02:00
}
2016-11-29 10:50:49 +01:00
2021-07-12 02:05:55 +02:00
# nick-change from $orig_nick to $nick
2020-02-15 23:38:32 +01:00
if ( defined $ orig_nick ) {
2021-07-12 02:05:55 +02:00
# get original nick's account id
my $ orig_id = $ self - > get_message_account_id ( "$orig_nick!$user\@$host" ) ;
2020-02-15 23:38:32 +01:00
2021-07-12 02:05:55 +02:00
# changing nick to a Guest
2020-02-15 23:38:32 +01:00
if ( $ nick =~ m/^Guest\d+$/ ) {
2021-07-12 02:05:55 +02:00
# find most recent *!user@host, if any
$ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask, last_seen FROM Hostmasks WHERE user = ? and host = ? ORDER BY last_seen DESC LIMIT 1' ) ;
2020-02-15 23:38:32 +01:00
$ sth - > execute ( $ user , $ host ) ;
2021-07-12 02:05:55 +02:00
2020-02-15 23:38:32 +01:00
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
2021-07-12 02:05:55 +02:00
# found a probable match
2020-02-15 23:38:32 +01:00
if ( defined $ rows - > [ 0 ] ) {
my $ link_type = $ self - > { alias_type } - > { STRONG } ;
2021-07-12 02:05:55 +02:00
# if 48 hours have elapsed since this *!user@host was seen
# then still link the Guest to this account, but weakly
if ( time - $ rows - > [ 0 ] - > { last_seen } > 60 * 60 * 48 ) {
2020-02-15 23:38:32 +01:00
$ link_type = $ self - > { alias_type } - > { WEAK } ;
2021-07-12 02:05:55 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log (
2021-07-12 02:05:55 +02:00
"Longer than 48 hours (" . concise duration ( time - $ rows - > [ 0 ] - > { last_seen } ) . ")"
. " for $rows->[0]->{hostmask} for $nick!$user\@$host, degrading to weak link\n"
) ;
2020-02-15 23:38:32 +01:00
}
2021-07-12 02:05:55 +02:00
# log match and return link
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "6: nick-change guest match: $rows->[0]->{id}: $rows->[0]->{hostmask}\n" ) ;
2021-07-12 02:05:55 +02:00
$ orig_nick = undef ; # nick-change handled
2020-02-15 23:38:32 +01:00
return ( $ rows , $ link_type ) ;
2016-11-29 10:50:49 +01:00
}
}
2021-07-12 02:05:55 +02:00
# find all accounts that match nick!*@*, sorted by last-seen
2020-02-15 23:38:32 +01:00
$ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask, last_seen FROM Hostmasks WHERE nick = ? ORDER BY last_seen DESC' ) ;
$ sth - > execute ( $ nick ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
2016-11-29 10:50:49 +01:00
2021-07-12 02:05:55 +02:00
# no nicks found, strongly link to original account
2020-02-15 23:38:32 +01:00
if ( not defined $ rows - > [ 0 ] ) {
2021-07-12 02:05:55 +02:00
$ rows - > [ 0 ] = { id = > $ orig_id , hostmask = > "$orig_nick!$user\@$host" } ;
$ orig_nick = undef ; # nick-change handled
2020-02-15 23:38:32 +01:00
return ( $ rows , $ self - > { alias_type } - > { STRONG } ) ;
2016-11-29 10:50:49 +01:00
}
2016-08-18 05:34:45 +02:00
2021-07-12 02:05:55 +02:00
# look up original nick's NickServ accounts outside of upcoming loop
my @ orig_nickserv_accounts = $ self - > get_nickserv_accounts ( $ orig_id ) ;
# go over the list of nicks and see if any identifying details match
2020-02-15 23:38:32 +01:00
my % processed_nicks ;
my % processed_akas ;
2021-07-12 02:05:55 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
2021-07-12 02:05:55 +02:00
$ self - > { pbot } - > { logger } - > log ( "Found matching nick-change account: [$row->{id}] $row->{hostmask}\n" ) ;
2020-02-15 23:38:32 +01:00
my ( $ tnick ) = $ row - > { hostmask } =~ m/^([^!]+)!/ ;
2021-07-12 02:05:55 +02:00
# don't process duplicates
2020-02-15 23:38:32 +01:00
next if exists $ processed_nicks { lc $ tnick } ;
$ processed_nicks { lc $ tnick } = 1 ;
2021-07-12 02:05:55 +02:00
# get all akas for this nick
2020-02-15 23:38:32 +01:00
my % akas = $ self - > get_also_known_as ( $ tnick ) ;
2021-07-12 02:05:55 +02:00
# check each aka for identifying details
2020-02-15 23:38:32 +01:00
foreach my $ aka ( keys % akas ) {
2021-07-12 02:05:55 +02:00
# skip dubious links
2020-02-15 23:38:32 +01:00
next if $ akas { $ aka } - > { type } == $ self - > { alias_type } - > { WEAK } ;
next if $ akas { $ aka } - > { nickchange } == 1 ;
2021-07-12 02:05:55 +02:00
# don't process duplicates
2020-02-15 23:38:32 +01:00
next if exists $ processed_akas { $ akas { $ aka } - > { id } } ;
$ processed_akas { $ akas { $ aka } - > { id } } = 1 ;
$ self - > { pbot } - > { logger } - > log ( "Testing alias [$akas{$aka}->{id}] $aka\n" ) ;
my $ match = 0 ;
2021-07-12 02:05:55 +02:00
# account ids or *!user@host matches
2020-02-15 23:38:32 +01:00
if ( $ akas { $ aka } - > { id } == $ orig_id || $ aka =~ m/^.*!\Q$user\E\@\Q$host\E$/i ) {
$ self - > { pbot } - > { logger } - > log ( "1: match: $akas{$aka}->{id} vs $orig_id // $aka vs *!$user\@$host\n" ) ;
$ match = 1 ;
goto MATCH ;
}
2021-07-12 02:05:55 +02:00
# check if any nickserv accounts match
2020-02-15 23:38:32 +01:00
if ( @ orig_nickserv_accounts ) {
my @ nickserv_accounts = $ self - > get_nickserv_accounts ( $ akas { $ aka } - > { id } ) ;
foreach my $ ns1 ( @ orig_nickserv_accounts ) {
foreach my $ ns2 ( @ nickserv_accounts ) {
if ( $ ns1 eq $ ns2 ) {
$ self - > { pbot } - > { logger } - > log ( "Got matching nickserv: $ns1\n" ) ;
$ match = 1 ;
goto MATCH ;
}
}
}
}
2021-07-12 02:05:55 +02:00
# check if hosts match
2020-02-15 23:38:32 +01:00
my ( $ thost ) = $ aka =~ m/@(.*)$/ ;
if ( $ thost =~ m {/} ) {
my ( $ account2 ) = $ thost =~ m {/([^/]+)$} ;
if ( $ account1 ne $ account2 ) {
$ self - > { pbot } - > { logger } - > log ( "Skipping non-matching cloaked hosts: $host vs $thost\n" ) ;
next ;
} else {
$ self - > { pbot } - > { logger } - > log ( "Cloaked hosts match: $host vs $thost\n" ) ;
2021-07-12 02:05:55 +02:00
$ rows - > [ 0 ] = {
id = > $ self - > get_ancestor_id ( $ akas { $ aka } - > { id } ) ,
hostmask = > $ aka ,
} ;
2020-02-15 23:38:32 +01:00
return ( $ rows , $ self - > { alias_type } - > { STRONG } ) ;
}
}
2021-07-12 02:05:55 +02:00
# fuzzy match hosts
2020-02-15 23:38:32 +01:00
my $ distance = fastdistance ( $ host , $ thost ) ;
my $ length = ( length ( $ host ) > length ( $ thost ) ) ? length $ host : length $ thost ;
#$self->{pbot}->{logger}->log("distance: " . ($distance / $length) . " -- $host vs $thost\n") if $length != 0;
if ( $ length != 0 && $ distance / $ length < 0.50 ) {
$ self - > { pbot } - > { logger } - > log ( "2: distance match: $host vs $thost == " . ( $ distance / $ length ) . "\n" ) ;
$ match = 1 ;
} else {
# handle cases like 99.57.140.149 vs 99-57-140-149.lightspeed.sntcca.sbcglobal.net
if ( defined $ hostip ) {
if ( $ hostip eq $ thost ) {
$ match = 1 ;
$ self - > { pbot } - > { logger } - > log ( "3: IP vs hostname match: $host vs $thost\n" ) ;
}
} elsif ( $ thost =~ m/(\d+[[:punct:]]\d+[[:punct:]]\d+[[:punct:]]\d+)\D/ ) {
my $ thostip = $ 1 ;
$ thostip =~ s/[[:punct:]]/./g ;
if ( $ thostip eq $ host ) {
$ match = 1 ;
$ self - > { pbot } - > { logger } - > log ( "4: IP vs hostname match: $host vs $thost\n" ) ;
}
}
}
MATCH:
if ( $ match ) {
$ self - > { pbot } - > { logger } - > log ( "Using this match.\n" ) ;
$ rows - > [ 0 ] = { id = > $ self - > get_ancestor_id ( $ akas { $ aka } - > { id } ) , hostmask = > $ aka } ;
return ( $ rows , $ self - > { alias_type } - > { STRONG } ) ;
}
}
}
2016-08-18 05:34:45 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Creating new nickchange account!\n" ) ;
2015-06-14 01:08:57 +02:00
2020-02-15 23:38:32 +01:00
my $ new_id = $ self - > add_message_account ( $ mask ) ;
$ self - > link_alias ( $ orig_id , $ new_id , $ self - > { alias_type } - > { WEAK } ) ;
2021-07-12 02:05:55 +02:00
$ self - > update_hostmask_data ( $ mask , { nickchange = > 1 , last_seen = > scalar time } ) ;
2015-06-14 01:08:57 +02:00
2020-02-15 23:38:32 +01:00
$ do_nothing = 1 ;
$ rows - > [ 0 ] = { id = > $ new_id } ;
return ( $ rows , 0 ) ;
2021-07-12 02:05:55 +02:00
} # end nick-change
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
if ( $ host =~ m {^gateway/web/irccloud.com} ) {
$ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask, last_seen FROM Hostmasks WHERE host = ? ORDER BY last_seen DESC' ) ;
$ sth - > execute ( "gateway/web/irccloud.com/x-$user" ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
if ( defined $ rows - > [ 0 ] ) {
$ self - > { pbot } - > { logger } - > log ( "5: irccloud match: $rows->[0]->{id}: $rows->[0]->{hostmask}\n" ) ;
return ( $ rows , $ self - > { alias_type } - > { STRONG } ) ;
}
}
2016-08-18 05:34:45 +02:00
2020-02-15 23:38:32 +01:00
if ( $ host =~ m {^nat/([^/]+)/} ) {
my $ nat = $ 1 ;
$ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask, last_seen FROM Hostmasks WHERE nick = ? AND host = ? ORDER BY last_seen DESC' ) ;
$ sth - > execute ( $ nick , "nat/$nat/x-$user" ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
if ( defined $ rows - > [ 0 ] ) {
$ self - > { pbot } - > { logger } - > log ( "6: nat match: $rows->[0]->{id}: $rows->[0]->{hostmask}\n" ) ;
return ( $ rows , $ self - > { alias_type } - > { STRONG } ) ;
}
2017-08-02 06:36:31 +02:00
}
2020-02-15 23:38:32 +01:00
# cloaked hostmask
if ( $ host =~ m {/} ) {
$ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask, last_seen FROM Hostmasks WHERE host = ? ORDER BY last_seen DESC' ) ;
$ sth - > execute ( $ host ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
if ( defined $ rows - > [ 0 ] ) {
$ self - > { pbot } - > { logger } - > log ( "6: cloak match: $rows->[0]->{id}: $rows->[0]->{hostmask}\n" ) ;
return ( $ rows , $ self - > { alias_type } - > { STRONG } ) ;
}
}
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
# guests
if ( $ nick =~ m/^Guest\d+$/ ) {
$ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask, last_seen FROM Hostmasks WHERE user = ? and host = ? ORDER BY last_seen DESC' ) ;
$ sth - > execute ( $ user , $ host ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
if ( defined $ rows - > [ 0 ] ) {
my $ link_type = $ self - > { alias_type } - > { STRONG } ;
2021-07-12 02:05:55 +02:00
if ( time - $ rows - > [ 0 ] - > { last_seen } > 60 * 60 * 48 ) {
2020-02-15 23:38:32 +01:00
$ link_type = $ self - > { alias_type } - > { WEAK } ;
$ self - > { pbot } - > { logger } - > log (
2021-07-12 02:05:55 +02:00
"Longer than 48 hours (" . concise duration ( time - $ rows - > [ 0 ] - > { last_seen } ) . ") for $rows->[0]->{hostmask} for $nick!$user\@$host, degrading to weak link\n" ) ;
2020-02-15 23:38:32 +01:00
}
$ self - > { pbot } - > { logger } - > log ( "6: guest match: $rows->[0]->{id}: $rows->[0]->{hostmask}\n" ) ;
return ( $ rows , $ link_type ) ;
}
}
2016-08-26 13:02:30 +02:00
2020-02-15 23:38:32 +01:00
$ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask, last_seen FROM Hostmasks WHERE nick = ? ORDER BY last_seen DESC' ) ;
$ sth - > execute ( $ nick ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
2016-08-18 05:34:45 +02:00
2020-02-15 23:38:32 +01:00
my $ link_type = $ self - > { alias_type } - > { WEAK } ;
my % processed_nicks ;
my % processed_akas ;
2016-08-26 13:02:30 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
$ self - > { pbot } - > { logger } - > log ( "Found matching nick $row->{hostmask} with id $row->{id}\n" ) ;
my ( $ tnick ) = $ row - > { hostmask } =~ m/^([^!]+)!/ ;
2016-08-18 05:34:45 +02:00
2020-02-15 23:38:32 +01:00
next if exists $ processed_nicks { lc $ tnick } ;
$ processed_nicks { lc $ tnick } = 1 ;
2016-08-18 05:34:45 +02:00
2020-02-15 23:38:32 +01:00
my % akas = $ self - > get_also_known_as ( $ tnick ) ;
foreach my $ aka ( keys % akas ) {
next if $ akas { $ aka } - > { type } == $ self - > { alias_type } - > { WEAK } ;
next if $ akas { $ aka } - > { nickchange } == 1 ;
2016-08-18 05:34:45 +02:00
2020-02-15 23:38:32 +01:00
next if exists $ processed_akas { $ akas { $ aka } - > { id } } ;
$ processed_akas { $ akas { $ aka } - > { id } } = 1 ;
2016-08-26 13:02:30 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Testing alias [$akas{$aka}->{id}] $aka\n" ) ;
2016-11-29 10:50:49 +01:00
2020-02-15 23:38:32 +01:00
my ( $ thost ) = $ aka =~ m/@(.*)$/ ;
2016-11-29 10:50:49 +01:00
2020-02-15 23:38:32 +01:00
if ( $ thost =~ m {/} ) {
my ( $ account2 ) = $ thost =~ m {/([^/]+)$} ;
2016-11-29 10:50:49 +01:00
2020-02-15 23:38:32 +01:00
if ( $ account1 ne $ account2 ) {
$ self - > { pbot } - > { logger } - > log ( "Skipping non-matching cloaked hosts: $host vs $thost\n" ) ;
next ;
} else {
$ self - > { pbot } - > { logger } - > log ( "Cloaked hosts match: $host vs $thost\n" ) ;
$ rows - > [ 0 ] = { id = > $ self - > get_ancestor_id ( $ akas { $ aka } - > { id } ) , hostmask = > $ aka } ;
return ( $ rows , $ self - > { alias_type } - > { STRONG } ) ;
}
}
2016-11-29 10:50:49 +01:00
2020-02-15 23:38:32 +01:00
my $ distance = fastdistance ( $ host , $ thost ) ;
my $ length = ( length ( $ host ) > length ( $ thost ) ) ? length $ host : length $ thost ;
#$self->{pbot}->{logger}->log("distance: " . ($distance / $length) . " -- $host vs $thost\n") if $length != 0;
my $ match = 0 ;
if ( $ length != 0 && $ distance / $ length < 0.50 ) {
$ self - > { pbot } - > { logger } - > log ( "7: distance match: $host vs $thost == " . ( $ distance / $ length ) . "\n" ) ;
$ match = 1 ;
} else {
# handle cases like 99.57.140.149 vs 99-57-140-149.lightspeed.sntcca.sbcglobal.net
if ( defined $ hostip ) {
if ( $ hostip eq $ thost ) {
$ match = 1 ;
$ self - > { pbot } - > { logger } - > log ( "8: IP vs hostname match: $host vs $thost\n" ) ;
}
} elsif ( $ thost =~ m/(\d+[[:punct:]]\d+[[:punct:]]\d+[[:punct:]]\d+)\D/ ) {
my $ thostip = $ 1 ;
$ thostip =~ s/[[:punct:]]/./g ;
if ( $ thostip eq $ host ) {
$ match = 1 ;
$ self - > { pbot } - > { logger } - > log ( "9: IP vs hostname match: $host vs $thost\n" ) ;
}
}
}
2016-11-29 10:50:49 +01:00
2020-02-15 23:38:32 +01:00
if ( $ match ) {
$ rows - > [ 0 ] = { id = > $ self - > get_ancestor_id ( $ akas { $ aka } - > { id } ) , hostmask = > $ aka } ;
return ( $ rows , $ self - > { alias_type } - > { STRONG } ) ;
}
2016-11-29 10:50:49 +01:00
}
2016-08-26 13:02:30 +02:00
}
2020-02-15 23:38:32 +01:00
if ( not defined $ rows - > [ 0 ] ) {
$ link_type = $ self - > { alias_type } - > { STRONG } ;
2017-06-18 12:40:51 +02:00
2020-02-15 23:38:32 +01:00
$ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask, last_seen FROM Hostmasks WHERE user = ? AND host = ? ORDER BY last_seen DESC' ) ;
$ sth - > execute ( $ user , $ host ) ;
$ rows = $ sth - > fetchall_arrayref ( { } ) ;
2014-05-13 12:15:52 +02:00
2021-07-12 02:05:55 +02:00
if ( defined $ rows - > [ 0 ] and time - $ rows - > [ 0 ] - > { last_seen } > 60 * 60 * 48 ) {
2020-02-15 23:38:32 +01:00
$ link_type = $ self - > { alias_type } - > { WEAK } ;
$ self - > { pbot } - > { logger } - > log (
2021-07-12 02:05:55 +02:00
"Longer than 48 hours (" . concise duration ( time - $ rows - > [ 0 ] - > { last_seen } ) . ") for $rows->[0]->{hostmask} for $nick!$user\@$host, degrading to weak link\n" ) ;
2020-02-15 23:38:32 +01:00
}
2016-08-18 05:34:45 +02:00
2015-02-16 05:30:28 +01:00
= cut
2014-05-13 12:15:52 +02:00
foreach my $ row ( @$ rows ) {
2014-05-18 22:09:05 +02:00
$ self - > { pbot } - > { logger } - > log ( "Found matching user\@host mask $row->{hostmask} with id $row->{id}\n" ) ;
2014-05-13 12:15:52 +02:00
}
2015-02-16 05:30:28 +01:00
= cut
2017-06-18 12:40:51 +02:00
2020-02-15 23:38:32 +01:00
}
if ( defined $ rows - > [ 0 ] ) { $ self - > { pbot } - > { logger } - > log ( "10: matching *!user\@host: $rows->[0]->{id}: $rows->[0]->{hostmask}\n" ) ; }
2017-06-18 12:40:51 +02:00
2020-02-15 23:38:32 +01:00
return ( $ rows , $ link_type ) ;
} ;
2021-07-12 02:05:55 +02:00
if ( my $ exception = $@ ) {
$ self - > { pbot } - > { logger } - > log ( "Exception getting account: $exception" ) ;
}
# nothing else to do here for nick-change, return id
2020-02-15 23:38:32 +01:00
return $ rows - > [ 0 ] - > { id } if $ do_nothing ;
if ( defined $ rows - > [ 0 ] and not defined $ orig_nick ) {
if ( $ link_type == $ self - > { alias_type } - > { STRONG } ) {
my $ host1 = lc "$nick!$user\@$host" ;
my $ host2 = lc $ rows - > [ 0 ] - > { hostmask } ;
2021-07-12 02:05:55 +02:00
2020-02-15 23:38:32 +01:00
my ( $ nick1 ) = $ host1 =~ m/^([^!]+)!/ ;
my ( $ nick2 ) = $ host2 =~ m/^([^!]+)!/ ;
2021-07-12 02:05:55 +02:00
2020-02-15 23:38:32 +01:00
my $ distance = fastdistance ( $ nick1 , $ nick2 ) ;
my $ length = ( length $ nick1 > length $ nick2 ) ? length $ nick1 : length $ nick2 ;
2021-07-12 02:05:55 +02:00
my $ irc_cloak = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'cloak' ) // 'user' ;
if ( $ distance > 1 && ( $ nick1 !~ /^guest/ && $ nick2 !~ /^guest/ ) && ( $ host1 !~ /$irc_cloak/ || $ host2 !~ /$irc_cloak/ ) ) {
2020-02-15 23:38:32 +01:00
my $ id = $ rows - > [ 0 ] - > { id } ;
$ self - > { pbot } - > { logger } - > log ( "[$nick1][$nick2] $distance / $length\n" ) ;
$ self - > { pbot } - > { logger } - > log ( "Possible bogus account: ($id) $host1 vs ($id) $host2\n" ) ;
}
}
$ self - > { pbot } - > { logger } - > log ( "message-history: [get-account] $nick!$user\@$host "
2021-07-12 02:05:55 +02:00
. ( $ link_type == $ self - > { alias_type } - > { WEAK } ? "weakly linked to" : "added to account" )
. " $rows->[0]->{hostmask} with id $rows->[0]->{id}\n"
) ;
2020-02-15 23:38:32 +01:00
$ self - > add_message_account ( "$nick!$user\@$host" , $ rows - > [ 0 ] - > { id } , $ link_type ) ;
$ self - > devalidate_all_channels ( $ rows - > [ 0 ] - > { id } ) ;
2021-07-12 02:05:55 +02:00
$ self - > update_hostmask_data ( "$nick!$user\@$host" , { last_seen = > scalar time } ) ;
2020-02-15 23:38:32 +01:00
my @ nickserv_accounts = $ self - > get_nickserv_accounts ( $ rows - > [ 0 ] - > { id } ) ;
foreach my $ nickserv_account ( @ nickserv_accounts ) {
$ self - > { pbot } - > { logger } - > log ( "$nick!$user\@$host [$rows->[0]->{id}] seen with nickserv account [$nickserv_account]\n" ) ;
$ self - > { pbot } - > { antiflood } - > check_nickserv_accounts ( $ nick , $ nickserv_account , "$nick!$user\@$host" ) ;
}
2021-07-12 02:05:55 +02:00
2020-02-15 23:38:32 +01:00
return $ rows - > [ 0 ] - > { id } ;
2014-05-13 12:15:52 +02:00
}
2021-07-12 02:05:55 +02:00
$ self - > { pbot } - > { logger } - > log ( "No account found for $mask, adding new account\n" ) ;
2020-02-15 23:38:32 +01:00
return $ self - > add_message_account ( $ mask ) ;
2014-05-13 12:15:52 +02:00
}
sub find_most_recent_hostmask {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id ) = @ _ ;
my $ hostmask = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT hostmask FROM Hostmasks WHERE ID = ? ORDER BY last_seen DESC LIMIT 1' ) ;
$ sth - > execute ( $ id ) ;
return $ sth - > fetchrow_hashref ( ) - > { 'hostmask' } ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return $ hostmask ;
2014-05-13 12:15:52 +02:00
}
sub update_hostmask_data {
2020-02-15 23:38:32 +01:00
my ( $ self , $ mask , $ data ) = @ _ ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
eval {
my $ sql = 'UPDATE Hostmasks SET ' ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ comma = '' ;
foreach my $ key ( keys %$ data ) {
$ sql . = "$comma$key = ?" ;
$ comma = ', ' ;
}
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ sql . = ' WHERE hostmask == ?' ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ sth = $ self - > { dbh } - > prepare ( $ sql ) ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ param = 1 ;
foreach my $ key ( keys %$ data ) { $ sth - > bind_param ( $ param + + , $ data - > { $ key } ) ; }
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( $ param , $ mask ) ;
$ sth - > execute ( ) ;
$ self - > { new_entries } + + ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2014-05-13 12:15:52 +02:00
}
2015-03-27 12:08:47 +01:00
sub get_nickserv_accounts_for_hostmask {
2020-02-15 23:38:32 +01:00
my ( $ self , $ hostmask ) = @ _ ;
2015-03-27 12:08:47 +01:00
2020-02-15 23:38:32 +01:00
my $ nickservs = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT nickserv FROM Hostmasks, Nickserv WHERE nickserv.id = hostmasks.id AND hostmasks.hostmask = ?' ) ;
$ sth - > execute ( $ hostmask ) ;
return $ sth - > fetchall_arrayref ( ) ;
} ;
2015-03-27 12:08:47 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return map { $ _ - > [ 0 ] } @$ nickservs ;
2015-03-27 12:08:47 +01:00
}
2016-02-10 12:42:42 +01:00
sub get_gecos_for_hostmask {
2020-02-15 23:38:32 +01:00
my ( $ self , $ hostmask ) = @ _ ;
2016-02-10 12:42:42 +01:00
2020-02-15 23:38:32 +01:00
my $ gecos = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT gecos FROM Hostmasks, Gecos WHERE gecos.id = hostmasks.id AND hostmasks.hostmask = ?' ) ;
$ sth - > execute ( $ hostmask ) ;
return $ sth - > fetchall_arrayref ( ) ;
} ;
2016-02-10 12:42:42 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return map { $ _ - > [ 0 ] } @$ gecos ;
2016-02-10 12:42:42 +01:00
}
2014-05-13 12:15:52 +02:00
sub get_hostmasks_for_channel {
2020-02-15 23:38:32 +01:00
my ( $ self , $ channel ) = @ _ ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ hostmasks = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT hostmasks.id, hostmask FROM Hostmasks, Channels WHERE channels.id = hostmasks.id AND channel = ?' ) ;
$ sth - > execute ( $ channel ) ;
return $ sth - > fetchall_arrayref ( { } ) ;
} ;
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return $ hostmasks ;
2014-05-13 12:15:52 +02:00
}
2015-03-27 12:08:47 +01:00
sub get_hostmasks_for_nickserv {
2020-02-15 23:38:32 +01:00
my ( $ self , $ nickserv ) = @ _ ;
2015-03-27 12:08:47 +01:00
2020-02-15 23:38:32 +01:00
my $ hostmasks = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT hostmasks.id, hostmask, nickserv FROM Hostmasks, Nickserv WHERE nickserv.id = hostmasks.id AND nickserv = ?' ) ;
$ sth - > execute ( $ nickserv ) ;
return $ sth - > fetchall_arrayref ( { } ) ;
} ;
2015-03-27 12:08:47 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return $ hostmasks ;
2015-03-27 12:08:47 +01:00
}
2014-05-13 12:15:52 +02:00
sub add_message {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ mask , $ channel , $ message ) = @ _ ;
#$self->{pbot}->{logger}->log("Adding message [$id][$mask][$channel][$message->{msg}][$message->{timestamp}][$message->{mode}]\n");
eval {
my $ sth = $ self - > { dbh } - > prepare ( 'INSERT INTO Messages VALUES (?, ?, ?, ?, ?)' ) ;
$ sth - > execute ( $ id , $ channel , $ message - > { msg } , $ message - > { timestamp } , $ message - > { mode } ) ;
$ 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 } } ) ;
2014-05-13 12:15:52 +02:00
}
sub get_recent_messages {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ channel , $ limit , $ mode , $ nick ) = @ _ ;
$ limit = 25 if not defined $ limit ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ channel = lc $ channel ;
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
my $ mode_query = '' ;
$ mode_query = "AND mode = $mode" if defined $ mode ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ messages = eval {
my $ sql = "SELECT msg, mode, timestamp FROM Messages WHERE " ;
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
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 } ; }
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
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 ;
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
$ ids . = "${or}id = ?" ;
$ or = ' OR ' ;
}
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
$ 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 ) ;
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
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 ;
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( $ param + + , $ akas { $ aka } - > { id } ) ;
}
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( $ param + + , $ channel ) ;
$ sth - > bind_param ( $ param + + , $ limit ) ;
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
% 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 } ) ;
}
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( $ param + + , $ channel ) ;
$ sth - > bind_param ( $ param , $ limit ) ;
$ sth - > execute ( ) ;
return $ sth - > fetchall_arrayref ( { } ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return $ messages ;
2014-05-13 12:15:52 +02:00
}
2020-01-31 10:56:02 +01:00
sub get_recent_messages_from_channel {
2020-02-15 23:38:32 +01:00
my ( $ self , $ channel , $ limit , $ mode , $ direction ) = @ _ ;
$ limit = 25 if not defined $ limit ;
$ direction = 'ASC' if not defined $ direction ;
$ channel = lc $ channel ;
my $ mode_query = '' ;
$ 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 $ sth = $ self - > { dbh } - > prepare ( $ sql ) ;
$ sth - > execute ( $ channel , $ limit ) ;
return $ sth - > fetchall_arrayref ( { } ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return $ messages ;
2020-01-31 10:56:02 +01:00
}
2015-01-23 16:36:39 +01:00
sub get_message_context {
2020-02-15 23:38:32 +01:00
my ( $ self , $ message , $ before , $ after , $ count , $ text , $ context_id ) = @ _ ;
my ( $ messages_before , $ messages_after , $ messages_count ) ;
if ( defined $ count and $ count > 1 ) {
2020-04-19 21:42:44 +02:00
my $ search = "%$text%" ;
$ search =~ s/\*/%/g ;
$ search =~ s/\?/_/g ;
2020-02-15 23:38:32 +01:00
$ messages_count = eval {
my $ sth ;
if ( defined $ context_id ) {
$ sth = $ self - > { dbh } - > prepare (
2020-04-19 21:42:44 +02:00
'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 ?' ) ;
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( 1 , $ context_id ) ;
$ sth - > bind_param ( 2 , $ message - > { channel } ) ;
2020-04-19 21:42:44 +02:00
$ sth - > bind_param ( 3 , $ search ) ;
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( 4 , $ message - > { timestamp } ) ;
$ sth - > bind_param ( 5 , $ count - 1 ) ;
} else {
$ sth = $ self - > { dbh }
2020-04-19 21:42:44 +02:00
- > 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 ?' ) ;
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( 1 , $ message - > { channel } ) ;
2020-04-19 21:42:44 +02:00
$ sth - > bind_param ( 2 , $ search ) ;
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( 3 , $ message - > { timestamp } ) ;
$ sth - > bind_param ( 4 , $ count - 1 ) ;
}
$ sth - > execute ( ) ;
return [ reverse @ { $ sth - > fetchall_arrayref ( { } ) } ] ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
}
2015-01-23 16:36:39 +01:00
2020-02-15 23:38:32 +01:00
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 ( ) ;
return [ reverse @ { $ sth - > fetchall_arrayref ( { } ) } ] ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
}
2015-06-16 02:58:25 +02:00
2020-02-15 23:38:32 +01:00
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 ( ) ;
return $ sth - > fetchall_arrayref ( { } ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
}
2015-06-16 02:58:25 +02:00
2020-02-15 23:38:32 +01:00
my @ messages ;
push ( @ messages , @$ messages_before ) if defined $ messages_before ;
push ( @ messages , @$ messages_count ) if defined $ messages_count ;
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 } } ;
2015-01-23 16:36:39 +01:00
}
2020-02-15 23:38:32 +01:00
return \ @ messages ;
2015-01-23 16:36:39 +01:00
}
2014-05-13 12:15:52 +02:00
sub recall_message_by_count {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ channel , $ count , $ ignore_command , $ use_aliases ) = @ _ ;
my $ messages ;
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 ' ;
}
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
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 ) ;
2014-05-14 23:23:59 +02:00
2020-02-15 23:38:32 +01:00
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 ;
2016-12-05 07:00:50 +01:00
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( $ param + + , $ akas { $ aka } - > { id } ) ;
}
2016-12-05 07:00:50 +01:00
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( $ param + + , $ channel ) ;
$ sth - > bind_param ( $ param + + , $ count ) ;
$ sth - > execute ( ) ;
return $ sth - > fetchall_arrayref ( { } ) ;
} 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 ( { } ) ;
}
} ;
} 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 ( { } ) ;
} ;
}
2016-12-05 07:00:50 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2016-12-05 07:00:50 +01:00
2020-02-15 23:38:32 +01:00
if ( defined $ ignore_command ) {
my $ botnick = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'botnick' ) ;
my $ bot_trigger = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'trigger' ) ;
foreach my $ message ( @$ messages ) {
next if $ message - > { msg } =~ m/^$botnick.? $ignore_command/ or $ message - > { msg } =~ m/^$bot_trigger$ignore_command/ ;
return $ message ;
2016-12-05 07:00:50 +01:00
}
2020-02-15 23:38:32 +01:00
return undef ;
2014-05-13 12:15:52 +02:00
}
2020-02-15 23:38:32 +01:00
return $ messages - > [ 0 ] ;
2014-05-13 12:15:52 +02:00
}
sub recall_message_by_text {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ channel , $ text , $ ignore_command ) = @ _ ;
2019-06-26 18:34:19 +02:00
2020-04-19 21:42:44 +02:00
my $ search = "%$text%" ;
$ search =~ s/\*/%/g ;
$ search =~ s/\?/_/g ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ messages ;
2014-05-14 23:23:59 +02:00
2020-02-15 23:38:32 +01:00
if ( defined $ id ) {
$ messages = eval {
2020-04-19 21:42:44 +02:00
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 ) ;
2020-02-15 23:38:32 +01:00
return $ sth - > fetchall_arrayref ( { } ) ;
} ;
} else {
$ messages = eval {
2020-04-19 21:42:44 +02:00
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 ) ;
2020-02-15 23:38:32 +01:00
return $ sth - > fetchall_arrayref ( { } ) ;
} ;
}
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
if ( defined $ ignore_command ) {
my $ bot_trigger = $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'trigger' ) ;
my $ botnick = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'botnick' ) ;
foreach my $ message ( @$ messages ) {
next
if $ message - > { msg } =~ m/^$botnick.? $ignore_command/i
or $ message - > { msg } =~ m/^(?:\s*[^,:\(\)\+\*\/ ]+[,:]?\s+)?$bot_trigger$ignore_command/i
or $ message - > { msg } =~ m/^\s*$ignore_command.? $botnick$/i ;
return $ message ;
}
return undef ;
2014-05-13 12:15:52 +02:00
}
2020-02-15 23:38:32 +01:00
return $ messages - > [ 0 ] ;
2014-05-13 12:15:52 +02:00
}
2020-04-30 02:49:31 +02:00
sub get_random_message {
my ( $ self , $ id , $ channel ) = @ _ ;
my $ message ;
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 ;
} ;
}
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return $ message ;
}
2014-05-13 12:15:52 +02:00
sub get_max_messages {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ channel , $ use_aliases ) = @ _ ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ count = eval {
my $ sql = "SELECT COUNT(*) FROM Messages WHERE channel = ? AND " ;
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
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 } ; }
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
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 ;
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
$ ids . = "${or}id = ?" ;
$ or = ' OR ' ;
}
$ sql . = "($ids)" ;
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
my $ sth = $ self - > { dbh } - > prepare ( $ sql ) ;
my $ param = 1 ;
$ sth - > bind_param ( $ param + + , $ channel ) ;
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
% 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 ;
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( $ param + + , $ akas { $ aka } - > { id } ) ;
}
2016-12-01 13:57:25 +01:00
2020-02-15 23:38:32 +01:00
$ sth - > execute ( ) ;
my $ row = $ sth - > fetchrow_hashref ( ) ;
$ sth - > finish ( ) ;
return $ row - > { 'COUNT(*)' } ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
$ count = 0 if not defined $ count ;
return $ count ;
2014-05-13 12:15:52 +02:00
}
sub create_channel {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ channel ) = @ _ ;
eval {
my $ sth = $ self - > { dbh } - > prepare ( 'INSERT OR IGNORE INTO Channels VALUES (?, ?, 0, 0, 0, 0, 0, 0, 0, 0)' ) ;
my $ rv = $ sth - > execute ( $ id , $ channel ) ;
$ self - > { new_entries } + + ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2014-05-13 12:15:52 +02:00
}
sub get_channels {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id ) = @ _ ;
my $ channels = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT channel FROM Channels WHERE id = ?' ) ;
$ sth - > execute ( $ id ) ;
return $ sth - > fetchall_arrayref ( ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return map { $ _ - > [ 0 ] } @$ channels ;
2014-05-13 12:15:52 +02:00
}
sub get_channel_data {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ channel , @ columns ) = @ _ ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ self - > create_channel ( $ id , $ channel ) ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ channel_data = eval {
my $ sql = 'SELECT ' ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
if ( not @ columns ) { $ sql . = '*' ; }
else {
my $ comma = '' ;
foreach my $ column ( @ columns ) {
$ sql . = "$comma$column" ;
$ comma = ', ' ;
}
}
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ sql . = ' FROM Channels WHERE id = ? AND channel = ?' ;
my $ sth = $ self - > { dbh } - > prepare ( $ sql ) ;
$ sth - > execute ( $ id , $ channel ) ;
return $ sth - > fetchrow_hashref ( ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return $ channel_data ;
2014-05-13 12:15:52 +02:00
}
sub update_channel_data {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ channel , $ data ) = @ _ ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ self - > create_channel ( $ id , $ channel ) ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
eval {
my $ sql = 'UPDATE Channels SET ' ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ comma = '' ;
foreach my $ key ( keys %$ data ) {
$ sql . = "$comma$key = ?" ;
$ comma = ', ' ;
}
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ sql . = ' WHERE id = ? AND channel = ?' ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ sth = $ self - > { dbh } - > prepare ( $ sql ) ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ param = 1 ;
foreach my $ key ( keys %$ data ) { $ sth - > bind_param ( $ param + + , $ data - > { $ key } ) ; }
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ sth - > bind_param ( $ param + + , $ id ) ;
$ sth - > bind_param ( $ param , $ channel ) ;
$ sth - > execute ( ) ;
$ self - > { new_entries } + + ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2014-05-13 12:15:52 +02:00
}
sub get_channel_datas_where_last_offense_older_than {
2020-02-15 23:38:32 +01:00
my ( $ self , $ timestamp ) = @ _ ;
my $ channel_datas = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id, channel, offenses, last_offense, unbanmes FROM Channels WHERE last_offense > 0 AND last_offense <= ?' ) ;
$ sth - > execute ( $ timestamp ) ;
return $ sth - > fetchall_arrayref ( { } ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return $ channel_datas ;
2014-05-13 12:15:52 +02:00
}
sub get_channel_datas_with_enter_abuses {
2020-02-15 23:38:32 +01:00
my ( $ self ) = @ _ ;
my $ channel_datas = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id, channel, enter_abuses, last_offense FROM Channels WHERE enter_abuses > 0' ) ;
$ sth - > execute ( ) ;
return $ sth - > fetchall_arrayref ( { } ) ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return $ channel_datas ;
2014-05-13 12:15:52 +02:00
}
2016-08-30 11:14:21 +02:00
sub devalidate_channel {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ channel , $ mode ) = @ _ ;
2016-08-30 11:14:21 +02:00
2020-02-15 23:38:32 +01:00
$ mode = 0 if not defined $ mode ;
2016-08-30 11:14:21 +02:00
2020-02-15 23:38:32 +01:00
eval {
my $ sth = $ self - > { dbh } - > prepare ( "UPDATE Channels SET validated = ? WHERE id = ? AND channel = ?" ) ;
$ sth - > execute ( $ mode , $ id , $ channel ) ;
$ self - > { new_entries } + + ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2016-08-30 11:14:21 +02:00
}
2014-05-13 12:15:52 +02:00
sub devalidate_all_channels {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ mode ) = @ _ ;
2014-08-12 07:51:21 +02:00
2020-02-15 23:38:32 +01:00
$ mode = 0 if not defined $ mode ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ where = '' ;
$ where = 'WHERE id = ?' if defined $ id ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
eval {
my $ sth = $ self - > { dbh } - > prepare ( "UPDATE Channels SET validated = ? $where" ) ;
$ sth - > bind_param ( 1 , $ mode ) ;
$ sth - > bind_param ( 2 , $ id ) if defined $ id ;
$ sth - > execute ( ) ;
$ self - > { new_entries } + + ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2014-05-13 12:15:52 +02:00
}
2015-05-12 06:27:22 +02:00
sub link_aliases {
2020-02-15 23:38:32 +01:00
my ( $ self , $ account , $ hostmask , $ nickserv ) = @ _ ;
2014-07-11 14:57:18 +02:00
2020-02-15 23:38:32 +01:00
my $ debug_link = $ self - > { pbot } - > { registry } - > get_value ( 'messagehistory' , 'debug_link' ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Linking [$account][" . ( $ hostmask ? $ hostmask : 'undef' ) . "][" . ( $ nickserv ? $ nickserv : 'undef' ) . "]\n" ) if $ debug_link >= 3 ;
2015-07-15 09:18:57 +02:00
2020-02-15 23:38:32 +01:00
eval {
my % ids ;
if ( $ hostmask ) {
my ( $ nick , $ host ) = $ hostmask =~ /^([^!]+)![^@]+@(.*)$/ ;
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id, last_seen FROM Hostmasks WHERE host = ?' ) ;
$ sth - > execute ( $ host ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
2021-07-12 02:05:55 +02:00
my $ now = time ;
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
my $ idhost = $ self - > find_most_recent_hostmask ( $ row - > { id } ) if $ debug_link >= 2 && $ row - > { id } != $ account ;
if ( $ now - $ row - > { last_seen } <= 60 * 60 * 48 ) {
$ ids { $ row - > { id } } = { id = > $ row - > { id } , type = > $ self - > { alias_type } - > { STRONG } , force = > 1 } ;
$ self - > { pbot } - > { logger } - > log ( "found STRONG matching id $row->{id} ($idhost) for host [$host]\n" ) if $ debug_link >= 2 && $ row - > { id } != $ account ;
} else {
$ ids { $ row - > { id } } = { id = > $ row - > { id } , type = > $ self - > { alias_type } - > { WEAK } } ;
$ self - > { pbot } - > { logger } - > log ( "found WEAK matching id $row->{id} ($idhost) for host [$host]\n" ) if $ debug_link >= 2 && $ row - > { id } != $ account ;
}
}
2015-07-15 09:18:57 +02:00
2020-02-15 23:38:32 +01:00
unless ( $ nick =~ m/^Guest\d+$/ ) {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask FROM Hostmasks WHERE nick = ?' ) ;
$ sth - > execute ( $ nick ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
2015-07-15 09:18:57 +02:00
2020-02-15 23:38:32 +01:00
my ( $ account1 ) = $ host =~ m {/([^/]+)$} ;
$ account1 = '' if not defined $ account1 ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
my $ hostip = undef ;
if ( $ host =~ m/(\d+[[:punct:]]\d+[[:punct:]]\d+[[:punct:]]\d+)\D/ ) {
$ hostip = $ 1 ;
$ hostip =~ s/[[:punct:]]/./g ;
}
2016-08-26 13:31:51 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
next if $ row - > { id } == $ account ;
$ self - > { pbot } - > { logger } - > log ( "Processing row $row->{hostmask}\n" ) ;
my ( $ thost ) = $ row - > { hostmask } =~ m/@(.*)$/ ;
if ( $ thost =~ m {/} ) {
my ( $ account2 ) = $ thost =~ m {/([^/]+)$} ;
if ( $ account1 ne $ account2 ) {
$ self - > { pbot } - > { logger } - > log ( "Skipping non-matching cloaked hosts: $host vs $thost\n" ) ;
next ;
} else {
$ self - > { pbot } - > { logger } - > log ( "Cloaked hosts match: $host vs $thost\n" ) ;
$ ids { $ row - > { id } } = { id = > $ row - > { id } , type = > $ self - > { alias_type } - > { STRONG } , force = > 1 } ;
}
}
my $ distance = fastdistance ( $ host , $ thost ) ;
my $ length = ( length ( $ host ) > length ( $ thost ) ) ? length $ host : length $ thost ;
#$self->{pbot}->{logger}->log("distance: " . ($distance / $length) . " -- $host vs $thost\n") if $length != 0;
if ( $ length != 0 && $ distance / $ length < 0.50 ) {
$ self - > { pbot } - > { logger } - > log ( "11: distance match: $host vs $thost == " . ( $ distance / $ length ) . "\n" ) ;
$ ids { $ row - > { id } } = { id = > $ row - > { id } , type = > $ self - > { alias_type } - > { STRONG } } ; # don't force linking
$ self - > { pbot } - > { logger } - > log ( "found STRONG matching id $row->{id} ($row->{hostmask}) for nick [$nick]\n" ) if $ debug_link >= 2 ;
} else {
# handle cases like 99.57.140.149 vs 99-57-140-149.lightspeed.sntcca.sbcglobal.net
if ( defined $ hostip ) {
if ( $ hostip eq $ thost ) {
$ ids { $ row - > { id } } = { id = > $ row - > { id } , type = > $ self - > { alias_type } - > { STRONG } } ; # don't force linking
$ self - > { pbot } - > { logger } - > log ( "IP vs hostname match: $host vs $thost\n" ) ;
}
} elsif ( $ thost =~ m/(\d+[[:punct:]]\d+[[:punct:]]\d+[[:punct:]]\d+)\D/ ) {
my $ thostip = $ 1 ;
$ thostip =~ s/[[:punct:]]/./g ;
if ( $ thostip eq $ host ) {
$ ids { $ row - > { id } } = { id = > $ row - > { id } , type = > $ self - > { alias_type } - > { STRONG } } ; # don't force linking
$ self - > { pbot } - > { logger } - > log ( "IP vs hostname match: $host vs $thost\n" ) ;
}
}
}
}
}
2016-08-26 13:31:51 +02:00
}
2020-02-15 23:38:32 +01:00
if ( $ nickserv ) {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id FROM Nickserv WHERE nickserv = ?' ) ;
$ sth - > execute ( $ nickserv ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
2016-08-26 13:31:51 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
my $ idhost = $ self - > find_most_recent_hostmask ( $ row - > { id } ) if $ debug_link >= 2 && $ row - > { id } != $ account ;
$ ids { $ row - > { id } } = { id = > $ row - > { id } , type = > $ self - > { alias_type } - > { STRONG } , force = > 1 } ;
$ self - > { pbot } - > { logger } - > log ( "12: found STRONG matching id $row->{id} ($idhost) for nickserv [$nickserv]\n" ) if $ debug_link >= 2 && $ row - > { id } != $ account ;
2016-08-26 13:31:51 +02:00
}
2015-05-12 06:27:22 +02:00
}
2020-02-15 23:38:32 +01:00
foreach my $ id ( sort keys % ids ) {
next if $ account == $ id ;
$ self - > link_alias ( $ account , $ id , $ ids { $ id } - > { type } , $ ids { $ id } - > { force } ) ;
}
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2015-05-12 06:27:22 +02:00
}
2015-05-12 21:59:45 +02:00
sub link_alias {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ alias , $ type , $ force ) = @ _ ;
my $ debug_link = $ self - > { pbot } - > { registry } - > get_value ( 'messagehistory' , 'debug_link' ) ;
$ self - > { pbot } - > { logger }
- > log ( "Attempting to " . ( $ force ? "forcefully " : "" ) . ( $ type == $ self - > { alias_type } - > { STRONG } ? "strongly" : "weakly" ) . " link $id to $alias\n" )
if $ debug_link >= 3 ;
my $ ret = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT type FROM Aliases WHERE id = ? AND alias = ? LIMIT 1' ) ;
$ sth - > execute ( $ alias , $ id ) ;
my $ row = $ sth - > fetchrow_hashref ( ) ;
if ( defined $ row ) {
if ( $ force ) {
if ( $ row - > { 'type' } != $ type ) {
$ self - > { pbot } - > { logger } - > log ( "$id already " . ( $ row - > { 'type' } == $ self - > { alias_type } - > { STRONG } ? "strongly" : "weakly" ) . " linked to $alias, forcing override\n" )
if $ debug_link >= 1 ;
$ sth = $ self - > { dbh } - > prepare ( 'UPDATE Aliases SET type = ? WHERE alias = ? AND id = ?' ) ;
$ sth - > execute ( $ type , $ id , $ alias ) ;
$ sth - > execute ( $ type , $ alias , $ id ) ;
return 1 ;
} else {
$ self - > { pbot } - > { logger } - > log ( "$id already " . ( $ row - > { 'type' } == $ self - > { alias_type } - > { STRONG } ? "strongly" : "weakly" ) . " linked to $alias, ignoring\n" )
if $ debug_link >= 4 ;
return 0 ;
}
} else {
$ self - > { pbot } - > { logger } - > log ( "$id already " . ( $ row - > { 'type' } == $ self - > { alias_type } - > { STRONG } ? "strongly" : "weakly" ) . " linked to $alias, ignoring\n" )
if $ debug_link >= 4 ;
return 0 ;
}
}
2016-08-25 10:41:32 +02:00
2020-02-15 23:38:32 +01:00
$ sth = $ self - > { dbh } - > prepare ( 'INSERT INTO Aliases VALUES (?, ?, ?)' ) ;
$ sth - > execute ( $ alias , $ id , $ type ) ;
$ sth - > execute ( $ id , $ alias , $ type ) ;
return 1 ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2016-08-25 10:41:32 +02:00
2020-02-15 23:38:32 +01:00
my $ host1 = $ self - > find_most_recent_hostmask ( $ id ) ;
my $ host2 = $ self - > find_most_recent_hostmask ( $ alias ) ;
$ self - > { pbot } - > { logger } - > log ( ( $ type == $ self - > { alias_type } - > { STRONG } ? "Strongly" : "Weakly" ) . " linked $id ($host1) to $alias ($host2).\n" ) if $ ret and $ debug_link ;
if ( $ ret ) {
$ host1 = lc $ host1 ;
$ host2 = lc $ host2 ;
my ( $ nick1 ) = $ host1 =~ m/^([^!]+)!/ ;
my ( $ nick2 ) = $ host2 =~ m/^([^!]+)!/ ;
my $ distance = fastdistance ( $ nick1 , $ nick2 ) ;
my $ length = ( length $ nick1 > length $ nick2 ) ? length $ nick1 : length $ nick2 ;
2021-07-12 02:05:55 +02:00
my $ irc_cloak = $ self - > { pbot } - > { registry } - > get_value ( 'irc' , 'cloak' ) // 'user' ;
if ( $ distance > 1 && ( $ nick1 !~ /^guest/ && $ nick2 !~ /^guest/ ) && ( $ host1 !~ /$irc_cloak/ || $ host2 !~ /$irc_cloak/ ) ) {
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "[$nick1][$nick2] $distance / $length\n" ) ;
$ self - > { pbot } - > { logger } - > log ( "Possible bogus link: ($id) $host1 vs ($alias) $host2\n" ) ;
2016-08-25 10:41:32 +02:00
}
2016-08-18 05:34:45 +02:00
}
2020-02-15 23:38:32 +01:00
return $ ret ;
2015-05-12 21:59:45 +02:00
}
sub unlink_alias {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id , $ alias ) = @ _ ;
my $ ret = eval {
my $ ret = 0 ;
my $ sth = $ self - > { dbh } - > prepare ( 'DELETE FROM Aliases WHERE id = ? AND alias = ?' ) ;
$ sth - > execute ( $ id , $ alias ) ;
if ( $ sth - > rows ) {
$ self - > { new_entries } + + ;
$ ret = 1 ;
}
2015-05-12 21:59:45 +02:00
2020-02-15 23:38:32 +01:00
$ sth - > execute ( $ alias , $ id ) ;
if ( $ sth - > rows ) {
$ self - > { new_entries } + + ;
$ ret = 1 ;
} else {
$ ret = 0 ;
}
return $ ret ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2015-05-12 21:59:45 +02:00
return $ ret ;
}
2015-05-12 06:27:22 +02:00
sub vacuum {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
eval { $ self - > { dbh } - > commit ( ) ; } ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "SQLite error $@ when committing $self->{new_entries} entries.\n" ) if $@ ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > do ( "VACUUM" ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { dbh } - > begin_work ( ) ;
$ self - > { new_entries } = 0 ;
2015-05-12 06:27:22 +02:00
}
sub rebuild_aliases_table {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
eval {
$ self - > { dbh } - > do ( 'DELETE FROM Aliases' ) ;
$ self - > vacuum ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask FROM Hostmasks ORDER BY id' ) ;
$ sth - > execute ( ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
2014-07-11 14:57:18 +02:00
2020-02-15 23:38:32 +01:00
$ sth = $ self - > { dbh } - > prepare ( 'SELECT nickserv FROM Nickserv WHERE id = ?' ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
$ self - > { pbot } - > { logger } - > log ( "Link [$row->{id}][$row->{hostmask}]\n" ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
$ self - > link_aliases ( $ row - > { id } , $ row - > { hostmask } ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
$ sth - > execute ( $ row - > { id } ) ;
my $ nrows = $ sth - > fetchall_arrayref ( { } ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ nrow ( @$ nrows ) { $ self - > link_aliases ( $ row - > { id } , undef , $ nrow - > { nickserv } ) ; }
}
} ;
2014-07-11 14:57:18 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2015-05-12 06:27:22 +02:00
}
sub get_also_known_as {
2020-02-15 23:38:32 +01:00
my ( $ self , $ nick , $ dont_use_aliases_table ) = @ _ ;
my $ debug = $ self - > { pbot } - > { registry } - > get_value ( 'messagehistory' , 'debug_aka' ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Looking for AKAs for nick [$nick]\n" ) if $ debug ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
my % akas = eval {
my ( % akas , % hostmasks , % ids ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
unless ( $ dont_use_aliases_table ) {
my ( $ id , $ hostmask ) = $ self - > find_message_account_by_nick ( $ nick ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
if ( not defined $ id ) { return % akas ; }
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
$ ids { $ id } = { id = > $ id , type = > $ self - > { alias_type } - > { STRONG } } ;
$ self - > { pbot } - > { logger } - > log ( "Adding $id -> $id\n" ) if $ debug ;
2015-05-13 06:46:40 +02:00
2020-02-15 23:38:32 +01:00
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT alias, type FROM Aliases WHERE id = ?' ) ;
$ sth - > execute ( $ id ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
2015-05-13 06:46:40 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
2020-02-16 00:16:38 +01:00
# next if $row->{type} == $self->{alias_type}->{WEAK};
2020-02-15 23:38:32 +01:00
$ ids { $ row - > { alias } } = { id = > $ id , type = > $ row - > { type } } ;
$ self - > { pbot } - > { logger } - > log ( "[$id] 1) Adding $row->{alias} -> $id [type $row->{type}]\n" ) if $ debug ;
}
2015-05-22 13:23:51 +02:00
2020-02-15 23:38:32 +01:00
my % seen_id ;
$ sth = $ self - > { dbh } - > prepare ( 'SELECT id, type FROM Aliases WHERE alias = ?' ) ;
2016-02-10 16:10:37 +01:00
2020-02-15 23:38:32 +01:00
while ( 1 ) {
my $ new_aliases = 0 ;
foreach my $ id ( keys % ids ) {
next if $ ids { $ id } - > { type } == $ self - > { alias_type } - > { WEAK } ;
next if exists $ seen_id { $ id } ;
$ seen_id { $ id } = $ id ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
$ sth - > execute ( $ id ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
next if exists $ ids { $ row - > { id } } ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
#next if $row->{type} == $self->{alias_type}->{WEAK};
$ ids { $ row - > { id } } = { id = > $ id , type = > $ ids { $ id } - > { type } == $ self - > { alias_type } - > { WEAK } ? $ self - > { alias_type } - > { WEAK } : $ row - > { type } } ;
$ new_aliases + + ;
$ self - > { pbot } - > { logger } - > log ( "[$id] 2) Adding $row->{id} -> $id [type $row->{type}]\n" ) if $ debug ;
}
}
last if not $ new_aliases ;
2015-05-12 06:27:22 +02:00
}
2016-02-10 12:42:42 +01:00
2020-05-18 09:05:15 +02:00
my $ hostmask_sth = $ self - > { dbh } - > prepare ( 'SELECT hostmask, nickchange, last_seen FROM Hostmasks WHERE id = ?' ) ;
2020-02-15 23:38:32 +01:00
my $ nickserv_sth = $ self - > { dbh } - > prepare ( 'SELECT nickserv FROM Nickserv WHERE id = ?' ) ;
my $ gecos_sth = $ self - > { dbh } - > prepare ( 'SELECT gecos FROM Gecos WHERE id = ?' ) ;
2016-02-10 12:42:42 +01:00
2020-02-15 23:38:32 +01:00
my $ csv = Text::CSV - > new ( { binary = > 1 } ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ id ( keys % ids ) {
$ hostmask_sth - > execute ( $ id ) ;
$ rows = $ hostmask_sth - > fetchall_arrayref ( { } ) ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
2020-05-18 09:05:15 +02:00
my ( $ nick , $ user , $ host ) = $ row - > { hostmask } =~ m/^([^!]+)!([^@]+)@(.*)/ ;
$ akas { $ row - > { hostmask } } = {
id = > $ id ,
alias = > $ ids { $ id } - > { id } ,
nick = > $ nick ,
user = > $ user ,
host = > $ host ,
hostmask = > $ row - > { hostmask } ,
type = > $ ids { $ id } - > { type } ,
nickchange = > $ row - > { nickchange } ,
last_seen = > $ row - > { last_seen } ,
} ;
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "[$id] Adding hostmask $row->{hostmask} -> $ids{$id}->{id} [type $ids{$id}->{type}]\n" ) if $ debug ;
}
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
$ nickserv_sth - > execute ( $ id ) ;
$ rows = $ nickserv_sth - > fetchall_arrayref ( { } ) ;
2014-07-11 14:57:18 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
foreach my $ aka ( keys % akas ) {
if ( $ akas { $ aka } - > { id } == $ id ) {
if ( exists $ akas { $ aka } - > { nickserv } ) { $ akas { $ aka } - > { nickserv } . = ",$row->{nickserv}" ; }
else { $ akas { $ aka } - > { nickserv } = $ row - > { nickserv } ; }
}
}
}
2014-07-11 14:57:18 +02:00
2020-02-15 23:38:32 +01:00
$ gecos_sth - > execute ( $ id ) ;
$ rows = $ gecos_sth - > fetchall_arrayref ( { } ) ;
foreach my $ row ( @$ rows ) {
foreach my $ aka ( keys % akas ) {
if ( $ akas { $ aka } - > { id } == $ id ) {
if ( exists $ akas { $ aka } - > { gecos } ) {
$ csv - > parse ( $ akas { $ aka } - > { gecos } ) ;
my @ gecos = $ csv - > fields ;
push @ gecos , $ row - > { gecos } ;
$ csv - > combine ( @ gecos ) ;
$ akas { $ aka } - > { gecos } = $ csv - > string ;
} else {
my @ gecos = ( $ row - > { gecos } ) ;
$ csv - > combine ( @ gecos ) ;
$ akas { $ aka } - > { gecos } = $ csv - > string ;
}
}
}
}
}
2014-07-11 14:57:18 +02:00
2020-02-15 23:38:32 +01:00
return % akas ;
}
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id, hostmask FROM Hostmasks WHERE nick = ? ORDER BY last_seen DESC' ) ;
$ sth - > execute ( $ nick ) ;
2014-07-11 14:57:18 +02:00
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
$ hostmasks { $ row - > { hostmask } } = $ row - > { id } ;
$ ids { $ row - > { id } } = $ row - > { hostmask } ;
$ akas { $ row - > { hostmask } } = { hostmask = > $ row - > { hostmask } , id = > $ row - > { id } } ;
$ self - > { pbot } - > { logger } - > log ( "Found matching nick [$nick] for hostmask $row->{hostmask} with id $row->{id}\n" ) ;
2014-07-11 14:57:18 +02:00
}
2020-02-15 23:38:32 +01:00
foreach my $ hostmask ( keys % hostmasks ) {
my ( $ host ) = $ hostmask =~ /(\@.*)$/ ;
$ sth = $ self - > { dbh } - > prepare ( 'SELECT id FROM Hostmasks WHERE host = ?' ) ;
$ sth - > execute ( $ host ) ;
$ rows = $ sth - > fetchall_arrayref ( { } ) ;
2014-07-11 14:57:18 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) {
next if exists $ ids { $ row - > { id } } ;
$ ids { $ row - > { id } } = $ row - > { id } ;
2014-07-11 14:57:18 +02:00
2020-02-15 23:38:32 +01:00
$ sth = $ self - > { dbh } - > prepare ( 'SELECT hostmask FROM Hostmasks WHERE id == ?' ) ;
$ sth - > execute ( $ row - > { id } ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
foreach my $ nrow ( @$ rows ) {
next if exists $ akas { $ nrow - > { hostmask } } ;
$ akas { $ nrow - > { hostmask } } = { hostmask = > $ nrow - > { hostmask } , id = > $ row - > { id } } ;
$ self - > { pbot } - > { logger } - > log ( "Adding matching host [$hostmask] and id [$row->{id}] AKA hostmask $nrow->{hostmask}\n" ) ;
}
}
2015-05-12 06:27:22 +02:00
}
2020-02-15 23:38:32 +01:00
my % nickservs ;
foreach my $ id ( keys % ids ) {
$ sth = $ self - > { dbh } - > prepare ( 'SELECT nickserv FROM Nickserv WHERE id == ?' ) ;
$ sth - > execute ( $ id ) ;
$ rows = $ sth - > fetchall_arrayref ( { } ) ;
2014-07-11 14:57:18 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ row ( @$ rows ) { $ nickservs { $ row - > { nickserv } } = $ id ; }
}
2014-07-11 14:57:18 +02:00
2020-02-15 23:38:32 +01:00
foreach my $ nickserv ( sort keys % nickservs ) {
foreach my $ aka ( keys % akas ) {
if ( $ akas { $ aka } - > { id } == $ nickservs { $ nickserv } ) {
if ( exists $ akas { $ aka } - > { nickserv } ) { $ akas { $ aka } - > { nickserv } . = ",$nickserv" ; }
else { $ akas { $ aka } - > { nickserv } = $ nickserv ; }
}
}
2014-07-11 14:57:18 +02:00
2020-02-15 23:38:32 +01:00
$ sth = $ self - > { dbh } - > prepare ( 'SELECT id FROM Nickserv WHERE nickserv == ?' ) ;
$ sth - > execute ( $ nickserv ) ;
$ rows = $ sth - > fetchall_arrayref ( { } ) ;
foreach my $ row ( @$ rows ) {
next if exists $ ids { $ row - > { id } } ;
$ ids { $ row - > { id } } = $ row - > { id } ;
$ sth = $ self - > { dbh } - > prepare ( 'SELECT hostmask FROM Hostmasks WHERE id == ?' ) ;
$ sth - > execute ( $ row - > { id } ) ;
my $ rows = $ sth - > fetchall_arrayref ( { } ) ;
foreach my $ nrow ( @$ rows ) {
if ( exists $ akas { $ nrow - > { hostmask } } ) {
if ( exists $ akas { $ nrow - > { hostmask } } - > { nickserv } ) { $ akas { $ nrow - > { hostmask } } - > { nickserv } . = ",$nickserv" ; }
else { $ akas { $ nrow - > { hostmask } } - > { nickserv } = $ nickserv ; }
} else {
$ akas { $ nrow - > { hostmask } } = { hostmask = > $ nrow - > { hostmask } , id = > $ row - > { id } , nickserv = > $ nickserv } ;
$ self - > { pbot } - > { logger } - > log ( "Adding matching nickserv [$nickserv] and id [$row->{id}] AKA hostmask $nrow->{hostmask}\n" ) ;
}
}
2015-05-12 06:27:22 +02:00
}
2014-07-11 14:57:18 +02:00
}
2020-02-15 23:38:32 +01:00
foreach my $ id ( keys % ids ) {
$ sth = $ self - > { dbh } - > prepare ( 'SELECT hostmask FROM Hostmasks WHERE id == ?' ) ;
$ sth - > execute ( $ id ) ;
$ rows = $ sth - > fetchall_arrayref ( { } ) ;
foreach my $ row ( @$ rows ) {
next if exists $ akas { $ row - > { hostmask } } ;
$ akas { $ row - > { hostmask } } = { hostmask = > $ row - > { hostmask } , id = > $ id } ;
$ self - > { pbot } - > { logger } - > log ( "Adding matching id [$id] AKA hostmask $row->{hostmask}\n" ) ;
}
}
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
return % akas ;
} ;
2015-05-12 06:27:22 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "bad aka: $@" ) if $@ ;
2015-05-12 06:27:22 +02:00
return % akas ;
2014-07-11 14:57:18 +02:00
}
2016-11-29 10:50:49 +01:00
sub get_ancestor_id {
2020-02-15 23:38:32 +01:00
my ( $ self , $ id ) = @ _ ;
2016-11-29 10:50:49 +01:00
2020-02-15 23:38:32 +01:00
$ id = 0 if not defined $ id ;
2019-05-10 03:04:15 +02:00
2020-02-15 23:38:32 +01:00
my $ ancestor = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id FROM Aliases WHERE alias = ? ORDER BY id LIMIT 1' ) ;
$ sth - > execute ( $ id ) ;
my $ row = $ sth - > fetchrow_hashref ( ) ;
return defined $ row ? $ row - > { id } : 0 ;
} ;
2016-11-29 10:50:49 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
2016-11-29 10:50:49 +01:00
2020-02-15 23:38:32 +01:00
return $ id if not $ ancestor ;
return $ ancestor < $ id ? $ ancestor : $ id ;
2016-11-29 10:50:49 +01:00
}
2014-05-13 12:15:52 +02:00
# End of public API, the remaining are internal support routines for this module
sub get_new_account_id {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
my $ id = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id FROM Accounts ORDER BY id DESC LIMIT 1' ) ;
$ sth - > execute ( ) ;
my $ row = $ sth - > fetchrow_hashref ( ) ;
return $ row - > { id } ;
} ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
return + + $ id ;
2014-05-13 12:15:52 +02:00
}
sub get_message_account_id {
2020-02-15 23:38:32 +01:00
my ( $ self , $ mask ) = @ _ ;
my $ id = eval {
my $ sth = $ self - > { dbh } - > prepare ( 'SELECT id FROM Hostmasks WHERE hostmask == ?' ) ;
$ sth - > execute ( $ mask ) ;
my $ row = $ sth - > fetchrow_hashref ( ) ;
return $ row - > { id } ;
} ;
$ self - > { pbot } - > { logger } - > log ( $@ ) if $@ ;
#$self->{pbot}->{logger}->log("get_message_account_id: returning id [". (defined $id ? $id: 'undef') . "] for mask [$mask]\n");
return $ id ;
2014-05-13 12:15:52 +02:00
}
sub commit_message_history {
2020-02-15 23:38:32 +01:00
my $ self = shift ;
2014-05-13 12:15:52 +02:00
2020-02-15 23:38:32 +01:00
return if not $ self - > { dbh } ;
2020-09-13 01:28:44 +02:00
return if $ self - > { pbot } - > { child } ; # don't commit() as child of fork()
2017-08-28 22:53:03 +02:00
2020-02-15 23:38:32 +01:00
if ( $ self - > { new_entries } > 0 ) {
# $self->{pbot}->{logger}->log("Commiting $self->{new_entries} messages to SQLite\n");
eval { $ self - > { dbh } - > commit ( ) ; } ;
2014-05-13 17:09:29 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "SQLite error $@ when committing $self->{new_entries} entries.\n" ) if $@ ;
$ self - > { dbh } - > begin_work ( ) ;
$ self - > { new_entries } = 0 ;
}
2014-05-13 12:15:52 +02:00
}
1 ;