diff options
| -rw-r--r-- | _data/scripts.yaml | 10 | ||||
| -rw-r--r-- | scripts/clones_scanner.pl | 297 |
2 files changed, 307 insertions, 0 deletions
diff --git a/_data/scripts.yaml b/_data/scripts.yaml index a823da2..f113260 100644 --- a/_data/scripts.yaml +++ b/_data/scripts.yaml @@ -886,6 +886,16 @@ url: "http://irssi.dgl.cx/" version: "2.01" +- authors: "Pablo Martín Báez Echevarría" + contact: "pab_24n@outlook.com" + description: "when a nick joins #channel, notifies you if there is (or there has been) someone in #channel with the same hostname" + filename: "clones_scanner.pl" + modified: "2014-12-20 22:30:25" + license: "Public domain" + name: "clones_scannner" + url: "http://reirssi.wordpress.com" + version: "1.5" + - authors: "Gabor Nyeki" contact: "bigmac@vim.hu" description: "kicking users for using colors or blinks" diff --git a/scripts/clones_scanner.pl b/scripts/clones_scanner.pl new file mode 100644 index 0000000..1f31fbb --- /dev/null +++ b/scripts/clones_scanner.pl @@ -0,0 +1,297 @@ +use strict; +use warnings; + +{ package Irssi::Nick } +# just in case, to avoid Irssi::Nick warnings ( see http://bugs.irssi.org/index.php?do=details&task_id=242 ) + +use Irssi; +use vars qw($VERSION %IRSSI); + +# Thanks to noi_esportista!#Girona@chathispano for his suggestions about how this script should work. + +$VERSION = '1.5'; +%IRSSI = ( + authors => 'Pablo Martín Báez Echevarría', + contact => 'pab_24n@outlook.com', + name => 'clones_scanner', + description => 'when a nick joins #channel, notifies you if there is (or there has been) someone in #channel with the same hostname', + license => 'Public Domain', + url => 'http://reirssi.wordpress.com', + changed => '22:30:25, Dec 20th, 2014 UYT', +); + +# +# USAGE +# ===== +# Copy the script to ~/.irssi/scripts/ +# +# In irssi: +# /run clones_scanner +# +# +# OPTIONS +# ======= +# Settings can be resetted to defaults with /set -default +# +# /set clones_scanner_maxtime <time> +# * This is the maximum time in which the script remembers that a specific hostname +# left a channel because of a PART, QUIT or KICK event (default is 900secs = 15mins). +# For example, suppose it is 1 hour. If someone with mask type nick1!*@host left #channel +# at 11:00 and then comes back at 12:01 with mask type nick2!*@host, you will not be +# notified that 'nick2' was seen earlier in #channel as 'nick1'. +# It must be a time type, that is a series of integers with optional unit specifiers. +# Valid specifiers are: +# +# d[ays] +# h[ours] +# m[inutes] +# s[econds] +# mil[liseconds] | ms[econds] +# +# Any unambiguous part of a specifier can be used, as shown by the strings in braces in +# the above list. Multiple specifiers can be combined, with or without spaces between them. +# +# Examples: +# +# /set clones_scanner_maxtime 1hour30mins +# /set clones_scanner_maxtime 2h +# /set clones_scanner_maxtime 3h 10secs +# +# There must not be a space between the number and the unit specifier. +# +# +# COMMANDS +# ======== +# /clones_scanner_size +# * Displays how many entries the data structure where the hosts are stored has, and how much +# memory is used for that purpose. +# +# WARNING: This feature requires Devel::Size module. It seems that when installing Devel::Size +# some tests started to fail since Perl 5.19.3 so if you're using the latest Perl release +# (Perl 5.20.1) you'll have to wait for someone to fix Devel::Size for recent Perl versions. +# See more about this issue at: https://rt.cpan.org/Public/Bug/Display.html?id=95493 +# Remeber that you can find out Perl version with +# $ perl -v +# in a terminal or alternatively executing /script exec print $^V in irssi. +# + + +Irssi::settings_add_time('clones_scanner', 'clones_scanner_maxtime', 900); + +# global variables +my $have_devel_size = eval { require Devel::Size }; +my %hosts_hash = (); +my $old_maxtime_msecs; +my $old_maxtime_str; +my $total_entries = 0; + +########## + +sub add_entry { + my ( $network, $channel, $address, $nick ) = @_; + + (my $host = $address) =~ s/^[^@]+@//; + + if (defined $hosts_hash{$network}{$channel}{$host}) { + my $old_tag = $hosts_hash{$network}{$channel}{$host}[2]; + Irssi::timeout_remove( $old_tag ); + $total_entries--; + } + + my $time = Irssi::settings_get_time("clones_scanner_maxtime"); + my @data = ( $network, $channel, $host ); + my $tag = Irssi::timeout_add_once($time, "remove_entry", \@data); + + my $entry = [$nick, time(), $tag]; + $hosts_hash{$network}{$channel}{$host} = $entry; + $total_entries++; + +} + +sub str_time { + my ( $secs ) = @_; + + my $d = int($secs/3600/24); + my $h = int($secs/3600%24); + my $m = int($secs/60%60); + my $s = int($secs%60); + + my $d_str = ($d == 1) ? "day": "days"; + my $h_str = ($h == 1) ? "hour": "hours"; + my $m_str = ($m == 1) ? "minute": "minutes"; + my $s_str = ($s == 1) ? "second": "seconds"; + + my $raw_str = $d.$d_str.", ".$h.$h_str.", ".$m.$m_str.", ".$s.$s_str; + + (my $str_res = $raw_str) =~ s/\b0\w+(?:,\s)?//g; + ($str_res = $str_res) =~ s/,\s$//; + ($str_res = $str_res) =~ s/(\d)([dhms])/$1 $2/g; + + return $str_res eq "" ? "less than 1 second" : $str_res; +} + +sub remove_entry { + my ( $ref_data ) = @_; + + my $network = @{$ref_data}[0]; + my $chan = @{$ref_data}[1]; + my $host = @{$ref_data}[2]; + + delete $hosts_hash{$network}{$chan}{$host}; + $total_entries--; + delete $hosts_hash{$network}{$chan} if (!keys %{$hosts_hash{$network}{$chan}}); + delete $hosts_hash{$network} if (!keys %{$hosts_hash{$network}}); +} + +sub update_hash { + my ( $nw_maxtime ) = @_; + my $remainder; + my $ni; + my $se; + my $tg; + my $nw_tg; + + foreach my $network (keys %hosts_hash) { + foreach my $channel (keys %{$hosts_hash{$network}}) { + foreach my $host (keys %{$hosts_hash{$network}{$channel}}) { + $ni = @{$hosts_hash{$network}{$channel}{$host}}[0]; + $se = @{$hosts_hash{$network}{$channel}{$host}}[1]; + $tg = @{$hosts_hash{$network}{$channel}{$host}}[2]; + Irssi::timeout_remove( $tg ); + $remainder = $nw_maxtime - (time() - $se); + if( $remainder > 0 ) { + my @data = ( $network, $channel, $host ); + $nw_tg = Irssi::timeout_add_once( $remainder*1000, "remove_entry", \@data); + $hosts_hash{$network}{$channel}{$host} = [$ni, $se, $nw_tg]; + } else { + delete $hosts_hash{$network}{$channel}{$host}; + $total_entries--; + } + } + delete $hosts_hash{$network}{$channel} if (!keys %{$hosts_hash{$network}{$channel}}); + } + delete $hosts_hash{$network} if (!keys %{$hosts_hash{$network}}); + } + +} + +sub setup_changed { + my $new_maxtime_msecs = Irssi::settings_get_time("clones_scanner_maxtime"); + if($new_maxtime_msecs < 10) { + Irssi::print("Invalid timestamp (must be >= 10 msecs)", MSGLEVEL_CLIENTERROR); + Irssi::settings_set_time("clones_scanner_maxtime", $old_maxtime_str); + $new_maxtime_msecs = Irssi::settings_get_time("clones_scanner_maxtime"); + } + update_hash(int($new_maxtime_msecs/1000)) if ($new_maxtime_msecs != $old_maxtime_msecs); +} + +########## + +sub part_method { + my ($server, $channel, $nick, $address, $reason) = @_; + + add_entry($server->{tag}, $channel, $address, $nick); +} + +sub quit_method { + my ($server, $nick, $address, $reason) = @_; + + foreach($server->channels()) { + if ($_->nick_find($nick)) { + add_entry($server->{tag}, $_->{name}, $address, $nick); + } + } +} + +sub kick_method { + my ($server, $channel, $nick, $kicker, $address, $reason) = @_; + + Irssi::signal_stop(); + my $kicked_address = $server->channel_find($channel)->nick_find($nick)->{host}; + Irssi::signal_continue(@_); + add_entry($server->{tag}, $channel, $kicked_address, $nick); +} + +########## + +sub join_method { + my ($server, $channel, $nick, $address) = @_; + + Irssi::signal_continue(@_); + + my $servtag = $server->{tag}; + (my $host = $address) =~ s/^[^@]+@//; + my $chan_rec = $server->channel_find($channel); + + # ==== find clones ==== + my $ni_host; + my $str_clones = ""; + my @clones; + foreach my $ni ($chan_rec->nicks()) { + ($ni_host = "$ni->{host}") =~ s/^[^@]+@//; + if ( ($ni->{nick} ne $nick)&&($ni_host eq $host) ) { + $str_clones .= "$ni->{nick}".", "; + push @clones, $ni->{nick}; + } + } + if( $str_clones ne "") { + ($str_clones = $str_clones) =~ s/,\s$//; + $chan_rec->printformat(Irssi::MSGLEVEL_JOINS, "clones_scanner_clones", $nick, $str_clones); + } + + # ==== search in %hosts_hash ==== + my $exists_nick_in_hash = (defined $hosts_hash{$servtag})&&(defined $hosts_hash{$servtag}{$channel}) + &&(defined $hosts_hash{$servtag}{$channel}{$host}); + + if ($exists_nick_in_hash) { + my @alias = @{ $hosts_hash{$servtag}{$channel}{$host} }; + if ( ($nick ne $alias[0]) && (!(grep {$_ eq $alias[0]} @clones)) ) { + my $time = Irssi::settings_get_time("clones_scanner_maxtime"); + $chan_rec->printformat( Irssi::MSGLEVEL_JOINS, "clones_scanner_track_nick", $nick, str_time(int($time/1000)), + $alias[0], str_time(time()-$alias[1])); + } + } + +} + +########## + +Irssi::theme_register([ + "clones_scanner_clones", 'Clones of {nick $0}: $1', + "clones_scanner_track_nick", '=> {nick $0} was seen during the last $1 as {nick $2} ($3 ago)', +]); + +########## + + +if ($have_devel_size) { + + sub clones_scanner_size_cmd { + #use Data::Dumper; + #print Dumper(\%hosts_hash); + my $bytes = Devel::Size::total_size(\%hosts_hash); + print "Number of entries in \%hosts_hash: ", $total_entries; + print "Size in bytes: ".$bytes; + print int($bytes/1024/1024)."MB ".int($bytes/1024%1024)."kB ".int($bytes%1024)."B of data"; + } + Irssi::command_bind('clones_scanner_size' , \&clones_scanner_size_cmd); + +} else { + + print "Missing Devel::Size module. The command `/clones_scanner_size` will not be available."; + +} + +Irssi::signal_add_first('message part', \&part_method); +Irssi::signal_add_first('message quit', \&quit_method); +Irssi::signal_add_first('message kick', \&kick_method); + +Irssi::signal_add_last('message join', \&join_method); + +Irssi::signal_add_last('setup changed', \&setup_changed); + +Irssi::signal_add_first('send command', +sub { + $old_maxtime_msecs = Irssi::settings_get_time("clones_scanner_maxtime"); + $old_maxtime_str = Irssi::settings_get_str("clones_scanner_maxtime"); +}); |
