diff options
| author | David Leadbeater | 2015-01-10 16:57:01 +0000 |
|---|---|---|
| committer | David Leadbeater | 2015-01-10 16:57:01 +0000 |
| commit | cbbdf2a1d93dbb64d3fb34346e9c57b14dfa2931 (patch) | |
| tree | 019203a11e96a90be76a4538433e1d223a747d55 | |
| parent | 0a3941245c1cbb55dd3c67af0b5c33caddd04f8b (diff) | |
| parent | 71dba3f266191eeeff577303232d700fefe2e6e1 (diff) | |
| download | scripts.irssi.org-cbbdf2a1d93dbb64d3fb34346e9c57b14dfa2931.tar.bz2 | |
Merge branch 'gh-pages' of gh:irssi/scripts.irssi.org into gh-pages
Conflicts:
_data/scripts.yaml
| -rw-r--r-- | .travis.yml | 10 | ||||
| -rw-r--r-- | _data/scripts.yaml | 224 | ||||
| -rw-r--r-- | scripts/auth_quakenet.pl | 197 | ||||
| -rw-r--r-- | scripts/autoop.pl | 71 | ||||
| -rw-r--r-- | scripts/cap_sasl.pl | 431 | ||||
| -rw-r--r-- | scripts/clones_scanner.pl | 296 | ||||
| -rw-r--r-- | scripts/cron.pl | 7 | ||||
| -rw-r--r-- | scripts/dice_concise.pl | 247 | ||||
| -rw-r--r-- | scripts/eng_no_translate_dpryo.pl | 57 | ||||
| -rw-r--r-- | scripts/fnotify.pl | 92 | ||||
| -rw-r--r-- | scripts/frm_outgmsgs.pl | 4 | ||||
| -rw-r--r-- | scripts/go.pl | 6 | ||||
| -rw-r--r-- | scripts/localize.pl | 21 | ||||
| -rw-r--r-- | scripts/rainbow.pl | 28 | ||||
| -rw-r--r-- | scripts/screen_away.pl | 11 | ||||
| -rw-r--r-- | scripts/slack_emoji.pl | 50 | ||||
| -rw-r--r-- | scripts/trackbar.pl | 325 |
17 files changed, 1850 insertions, 227 deletions
diff --git a/.travis.yml b/.travis.yml index 64d0fcb..1c69a58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,21 +20,21 @@ before_install: _testing/config.yml` - | if [ $TRAVIS_PULL_REQUEST = false ] && [ $USE_ARTEFACTS_CACHE = yes ] && [ $EXTRACT_TEST_ARTEFACTS = yes ]; then - ./_testing/travis/load-old-artefacts.zsh + env -u REPO_LOGIN_TOKEN ./_testing/travis/load-old-artefacts.zsh fi -- ./_testing/travis/autoinstall-perl-prereqs.zsh +- env -u REPO_LOGIN_TOKEN ./_testing/travis/autoinstall-perl-prereqs.zsh install: - true before_script: -- ./_testing/run-test.zsh +- env -u REPO_LOGIN_TOKEN ./_testing/run-test.zsh - | if [ $TRAVIS_PULL_REQUEST != false ]; then git diff --stat $TRAVIS_BRANCH fi script: -- ./_testing/report-test.zsh +- env -u REPO_LOGIN_TOKEN ./_testing/report-test.zsh after_script: -- ./_testing/travis/show-failures.zsh +- env -u REPO_LOGIN_TOKEN ./_testing/travis/show-failures.zsh - | if [ $TRAVIS_PULL_REQUEST = false ] && $TRAVIS_SECURE_ENV_VARS; then perl ./_testing/travis/update-scripts-yaml.pl diff --git a/_data/scripts.yaml b/_data/scripts.yaml index 9e91c59..06e9019 100644 --- a/_data/scripts.yaml +++ b/_data/scripts.yaml @@ -230,6 +230,17 @@ url: "http://www.mimir.ch/ph/" version: "1.00" +- authors: "Mantas Mikulėnas" + contact: "grawity@gmail.com" + description: "Implements QuakeNet's CHALLENGE authentication" + filename: "auth_quakenet.pl" + modified: "2014-12-18 06:38" + license: "WTFPL v2 <http://sam.zoy.org/wtfpl/>" + modules: "Digest::HMAC Digest::SHA Digest::SHA1 Digest::MD5" + name: "auth_quakenet" + url: "https://nullroute.eu.org/~grawity/irssi.html" + version: "1.0" + - authors: "Andreas ads Scherbaum" contact: "ads@ufp.de" description: "/WHOIS all the users who send you a private message." @@ -291,13 +302,13 @@ url: "http://irssi.dgl.cx/" version: "1.00" -- authors: "Timo Sirainen" +- authors: "Timo Sirainen & Jostein Kjønigsen" description: "Simple auto-op script" filename: "autoop.pl" - modified: "2002-03-10 23:18:00" + modified: "2014-10-24 12:55:00" license: "Public Domain" name: "autoop" - version: "1.00" + version: "1.10" - authors: "Toni Salomäki" contact: "Toni@IRCNet" @@ -711,6 +722,18 @@ url: "http://scripts.irssi.org/" version: "1.0" +- authors: "Michael Tharp (gxti), Jilles Tjoelker (jilles), Mantas Mikulėnas (grawity)" + commands: "sasl" + contact: "grawity@gmail.com" + description: "Implements SASL authentication (and multi-prefix as free bonus)" + filename: "cap_sasl.pl" + modified: "2015-01-07 06:03" + modules: "Crypt::PK::ECC MIME::Base64" + license: "GPLv2" + name: "cap_sasl" + url: "https://nullroute.eu.org/~grawity/irssi.html" + version: "1.10" + - authors: "ZaMz0n" commands: "cddb" contact: "zamzon@freakpower.com" @@ -874,6 +897,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.6" + - authors: "Gabor Nyeki" contact: "bigmac@vim.hu" description: "kicking users for using colors or blinks" @@ -1091,6 +1124,17 @@ url: "http://www.enumerator.org/component/option,com_docman/task,view_category/Itemid,34/subcat,7/" version: "0.00.04" +- authors: "Marcell Kossin, Makaze" + contact: "izaya.orihara@gmail.com" + description: "A concise dice simulator for channels." + filename: "dice_concise.pl" + modified: "2014-03-28 21:15:23" + license: "GNU GPL Version 2 or later" + modules: "Scalar::Util" + name: "dice_concise" + url: "https://github.com/Makaze/" + version: "0.1.5" + - authors: "Juerd (first version: Timo Sirainen)" contact: "juerd@juerd.nl" description: "Caching dictionary based tab completion" @@ -1173,15 +1217,6 @@ url: "http://www.holmnilsen.com/" version: "1.2" -- authors: "Ilkka Pale" - contact: "ilkka.pale@gmail.com" - description: "Outputs various unicode emoticons." - filename: "emo.pl" - modified: "2014-06-12 19:06:00" - license: "Public Domain" - name: "emo" - version: "0.0.1" - - authors: "Ilya Cassina" contact: "icassina@gmail.com" description: "This script allow advanced parametrization of the /list command. Accepted parameters are -minusers <#users> and -maxusers <#users>. " @@ -1225,16 +1260,14 @@ url: "http://www.princessleia.com/" version: "1.0" -- authors: "Harald Nesland" - contact: "hnesland@samsen.com" - description: "Very simple script that sends incoming text to freetranslation.com for english->norwegian translation. May be modified to translate other languages." - filename: "eng_no_translate_dpryo.pl" - modified: "2002-04-11 14:15:25" +- authors: "Ilkka Pale" + contact: "ilkka.pale@gmail.com" + description: "Outputs various unicode emoticons." + filename: "emo.pl" + modified: "2014-06-12 19:06:00" license: "Public Domain" - modules: "LWP::Simple" - name: "EngNoTranslate" - url: "http://www.satyra.net" - version: "0.2" + name: "emo" + version: "0.0.1" - authors: "Taneli Kaivola" contact: "dist@sci.fi" @@ -1337,6 +1370,16 @@ url: "http://www.krukowiecki.net/code/irssi/" version: "0.0.2i" +- authors: "Thorsten Leemhuis, James Shubin, Serge van Ginderachter" + contact: "fedora@leemhuis.info, serge@vanginderachter.be" + description: "Write notifications to a file in a consistent format." + filename: "fnotify.pl" + modified: "2014-11-25 21:02" + license: "GNU General Public License" + name: "fnotify" + url: "http://www.leemhuis.info/files/fnotify/fnotify https://ttboj.wordpress.com/" + version: "0.0.5" + - authors: "Juerd" contact: "juerd@juerd.nl" description: "Automatically switch to active windows" @@ -1428,6 +1471,15 @@ url: "http://toxcorp.com/irc/irssi/friends/" version: "2.4.9" +- authors: "Pablo Martín Báez Echevarría" + contact: "pab_24n@outlook.com" + description: "define a permanent text formatting (bold, underline, etc.) for outgoing messages" + filename: "frm_outgmsgs.pl" + modified: "2014-10-16" + license: "Public Domain" + name: "frm_outgmsgs" + version: "1.1" + - authors: "Piotr Krukowiecki & others" contact: "piotr at pingu.ii.uj.edu.pl" description: "File server for irssi" @@ -1471,10 +1523,10 @@ contact: "nohar@freenode" description: "Implements /go command that activates a window given a name/partial name. It features a nice completion." filename: "go.pl" - modified: "2004-08-17 00:00:00" + modified: "2014-10-19 00:00:00" license: "GPLv2 or later" name: "go to window" - version: "1.00" + version: "1.01" - authors: "cxreg" contact: "cxreg@pobox.com" @@ -2147,12 +2199,12 @@ contact: "stefan@pico.ruhr.de" description: "Localizes users using traceroute, the localizer database or IP-Atlas" filename: "localize.pl" - modified: "2008-05-17 17:39:09" + modified: "2014-11-25 14:20:00" license: "GPLv2" modules: "LWP::UserAgent HTML::Entities Data::Dumper Socket" name: "localize" url: "" - version: "2003112301" + version: "2014112501" - authors: "Peder Stray" contact: "peder@ninja.no" @@ -2775,6 +2827,15 @@ name: "oops" version: "20071209" +- authors: "David Leadbeater" + contact: "dgl@dgl.cx" + description: "Stops those silly mistakes being sent (spaces at start of line, /1/1 for window changes, etc)." + filename: "oopsie.pl" + modified: "2014-08-13" + license: "WTFPL" + name: "oopsie" + version: "1.0" + - authors: "Stefan tommie Tomanek" commands: "openurl" contact: "stefan@pico.ruhr.de" @@ -3017,6 +3078,15 @@ name: "q_username" version: "0.1" +- authors: "Doug Freed" + contact: "dwfreed!#irssi@freenode" + description: "Authenticates with QuakeNet's Q service upon connection, using their CHALLENGEAUTH protocol" + filename: "qchallengeauth.pl" + modified: "2014-08-23" + license: "GPLv3+" + name: "qchallengeauth" + version: "1.0" + - authors: "Peder Stray" contact: "peder@ninja.no" description: "Give you more control over when to jump to query windows and when to just tell you one has been created. Enhanced autoclose." @@ -3105,11 +3175,11 @@ contact: "shasta@atn.pl" description: "Prints colored text. Rather simple than sophisticated." filename: "rainbow.pl" - modified: "2008-05-17 17:39:09" + modified: "2014-12-01 13:04:09" license: "GNU GPLv2 or later" name: "rainbow" url: "http://irssi.atn.pl/" - version: "1.4" + version: "1.6" - authors: "Lasse Karstensen" contact: "lkarsten@stud.ntnu.no" @@ -3272,12 +3342,12 @@ - authors: "Andreas ads Scherbaum <ads@wars-nicht.de>" description: "set (un)away, if screen is attached/detached" filename: "screen_away.pl" - modified: "2008-05-22 01:28:25" + modified: "2014-12-20 20:30:00" license: "GPL v2" modules: "FileHandle" name: "screen_away" url: "none" - version: "0.9.7.1" + version: "0.9.8.1" - authors: "Stefan tommie Tomanek" commands: "scriptassist" @@ -3418,6 +3488,25 @@ name: "showmode" version: "0.3" +- authors: "Lars Djerf" + contact: "lars.djerf@gmail.com" + description: "Converts Slack emoji to smileys." + filename: "slack_emoji.pl" + modified: "2015-01-03 19:55:28" + license: "GPLv3" + name: "slack_emoji" + version: "0.03" + +- authors: "crshd" + contact: "christian.brassat@gmail.com" + description: "Filters join/part messages if user hasn't been active in a while." + filename: "smartfilter.pl" + modified: "2012-10-02 21:05:40" + license: "BSD" + name: "smartfilter" + url: "none" + version: "0.1" + - authors: "Jonne Piittinen" contact: "jip@loota.org" description: "Very useful smiley-flooder" @@ -3737,11 +3826,11 @@ contact: "peter@pfoe.be" description: "Shows a bar where youve last read a window" filename: "trackbar.pl" - modified: "2003-02-20 16:18:08" + modified: "2013-11-19 13:44:07" license: "GPLv2" name: "trackbar" url: "http://www.pfoe.be/~peter/trackbar/" - version: "1.4" + version: "1.7" - authors: "Timo Sirainen" contact: "tss@iki.fi" @@ -3843,6 +3932,15 @@ url: "http://otoria.freecode.nl/~michiel/u.pl" version: "1.3" +- authors: "David Leadbeater" + contact: "dgl@dgl.cx" + description: "Get infomation about unicode characters" + filename: "unicode.pl" + modified: "2014-07-06" + license: "WTFPL" + name: "unicode" + version: "2" + - authors: "Peder Stray" contact: "peder@ninja.no" description: "Statusbaritem notifying you about updated binary" @@ -3906,6 +4004,15 @@ url: "http://irssi.dgl.cx/" version: "0.2" +- authors: "David Leadbeater" + contact: "dgl@dgl.cx" + description: "Print short summaries about URLs from known services that are mentioned on IRC. (Including YouTube, etc.)" + filename: "urlinfo.pl" + modified: "2014-08-22" + license: "WTFPL" + name: "urlinfo" + version: "1.3" + - authors: "bwolf" contact: "bwolf@geekmind.org" description: "URL grabber with HTML generation and cmd execution" @@ -4240,58 +4347,3 @@ name: "XQF" url: "none" version: "0.14" - -- authors: "crshd" - contact: "christian.brassat@gmail.com" - description: "Filters join/part messages if user hasn't been active in a while." - filename: "smartfilter.pl" - modified: "2012-10-02 21:05:40" - license: "BSD" - name: "smartfilter" - url: "none" - version: "0.1" - -- authors: "David Leadbeater" - contact: "dgl@dgl.cx" - description: "Get infomation about unicode characters" - filename: "unicode.pl" - modified: "2014-07-06" - license: "WTFPL" - name: "unicode" - version: "2" - -- authors: "David Leadbeater" - contact: "dgl@dgl.cx" - description: "Print short summaries about URLs from known services that are mentioned on IRC. (Including YouTube, etc.)" - filename: "urlinfo.pl" - modified: "2014-08-22" - license: "WTFPL" - name: "urlinfo" - version: "1.3" - -- authors: "David Leadbeater" - contact: "dgl@dgl.cx" - description: "Stops those silly mistakes being sent (spaces at start of line, /1/1 for window changes, etc)." - filename: "oopsie.pl" - modified: "2014-08-13" - license: "WTFPL" - name: "oopsie" - version: "1.0" - -- authors: "Doug Freed" - contact: "dwfreed!#irssi@freenode" - description: "Authenticates with QuakeNet's Q service upon connection, using their CHALLENGEAUTH protocol" - filename: "qchallengeauth.pl" - modified: "2014-08-23" - license: "GPLv3+" - name: "qchallengeauth" - version: "1.0" - -- authors: "Pablo Martín Báez Echevarría" - contact: "pab_24n@outlook.com" - description: "define a permanent text formatting (bold, underline, etc.) for outgoing messages" - filename: "frm_outgmsgs.pl" - modified: "2014-10-16" - license: "Public Domain" - name: "frm_outgmsgs" - version: "1.0"
\ No newline at end of file diff --git a/scripts/auth_quakenet.pl b/scripts/auth_quakenet.pl new file mode 100644 index 0000000..53f8b96 --- /dev/null +++ b/scripts/auth_quakenet.pl @@ -0,0 +1,197 @@ +# vim: ft=perl +use strict; +use Irssi; +use vars qw($VERSION %IRSSI); + +## Settings: +# +# quakenet_account (string) = ( [<servertag>:]<username>:<password> )* +# +# Your QuakeNet account details, in format <username>:<password>. Example: +# +# /set quakenet_account [fishking]:iLOVEfish12345 +# +# Different accounts for different Irssi "networks" ("server tags") can be +# set in format <servertag>:<username>:<password> (space-separated). The +# account with empty (or missing) <servertag> will be used as the default +# account for all other connections.) +# +# quakenet_auth_allowed_mechs (string) = any +# +# List of allowed mechanisms, separated by spaces. +# Can be "any" to allow all supported mechanisms. +# +# Currently supported: +# HMAC-SHA-256 (Digest::SHA) +# HMAC-SHA-1 (Digest::SHA1) +# HMAC-MD5 (Digest::MD5) +# LEGACY-MD5 (Digest::MD5 without HMAC) +# +# Note: LEGACY-MD5 is excluded from "any"; if you want to use it, specify +# it manually. +# +## To trigger the script manually, use: +## /msg Q@cserve.quakenet.org challenge + +$VERSION = "1.0"; +%IRSSI = ( + authors => 'Mantas Mikulėnas', + contact => 'grawity@gmail.com', + name => 'auth_quakenet_challenge.pl', + description => "Implements QuakeNet's CHALLENGE authentication", + license => 'WTFPL v2 <http://sam.zoy.org/wtfpl/>', + url => 'http://purl.net/net/grawity/irssi.html', +); + +require Digest::HMAC; + +my @preferred_mechs = qw(HMAC-SHA-256 HMAC-SHA-1 HMAC-MD5); + +my %supported_mechs = (); + +eval { + require Digest::SHA; + $supported_mechs{"HMAC-SHA-256"} = sub { + hmac(\&Digest::SHA::sha256_hex, \&Digest::SHA::sha256, @_); + }; +}; + +eval { + require Digest::SHA1; + $supported_mechs{"HMAC-SHA-1"} = sub { + hmac(\&Digest::SHA1::sha1_hex, \&Digest::SHA1::sha1, @_); + }; +}; + +eval { + require Digest::MD5; + $supported_mechs{"HMAC-MD5"} = sub { + hmac(\&Digest::MD5::md5_hex, \&Digest::MD5::md5, @_); + }; + $supported_mechs{"LEGACY-MD5"} = sub { + Irssi::print("WARNING: LEGACY-MD5 should not be used."); + my ($challenge, $username, $password) = @_; + Digest::MD5::md5_hex($password . " " . $challenge); + }; +}; + +if (scalar keys %supported_mechs == 0) { + die "No mechanisms available. Please install these Perl modules:\n" + ." - Digest::HMAC\n" + ." - Digest::SHA, Digest::SHA1, Digest::MD5 (at least one)\n"; +} + +sub hmac { + my ($fnhex, $fnraw, $challenge, $username, $password) = @_; + my $key = &$fnhex($username . ":" . &$fnhex($password)); + Digest::HMAC::hmac_hex($challenge, $key, $fnraw); +} + +sub lcnick { + my $str = shift; + $str =~ tr/[\\]/{|}/; + lc $str; +} + +sub get_account { + my ($servertag) = @_; + my $accounts = Irssi::settings_get_str("quakenet_account"); + my ($defuser, $defpass) = (undef, undef); + for my $acct (split /\s+/, $accounts) { + my ($tag, $user, $pass); + + my @acct = split(/:/, $acct); + if (@acct == 3) { + ($tag, $user, $pass) = @acct; + } elsif (@acct == 2) { + ($tag, $user, $pass) = ("*", @acct); + } else { + next; + } + + if (lc $tag eq lc $servertag) { + return ($user, $pass); + } + elsif ($tag eq "*" or $tag eq "") { + ($defuser, $defpass) = ($user, $pass); + } + } + return ($defuser, $defpass); +} + +Irssi::signal_add_last("event 001" => sub { + my ($server, $evargs, $srcnick, $srcaddr) = @_; + return unless $srcnick =~ /\.quakenet\.org$/; + + my ($user, $pass) = get_account($server->{tag}); + return if !length($pass); + + $server->print("", "Authenticating to Q"); + $server->send_message('Q@cserve.quakenet.org', "CHALLENGE", 1); +}); + +Irssi::signal_add_first("message irc notice" => sub { + my ($server, $msg, $nick, $address, $target) = @_; + return unless $server->mask_match_address('Q!*@cserve.quakenet.org', $nick, $address); + + if ($msg =~ /^CHALLENGE ([0-9a-f]+) (.+)$/) { + Irssi::signal_stop(); + + my $challenge = $1; + my @server_mechs = split(" ", $2); + + my ($user, $pass) = get_account($server->{tag}); + return if !length($pass); + + $user = lcnick($user); + $pass = substr($pass, 0, 10); + + my $mech; + my @allowed_mechs = (); + my $allowed_mechs = uc Irssi::settings_get_str("quakenet_auth_allowed_mechs"); + if ($allowed_mechs eq "ANY") { + # @preferred_mechs is sorted by strength + @allowed_mechs = @preferred_mechs; + } else { + @allowed_mechs = split(/\s+/, $allowed_mechs); + } + + # choose first mech supported by both sides + for my $m (@allowed_mechs) { + if (grep {$_ eq $m} @server_mechs && + grep {$_ eq $m} (keys %supported_mechs)) { + $mech = $m; + last; + } + } + + if (!defined $mech) { + $server->print("", "Authentication failed (no mechanisms available)"); + $server->print("", " Server offers: ".join(", ", @server_mechs)); + $server->print("", " Client supports: ".join(", ", keys %supported_mechs)); + $server->print("", " Restricted to: ".join(", ", @allowed_mechs)); + return; + } + + my $authfn = $supported_mechs{$mech}; + + my $response = &$authfn($challenge, $user, $pass); + $server->send_message('Q@cserve.quakenet.org', "CHALLENGEAUTH $user $response $mech", 1); + } + + elsif ($msg =~ /^You are now logged in as (.+?)\.$/) { + Irssi::signal_stop(); + $server->print("", "Authentication successful, logged in as $1"); + } + + elsif ($msg =~ /^Username or password incorrect\.$/) { + Irssi::signal_stop(); + $server->print("", "Authentication failed (username or password incorrect)"); + } +}); + +Irssi::settings_add_str("misc", "quakenet_auth_allowed_mechs", "any"); +Irssi::settings_add_str("misc", "quakenet_account", ""); +if (Irssi::settings_get_str("quakenet_account") eq "") { + Irssi::print("Set your QuakeNet account using /set quakenet_account username:password"); +} diff --git a/scripts/autoop.pl b/scripts/autoop.pl index d46d099..b72def1 100644 --- a/scripts/autoop.pl +++ b/scripts/autoop.pl @@ -5,13 +5,13 @@ use Irssi; use strict; use vars qw($VERSION %IRSSI); -$VERSION = "1.00"; +$VERSION = "1.10"; %IRSSI = ( - authors => 'Timo Sirainen', + authors => 'Timo Sirainen & Jostein Kjønigsen', name => 'autoop', description => 'Simple auto-op script', license => 'Public Domain', - changed => 'Sun Mar 10 23:18 EET 2002' + changed => 'Fri Nov 24 12:55 GMT+1 2014' ); my (%opnicks, %temp_opped); @@ -89,3 +89,68 @@ sub event_massjoin { Irssi::command_bind('autoop', 'cmd_autoop'); Irssi::signal_add_last('massjoin', 'event_massjoin'); + +sub load_autoops { + my($file) = Irssi::get_irssi_dir."/autoop"; + my($count) = 0; + local(*CONF); + + %opnicks = (); + open(CONF, "<", "$file") or return; + while (my $line = <CONF>) { + if ($line !=~ /^\s*$/) { + cmd_autoop($line); + $count++; + } + } + close(CONF); + + Irssi::print("Loaded $count channels from $file"); +} + +# --------[ save_autoops ]------------------------------------------------ + +sub save_autoops { + my($auto) = @_; + my($file) = Irssi::get_irssi_dir."/autoop"; + my($count) = 0; + my($channel) = ""; + local(*CONF); + + return if $auto; + + open(CONF, ">", "$file"); + foreach $channel (keys %opnicks) { + my $masks = $opnicks{$channel}; + print CONF "$channel\t$masks\n"; + $count++; + } + close(CONF); + + Irssi::print("Saved $count channels to $file") + unless $auto; +} + + +# --------[ sig_setup_reread ]------------------------------------------ + +# main setup is reread, so let us do it too +sub sig_setup_reread { + load_autoops; +} + +# --------[ sig_setup_save ]-------------------------------------------- + +# main config is saved, and so we should save too +sub sig_setup_save { + my($mainconf,$auto) = @_; + save_autoops($auto); +} + +# persistance + +Irssi::signal_add('setup saved', 'sig_setup_save'); +Irssi::signal_add('setup reread', 'sig_setup_reread'); + +# ensure we load persisted values on start +load_autoops; diff --git a/scripts/cap_sasl.pl b/scripts/cap_sasl.pl new file mode 100644 index 0000000..7c1fba2 --- /dev/null +++ b/scripts/cap_sasl.pl @@ -0,0 +1,431 @@ +use strict; +use Irssi; +use MIME::Base64; +use vars qw($VERSION %IRSSI); +use constant CHALLENGE_SIZE => 32; + +$VERSION = "1.10"; +%IRSSI = ( + authors => 'Michael Tharp (gxti), Jilles Tjoelker (jilles), Mantas Mikulėnas (grawity)', + contact => 'grawity@gmail.com', + name => 'cap_sasl.pl', + description => 'Implements SASL authentication and enables CAP "multi-prefix"', + license => 'GPLv2', + url => 'http://ircv3.atheme.org/extensions/sasl-3.1', +); + +my %sasl_auth = (); +my %mech = (); + +sub irssi_abspath { + my $f = shift; + $f =~ s!^~/!$ENV{HOME}/!; + if ($f !~ m!^/!) { + $f = Irssi::get_irssi_dir()."/".$f; + } + return $f; +} + +sub timeout; + +sub server_connected { + my $server = shift; + if (uc $server->{chat_type} eq 'IRC') { + $server->send_raw_now("CAP LS"); + } +} + +sub event_cap { + my ($server, $args, $nick, $address) = @_; + my ($subcmd, $caps, $tosend, $sasl); + + $tosend = ''; + $sasl = $sasl_auth{$server->{tag}}; + if ($args =~ /^\S+ (\S+) :(.*)$/) { + $subcmd = uc $1; + $caps = ' '.$2.' '; + if ($subcmd eq 'LS') { + $tosend .= ' multi-prefix' if $caps =~ / multi-prefix /i; + $tosend .= ' sasl' if $caps =~ / sasl /i && defined($sasl); + $tosend =~ s/^ //; + $server->print('', "CLICAP: supported by server:$caps"); + if (!$server->{connected}) { + if ($tosend eq '') { + $server->send_raw_now("CAP END"); + } else { + $server->print('', "CLICAP: requesting: $tosend"); + $server->send_raw_now("CAP REQ :$tosend"); + } + } + Irssi::signal_stop(); + } elsif ($subcmd eq 'ACK') { + $server->print('', "CLICAP: now enabled:$caps"); + if ($caps =~ / sasl /i) { + $sasl->{buffer} = ''; + $sasl->{step} = 0; + if ($mech{$sasl->{mech}}) { + $server->send_raw_now("AUTHENTICATE " . $sasl->{mech}); + Irssi::timeout_add_once(7500, \&timeout, $server->{tag}); + } else { + $server->print('', 'SASL: attempted to start unknown mechanism "' . $sasl->{mech} . '"'); + } + } + elsif (!$server->{connected}) { + $server->send_raw_now("CAP END"); + } + Irssi::signal_stop(); + } elsif ($subcmd eq 'NAK') { + $server->print('', "CLICAP: refused:$caps"); + if (!$server->{connected}) { + $server->send_raw_now("CAP END"); + } + Irssi::signal_stop(); + } elsif ($subcmd eq 'LIST') { + $server->print('', "CLICAP: currently enabled:$caps"); + Irssi::signal_stop(); + } + } +} + +sub event_authenticate { + my ($server, $args, $nick, $address) = @_; + my $sasl = $sasl_auth{$server->{tag}}; + return unless $sasl && $mech{$sasl->{mech}}; + + $sasl->{buffer} .= $args; + return if length($args) == 400; + + my $data = ($sasl->{buffer} eq '+') ? '' : decode_base64($sasl->{buffer}); + my $out = $mech{$sasl->{mech}}($sasl, $data); + + if (defined $out) { + $out = ($out eq '') ? '+' : encode_base64($out, ''); + while (length $out >= 400) { + my $subout = substr($out, 0, 400, ''); + $server->send_raw_now("AUTHENTICATE $subout"); + } + if (length $out) { + $server->send_raw_now("AUTHENTICATE $out"); + } else { + # Last piece was exactly 400 bytes, we have to send + # some padding to indicate we're done. + $server->send_raw_now("AUTHENTICATE +"); + } + } else { + $server->send_raw_now("AUTHENTICATE *"); + } + + $sasl->{buffer} = ""; + Irssi::signal_stop(); +} + +sub event_saslend { + my ($server, $args, $nick, $address) = @_; + + my $data = $args; + $data =~ s/^\S+ :?//; + # need this to see it, ?? -- jilles + + $server->print('', $data); + if (!$server->{connected}) { + $server->send_raw_now("CAP END"); + } +} + +sub event_saslfail { + my ($server, $args, $nick, $address) = @_; + + my $data = $args; + $data =~ s/^\S+ :?//; + + if (Irssi::settings_get_bool('sasl_disconnect_on_fail')) { + $server->print('', "$data - disconnecting from server", MSGLEVEL_CLIENTERROR); + $server->disconnect(); + } else { + $server->print('', "$data - continuing anyway"); + if (!$server->{connected}) { + $server->send_raw_now("CAP END"); + } + } +} + +sub timeout { + my $tag = shift; + my $server = Irssi::server_find_tag($tag); + if ($server && !$server->{connected}) { + $server->print('', "SASL: authentication timed out", MSGLEVEL_CLIENTERROR); + $server->send_raw_now("CAP END"); + } +} + +sub cmd_sasl { + my ($data, $server, $item) = @_; + + if ($data ne '') { + Irssi::command_runsub ('sasl', $data, $server, $item); + } else { + cmd_sasl_show(@_); + } +} + +sub cmd_sasl_set { + my ($data, $server, $item) = @_; + + if (my ($net, $u, $p, $m) = $data =~ /^(\S+) (\S+) (\S+) (\S+)$/) { + if ($mech{uc $m}) { + $sasl_auth{$net}{user} = $u; + $sasl_auth{$net}{password} = $p; + $sasl_auth{$net}{mech} = uc $m; + Irssi::print("SASL: added $net: [$m] $sasl_auth{$net}{user} *"); + } else { + Irssi::print("SASL: unknown mechanism $m", MSGLEVEL_CLIENTERROR); + } + } elsif ($data =~ /^(\S+)$/) { + $net = $1; + if (defined($sasl_auth{$net})) { + delete $sasl_auth{$net}; + Irssi::print("SASL: deleted $net"); + } else { + Irssi::print("SASL: no entry for $net"); + } + } else { + Irssi::print("SASL: usage: /sasl set <net> <user> <password or keyfile> <mechanism>"); + } +} + +sub cmd_sasl_show { + #my ($data, $server, $item) = @_; + my @nets = keys %sasl_auth; + for my $net (@nets) { + Irssi::print("SASL: $net: [$sasl_auth{$net}{mech}] $sasl_auth{$net}{user} *"); + } + Irssi::print("SASL: no networks defined") if !@nets; +} + +sub cmd_sasl_save { + #my ($data, $server, $item) = @_; + my $file = Irssi::get_irssi_dir()."/sasl.auth"; + if (open(my $fh, ">", $file)) { + chmod(0600, $file); + for my $net (keys %sasl_auth) { + printf $fh ("%s\t%s\t%s\t%s\n", + $net, + $sasl_auth{$net}{user}, + $sasl_auth{$net}{password}, + $sasl_auth{$net}{mech}); + } + close($fh); + Irssi::print("SASL: auth saved to '$file'"); + } else { + Irssi::print("SASL: couldn't access '$file': $@"); + } +} + +sub cmd_sasl_load { + #my ($data, $server, $item) = @_; + my $file = Irssi::get_irssi_dir()."/sasl.auth"; + if (open(my $fh, "<", $file)) { + %sasl_auth = (); + while (<$fh>) { + chomp; + my ($net, $u, $p, $m) = split(/\t/, $_, 4); + $m ||= "PLAIN"; + if ($mech{uc $m}) { + $sasl_auth{$net}{user} = $u; + $sasl_auth{$net}{password} = $p; + $sasl_auth{$net}{mech} = uc $m; + } else { + Irssi::print("SASL: unknown mechanism $m", MSGLEVEL_CLIENTERROR); + } + } + close($fh); + Irssi::print("SASL: cap_sasl $VERSION, auth loaded from '$file'"); + } +} + +sub cmd_sasl_mechanisms { + Irssi::print("SASL: mechanisms supported: " . join(", ", sort keys %mech)); +} + +Irssi::settings_add_bool('server', 'sasl_disconnect_on_fail', 1); + +Irssi::signal_add_first('server connected', \&server_connected); +Irssi::signal_add('event cap', \&event_cap); +Irssi::signal_add('event authenticate', \&event_authenticate); +Irssi::signal_add('event 903', \&event_saslend); +Irssi::signal_add('event 904', \&event_saslfail); +Irssi::signal_add('event 905', \&event_saslend); +Irssi::signal_add('event 906', \&event_saslfail); +Irssi::signal_add('event 907', \&event_saslend); + +Irssi::command_bind('sasl', \&cmd_sasl); +Irssi::command_bind('sasl load', \&cmd_sasl_load); +Irssi::command_bind('sasl save', \&cmd_sasl_save); +Irssi::command_bind('sasl set', \&cmd_sasl_set); +Irssi::command_bind('sasl show', \&cmd_sasl_show); +Irssi::command_bind('sasl mechanisms', \&cmd_sasl_mechanisms); + +$mech{PLAIN} = sub { + my ($sasl, $data) = @_; + my $u = $sasl->{user}; + my $p = $sasl->{password}; + return join("\0", $u, $u, $p); +}; + +$mech{EXTERNAL} = sub { + my ($sasl, $data) = @_; + return $sasl->{user} // ""; +}; + +if (eval {require Crypt::PK::ECC}) { + my $mech = "ECDSA-NIST256P-CHALLENGE"; + + $mech{'ECDSA-NIST256P-CHALLENGE'} = sub { + my ($sasl, $data) = @_; + my $u = $sasl->{user}; + my $f = $sasl->{password}; + $f = irssi_abspath($f); + if (!-f $f) { + Irssi::print("SASL: key file '$f' not found", MSGLEVEL_CLIENTERROR); + return; + } + my $pk = eval {Crypt::PK::ECC->new($f)}; + if ($@ || !$pk || !$pk->is_private) { + Irssi::print("SASL: no private key in file '$f'", MSGLEVEL_CLIENTERROR); + return; + } + my $step = ++$sasl->{step}; + if ($step == 1) { + if (length $data == CHALLENGE_SIZE) { + my $sig = $pk->sign_hash($data); + return $u."\0".$u."\0".$sig; + } elsif (length $data) { + return; + } else { + return $u."\0".$u; + } + } + elsif ($step == 2) { + if (length $data == CHALLENGE_SIZE) { + return $pk->sign_hash($data); + } else { + return; + } + } + }; + + sub cmd_sasl_keygen { + my ($data, $server, $witem) = @_; + + my $print = $server + ? sub { $server->print("", shift, shift // MSGLEVEL_CLIENTNOTICE) } + : sub { Irssi::print(shift, shift // MSGLEVEL_CLIENTNOTICE) }; + + my $net = $server ? $server->{tag} : $data; + if (!length $net) { + Irssi::print("SASL: please connect to a server first", + MSGLEVEL_CLIENTERROR); + return; + } + + my $f_name = lc "sasl-ecdsa-$net"; + $f_name =~ s![ /]+!_!g; + my $f_priv = Irssi::get_irssi_dir()."/$f_name.key"; + my $f_pub = Irssi::get_irssi_dir()."/$f_name.pub"; + if (-e $f_priv) { + $print->("SASL: refusing to overwrite '$f_priv'", MSGLEVEL_CLIENTERROR); + return; + } + + $print->("SASL: generating keypair for '$net'..."); + my $pk = Crypt::PK::ECC->new; + $pk->generate_key("prime256v1"); + + my $priv = $pk->export_key_pem("private"); + my $pub = encode_base64($pk->export_key_raw("public_compressed"), ""); + + if (open(my $fh, ">", $f_priv)) { + chmod(0600, $f_priv); + print $fh $priv; + close($fh); + $print->("SASL: wrote private key to '$f_priv'"); + } else { + $print->("SASL: could not write '$f_priv': $!", MSGLEVEL_CLIENTERROR); + return; + } + + if (open(my $fh, ">", $f_pub)) { + print $fh $pub."\n"; + close($fh); + } else { + $print->("SASL: could not write '$f_pub': $!", MSGLEVEL_CLIENTERROR); + } + + my $cmdchar = substr(Irssi::settings_get_str("cmdchars"), 0, 1); + my $cmd = "msg NickServ SET PROPERTY pubkey $pub"; + # TODO: change to 'SET PUBKEY' when freenode gets support for that + + if ($server) { + $print->("SASL: updating your Irssi settings..."); + $sasl_auth{$net}{user} //= $server->{nick}; + $sasl_auth{$net}{password} = "$f_name.key"; + $sasl_auth{$net}{mech} = $mech; + cmd_sasl_save(@_); + $print->("SASL: submitting pubkey to NickServ..."); + $server->command($cmd); + } else { + $print->("SASL: update your Irssi settings:"); + $print->("%P".$cmdchar."sasl set $net <nick> $f_name.key $mech"); + $print->("SASL: submit your pubkey to $net:"); + $print->("%P".$cmdchar.$cmd); + } + } + + sub cmd_sasl_pubkey { + my ($data, $server, $witem) = @_; + + my $arg = $server ? $server->{tag} : $data; + + my $f; + if (!length $arg) { + Irssi::print("SASL: please select a server or specify a keyfile path", + MSGLEVEL_CLIENTERROR); + return; + } elsif ($arg =~ m![/.]!) { + $f = $arg; + } else { + if ($sasl_auth{$arg}{mech} eq $mech) { + $f = $sasl_auth{$arg}{password}; + } else { + $f = lc "sasl-ecdsa-$arg"; + $f =~ s![ /]+!_!g; + $f = "$f.key"; + } + } + + $f = irssi_abspath($f); + if (!-e $f) { + Irssi::print("SASL: keyfile '$f' not found", MSGLEVEL_CLIENTERROR); + return; + } + + my $pk = eval {Crypt::PK::ECC->new($f)}; + if ($@ || !$pk || !$pk->is_private) { + Irssi::print("SASL: no private key in file '$f'", MSGLEVEL_CLIENTERROR); + Irssi::print("(keys using named parameters or PKCS#8 are not yet supported)", + MSGLEVEL_CLIENTERROR); + return; + } + + my $pub = encode_base64($pk->export_key_raw("public_compressed"), ""); + Irssi::print("SASL: loaded keyfile '$f'"); + Irssi::print("SASL: your pubkey is $pub"); + } + + Irssi::command_bind('sasl keygen', \&cmd_sasl_keygen); + Irssi::command_bind('sasl pubkey', \&cmd_sasl_pubkey); +}; + +cmd_sasl_load(); + +# vim: ts=4:sw=4 diff --git a/scripts/clones_scanner.pl b/scripts/clones_scanner.pl new file mode 100644 index 0000000..bf308a0 --- /dev/null +++ b/scripts/clones_scanner.pl @@ -0,0 +1,296 @@ +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. +# -dg!#irssi@freenode (David Leadbeater) for the several code style issues that he pointed out and that helped me to improve my Perl. + +$VERSION = '1.6'; +%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) { + + Irssi::command_bind('clones_scanner_size' , sub { + 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"; + }); + +} 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"); +}); diff --git a/scripts/cron.pl b/scripts/cron.pl index 9e1d125..0d1ac1e 100644 --- a/scripts/cron.pl +++ b/scripts/cron.pl @@ -40,6 +40,9 @@ # ? should we remember if the server was given with -server # # Changelog: +# 0.12 (2014.11.12) +# Automatically load jobs when loaded +# # 0.11 (2004.12.12) # Job are executed exactly at the time (+- 1s), not up to 59s late # @@ -69,7 +72,7 @@ use Irssi; use strict; use vars qw($VERSION %IRSSI); -$VERSION = "0.11"; +$VERSION = "0.12"; %IRSSI = ( authors => 'Piotr Krukowiecki', contact => 'piotr \at/ krukowiecki /dot\ net', @@ -290,6 +293,8 @@ sub cmd_jobsload { Irssi::print("Jobs loaded"); } +cmd_jobsload(); + Irssi::command_bind('jobs', 'cmd_jobs', 'Cron'); Irssi::command_bind('jobadd', 'cmd_jobadd', 'Cron'); Irssi::command_bind('jobdel', 'cmd_jobdel', 'Cron'); diff --git a/scripts/dice_concise.pl b/scripts/dice_concise.pl new file mode 100644 index 0000000..8d5fbe3 --- /dev/null +++ b/scripts/dice_concise.pl @@ -0,0 +1,247 @@ +# dice_concise / Based on Marcel Kossin's 'dice' RP Dice Simulator +# +# What is this? +# +# -- Marcel Kossin's notes: -- +# +# I (mkossin) often Dungeon Master on our Neverwinternights Servers called 'Bund der +# alten Reiche' (eng. 'Alliance of the old realms') at bundderaltenreiche.de +# (German Site) Often idling in our Channel I thought it might be Fun to have +# a script to dice. Since I found nothing for irssi I wrote this little piece +# of script. The script assumes, that if a 'd' for english dice is given it +# should print the output in English. On the other hand if a 'w' for German +# 'Würfel' is given it prints the output in German. +# +# Usage. +# +# Anyone on the Channel kann ask '!roll' to toss the dice for him. He just has +# to say what dice he want to use. The notation should be well known from +# RP :-) Thus +# +# Write: !roll <quantity of dice>d[or w for german users]<sides on dice> +# +# Here are some examples +# +# !roll 2d20 +# !roll 3d6 +# +# OK, I think you got it already :-) +# +# Write: !roll version +# For Version Information +# +# Write: !roll help +# For Information about how to use it +# +# -- Makaze's notes: -- +# +# [Changes in dice_concise:] +# +# Features added: +# +# [ ] Can add bonuses to the roll. e.g. "!roll 3d6+10" +# [ ] Output changed to one line only. e.g. "Makaze rolls the 3d6 and gets: 9 [4, +# 4, 1]" +# [ ] Corrected English grammar. +# [ ] Removed insults. +# [ ] Cleaner code with fewer nested if statements and true case switches. +# [ ] Errors call before the loop, saving clock cycles. +# +# Bugs fixed: +# +# [ ] Rolls within the correct range.* +# +# Edge cases added: +# +# [ ] Catch if rolling less than 1 dice. +# [ ] Catch if dice or sides are above 100 instead of 99. +# +# ----------------------------------------- +# +# * [The original dice.pl rolled a number between 1 and (<number of sides> - 1)] +# [instead of using the full range. e.g. "!roll 1d6" would output 1 through ] +# [5, but never 6. ] +# +# ----------------------------------------- +# +# Original script 'dice.pl' by mkossin. +# +# Updated script 'dice_concise.pl' by Makaze. + +use strict; +use vars qw($VERSION %IRSSI); +use feature qw(switch); +use Scalar::Util qw(looks_like_number); + +use Irssi qw(command_bind signal_add); + +$VERSION = '0.1.5'; +%IRSSI = ( + authors => 'Marcel Kossin, Makaze', + contact => 'izaya.orihara@gmail', + name => 'dice_concise', + description => 'A concise dice simulator for channels.', + license => 'GNU GPL v2 or later' +); + +sub own_question { + my ($server, $msg, $nick, $address, $target) = @_; + question($server, $msg, $nick, $target); +} + +sub public_question { + my ($server, $msg, $nick, $address, $target) = @_; + question($server, $msg, $nick, $target); +} + +sub question($server, $msg, $nick, $target) { + my ($server, $msg, $nick, $target) = @_; + $_ = $msg; + + my $msgCompare = lc; + + if (substr($msgCompare, 0, 5) ne '!roll') { + return 0; + } + + unless (length $target) { + $target = $nick; + $nick = $server->{nick}; + } + + if (/\d[dw]\d/i) { + my $rnd; + my $forloop; + my $lang; + my @roll = split(/\s/, $_, 2); + my ($dice, $sides) = (@roll[1] =~ /(\d+)[dw](\d+)/i); + my @modifiers = ($roll[1] =~ /([\+\-\*\/]\d+)/gi); + my $modifyType; + my $modifyVal; + my @modifyErrors = ($roll[1] =~ /([\+\-\*\/][^\d\+\-\*\/]+)/); + my $value; + # Plus support added + my @rolls; + + if (/\d[w]\d/i) { + $lang = 'DE'; + } else { + $lang = 'EN'; + } + + if ($dice < 1) { + given ($lang) { + when ('DE') { + $server->command('msg ' . $target . ' ' . $nick . ' macht nichts... Würfeln funktioniert am besten mit Würfeln.'); + } + when ('EN') { + $server->command('msg ' . $target . ' ' . $nick . ' does nothing... Rolling dice works best with dice.'); + } + } + return 0; + } elsif ($dice > 100) { + given ($lang) { + when ('DE') { + $server->command('msg ' . $target . ' ' . $nick . ' scheitert den ' . $roll[1] . ' zu werfen... Versuch es mit weniger Würfeln.'); + } + when ('EN') { + $server->command('msg ' . $target . ' ' . $nick . ' fails to roll the ' . $roll[1] . '... Try fewer dice.'); + } + } + return 0; + } elsif ($sides <= 1) { + if ($sides == 0) { + given ($lang) { + when ('DE') { + $server->command('msg ' . $target . ' ' . $nick . ' verursacht ein Paradox... Oder hat jemand schon mal einen Würfel ohne Seiten gesehen?'); + } + when ('EN') { + $server->command('msg ' . $target . ' ' . $nick . ' causes a paradox... Or has anybody ever seen a die without sides?'); + } + } + return 0; + } elsif ($sides == 1) { + given ($lang) { + when ('DE') { + $server->command('msg ' . $target . ' ' . $nick . ' verursacht ein Paradox... Oder hat jemand schon mal einen Würfel mit nur einer Seite gesehen?'); + } + when ('EN') { + $server->command('msg ' . $target . ' ' . $nick . ' causes a paradox... Or has anybody ever seen a die with only one side?'); + } + } + return 0; + } + } elsif ($sides > 100) { + given ($lang) { + when ('DE') { + $server->command('msg ' . $target . ' ' . $nick . ' scheitert den ' . $roll[1] . ' zu werfen... Versuch es mit weniger Augen.'); + } + when ('EN') { + $server->command('msg ' . $target . ' ' . $nick . ' fails to roll the ' . $roll[1] . '... Try fewer sides.'); + } + } + return 0; + } + for ($forloop = 0; $forloop < $dice; $forloop++) { + $rnd = int(rand($sides)); + if ($rnd == 0) { + $rnd = $sides; + } + $value += $rnd; + $rolls[$forloop] = $rnd; + } + foreach (@modifiers) { + ($modifyType) = ($_ =~ /([\+\-\*\/])/); + ($modifyVal) = ($_ =~ /(\d+)/); + given ($modifyType) { + when ('*') { + $value = $value * $modifyVal; + } + when ('/') { + $value = $value / $modifyVal; + } + when ('+') { + $value = $value + $modifyVal; + } + when ('-') { + $value = $value - $modifyVal; + } + } + } + given ($lang) { + when ('DE') { + $server->command('msg ' . $target . ' '. $nick . ' würfelt mit dem ' . $roll[1] . ' und erhält: ' . $value . ' [' . join(', ', @rolls) . ']'); + } + when ('EN') { + $server->command('msg ' . $target . ' '. $nick . ' rolls the ' . $roll[1] . ' and gets: ' . $value . ' [' . join(', ', @rolls) . ']'); + } + } + if (@modifyErrors) { + given ($lang) { + when ('DE') { + $server->command('msg ' . $target . ' ' . $nick . ' scheitert ihr Ergebnis zu ändern. Versuch es mit Zahlen. [' . join(', ', @modifyErrors) . ']'); + } + when ('EN') { + $server->command('msg ' . $target . ' ' . $nick . ' fails to modify their result. Try using numbers. [' . join(', ', @modifyErrors) . ']'); + } + } + } + return 1; + } elsif (substr($msgCompare, 0, 13) eq '!roll version') { + $server->command('msg ' . $target . " \x039" . $IRSSI{'name'} . ": Version " . $VERSION . " by Makaze & mkossin"); + return 0; + } elsif (substr($msgCompare, 0, 10) eq '!roll help') { + $server->command('msg ' . $target . ' Syntax: "!roll <quantity of dice>d<sides on dice>[<+-*/>modifier]" - e.g. "!roll 2d20", "!roll 2d20*2+10"'); + return 0; + } elsif (substr($msgCompare, 0, 11) eq '!roll hilfe') { + $server->command('msg ' . $target . ' Syntax: "!roll <Anzahl der Würfel>w<Augen des Würfels>[<+-*/>Modifikator]" - z.B. "!roll 2w20", "!roll 2w20*2+10"'); + return 0; + } else { + $server->command('msg ' . $target .' "!roll help" - gives the English help'); + $server->command('msg ' . $target . ' "!roll hilfe" - zeigt die deutsche Hilfe an'); + return 0; + } +} + +signal_add('message public', 'public_question'); +signal_add('message own_public', 'own_question');
\ No newline at end of file diff --git a/scripts/eng_no_translate_dpryo.pl b/scripts/eng_no_translate_dpryo.pl deleted file mode 100644 index f00f40c..0000000 --- a/scripts/eng_no_translate_dpryo.pl +++ /dev/null @@ -1,57 +0,0 @@ -# A simple script for all Norwegians who like to get -# all incoming english text translated to Norwegian :D -# Written by dpryo <hnesland@samsen.com> -# -# WARNING: -# Dunno what freetranslation.com thinks about it ;D -# ..so remember, this scripts sends ALL incoming public messages -# as a webrequest to their server. That is, one request pr. -# message you get. In other words, if somebody pubfloods 100 lines, you will -# visit freetranslation.com 100 times ;) -###### -# -# There is at least one bug in it .. It doesn't check wether the -# incoming text is english or not before it sends the request. -# -# Somebody could perhaps fix that?, since i'm a lazy asshole. -# -# Another thing, it doesn't handles channels or anything, so -# I could call this a "Technology Preview" as all the big -# guys are calling their software when it's in a buggy and -# not-so-very-usefull stage of development :P -# -use Irssi; -use LWP::Simple; -use vars qw($VERSION %IRSSI); -$translate =0; - -$VERSION = "0.2"; -%IRSSI = ( - authors => "Harald Nesland", - contact => "hnesland\@samsen.com", - name => "EngNoTranslate", - description => "Very simple script that sends incoming text to freetranslation.com for english->norwegian translation. May be modified to translate other languages.", - license => "Public Domain", - url => "http://www.satyra.net", - changed => "Thu Apr 11 14:15:25 CEST 2002" -); - -sub income { -my ($server, $data, $nick, $mask, $target) = @_; - $eng = $data; - if($translate=1) { - $eng =~ s/ /+/ig; - chop($eng); - Irssi::command("/echo [$nick] $eng"); - $result = get("http://ets.freetranslation.com:5081/?Sequence=core&Mode=txt&template=TextResults2.htm&Language=English/Norwegian&SrcText=$eng"); - Irssi::command("/echo [$nick] $result"); - } -} - -sub trans { - - if($translate =0) { $translate=1; } else { $translate =0; } -} - -Irssi::signal_add("message public", "income"); -Irssi::command_bind("translate","trans"); diff --git a/scripts/fnotify.pl b/scripts/fnotify.pl new file mode 100644 index 0000000..76e46b4 --- /dev/null +++ b/scripts/fnotify.pl @@ -0,0 +1,92 @@ +use strict; +use warnings; +use vars qw($VERSION %IRSSI); +use Irssi; + +$VERSION = '0.0.5'; +%IRSSI = ( + name => 'fnotify', + authors => 'Thorsten Leemhuis, James Shubin, Serge van Ginderachter', + description => 'Write notifications to a file in a consistent format.', + license => 'GNU General Public License', +); + +# +# README +# +# To use: +# $ cp fnotify.pl ~/.irssi/scripts/fnotify.pl +# irssi> /load perl +# irssi> /script load fnotify +# + +# +# AUTHORS +# +# Strip non-parsed left over codes (Bitlbee otr messages) +# version: 0.0.5 +# Serge van Ginderachter <serge@vanginderachter.be> +# +# Consistent output formatting by James Shubin: +# version: 0.0.4 +# https://ttboj.wordpress.com/ +# note: changed license back to original GPL from Thorsten Leemhuis (svg) +# +# Modified from the Thorsten Leemhuis <fedora@leemhuis.info> +# version: 0.0.3 +# http://www.leemhuis.info/files/fnotify/fnotify +# +# In parts based on knotify.pl 0.1.1 by Hugo Haas: +# http://larve.net/people/hugo/2005/01/knotify.pl +# +# Which is based on osd.pl 0.3.3 by Jeroen Coekaerts, Koenraad Heijlen: +# http://www.irssi.org/scripts/scripts/osd.pl +# +# Other parts based on notify.pl from Luke Macken: +# http://fedora.feedjack.org/user/918/ +# + +# +# catch private messages +# +sub priv_msg { + my ($server, $msg, $nick, $address, $target) = @_; + my $msg_stripped = Irssi::strip_codes($msg); + my $network = $server->{tag}; + filewrite('' . $network . ' ' . $nick . ' ' . $msg_stripped); +} + +# +# catch 'hilight's +# +sub hilight { + my ($dest, $text, $stripped) = @_; + if ($dest->{level} & MSGLEVEL_HILIGHT) { + my $server = $dest->{server}; + my $network = $server->{tag}; + filewrite($network . ' ' . $dest->{target} . ' ' . $stripped); + } +} + +# +# write to file +# +sub filewrite { + my ($text) = @_; + my $fnfile = Irssi::get_irssi_dir() . "/fnotify"; + if (!open(FILE, ">>", $fnfile)) { + print CLIENTCRAP "Error: cannot open $fnfile: $!"; + } else { + print FILE $text . "\n"; + if (!close(FILE)) { + print CLIENTCRAP "Error: cannot close $fnfile: $!"; + } + } +} + +# +# irssi signals +# +Irssi::signal_add_last("message private", "priv_msg"); +Irssi::signal_add_last("print text", "hilight"); + diff --git a/scripts/frm_outgmsgs.pl b/scripts/frm_outgmsgs.pl index 7e51566..71c9b02 100644 --- a/scripts/frm_outgmsgs.pl +++ b/scripts/frm_outgmsgs.pl @@ -5,7 +5,7 @@ use Scalar::Util qw(looks_like_number); use Irssi; use vars qw($VERSION %IRSSI); -$VERSION = '1.0'; +$VERSION = '1.1'; %IRSSI = ( authors => 'Pablo Martín Báez Echevarría', contact => 'pab_24n@outlook.com', @@ -95,7 +95,7 @@ sub cmd_colors { } sub is_mIRC_color { - my $num = @_; + my ( $num ) = @_; return (looks_like_number($num)) ? ((0 <= $num) && ($num <= 15)) : 0; } diff --git a/scripts/go.pl b/scripts/go.pl index 3ca6744..b656a0f 100644 --- a/scripts/go.pl +++ b/scripts/go.pl @@ -8,7 +8,7 @@ use Irssi::Irc; # If you are in #irssi you can type /go #irssi or /go irssi or even /go ir ... # also try /go ir<tab> and /go <tab> (that's two spaces) -$VERSION = '1.00'; +$VERSION = '1.01'; %IRSSI = ( authors => 'nohar', @@ -16,7 +16,7 @@ $VERSION = '1.00'; name => 'go to window', description => 'Implements /go command that activates a window given a name/partial name. It features a nice completion.', license => 'GPLv2 or later', - changed => '08-17-04' + changed => '2014-10-19' ); sub signal_complete_go { @@ -24,7 +24,7 @@ sub signal_complete_go { my $channel = $window->get_active_name(); my $k = Irssi::parse_special('$k'); - return unless ($linestart =~ /^\Q${k}\Ego/i); + return unless ($linestart =~ /^\Q${k}\Ego\b/i); @$complist = (); foreach my $w (Irssi::windows) { diff --git a/scripts/localize.pl b/scripts/localize.pl index 8d00985..7aabe9f 100644 --- a/scripts/localize.pl +++ b/scripts/localize.pl @@ -52,11 +52,15 @@ # # 03.07.2002 # *switched to Data::Dumper +# +# 25.11.2014 +# Added utrace.de as a localizer +# http://www.utrace.de/ use strict; use vars qw($VERSION %IRSSI); -$VERSION = "2003112301"; +$VERSION = "2014112501"; %IRSSI = ( authors => "Stefan 'tommie' Tomanek", contact => "stefan\@pico.ruhr.de", @@ -186,16 +190,17 @@ $procs = 0; ); %ipdb = ( - d1localizer=>{ name=>'localizer', + # For utrace.de API documentation, see http://en.utrace.de/api.php + d1utrace=>{ name=>'utrace', active=>1, - url=>'http://jan.kneschke.de/projects/localizer/index.php?query=', - city=>'<tr><td><b>City<\/b><\/td><td>(.*?)<\/td>', - province=>'<tr><td><b>Province<\/b><\/td><td>(.*?)<\/td><td>', - country=>'<tr><td><b>Country<\/b><\/td><td>(.*?)<\/td>', - provider=>'<tr><td><b>\(Backbone-\)Provider<\/b><\/td><td>(.*?)<\/td>', + url=>'http://xml.utrace.de/?query=', + city=>'<region>(.*?)<\/region>', + province=>'<org>(.*?)<\/org>', + country=>'<countrycode>(.*?)<\/countrycode>', + provider=>'<isp>(.*?)<\/isp>', failure=>'request-limit-exceeded|Host not found'}, d2ipatlas=> { name=>'IP-Atlas', - active=>1, + active=>0, url=>'http://www.xpenguin.com/plot.php?address=', city=>'is located in (.*?),', province=>'is located in.*, (.*?) \(state\),', diff --git a/scripts/rainbow.pl b/scripts/rainbow.pl index 3fc2375..0dd292d 100644 --- a/scripts/rainbow.pl +++ b/scripts/rainbow.pl @@ -13,6 +13,9 @@ # # /RKICK <nick> [reason] # - kicks nick from the current channel with coloured reason +# +# /RKNOCKOUT [time] <nicks> [reason] +# - knockouts nicks from the current channel with coloured reason for time # Written by Jakub Jankowski <shasta@atn.pl> # for Irssi 0.7.98.4 and newer @@ -20,7 +23,7 @@ use strict; use vars qw($VERSION %IRSSI); -$VERSION = "1.4"; +$VERSION = "1.6"; %IRSSI = ( authors => 'Jakub Jankowski', contact => 'shasta@atn.pl', @@ -47,6 +50,7 @@ my @colors = ('0', '4', '8', '9', '11', '12', '13'); # returns random-coloured string sub make_colors { my ($string) = @_; + Encode::_utf8_on($string); my $newstr = ""; my $last = 255; my $color = 0; @@ -132,10 +136,30 @@ sub rkick { } } +# void rknockout($text, $server, $destination) +# handles /rknockout +sub rknockout { + my ($text, $server, $dest) = @_; + + if (!$server || !$server->{connected}) { + Irssi::print("Not connected to server"); + return; + } + + if ($dest && $dest->{type} eq "CHANNEL") { + my ($time, $nick, $reason) = split(/ +/, $text, 3); + ($time, $nick, $reason) = (300, $time, $nick . " " . $reason) if ($time !~ m/^\d+$/); + return unless $nick; + $reason = "See you in " . $time . " seconds!" if ($reason =~ /^[\ ]*$/); + $dest->command("/knockout " . $time . " " . $nick . " " . make_colors($reason)); + } +} + Irssi::command_bind("rsay", "rsay"); Irssi::command_bind("rtopic", "rtopic"); Irssi::command_bind("rme", "rme"); Irssi::command_bind("rkick", "rkick"); +Irssi::command_bind("rknockout", "rknockout"); # changes: # @@ -144,3 +168,5 @@ Irssi::command_bind("rkick", "rkick"); # 29.01.2002: /rsay works with dcc chats now (v1.2) # 02.02.2002: make_colors() doesn't assign any color to spaces (v1.3) # 23.02.2002: /rkick added +# 26.11.2014: utf-8 support +# 01.12.2014: /rknockout added (v1.6) diff --git a/scripts/screen_away.pl b/scripts/screen_away.pl index 86e3087..722ceed 100644 --- a/scripts/screen_away.pl +++ b/scripts/screen_away.pl @@ -4,7 +4,7 @@ use FileHandle; use vars qw($VERSION %IRSSI); -$VERSION = "0.9.7.1"; +$VERSION = "0.9.8.1"; %IRSSI = ( authors => 'Andreas \'ads\' Scherbaum <ads@wars-nicht.de>', name => 'screen_away', @@ -18,6 +18,7 @@ $VERSION = "0.9.7.1"; # written by Andreas 'ads' Scherbaum <ads@ufp.de> # # changes: +# 20.12.2014 fix the bug when screenname is changed during the session # 07.02.2004 fix error with away mode # thanks to Michael Schiansky for reporting and fixing this one # 07.08.2004 new function for changing nick on away @@ -83,7 +84,7 @@ if (!defined($ENV{STY})) { return; } -my ($socket_name, $socket_path); +my ($socket_pid, $socket_name, $socket_path); # search for socket # normal we could search the socket file, ... if we know the path @@ -99,9 +100,13 @@ my $running_in_screen = 0; # locale doesnt seems to be an problem (yet) if ($socket !~ /^No Sockets found/s) { # ok, should have only one socket - $socket_name = $ENV{'STY'}; + # $STY won't change if sessionname is changed during session + # therefore first find the pid and use that to find the actual sessionname + $socket_pid = substr($ENV{'STY'}, 0, index($ENV{'STY'}, '.')); $socket_path = $socket; $socket_path =~ s/^.+\d+ Sockets? in ([^\n]+)\.\n.+$/$1/s; + $socket_name = $socket; + $socket_name =~ s/^.+?($socket_pid\.\S+).+$/$1/s; if (length($socket_path) != length($socket)) { # only activate, if string length is different # (to make sure, we really got a dir name) diff --git a/scripts/slack_emoji.pl b/scripts/slack_emoji.pl new file mode 100644 index 0000000..e94cec5 --- /dev/null +++ b/scripts/slack_emoji.pl @@ -0,0 +1,50 @@ +# slack_emoji.pl +# This script converts slack emoji to smileys. + +use strict; +use warnings; +use vars qw($VERSION %IRSSI); + +use Irssi; +$VERSION = '0.03'; +%IRSSI = ( + authors=> 'Lars Djerf', + contact=> 'lars.djerf@gmail.com', + name=> 'slack_emoji', + description=> 'This script converts Slack emoji to smileys.', + license=> 'GPLv3', + ); + +my %emoji = ('smile' => ':)', + 'simple_smile' => ':)', + 'smiley' => ':-)', + 'grin' => ':D', + 'wink' => ';)', + 'smirk' => ';)', + 'blush' => ':$', + 'stuck_out_tongue' => ':P', + 'stuck_out_tongue_winking_eye' => ';P', + 'stuck_out_tongue_closed_eyes' => '', + 'disappointed' => ':(', + 'astonished' => ':O', + 'open_mouth' => ':O', + 'heart' => '<3', + 'broken_heart' => '</3', + 'thumb' => '*thumbs-up*', + 'thumbsup' => '*thumbs-up*', + 'confused' => ':S'); + +sub event_message ($$$) { + my ($server, $msg, @rest) = @_; + my @matches = ($msg =~ /\:(\w+)\:/g); + foreach (@matches) { + if ($emoji{$_}) { + my $smiley = $emoji{$_}; + $msg =~ s/\:$_\:/$smiley/; + } + } + Irssi::signal_continue($server, $msg, @rest); +} + +Irssi::signal_add_first('message public', 'event_message'); +Irssi::signal_add_first('message private', 'event_message'); diff --git a/scripts/trackbar.pl b/scripts/trackbar.pl index 7123330..8f38a52 100644 --- a/scripts/trackbar.pl +++ b/scripts/trackbar.pl @@ -1,4 +1,22 @@ # trackbar.pl +# +# Track what you read last when switching to a window. +# +# Copyright (C) 2003 Peter Leurs +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # This little script will do just one thing: it will draw a line each time you # switch away from a window. This way, you always know just upto where you've @@ -10,27 +28,46 @@ # The script works right out of the box, but if you want you can change # the working by /set'ing the following variables: # -# trackbar_string The characters to repeat to draw the bar -# trackbar_style The style for the bar, %r is red for example -# See formats.txt that came with irssi +# trackbar_string The characters to repeat to draw the bar +# "---" => U+2500 => a line +# "===" => U+2550 => a double line +# "___" => U+2501 => a wide line +# "# #" => U+25ad.U+0020 => a white rectangle and a space +# trackbar_style The style for the bar, %r is red for example +# See formats.txt that came with irssi +# trackbar_hide_windows Comma seperated list of window names where the +# trackbar should not be drawn. +# trackbar_timestamp Prints a timestamp at the start of the bar +# trackbar_timestamp_styled When enabled, the timestamp respects +# trackbar_style # -# /mark is a command that will redraw the line at the bottom. However! This -# requires irssi version after 20021228. otherwise you'll get the error -# redraw: unknown command, and your screen is all goofed up :) +# /tb or /tb scroll is a command that will scroll to trackbar. # -# /upgrade & buf.pl notice: This version tries to remove the trackbars before -# the upgrade is done, so buf.pl does not restore them, as they are not removeable -# afterwards by trackbar. Unfortiounatly, to make this work, trackbar and buf.pl -# need to be loaded in a specific order. Please experiment to see which order works -# for you (strangely, it differs from configuration to configuration, something I will -# try to fix in a next version) +# /tb mark is a command that will redraw the line at the bottom. However! +# This requires irssi version after 20021228. otherwise you'll get the +# error redraw: unknown command, and your screen is all goofed up :) +# +# /upgrade & buf.pl notice: This version tries to remove the trackbars +# before the upgrade is done, so buf.pl does not restore them, as they are +# not removeable afterwards by trackbar. Unfortunately, to make this work, +# trackbar and buf.pl need to be loaded in a specific order. Please +# experiment to see which order works for you (strangely, it differs from +# configuration to configuration, something I will try to fix in a next +# version) # # Authors: # - Main maintainer & author: Peter 'kinlo' Leurs # - Many thanks to Timo 'cras' Sirainen for placing me on my way # - on-upgrade-remove-line patch by Uwe Dudenhoeffer +# - trackbar resizing by Michiel Holtkamp (02 Jul 2012) +# - scroll to trackbar, window excludes, and timestamp options by Nico R. +# Wohlgemuth (22 Sep 2012) # # Version history: +# 1.7: - Added /tb scroll, trackbar_hide_windows, trackbar_timestamp_timestamp +# and trackbar_timestamp_styled +# 1.6: - Work around Irssi resize bug, please do /upgrade! (see below) +# 1.5: - Resize trackbars in all windows when terminal is resized # 1.4: - Changed our's by my's so the irssi script header is valid # - Removed utf-8 support. In theory, the script should work w/o any # problems for utf-8, just set trackbar_string to a valid utf-8 character @@ -47,65 +84,93 @@ # 1.1: - Fixed bug when closing window # 1.0: - Initial release # -# -# Call for help! -# -# There is a trackbar version 2.0 that properly handles resizes and immediate config change -# activation. However, there is/are some bug(s) in irssi's main buffer/window code that causes -# irssi to 'forget' lines, which is ofcourse completly unaccepteable. I haven't found the time -# nor do I know the irssi's internals enough to find and fix this bug, if you want to help, please -# contact me, I'll give you a copy of the 2.0 version that will immediatly show you the problems. +# Contacts +# https://github.com/mjholtkamp/irssi-trackbar # # Known bugs: # - if you /clear a window, it will be uncleared when returning to the window -# - UTF-8 characters in the trackbar_string doesnt work. This is an irssi bug. -# - if you resize your irssi (in xterm or so) the bar is not resized # - changing the trackbar style is only visible after returning to a window -# however, changing style/resize takes in effect after you left the window. +# however, changing style/resize takes in effect after you left the window. # # Whishlist/todo: # - instead of drawing a line, just invert timestamp or something, # to save a line (but I don't think this is possible with current irssi) -# - some pageup keybinding possibility, to scroll up upto the trackbar # - <@coekie> kinlo: if i switch to another window, in another split window, i # want the trackbar to go down in the previouswindow in that splitwindow :) # - < bob_2> anyway to clear the line once the window is read? # - < elho> kinlo: wishlist item: a string that gets prepended to the repeating pattern -# - < elho> an option to still have the timestamp in front of the bar -# - < elho> oh and an option to not draw it in the status window :P # # BTW: when you have feature requests, mailing a patch that works is the fastest way # to get it added :p +# IRSSI RESIZE BUG: +# when resizing from a larger window to a smaller one, the width of the +# trackbar causes some lines at the bottom not to be shown. This only happens +# if the trackbar was not the last line. This glitch can be 'fixed' by +# resetting the trackbar to the last line (e.g. by switching to another window +# and back) and then resize twice (e.g. to a bigger size and back). Of course, +# this is not convenient for the user. +# This script works around this problem by printing not one, but two lines and +# then removing the second line. My guess is that irssi does something to the +# previous line (or the line cache) whenever a line is 'completed' (i.e. the +# EOL is sent). When only one line is printed, it is not 'completed', but when +# printing the second line, the first line is 'completed'. The second line is +# still not completed, but since we delete it straight away, it doesn't matter. +# +# Some effects from older versions (<1.6) of trackbar.pl can still screw up your +# buffer so we recommend to restart your irssi, or do an "/upgrade". After +# installing this version of trackbar.pl + use strict; use 5.6.1; use Irssi; use Irssi::TextUI; +use POSIX qw(strftime); +use utf8; -my $VERSION = "1.4"; +my $VERSION = "1.7"; my %IRSSI = ( - authors => "Peter 'kinlo' Leurs", - contact => "peter\@pfoe.be", + authors => "Peter 'kinlo' Leurs, Uwe Dudenhoeffer, " . + "Michiel Holtkamp, Nico R. Wohlgemuth", + contact => "irssi-trackbar\@supermind.nl", name => "trackbar", description => "Shows a bar where you've last read a window", license => "GPLv2", - url => "http://www.pfoe.be/~peter/trackbar/", - changed => "Thu Feb 20 16:18:08 2003", + url => "http://github.com/mjholtkamp/irssi-trackbar/", + changed => "Tue, 22 Sep 2012 14:33:31 +0000", ); my %config; +my $screen_resizing = 0; # terminal is being resized + +# This could be '(status)' if you want to hide the status window +Irssi::settings_add_str('trackbar', 'trackbar_hide_windows' => ''); +$config{'trackbar_hide_windows'} = Irssi::settings_get_str('trackbar_hide_windows'); + Irssi::settings_add_str('trackbar', 'trackbar_string' => '-'); $config{'trackbar_string'} = Irssi::settings_get_str('trackbar_string'); Irssi::settings_add_str('trackbar', 'trackbar_style' => '%K'); $config{'trackbar_style'} = Irssi::settings_get_str('trackbar_style'); +Irssi::settings_add_bool('trackbar', 'trackbar_timestamp' => 0); +$config{'trackbar_timestamp'} = Irssi::settings_get_bool('trackbar_timestamp'); + +Irssi::settings_add_bool('trackbar', 'trackbar_timestamp_styled' => 1); +$config{'trackbar_timestamp_styled'} = Irssi::settings_get_bool('trackbar_timestamp_styled'); + +$config{'timestamp_format'} = Irssi::settings_get_str('timestamp_format'); + Irssi::signal_add( 'setup changed' => sub { $config{'trackbar_string'} = Irssi::settings_get_str('trackbar_string'); $config{'trackbar_style'} = Irssi::settings_get_str('trackbar_style'); + $config{'trackbar_hide_windows'} = Irssi::settings_get_str('trackbar_hide_windows'); + $config{'trackbar_timestamp'} = Irssi::settings_get_bool('trackbar_timestamp'); + $config{'trackbar_timestamp_styled'} = Irssi::settings_get_bool('trackbar_timestamp_styled'); + $config{'timestamp_format'} = Irssi::settings_get_str('timestamp_format'); if ($config{'trackbar_style'} =~ /(?<!%)[^%]|%%|%$/) { Irssi::print( "trackbar: %RWarning!%n 'trackbar_style' seems to contain " @@ -119,7 +184,12 @@ Irssi::signal_add( 'window changed' => sub { my (undef, $oldwindow) = @_; - if ($oldwindow) { + my @hidden = split(',', $config{'trackbar_hide_windows'}); + + # remove whitespace around window names + s{^\s+|\s+$}{}g foreach @hidden; + + if ($oldwindow && !($oldwindow->{'name'} ~~ @hidden)) { my $line = $oldwindow->view()->get_bookmark('trackbar'); $oldwindow->view()->remove_line($line) if defined $line; $oldwindow->print(line($oldwindow->{'width'}), MSGLEVEL_NEVER); @@ -128,62 +198,201 @@ Irssi::signal_add( } ); +# terminal resize code inspired on nicklist.pl +sub sig_terminal_resized { + if ($screen_resizing) { + # prevent multiple resize_trackbars from running + return; + } + $screen_resizing = 1; + Irssi::timeout_add_once(10,\&resize_trackbars,[]); +} + +sub resize_trackbars { + my $active_win = Irssi::active_win(); + for my $window (Irssi::windows) { + next unless defined $window; + my $line = $window->view()->get_bookmark('trackbar'); + next unless defined $line; + + # first add new trackbar line, then remove the old one. For some reason + # this works better than removing the old one, then adding a new one + $window->print_after($line, MSGLEVEL_NEVER, line($window->{'width'})); + my $next = $line->next(); + $window->view()->set_bookmark('trackbar', $next); + $window->view()->remove_line($line); + + # This hack exists to work around a bug: see IRSSI RESIZE BUG above. + # Add a line after the trackbar and delete it immediately + $window->print_after($next, MSGLEVEL_NEVER, line(1)); + $window->view()->remove_line($next->next); + } + $active_win->view()->redraw(); + $screen_resizing = 0; +} + +Irssi::signal_add('terminal resized' => \&sig_terminal_resized); + sub line { my $width = shift; my $string = $config{'trackbar_string'}; - $string = '-' unless defined $string; - # There is a bug in irssi's utf-8 handling on config file settings, as you + my $tslen = 0; + + if ($config{'trackbar_timestamp'}) { + $tslen = int(1 + length $config{'timestamp_format'}); + } + + if (!defined($string) || $string eq '') { + $string = '-'; + } + + # There is a bug in (irssi's) utf-8 handling on config file settings, as you # can reproduce/see yourself by the following code sniplet: # # my $quake = pack 'U*', 8364; # EUR symbol # Irssi::settings_add_str 'temp', 'temp_foo' => $quake; - # Irssi::print length $quake; - # # prints 1 - # Irssi::print length Irssi::settings_get_str 'temp_foo'; - # # prints 3 - # - # - # Trackbar used to have a workaround, but on recent versions of perl/irssi - # it does no longer work. Therefore, if you want your trackbar to contain - # unicode characters, uncomment the line below for a nice full line, or set - # the string to whatever char you want. + # $a= length($quake); + # # $a => 1 + # $a= length(Irssi::settings_get_str 'temp_foo'); + # # $a => 3 + # $a= utf8::is_utf8(Irssi::settings_get_str 'temp_foo'); + # # $a => false - # $string = pack('U*', 0x2500); + utf8::decode($string); + if ($string =~ m/---/) { + $string = pack('U*', 0x2500); + } - my $length = length $string; + if ($string =~ m/===/) { + $string = pack('U*', 0x2550); + } - if ($length == 0) { - $string = '-'; - $length = 1; - } + if ($string =~ m/___/) { + $string = pack('U*', 0x2501); + } - my $times = $width / $length; + if ($string =~ m/# #/) { + $string = pack('U*', 0x25ad)." "; + } + + my $length = length $string; + + my $times = $width / $length - $tslen; $times = int(1 + $times) if $times != int($times); $string =~ s/%/%%/g; - return $config{'trackbar_style'} . substr($string x $times, 0, $width); + + if ($tslen) { + # why $config{'timestamp_format'} won't work here? + my $ts = strftime(Irssi::settings_get_str('timestamp_format')." ", localtime); + + if ($config{'trackbar_timestamp_styled'}) { + return $config{'trackbar_style'} . $ts . substr($string x $times, 0, $width); + } else { + return $ts . $config{'trackbar_style'} . substr($string x $times, 0, $width); + } + } else { + return $config{'trackbar_style'} . substr($string x $times, 0, $width); + } } # Remove trackbars on upgrade - but this doesn't really work if the scripts are not loaded in the correct order... watch out! -Irssi::signal_add_first( 'session save' => sub { - for my $window (Irssi::windows) { +Irssi::signal_add_first('session save' => sub { + for my $window (Irssi::windows) { next unless defined $window; my $line = $window->view()->get_bookmark('trackbar'); $window->view()->remove_line($line) if defined $line; - } } +} ); sub cmd_mark { my $window = Irssi::active_win(); -# return unless defined $window; my $line = $window->view()->get_bookmark('trackbar'); $window->view()->remove_line($line) if defined $line; $window->print(line($window->{'width'}), MSGLEVEL_NEVER); $window->view()->set_bookmark_bottom('trackbar'); - Irssi::command("redraw"); + Irssi::active_win()->view()->redraw(); +} + +# mark all visible windows with a line +sub cmd_mark_visual { + my $w= Irssi::active_win(); + my $refs =$w->{refnum}; + my $refa; + + cmd_mark(); + + do { + Irssi::command('window down'); + $w= Irssi::active_win(); + $refa =$w->{refnum}; + + if ($refs != $refa) { + cmd_mark(); + } + + } while ($refs != $refa) +} + +# /tb or /trackbar +sub cmd_tb { + if ($#_ >=0 ) { + my $sc = shift @_; + $sc =~ s/\s+$//; + + if ($sc eq "mark") { + cmd_mark(); + } elsif ($sc eq "help") { + cmd_help("trackbar"); + } elsif ($sc eq "vmark") { + cmd_mark_visual(); + } else { + cmd_scroll(); + } + } +} + +sub cmd_scroll { + my $window = Irssi::active_win(); + my $line = $window->view()->get_bookmark('trackbar'); + $window->view()->scroll_line($line) if defined $line; } -Irssi::command_bind('mark', 'cmd_mark'); +sub cmd_help { + my $help = <<HELP; +/trackbar or /tb + /tb mark + - Set the trackbar of the current window at the bottom + /tb vmark + - Set the trackbar on all visible windows + /tb scroll + - Scroll to where the trackbar is right now + /tb help + - this help + /tb + - Same as /tb scroll +HELP + if ($_[0] =~ m/^tb/ or $_[0] =~ m/^trackbar/ ) { + Irssi::print($help, MSGLEVEL_CLIENTCRAP); + Irssi::signal_stop; + } +} + +Irssi::command_bind('tb', 'cmd_tb'); +Irssi::command_bind('tb help', 'cmd_tb'); +Irssi::command_bind('tb mark', 'cmd_tb'); +Irssi::command_bind('tb scroll', 'cmd_tb'); +Irssi::command_bind('tb vmark', 'cmd_tb'); + +Irssi::command_bind('trackbar', 'cmd_tb'); +Irssi::command_bind('trackbar help', 'cmd_tb'); +Irssi::command_bind('trackbar mark', 'cmd_tb'); +Irssi::command_bind('trackbar scroll', 'cmd_tb'); +Irssi::command_bind('trackbar vmark', 'cmd_tb'); + +Irssi::command_bind('mark', 'cmd_mark'); +#Irssi::command_bind('scroll', 'cmd_scroll'); +Irssi::command_bind('help', 'cmd_help'); |
