summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--_data/scripts.yaml10
-rw-r--r--scripts/clones_scanner.pl297
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");
+});