diff --git a/.gitignore b/.gitignore index bbfb08e..a082e14 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ *.kate-swp result/* result -.irssi/certs/ +home/.irssi/certs/ diff --git a/home/.irssi/README.md b/home/.irssi/README.md new file mode 100644 index 0000000..39af52c --- /dev/null +++ b/home/.irssi/README.md @@ -0,0 +1,29 @@ +# README # + +This README would normally document whatever steps are necessary to get your application up and running. + +### What is this repository for? ### + +* Quick summary +* Version +* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo) + +### How do I get set up? ### + +* Summary of set up +* Configuration +* Dependencies +* Database configuration +* How to run tests +* Deployment instructions + +### Contribution guidelines ### + +* Writing tests +* Code review +* Other guidelines + +### Who do I talk to? ### + +* Repo owner or admin +* Other community or team contact \ No newline at end of file diff --git a/home/.irssi/_default.theme b/home/.irssi/_default.theme new file mode 100644 index 0000000..79b1af5 --- /dev/null +++ b/home/.irssi/_default.theme @@ -0,0 +1,295 @@ +# When testing changes, the easiest way to reload the theme is with /RELOAD. +# This reloads the configuration file too, so if you did any changes remember +# to /SAVE it first. Remember also that /SAVE overwrites the theme file with +# old data so keep backups :) + +# TEMPLATES: + +# The real text formats that irssi uses are the ones you can find with +# /FORMAT command. Back in the old days all the colors and texts were mixed +# up in those formats, and it was really hard to change the colors since you +# might have had to change them in tens of different places. So, then came +# this templating system. + +# Now the /FORMATs don't have any colors in them, and they also have very +# little other styling. Most of the stuff you need to change is in this +# theme file. If you can't change something here, you can always go back +# to change the /FORMATs directly, they're also saved in these .theme files. + +# So .. the templates. They're those {blahblah} parts you see all over the +# /FORMATs and here. Their usage is simply {name parameter1 parameter2}. +# When irssi sees this kind of text, it goes to find "name" from abstracts +# block below and sets "parameter1" into $0 and "parameter2" into $1 (you +# can have more parameters of course). Templates can have subtemplates. +# Here's a small example: +# /FORMAT format hello {colorify {underline world}} +# abstracts = { colorify = "%G$0-%n"; underline = "%U$0-%U"; } +# When irssi expands the templates in "format", the final string would be: +# hello %G%Uworld%U%n +# ie. underlined bright green "world" text. +# and why "$0-", why not "$0"? $0 would only mean the first parameter, +# $0- means all the parameters. With {underline hello world} you'd really +# want to underline both of the words, not just the hello (and world would +# actually be removed entirely). + +# COLORS: + +# You can find definitions for the color format codes in docs/formats.txt. + +# There's one difference here though. %n format. Normally it means the +# default color of the terminal (white mostly), but here it means the +# "reset color back to the one it was in higher template". For example +# if there was /FORMAT test %g{foo}bar, and foo = "%Y$0%n", irssi would +# print yellow "foo" (as set with %Y) but "bar" would be green, which was +# set at the beginning before the {foo} template. If there wasn't the %g +# at start, the normal behaviour of %n would occur. If you _really_ want +# to use the terminal's default color, use %N. + +############################################################################# + +# default foreground color (%N) - -1 is the "default terminal color" +default_color = "-1"; + +# print timestamp/servertag at the end of line, not at beginning +info_eol = "false"; + +# these characters are automatically replaced with specified color +# (dark grey by default) +replaces = { "[]=" = "%K$*%n"; }; + +abstracts = { + ## + ## generic + ## + + # text to insert at the beginning of each non-message line + line_start = "%B-%n!%B-%n "; + + # timestamp styling, nothing by default + timestamp = "$*"; + + # any kind of text that needs hilighting, default is to bold + hilight = "%_$*%_"; + + # any kind of error message, default is bright red + error = "%R$*%n"; + + # channel name is printed + channel = "%_$*%_"; + + # nick is printed + nick = "%_$*%_"; + + # nick host is printed + nickhost = "[$*]"; + + # server name is printed + server = "%_$*%_"; + + # some kind of comment is printed + comment = "[$*]"; + + # reason for something is printed (part, quit, kick, ..) + reason = "{comment $*}"; + + # mode change is printed ([+o nick]) + mode = "{comment $*}"; + + ## + ## channel specific messages + ## + + # highlighted nick/host is printed (joins) + channick_hilight = "%C$*%n"; + chanhost_hilight = "{nickhost %c$*%n}"; + + # nick/host is printed (parts, quits, etc.) + channick = "%c$*%n"; + chanhost = "{nickhost $*}"; + + # highlighted channel name is printed + channelhilight = "%c$*%n"; + + # ban/ban exception/invite list mask is printed + ban = "%c$*%n"; + + ## + ## messages + ## + + # the basic styling of how to print message, $0 = nick mode, $1 = nick + msgnick = "%K<%n$0$1-%K>%n %|"; + + # message from you is printed. "ownnick" specifies the styling of the + # nick ($0 part in msgnick) and "ownmsgnick" specifies the styling of the + # whole line. + + # Example1: You want the message text to be green: + # ownmsgnick = "{msgnick $0 $1-}%g"; + # Example2.1: You want < and > chars to be yellow: + # ownmsgnick = "%Y{msgnick $0 $1-%Y}%n"; + # (you'll also have to remove <> from replaces list above) + # Example2.2: But you still want to keep <> grey for other messages: + # pubmsgnick = "%K{msgnick $0 $1-%K}%n"; + # pubmsgmenick = "%K{msgnick $0 $1-%K}%n"; + # pubmsghinick = "%K{msgnick $1 $0$2-%n%K}%n"; + # ownprivmsgnick = "%K{msgnick $*%K}%n"; + # privmsgnick = "%K{msgnick %R$*%K}%n"; + + # $0 = nick mode, $1 = nick + ownmsgnick = "{msgnick $0 $1-}"; + ownnick = "%_$*%n"; + + # public message in channel, $0 = nick mode, $1 = nick + pubmsgnick = "{msgnick $0 $1-}"; + pubnick = "%N$*%n"; + + # public message in channel meant for me, $0 = nick mode, $1 = nick + pubmsgmenick = "{msgnick $0 $1-}"; + menick = "%Y$*%n"; + + # public highlighted message in channel + # $0 = highlight color, $1 = nick mode, $2 = nick + pubmsghinick = "{msgnick $1 $0$2-%n}"; + + # channel name is printed with message + msgchannel = "%K:%c$*%n"; + + # private message, $0 = nick, $1 = host + privmsg = "[%R$0%K(%r$1-%K)%n] "; + + # private message from you, $0 = "msg", $1 = target nick + ownprivmsg = "[%r$0%K(%R$1-%K)%n] "; + + # own private message in query + ownprivmsgnick = "{msgnick $*}"; + ownprivnick = "%_$*%n"; + + # private message in query + privmsgnick = "{msgnick %R$*%n}"; + + ## + ## Actions (/ME stuff) + ## + + # used internally by this theme + action_core = "%_ * $*%n"; + + # generic one that's used by most actions + action = "{action_core $*} "; + + # own action, both private/public + ownaction = "{action $*}"; + + # own action with target, both private/public + ownaction_target = "{action_core $0}%K:%c$1%n "; + + # private action sent by others + pvtaction = "%_ (*) $*%n "; + pvtaction_query = "{action $*}"; + + # public action sent by others + pubaction = "{action $*}"; + + + ## + ## other IRC events + ## + + # whois + whois = "%# $[8]0 : $1-"; + + # notices + ownnotice = "[%r$0%K(%R$1-%K)]%n "; + notice = "%K-%M$*%K-%n "; + pubnotice_channel = "%K:%m$*"; + pvtnotice_host = "%K(%m$*%K)"; + servernotice = "%g!$*%n "; + + # CTCPs + ownctcp = "[%r$0%K(%R$1-%K)] "; + ctcp = "%g$*%n"; + + # wallops + wallop = "%_$*%n: "; + wallop_nick = "%n$*"; + wallop_action = "%_ * $*%n "; + + # netsplits + netsplit = "%R$*%n"; + netjoin = "%C$*%n"; + + # /names list + names_prefix = ""; + names_nick = "[%_$0%_$1-] "; + names_nick_op = "{names_nick $*}"; + names_nick_halfop = "{names_nick $*}"; + names_nick_voice = "{names_nick $*}"; + names_users = "[%g$*%n]"; + names_channel = "%G$*%n"; + + # DCC + dcc = "%g$*%n"; + dccfile = "%_$*%_"; + + # DCC chat, own msg/action + dccownmsg = "[%r$0%K($1-%K)%n] "; + dccownnick = "%R$*%n"; + dccownquerynick = "%_$*%n"; + dccownaction = "{action $*}"; + dccownaction_target = "{action_core $0}%K:%c$1%n "; + + # DCC chat, others + dccmsg = "[%G$1-%K(%g$0%K)%n] "; + dccquerynick = "%G$*%n"; + dccaction = "%_ (*dcc*) $*%n %|"; + + ## + ## statusbar + ## + + # default background for all statusbars. You can also give + # the default foreground color for statusbar items. + sb_background = "%4%w"; + window_border = "%4%w"; + + # default backround for "default" statusbar group + #sb_default_bg = "%4"; + # background for prompt / input line + sb_prompt_bg = "%n"; + # background for info statusbar + sb_info_bg = "%8"; + # background for topicbar (same default) + #sb_topic_bg = "%4"; + + # text at the beginning of statusbars. "sb" already puts a space there, + # so we don't use anything by default. + sbstart = ""; + # text at the end of statusbars. Use space so that it's never + # used for anything. + sbend = " "; + + topicsbstart = "{sbstart $*}"; + topicsbend = "{sbend $*}"; + + prompt = "[$*] "; + + sb = " %c[%n$*%c]%n"; + sbmode = "(%c+%n$*)"; + sbaway = " (%GzZzZ%n)"; + sbservertag = ":$0 (change with ^X)"; + sbnickmode = "$0"; + + # activity in statusbar + + # ',' separator + sb_act_sep = "%c$*"; + # normal text + sb_act_text = "%c$*"; + # public message + sb_act_msg = "%W$*"; + # hilight + sb_act_hilight = "%M$*"; + # hilight with specified color, $0 = color, $1 = text + sb_act_hilight_color = "$0$1-%n"; +}; diff --git a/home/.irssi/config b/home/.irssi/config new file mode 100644 index 0000000..5523e91 --- /dev/null +++ b/home/.irssi/config @@ -0,0 +1,373 @@ +ignores = ( { level = "JOINS PARTS QUITS"; target = "*"; } ); +setting = { }; +servers = ( + { address = "irc.dal.net"; chatnet = "DALnet"; port = "6667"; }, + { + address = "ssl.efnet.org"; + chatnet = "EFNet"; + port = "9999"; + use_tls = "yes"; + tls_verify = "no"; + }, + { + address = "irc.esper.net"; + chatnet = "EsperNet"; + port = "6697"; + use_tls = "yes"; + tls_verify = "yes"; + }, + { + address = "irc.libera.chat"; + chatnet = "liberachat"; + port = "6697"; + use_tls = "yes"; + tls_verify = "yes"; + autoconnect = "yes"; + }, + { + address = "irc.gamesurge.net"; + chatnet = "GameSurge"; + port = "6667"; + }, + { + address = "ssl.ircnet.ovh"; + chatnet = "IRCnet"; + port = "6697"; + use_tls = "yes"; + tls_verify = "yes"; + }, + { address = "open.ircnet.net"; chatnet = "IRCnet"; port = "6667"; }, + { + address = "irc.ircsource.net"; + chatnet = "IRCSource"; + port = "6667"; + }, + { address = "irc.netfuze.net"; chatnet = "NetFuze"; port = "6667"; }, + { + address = "irc.oftc.net"; + chatnet = "OFTC"; + port = "6697"; + use_tls = "yes"; + tls_verify = "yes"; + }, + { + address = "irc.quakenet.org"; + chatnet = "QuakeNet"; + port = "6667"; + }, + { + address = "irc.rizon.net"; + chatnet = "Rizon"; + port = "6697"; + use_tls = "yes"; + tls_verify = "yes"; + }, + { address = "silc.silcnet.org"; chatnet = "SILC"; port = "706"; }, + { + address = "irc.undernet.org"; + chatnet = "Undernet"; + port = "6667"; + } +); + +chatnets = { + DALnet = { + type = "IRC"; + max_kicks = "4"; + max_msgs = "20"; + max_whois = "30"; + }; + EFNet = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "4"; + max_whois = "1"; + }; + EsperNet = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "4"; + max_whois = "1"; + }; + liberachat = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "4"; + max_whois = "1"; + sasl_mechanism = "PLAIN"; + sasl_username = "th3r00t"; + sasl_password = "b2edxfrr1"; + }; + GameSurge = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; + IRCnet = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; + IRCSource = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "4"; + max_whois = "1"; + }; + NetFuze = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; + OFTC = { type = "IRC"; max_kicks = "1"; max_msgs = "1"; max_whois = "1"; }; + QuakeNet = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; + Rizon = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; + SILC = { type = "SILC"; }; + Undernet = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; +}; + +channels = ( + { name = "#lobby"; chatnet = "EsperNet"; autojoin = "No"; }, + { name = "#libera"; chatnet = "liberachat"; autojoin = "No"; }, + { name = "#irssi"; chatnet = "liberachat"; autojoin = "No"; }, + { name = "#gamesurge"; chatnet = "GameSurge"; autojoin = "No"; }, + { name = "#irssi"; chatnet = "IRCNet"; autojoin = "No"; }, + { name = "#ircsource"; chatnet = "IRCSource"; autojoin = "No"; }, + { name = "#netfuze"; chatnet = "NetFuze"; autojoin = "No"; }, + { name = "#oftc"; chatnet = "OFTC"; autojoin = "No"; }, + { name = "silc"; chatnet = "SILC"; autojoin = "No"; }, + { name = "#linux"; chatnet = "liberachat"; autojoin = "yes"; }, + { name = "#archlinux"; chatnet = "liberachat"; autojoin = "yes"; }, + { name = "#zig"; chatnet = "liberachat"; autojoin = "yes"; }, + { name = "#python"; chatnet = "liberachat"; autojoin = "yes"; }, + { name = "#vimwiki"; chatnet = "liberachat"; autojoin = "yes"; }, + { name = "#foot"; chatnet = "liberachat"; autojoin = "yes"; } +); + +aliases = { + ATAG = "WINDOW SERVER"; + ADDALLCHANS = "SCRIPT EXEC foreach my \\$channel (Irssi::channels()) { Irssi::command(\"CHANNEL ADD -auto \\$channel->{visible_name} \\$channel->{server}->{tag} \\$channel->{key}\")\\;}"; + B = "BAN"; + BACK = "AWAY"; + BANS = "BAN"; + BYE = "QUIT"; + C = "CLEAR"; + CALC = "EXEC - if command -v bc >/dev/null 2>&1\\; then printf '%s=' '$*'\\; echo '$*' | bc -l\\; else echo bc was not found\\; fi"; + CHAT = "DCC CHAT"; + CS = "QUOTE CS"; + DATE = "TIME"; + DEHIGHLIGHT = "DEHILIGHT"; + DESCRIBE = "ACTION"; + DHL = "DEHILIGHT"; + EXEMPTLIST = "MODE $C +e"; + EXIT = "QUIT"; + GOTO = "SCROLLBACK GOTO"; + HIGHLIGHT = "HILIGHT"; + HL = "HILIGHT"; + HOST = "USERHOST"; + INVITELIST = "MODE $C +I"; + J = "JOIN"; + K = "KICK"; + KB = "KICKBAN"; + KN = "KNOCKOUT"; + LAST = "LASTLOG"; + LEAVE = "PART"; + M = "MSG"; + MS = "QUOTE MS"; + MUB = "UNBAN *"; + N = "NAMES"; + NMSG = "^MSG"; + NS = "QUOTE NS"; + OS = "QUOTE OS"; + P = "PART"; + Q = "QUERY"; + RESET = "SET -default"; + RUN = "SCRIPT LOAD"; + SAY = "MSG *"; + SB = "SCROLLBACK"; + SBAR = "STATUSBAR"; + SHELP = "QUOTE HELP"; + SIGNOFF = "QUIT"; + SV = "MSG * Irssi $J ($V) - https://irssi.org"; + T = "TOPIC"; + UB = "UNBAN"; + UMODE = "MODE $N"; + UNSET = "SET -clear"; + W = "WHO"; + WC = "WINDOW CLOSE"; + WG = "WINDOW GOTO"; + WJOIN = "JOIN -window"; + WI = "WHOIS"; + WII = "WHOIS $0 $0"; + WL = "WINDOW LIST"; + WN = "WINDOW NEW HIDDEN"; + WQUERY = "QUERY -window"; + WW = "WHOWAS"; + + statusbar = { + + items = { + + barstart = "{sbstart}"; + barend = "{sbend}"; + + topicbarstart = "{topicsbstart}"; + topicbarend = "{topicsbend}"; + + time = "{sb $Z}"; + user = "{sb {sbnickmode $cumode}$N{sbmode $usermode}{sbaway $A}}"; + + window = "{sb $winref:$tag/$itemname{sbmode $M}}"; + window_empty = "{sb $winref{sbservertag $tag}}"; + + prompt = "{prompt $[.15]itemname}"; + prompt_empty = "{prompt $winname}"; + + topic = " $topic"; + topic_empty = " Irssi v$J - https://irssi.org"; + + lag = "{sb Lag: $0-}"; + act = "{sb Act: $0-}"; + more = "-- more --"; + }; + + default = { + + window = { + + disabled = "no"; + type = "window"; + placement = "bottom"; + position = "1"; + visible = "active"; + + items = { + barstart = { priority = "100"; }; + time = { }; + user = { }; + window = { }; + window_empty = { }; + lag = { priority = "-1"; }; + act = { priority = "10"; }; + more = { priority = "-1"; alignment = "right"; }; + barend = { priority = "100"; alignment = "right"; }; + windowlist = { }; + }; + }; + + window_inact = { + + type = "window"; + placement = "bottom"; + position = "1"; + visible = "inactive"; + + items = { + barstart = { priority = "100"; }; + window = { }; + window_empty = { }; + more = { priority = "-1"; alignment = "right"; }; + barend = { priority = "100"; alignment = "right"; }; + }; + }; + + prompt = { + + type = "root"; + placement = "bottom"; + position = "100"; + visible = "always"; + + items = { + prompt = { priority = "-1"; }; + prompt_empty = { priority = "-1"; }; + input = { priority = "10"; }; + }; + }; + + topic = { + + type = "root"; + placement = "top"; + position = "1"; + visible = "always"; + + items = { + topicbarstart = { priority = "100"; }; + topic = { }; + topic_empty = { }; + topicbarend = { priority = "100"; alignment = "right"; }; + }; + }; + }; + }; + settings = { + core = { + real_name = "th3r00t"; + user_name = "th3r00t"; + nick = "th3r00t"; + }; + "perl/core/scripts" = { awl_shared_sbar = "OFF"; }; + }; +}; +settings = { + core = { + real_name = "Unknown"; + user_name = "th3r00t"; + nick = "th3r00t"; + }; + "perl/core/scripts" = { + awl_shared_sbar = "OFF"; + awl_placement = "top"; + awl_viewer = "no"; + }; + "fe-common/core" = { theme = "default"; }; + fifo_pipe = "/tmp/irssi_pipe"; +}; +statusbar = { + default = { + prompt = { + items = { + uberprompt = { priority = "-1"; }; + input = { priority = "10"; }; + }; + position = "100"; + }; + awl_0 = { + placement = "top"; + items = { + barstart = { priority = "100"; }; + awl_0 = { }; + barend = { priority = "100"; alignment = "right"; }; + }; + }; + }; +}; +keyboard = ( + { key = "meta-w"; id = "command"; data = "toggle awl_viewer"; }, + { + key = "meta-u"; + id = "command"; + data = "script exec Irssi::Script::tmux_nicklist_portable::toggle_nicklist"; + }, + { key = "meta-C"; id = "command"; data = "WINDOW KILL"; }, + { key = "meta-x"; id = "command"; data = "ido_switch_start "; } +); diff --git a/home/.irssi/config.autosave b/home/.irssi/config.autosave new file mode 100644 index 0000000..5523e91 --- /dev/null +++ b/home/.irssi/config.autosave @@ -0,0 +1,373 @@ +ignores = ( { level = "JOINS PARTS QUITS"; target = "*"; } ); +setting = { }; +servers = ( + { address = "irc.dal.net"; chatnet = "DALnet"; port = "6667"; }, + { + address = "ssl.efnet.org"; + chatnet = "EFNet"; + port = "9999"; + use_tls = "yes"; + tls_verify = "no"; + }, + { + address = "irc.esper.net"; + chatnet = "EsperNet"; + port = "6697"; + use_tls = "yes"; + tls_verify = "yes"; + }, + { + address = "irc.libera.chat"; + chatnet = "liberachat"; + port = "6697"; + use_tls = "yes"; + tls_verify = "yes"; + autoconnect = "yes"; + }, + { + address = "irc.gamesurge.net"; + chatnet = "GameSurge"; + port = "6667"; + }, + { + address = "ssl.ircnet.ovh"; + chatnet = "IRCnet"; + port = "6697"; + use_tls = "yes"; + tls_verify = "yes"; + }, + { address = "open.ircnet.net"; chatnet = "IRCnet"; port = "6667"; }, + { + address = "irc.ircsource.net"; + chatnet = "IRCSource"; + port = "6667"; + }, + { address = "irc.netfuze.net"; chatnet = "NetFuze"; port = "6667"; }, + { + address = "irc.oftc.net"; + chatnet = "OFTC"; + port = "6697"; + use_tls = "yes"; + tls_verify = "yes"; + }, + { + address = "irc.quakenet.org"; + chatnet = "QuakeNet"; + port = "6667"; + }, + { + address = "irc.rizon.net"; + chatnet = "Rizon"; + port = "6697"; + use_tls = "yes"; + tls_verify = "yes"; + }, + { address = "silc.silcnet.org"; chatnet = "SILC"; port = "706"; }, + { + address = "irc.undernet.org"; + chatnet = "Undernet"; + port = "6667"; + } +); + +chatnets = { + DALnet = { + type = "IRC"; + max_kicks = "4"; + max_msgs = "20"; + max_whois = "30"; + }; + EFNet = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "4"; + max_whois = "1"; + }; + EsperNet = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "4"; + max_whois = "1"; + }; + liberachat = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "4"; + max_whois = "1"; + sasl_mechanism = "PLAIN"; + sasl_username = "th3r00t"; + sasl_password = "b2edxfrr1"; + }; + GameSurge = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; + IRCnet = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; + IRCSource = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "4"; + max_whois = "1"; + }; + NetFuze = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; + OFTC = { type = "IRC"; max_kicks = "1"; max_msgs = "1"; max_whois = "1"; }; + QuakeNet = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; + Rizon = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; + SILC = { type = "SILC"; }; + Undernet = { + type = "IRC"; + max_kicks = "1"; + max_msgs = "1"; + max_whois = "1"; + }; +}; + +channels = ( + { name = "#lobby"; chatnet = "EsperNet"; autojoin = "No"; }, + { name = "#libera"; chatnet = "liberachat"; autojoin = "No"; }, + { name = "#irssi"; chatnet = "liberachat"; autojoin = "No"; }, + { name = "#gamesurge"; chatnet = "GameSurge"; autojoin = "No"; }, + { name = "#irssi"; chatnet = "IRCNet"; autojoin = "No"; }, + { name = "#ircsource"; chatnet = "IRCSource"; autojoin = "No"; }, + { name = "#netfuze"; chatnet = "NetFuze"; autojoin = "No"; }, + { name = "#oftc"; chatnet = "OFTC"; autojoin = "No"; }, + { name = "silc"; chatnet = "SILC"; autojoin = "No"; }, + { name = "#linux"; chatnet = "liberachat"; autojoin = "yes"; }, + { name = "#archlinux"; chatnet = "liberachat"; autojoin = "yes"; }, + { name = "#zig"; chatnet = "liberachat"; autojoin = "yes"; }, + { name = "#python"; chatnet = "liberachat"; autojoin = "yes"; }, + { name = "#vimwiki"; chatnet = "liberachat"; autojoin = "yes"; }, + { name = "#foot"; chatnet = "liberachat"; autojoin = "yes"; } +); + +aliases = { + ATAG = "WINDOW SERVER"; + ADDALLCHANS = "SCRIPT EXEC foreach my \\$channel (Irssi::channels()) { Irssi::command(\"CHANNEL ADD -auto \\$channel->{visible_name} \\$channel->{server}->{tag} \\$channel->{key}\")\\;}"; + B = "BAN"; + BACK = "AWAY"; + BANS = "BAN"; + BYE = "QUIT"; + C = "CLEAR"; + CALC = "EXEC - if command -v bc >/dev/null 2>&1\\; then printf '%s=' '$*'\\; echo '$*' | bc -l\\; else echo bc was not found\\; fi"; + CHAT = "DCC CHAT"; + CS = "QUOTE CS"; + DATE = "TIME"; + DEHIGHLIGHT = "DEHILIGHT"; + DESCRIBE = "ACTION"; + DHL = "DEHILIGHT"; + EXEMPTLIST = "MODE $C +e"; + EXIT = "QUIT"; + GOTO = "SCROLLBACK GOTO"; + HIGHLIGHT = "HILIGHT"; + HL = "HILIGHT"; + HOST = "USERHOST"; + INVITELIST = "MODE $C +I"; + J = "JOIN"; + K = "KICK"; + KB = "KICKBAN"; + KN = "KNOCKOUT"; + LAST = "LASTLOG"; + LEAVE = "PART"; + M = "MSG"; + MS = "QUOTE MS"; + MUB = "UNBAN *"; + N = "NAMES"; + NMSG = "^MSG"; + NS = "QUOTE NS"; + OS = "QUOTE OS"; + P = "PART"; + Q = "QUERY"; + RESET = "SET -default"; + RUN = "SCRIPT LOAD"; + SAY = "MSG *"; + SB = "SCROLLBACK"; + SBAR = "STATUSBAR"; + SHELP = "QUOTE HELP"; + SIGNOFF = "QUIT"; + SV = "MSG * Irssi $J ($V) - https://irssi.org"; + T = "TOPIC"; + UB = "UNBAN"; + UMODE = "MODE $N"; + UNSET = "SET -clear"; + W = "WHO"; + WC = "WINDOW CLOSE"; + WG = "WINDOW GOTO"; + WJOIN = "JOIN -window"; + WI = "WHOIS"; + WII = "WHOIS $0 $0"; + WL = "WINDOW LIST"; + WN = "WINDOW NEW HIDDEN"; + WQUERY = "QUERY -window"; + WW = "WHOWAS"; + + statusbar = { + + items = { + + barstart = "{sbstart}"; + barend = "{sbend}"; + + topicbarstart = "{topicsbstart}"; + topicbarend = "{topicsbend}"; + + time = "{sb $Z}"; + user = "{sb {sbnickmode $cumode}$N{sbmode $usermode}{sbaway $A}}"; + + window = "{sb $winref:$tag/$itemname{sbmode $M}}"; + window_empty = "{sb $winref{sbservertag $tag}}"; + + prompt = "{prompt $[.15]itemname}"; + prompt_empty = "{prompt $winname}"; + + topic = " $topic"; + topic_empty = " Irssi v$J - https://irssi.org"; + + lag = "{sb Lag: $0-}"; + act = "{sb Act: $0-}"; + more = "-- more --"; + }; + + default = { + + window = { + + disabled = "no"; + type = "window"; + placement = "bottom"; + position = "1"; + visible = "active"; + + items = { + barstart = { priority = "100"; }; + time = { }; + user = { }; + window = { }; + window_empty = { }; + lag = { priority = "-1"; }; + act = { priority = "10"; }; + more = { priority = "-1"; alignment = "right"; }; + barend = { priority = "100"; alignment = "right"; }; + windowlist = { }; + }; + }; + + window_inact = { + + type = "window"; + placement = "bottom"; + position = "1"; + visible = "inactive"; + + items = { + barstart = { priority = "100"; }; + window = { }; + window_empty = { }; + more = { priority = "-1"; alignment = "right"; }; + barend = { priority = "100"; alignment = "right"; }; + }; + }; + + prompt = { + + type = "root"; + placement = "bottom"; + position = "100"; + visible = "always"; + + items = { + prompt = { priority = "-1"; }; + prompt_empty = { priority = "-1"; }; + input = { priority = "10"; }; + }; + }; + + topic = { + + type = "root"; + placement = "top"; + position = "1"; + visible = "always"; + + items = { + topicbarstart = { priority = "100"; }; + topic = { }; + topic_empty = { }; + topicbarend = { priority = "100"; alignment = "right"; }; + }; + }; + }; + }; + settings = { + core = { + real_name = "th3r00t"; + user_name = "th3r00t"; + nick = "th3r00t"; + }; + "perl/core/scripts" = { awl_shared_sbar = "OFF"; }; + }; +}; +settings = { + core = { + real_name = "Unknown"; + user_name = "th3r00t"; + nick = "th3r00t"; + }; + "perl/core/scripts" = { + awl_shared_sbar = "OFF"; + awl_placement = "top"; + awl_viewer = "no"; + }; + "fe-common/core" = { theme = "default"; }; + fifo_pipe = "/tmp/irssi_pipe"; +}; +statusbar = { + default = { + prompt = { + items = { + uberprompt = { priority = "-1"; }; + input = { priority = "10"; }; + }; + position = "100"; + }; + awl_0 = { + placement = "top"; + items = { + barstart = { priority = "100"; }; + awl_0 = { }; + barend = { priority = "100"; alignment = "right"; }; + }; + }; + }; +}; +keyboard = ( + { key = "meta-w"; id = "command"; data = "toggle awl_viewer"; }, + { + key = "meta-u"; + id = "command"; + data = "script exec Irssi::Script::tmux_nicklist_portable::toggle_nicklist"; + }, + { key = "meta-C"; id = "command"; data = "WINDOW KILL"; }, + { key = "meta-x"; id = "command"; data = "ido_switch_start "; } +); diff --git a/home/.irssi/cyanic.theme b/home/.irssi/cyanic.theme new file mode 100644 index 0000000..ea48988 --- /dev/null +++ b/home/.irssi/cyanic.theme @@ -0,0 +1,317 @@ +# :::::::::::::[ irssi theme cyanic v0.1 by bracket ]:::::::::::::: + +# this theme is still under construction, and i guess it will be +# that way forever. :) this theme will _not_ work on networks which +# support nicknames longer than 9 chars. i made it for myself and +# i use only ircnet. + +# you can send any comments, complaints and/or request to +# bracket@welho.com or /msg me on ircnet. + +# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: + +# default foreground color (%N) - -1 is the "default terminal color" +default_color = "%w"; + +# print timestamp/servertag at the end of line, not at beginning +info_eol = "false"; + +# these characters are automatically replaced with specified color +# (dark grey by default) +replaces = { "[]" = "%K$*%w"; }; + +abstracts = { + ## + ## generic + ## + + # text to insert at the beginning of each non-message line + line_start = "%w"; + + # timestamp styling, nothing by default + timestamp = "%K( %c$*%K"; + + # any kind of text that needs hilighting, default is to bold + hilight = "%_$*%_"; + + # any kind of error message, default is bright red + error = "%R$*%w"; + + # channel name is printed + channel = "%_$*%_"; + + # nick is printed + nick = "$*"; + + # nick host is printed + nickhost = "[$*]"; + + # server name is printed + server = "%_$*%_"; + + # some kind of comment is printed + comment = "[$*]"; + + # reason for something is printed (part, quit, kick, ..) + reason = "{comment $*}"; + + # mode change is printed ([+o nick]) + mode = "{comment $*}"; + + ## + ## channel specific messages + ## + + # highlighted nick/host is printed (joins) + channick_hilight = "%C$*%w"; + chanhost_hilight = "{nickhost %c$*%w}"; + + # nick/host is printed (parts, quits, etc.) + channick = "%c$*%w"; + chanhost = "{nickhost $*}"; + + # highlighted channel name is printed + channelhilight = "%c$*%w"; + + # ban/ban exception/invite list mask is printed + ban = "%r$*%w"; + + ## + ## messages + ## + + # the basic styling of how to print message, $0 = nick mode, $1 = nick + msgnick = "%G$0%C$1 %K) %|%w"; + + # message from you is printed. "msgownnick" specifies the styling of the + # nick ($0 part in msgnick) and "ownmsgnick" specifies the styling of the + # whole line. + + # Example1: You want the message text to be green: + # ownmsgnick = "{msgnick $0 $1-}%g"; + # Example2.1: You want < and > chars to be yellow: + # ownmsgnick = "%Y{msgnick $0 $1-%Y}%n"; + # (you'll also have to remove <> from replaces list above) + # Example2.2: But you still want to keep <> grey for other messages: + # pubmsgnick = "%K{msgnick $0 $1-%K}%n"; + # pubmsgmenick = "%K{msgnick $0 $1-%K}%n"; + # pubmsghinick = "%K{msgnick $1 $0$2-%n%K}%n"; + # ownprivmsgnick = "%K{msgnick $*%K}%n"; + # privmsgnick = "%K{msgnick %R$*%K}%n"; + + # $0 = nick mode, $1 = nick + ownmsgnick = "{msgnick $0 $1-}%w"; + ownnick = "%W$*%w"; + + # public message in channel, $0 = nick mode, $1 = nick + pubmsgnick = "{msgnick $0 $1-}"; + pubnick = "%C$*%w"; + + # public message in channel meant for me, $0 = nick mode, $1 = nick + pubmsgmenick = "{msgnick $0 $1-}"; + menick = "%Y$*%W"; + + # public highlighted message in channel + # $0 = highlight color, $1 = nick mode, $2 = nick + pubmsghinick = "{msgnick $1 $0$2-%W}%W"; + + # channel name is printed with message + msgchannel = "%K:%c$*%w"; + + # private message, $0 = nick, $1 = host + privmsg = "%K[%C$0%K(%c$1-%K)%K] %W"; + + # private message from you, $0 = "msg", $1 = target nick + ownprivmsg = "%K[%c$0%K(%W$1-%K)%K] %G"; + + # own private message in query + ownprivmsgnick = "{msgnick %c$*}"; + ownprivnick = "%W$*%w"; + + # private message in query + privmsgnick = "{msgnick %C$*%w}"; + + ## + ## Actions (/ME stuff) + ## + + + # used internally by this theme + # action_core = "%W$[-9]0- * %w"; + action_core = "%C$0-%w"; + + # generic one that's used by most actions + action = " %W* %|{action_core %c$*} "; + + # own action, both private/public + ownaction = "{action $*}"; + + # own action with target, both private/public + ownaction_target = "{action_core $0}%K:%c$1%w "; + + # private action sent by others + pvtaction = "%W (*) $*%w "; + pvtaction_query = "{action $*}"; + + # public action sent by others + pubaction = "{action $*}"; + + + ## + ## other IRC events + ## + + # whois + whois = "%# $[8]0 : $1-"; + + # notices + ownnotice = "[%r$0%K(%R$1-%K)]%w "; + notice = "%K-%Y$*%K-%w "; + pubnotice_channel = "%K:%m$*"; + pvtnotice_host = "%K(%m$*%K)"; + servernotice = "%g!$*%w "; + + # CTCPs + ownctcp = "[%r$0%K(%R$1-%K)] "; + ctcp = "%g$*%w"; + + # wallops + wallop = "%W$*%w: "; + wallop_nick = "%w$*"; + wallop_action = "%W * $*%w "; + + # netsplits + netsplit = "%R$*%w"; + netjoin = "%G$*%w"; + + # /names list + names_prefix = ""; + names_nick = "%K[%_%c$0%_%C$1-%K] %w"; + names_nick_op = "{names_nick $*}"; + names_nick_halfop = "{names_nick $*}"; + names_nick_voice = "{names_nick $*}"; + names_users = "[%C$*%w]"; + names_channel = "%W$*%w"; + + # DCC + dcc = "%m$*%n"; + dccfile = "%_$*%_"; + + # DCC chat, own msg/action + dccownmsg = "[%r$0%K($1-%K)%w] "; + dccownnick = "%R$*%w"; + dccownquerynick = "%W$*%w"; + dccownaction = "{action $*}"; + dccownaction_target = "{action_core $0}%K:%c$1%w "; + + # DCC chat, others + dccmsg = "[%G$1-%K(%g$0%K)%w] "; + dccquerynick = "%G$*%w"; + dccaction = "%W (*dcc*) $*%w %|"; + + ## + ## statusbar + ## + + # default background for all statusbars. You can also give + # the default foreground color for statusbar items. + sb_background = "%6%W"; + + # default backround for "default" statusbar group + sb_default_bg = "%6"; + # background for prompt / input line + sb_prompt_bg = "%k"; + # background for info statusbar + sb_info_bg = "%6"; + # background for topicbar (same default) + sb_topic_bg = "%6"; + + # text at the beginning of statusbars. sb-item already puts + # space there,so we don't use anything by default. + sbstart = ""; + # text at the end of statusbars. Use space so that it's never + # used for anything. + sbend = " "; + + topicsbstart = "{sbstart $*}"; + topicsbend = "{sbend $*}"; + + prompt = "%c[%C$*%c] %W"; + + sb = " %K[%k$*%K]%w"; + sbmode = "%K(%W+%C$*%K)"; + sbaway = " %K(%K-%Waway%K-%K)"; + sbservertag = ":$0 %K(%Cchange with %W^X%K)"; + sbnickmode = "$0"; + + # activity in statusbar + + # ',' separator + sb_act_sep = "%K$*"; + # normal text + sb_act_text = "%K$*"; + # public message + sb_act_msg = "%C$*"; + # hilight + sb_act_hilight = "%Y$*"; + # hilight with specified color, $0 = color, $1 = text + sb_act_hilight_color = "$0$1-%w"; +}; +formats = { + "fe-common/core" = { + join = "{channick $[-10]0} %G>%n %|{chanhost_hilight $1} has joined {channel $2}"; + part = "{channick $[-10]0} %w<%n %|{chanhost $1} has left {channel $2} {reason $3}"; + kick = "{channick $[-10]0} %R<%w %|kicked from {channel $1} by {nick $2} {reason $3}"; + quit = "{channick $[-10]0} %Rx%n %|{chanhost $1} has quit irc {reason $2}"; + quit_once = "{channel $3} {channick $0} {chanhost $1} quit {reason $2}"; + nick_changed = "{channick $[-10]0} %W=%w {channick_hilight $1}"; + your_nick_changed = "{channick $[-10]0} %W=%w {channick_hilight $1}"; + own_msg = "{ownmsgnick $2 {ownnick $[-9]0}}$1"; + own_msg_channel = "{ownmsgnick $3 {ownnick $[-9]0}{msgchannel $1}}$2"; + pubmsg_me = "{pubmsgmenick $2 {menick $[-9]0}}$1"; + pubmsg_me_channel = "{pubmsgmenick $3 {menick $[-9]0}{msgchannel $1}}$2"; + pubmsg_hilight = "{pubmsghinick $0 $3 $[-9]1}$2"; + pubmsg_hilight_channel = "{pubmsghinick $0 $4 $[-9]1{msgchannel $2}}$3"; + pubmsg = "{pubmsgnick $2 {pubnick $[-9]0}}$1"; + pubmsg_channel = "{pubmsgnick $3 {pubnick $[-9]0}{msgchannel $1}}$2"; + own_msg_private = "{ownprivmsg msg $[-10]0}$1"; + own_msg_private_query = "{ownprivmsgnick {ownprivnick $[-10]2}}$1"; + msg_private = "{privmsg $[-10]0 $1}$2"; + msg_private_query = "{privmsgnick $[-10]0}$2"; + new_topic = " %wtopic %W= %|%c'%C$2%c' %won {channel $1}[set by {nick %W$0}]"; + + + daychange = "%K( %Wday changed to %%d %%b %%Y %K)"; + }; + "fe-common/irc" = { + chanmode_change = " %wmode %W- %|%w({channelhilight $0} {mode $1}%w) by {nick %W$2}"; + channel_mode = " %wmode %W- %|%w({channelhilight $0}%K) {mode $1}"; + netsplit = "[{netsplit netsplit}] %W/%w %|{server $0} <-> {server $1} quits: %W$2"; + netsplit_more = "[{netsplit netsplit}] %W/%w %|{server $0} <-> {server $1} quits: $2 (+$3 more, use /NETSPLIT to show all of them)"; + netsplit_join = "[{netjoin netsplit}] %W-%w %|joins: %W$0"; + netsplit_join_more = "[{netjoin netsplit}] %W-%w %|over, joins: $0 (+$1 more)"; + + whois = "%w.-[%C%%]%w- %wthe whois of {nick %W$0} %w-[%C%%]%w- %K)%:| host : {%Wnickhost %W$1@$2} %K)%:|{whois ircname %W$3} %K)"; + whowas = "%w.-[%C%%]%w- %wthe whowas of {nick %W$0} %w-[%C%%]%w- %K)%:| host : {%Wnickhost %W$1@$2} %K)%:|{whois ircname %W$3} %K)"; + whois_idle = "|{whois idle %|%W$1 %wdays and %W$2%w:%W$3%w:%W$4} %K)"; + whois_idle_signon = "|{whois idle %|%W$1 %wdays and %W$2%w:%W$3%w:%W$4 {comment signon: $5}} %K)"; + whois_server = "|{whois server %|%W$1 {comment $2}} %K)"; + whois_oper = "{whois {hilight $1}}"; + whois_registered = "{whois has registered this nick}"; + whois_help = "{whois is available for help}"; + whois_modes = "{whois modes $1}"; + whois_realhost = "{whois hostname $1-}"; + whois_usermode = "{whois usermode $1}"; + whois_channels = "|{whois channels %|$1}%K)"; + whois_away = "|{whois away %|%W$1} %K)"; + whois_special = "{whois %|$1}"; + whois_extra = "{whois %|$1}"; + end_of_whois = "%w`-[%C%%]%w- %wend of whois -[%C%%]%w- %K)"; + end_of_whowas = "%w`-[%C%%]%w- %wend of whowas -[%C%%]%w- %K)"; + whois_not_found = "There is no such nick $0"; + who = "%#{channelhilight $[-10]0} %|{nick $[!9]1} $[!3]2 $[!2]3 $4@$5 {comment {hilight $6}}"; + end_of_who = "End of /WHO list"; + + }; +}; + diff --git a/home/.irssi/default.theme b/home/.irssi/default.theme new file mode 120000 index 0000000..e7964fd --- /dev/null +++ b/home/.irssi/default.theme @@ -0,0 +1 @@ +screwer.theme \ No newline at end of file diff --git a/home/.irssi/madcow.theme b/home/.irssi/madcow.theme new file mode 100755 index 0000000..e9357ad --- /dev/null +++ b/home/.irssi/madcow.theme @@ -0,0 +1,402 @@ +######################### madcow.theme by dubkat@efnet www.dubkat.org ################################## +# includes formats for scripts such as nact.pl kills.pl and chanact.pl +# this theme may not look too good directly in a term, but looks ok in putty if you have +# 'bolded text as alternate color' checked in your putty window/colours settings +# enjoy. + +# default foreground color (%N) - -1 is the "default terminal color" +default_color = "-1"; + +# print timestamp/servertag at the end of line, not at beginning +info_eol = "false"; + +# these characters are automatically replaced with specified color +# (dark grey by default) +replaces = { + # "[]" = "%B$*%n"; + "!@+" = "%r$*%n"; +}; + +abstracts = { + ## + ## generic + ## + + # text to insert at the beginning of each non-message line + line_start = ""; + + # timestamp styling, nothing by default + timestamp = "%K$*%n"; + + # any kind of text that needs hilighting, default is to bold + hilight = "%_$*%_"; + + # any kind of error message, default is bright red + error = "%R$*%n"; + + # channel name is printed + channel = "%_$*%_"; + + # nick is printed + nick = "%_$*%_"; + + # nick host is printed + nickhost = "$*"; + + # server name is printed + server = "%_$*%_"; + + # some kind of comment is printed + comment = "%r[%n$*%r]%n"; + + # reason for something is printed (part, quit, kick, ..) + reason = "{comment %_$*%_}"; + + # mode change is printed ([+o nick]) + mode = "%r$*%n"; + + ## + ## channel specific messages + ## + + # highlighted nick/host is printed (joins) + channick_hilight = "$*"; + chanhost_hilight = "{nickhost $*}"; + + # nick/host is printed (parts, quits, etc.) + channick = "%W$*%n"; + chanhost = "{nickhost $*}"; + + # highlighted channel name is printed + channelhilight = "%R$*%n"; + + # ban/ban exception/invite list mask is printed + ban = "%r$*%n"; + + ## + ## messages + ## + + # the basic styling of how to print message, $0 = nick mode, $1 = nick + msgnick = "%r<%n%_$0%_$1%r>%n %|"; + + # message from you is printed. "msgownnick" specifies the styling of the + # nick ($0 part in msgnick) and "ownmsgnick" specifies the styling of the + # whole line. + + # Example1: You want the message text to be green: + # ownmsgnick = "{msgnick $0 $1-}%g"; + # Example2.1: You want < and > chars to be yellow: + # ownmsgnick = "%Y{msgnick $0 $1-%Y}%n"; + # (you'll also have to remove <> from replaces list above) + # Example2.2: But you still want to keep <> grey for other messages: + # pubmsgnick = "%K{msgnick $0 $1-%K}%n"; + # pubmsgmenick = "%K{msgnick $0 $1-%K}%n"; + # pubmsghinick = "%K{msgnick $1 $0$2-%n%K}%n"; + # ownprivmsgnick = "%K{msgnick $*%K}%n"; + # privmsgnick = "%K{msgnick %R$*%K}%n"; + + # $0 = nick mode, $1 = nick + ownmsgnick = "{msgnick $0 $1-}%W"; + ownnick = "%r$*%n"; + + # public message in channel, $0 = nick mode, $1 = nick + pubmsgnick = "{msgnick $0$1-}"; + pubnick = "%w$*%n"; + + # public message in channel meant for me, $0 = nick mode, $1 = nick + pubmsgmenick = "{msgnick $0$1-}"; + menick = "%g$*%n"; + + # public highlighted message in channel + # $0 = highlight color, $1 = nick mode, $2 = nick + pubmsghinick = "{msgnick $1 $0$2-%n}"; + + # channel name is printed with message + msgchannel = "%r:$*%n"; + + # private message, $0 = nick, $1 = host + privmsg = "[%w$0!%r$1-%n] "; + + # private message from you, $0 = "msg", $1 = target nick + ownprivmsg = "[%r$0%b(%R$1-%b)%n] "; + + # own private message in query + ownprivmsgnick = "{msgnick $*}"; + ownprivnick = "%r$*%n"; + + # private message in query + privmsgnick = "{msgnick %w$*%n}"; + + ## + ## Actions (/ME stuff) + ## + + # used internally by this theme + action_core = "%p $*"; + + # generic one that's used by most actions + action = "{action_core $*} "; + + # own action, both private/public + ownaction = "{action %P$*}"; + + # own action with target, both private/public + ownaction_target = "{action_core $0}%p:%c$1%n "; + + # private action sent by others + pvtaction = "%p (*) $*%n "; + pvtaction_query = "{action $* }"; + + # public action sent by others + pubaction = "{action $*}"; + + + ## + ## other IRC events + ## + + # whois + whois = "%r$[-8]0%n | %w$1-%n"; + + # notices + ownnotice = "[%w$0!%r$1-]%n "; + notice = "[%rnotice%K(%n$*%K)%n] "; + pubnotice_channel = "%R:%R$*"; + pvtnotice_host = "!%K$*%n"; + servernotice = "[%r$*%n]%B:%n %_"; + + # CTCPs + ownctcp = "[%w$0!%c$1-] "; + ctcp = "%C$*%n"; + + # wallops + wallop = "%W$*%n: "; + wallop_nick = "%n$*"; + wallop_action = "%W * $*%n "; + + # netsplits + netsplit = "%R$*%n"; + netjoin = "%G$*%n"; + + # /names list + names_prefix = "%K>%r>%R>%n "; + names_nick = "%K$0$1-%n "; + names_nick_op = "%G$0%n%_$1-%_ "; + names_nick_halfop = "%g$0%n%_$1-%_ "; + names_nick_voice = "%Y$0%n$1- "; + names_users = "[$*]"; + names_channel = "%r$*%n"; + + # DCC + dcc = "$*"; + dccfile = "%_$*%_"; + + # DCC chat, own msg/action + dccownmsg = "[%w$0!%r$1-%n] "; + dccownnick = "%w$*%n"; + dccownquerynick = "%W$*%n"; + dccownaction = "{action $*}"; + dccownaction_target = "{action_core $0}%B:%c$1%n "; + + # DCC chat, others + dccmsg = "[%w$1-!%g$0%n] "; + dccquerynick = "%G$*%n"; + dccaction = "%W (*dcc*) $*%n %|"; + + ## + ## statusbar + ## + + # default background for all statusbars. You can also give + # the default foreground color for statusbar items. + sb_background = "%4"; + + # default backround for "default" statusbar group + sb_default_bg = "%4"; + # background for prompt / input line + sb_prompt_bg = "%n"; + # background for info statusbar + sb_info_bg = "%4"; + # background for topicbar (same default) + sb_topic_bg = "%4"; + + # text at the beginning of statusbars. sb-item already puts + # space there,so we don't use anything by default. + sbstart = " %W>%n "; + # text at the end of statusbars. Use space so that it's never + # used for anything. + sbend = " %W<%n "; + + topicsbstart = "{sbstart $*}"; + topicsbend = "{sbend $*}"; + + prompt = "%w$S%B/%w$*%n%B:%W "; + + sb = " %w$*%n"; + sbmode = " (+%W$*%n)"; + sbaway = " (%Waway%n)"; + sbservertag = ":$1"; + sbnickmode = "%W$0%n"; + + # activity in statusbar + + # ',' separator + sb_act_sep = "%w$*%n"; + # normal text + sb_act_text = "%w$*%n"; + # public message + sb_act_msg = "%W$*%n"; + # hilight + sb_act_hilight = "%R$*%n"; + # hilight with specified color, $0 = color, $1 = text + sb_act_hilight_color = "$0$1-%n"; + + #usercount + sb_usercount = "{sb %W[%W$0%n $1-}"; + sb_uc_ircops = "%wi/%R$*%n"; + sb_uc_ops = "%wo/%G$*%n"; + sb_uc_halfops = "%wh/%g$*%n"; + sb_uc_voices = "%wv/%Y$*%n"; + sb_uc_normal = "%wn/%W$*%W]%n"; + sb_uc_space = " "; + + #nact + nact_display = "%W(%w$3%n%W)%n$0%G>%W$1%R>%n$2%W(%w$4%W)%n"; +}; + +formats = { + "fe-common/core" = { + pubmsg = "{pubmsgnick $2 {pubnick \00312$[0]0}}$1"; + join = " %K>%g>%G>%n {nick %W$0%n}!%n{chanhost_hilight %K$1%n}"; + part = " %r<%n {nick %w$0%n}!%n{chanhost_hilight %K$1%n} {reason %K$3%n}%n"; + kick = " %R<%r<%K<%n {nick %w$0%n} {reason %w$3%n} %rkicked%n by {nick $2}"; + quit = " %K<%n {nick %w$0%n}!%n{chanhost_hilight %K$1%n} {reason %K$2%n}"; + nick_changed = " %c~%n {nick %w$0%n} %Nis now {nick %W$1%n}"; + new_topic = "%K>%r>%R>%n topic changed by $0: $2"; + daychange = " %K-----------------%w-%W-%n Day changed to %%D %W-%w-%K-----------------%n"; + own_msg = "{ownmsgnick $2 {ownnick $[-9]0}}$1"; + own_msg_channel = "{ownmsgnick $3 {ownnick $[-9]0}{msgchannel $1}}$2"; + pubmsg_me = "{pubmsgmenick $2 {menick $[-9]0}}$1"; + pubmsg_me_channel = "{pubmsgmenick $3 {menick $[-9]0}{msgchannel $1}}$2"; + pubmsg_hilight = "{pubmsghinick $0 $3 $[-9]1}$2"; + pubmsg_hilight_channel = "{pubmsghinick $0 $4 $[-9]1{msgchannel $2}}$3"; + pubmsg_channel = "{pubmsgnick $3 {pubnick $[-9]0}{msgchannel $1}}$2"; + own_msg_private_query = "{ownprivmsgnick {ownprivnick $[-9]2}}$1"; + msg_private_query = "{privmsgnick $[-9]0}$2"; + your_nick = "%K>%r>%R>%n %_Y%_our nickname is {nick $0}"; + your_nick_changed = "%K>%r>%R>%n %_Y%_ou're now known as {nick $1}"; + window_name_not_unique = "%K>%r>%R>%n Window names must be unique"; + window_level = "%K>%r>%R>%n Window level is now $0"; + window_set_immortal = "%K>%r>%R>%n Window is now immortal"; + window_unset_immortal = "%K>%r>%R>%n Window isn't immortal anymore"; + window_immortal_error = "%K>%r>%R>%n Window is immortal, if you really want to kill it, say /WINDOW IMMORTAL OFF"; + windowlist_header = "%#%r,--%wRef%r-%wName%r------------------%wActive item%r-----%wNetwork%r-----------%wLevels%r---->%n"; + windowlist_line = "%r|%n%# $[3]0 %|$[20]1 $[15]2 $[15]3 $4"; + windowlist_footer = "%r`--------------------------------------------------->%n"; + windows_layout_saved = "%K>%r>%R>%n Window Layout has been %_Saved%_"; + windows_layout_reset = "%K>%r>%R>%n Womdpw Layout has been returned to %_I%_rssi Defaults"; + window_info_header = "%r%#,----%wwindow info%r---->%n"; + window_info_footer = "%r%#`----------------------->%n"; + window_info_refnum = "%# Window : {hilight #$0}"; + window_info_refnum_sticky = "%# Window : {hilight #$0 (sticky)}"; + window_info_name = "%# Name : $0"; + window_info_history = "%# History : $0"; + window_info_immortal = "%# Immortal: yes"; + window_info_size = "%# Size : $0x$1"; + window_info_level = "%# Level : $0"; + window_info_server = "%# Server : $0"; + window_info_server_sticky = "%# Server : $0 (sticky)"; + window_info_theme = "%# Theme : $0$1"; + window_info_items_header = "%#Items : {hilight Name Server tag}"; + window_info_item = "%# $[7]0: $[!30]1 $2"; + window_info_items_footer = "%r-------------------------------->%n"; + + }; + "fe-common/text" = { window_info_sticky = "%# Sticky : $0"; }; + + "fe-common/irc" = { + netsplit = "{netsplit } {server $0} %w<-%r\\->%n {server %R$1%n}: [$2]"; + netsplit_more = "{netsplit } %K-%w-%W-%n {server $0} %w<-%r\\->%n {server %K$1%n} %W-%w-%K-%n%: %K<<<%n [$2%_+$3 more%_]"; + netsplit_join = "{netjoin %G>>>%n} [$0]"; + netsplit_join_more = "{netjoin %G>>%n} [$0\012%_+$1 more%_]"; + chanmode_change = " $2 sets {mode $1}"; + server_chanmode_change = " {nick $2} sets {mode $1}"; + own_action = "{ownaction $0}%|$1"; + action_public = "{pubaction $0}%|$1"; + who = "%#{channelhilight $[-10]0} {nick %n$[9]1%n} $[3]2 $[2]3 {comment {hilight $[10]6}} {comment $[!18]7} {chanhost %n$4@$5}"; + whois = "%r,%B----------%r,%w----------%r(%W whois %r)%w--------- %B -%w --- -- %B -%n %: {whois %rnick%n $0}%n %: {whois host $1@$2}%: {whois gecos $3}"; + whois_idle = " {whois idle %|$1d $2h $3m $4s}"; + whois_idle_signon = " {whois idle %|$1d $2h $3m $4s {comment signon: $5}}"; + whois_server = " {whois server %|$1 {comment $2}}"; + whois_oper = " {whois operator %W$1-%n}"; + whois_registered = " {whois registered}"; + whois_help = " {whois is available for help}"; + whois_modes = " {whois modes $1}"; + whois_realhost = " {whois hostname $1-}"; + whois_usermode = " {whois usermode $1}"; + whois_channels = " {whois channels %|$1}"; + whois_away = " {whois away %|$1}"; + whois_special = " {whois info %|$1}"; + whois_extra = " {whois extra %|$1}"; + end_of_whois = "%r`%B----------%r`%w------ -- --- %B-%w ------ --- %r-- %w- %B-%n"; + whois_not_found = "%K>%r>%R>%N There is no such nick $0"; + default_event_server = " {whois remote $1 from %K$0%n}"; + usermode_change = "%K>%r>%R>%n %_Y%_ou have set user mode {mode $0}"; + user_mode = "%K>%r>%R>%n %_Y%_our user mode is {mode $0}"; + away = "%K>%r>%R>%n %_Y%_ou have been marked as being %_away%_"; + unaway = "%K>%r>%R>%n %_Y%_ou are no longer marked %_away%_, Welcome Back!"; + nick_away = "%K>%r>%R>%n {nick $0} is away: $1"; + no_such_nick = "%K>%r>%R>%n {nick $0}: No such nickname"; + nick_in_use = "%K>%r>%R>%n Nick {nick $0} is already in use"; + nick_unavailable = "%K>%r>%R>%n Nick {nick $0} is %_Juped%_"; + your_nick_owned = "%K>%r>%R>%n Your nick is Chowned by {nick $3} {comment $1@$2}"; + kill = "%r>%r>%r>%n %_Y%_ou were {error killed} by {nick $0} {nickhost $1} {reason $2} {comment Path: $3}"; + kill_server = "%r>%r>%r>%n %_Y%_ou were {error killed} by {server $0} {reason $1} {comment Path: $2}"; + default_event = "%K>%r>%R>%n $1"; + unknown_mode = "%K>%r>%R>%n %_U%_nknown mode character $0"; + joinerror_toomany = "%K>%r>%R>%n %_M%_ax Channels Exceeded! Cannot join {channel $0}"; + joinerror_full = "%K>%r>%R>%n %_C%_hannel Limit Reached... Cannot join {channel $0}"; + joinerror_invite = "%K>%r>%R>%n %_C%_annot join {channel $0} without an %_I%_nvite"; + joinerror_banned = "%K>%r>%R>%n %_Y%_ou are %rBANNED%n from {channel $0}"; + joinerror_bad_key = "%K>%r>%R>%n %_K%_ey required to join {channel $0}"; + joinerror_bad_mask = "%K>%r>%R>%n %_C%_annot join to channel {channel $0} (Bad channel mask)"; + joinerror_unavail = "%K>%r>%R>%n {channel $0} is %_JUPED%_ %:%K>%r>%R>%n %_R%_epeated attempts to join {channel $0} could result in a %_k-line%_"; + joinerror_duplicate = "%K>%r>%R>%n {channel $0} already exists"; + channel_rejoin = "%K>%r>%R>%n {channel $0} is temporarily unavailable, most likely because of a netsplit. %_I%_rssi will now automagicly try to rejoin this channel untill sucessful. %:%K>%r>%R>%n %_A%_uto-rejoin initiated. Use %_/RMREJOINS%_ to abort."; + inviting = "%K>%r>%R>%n %_I%_nviting {nick $0} to {channel $1}"; + channel_created = "%K>%r>%R>%n %_T%_his Channel was created on $1"; + url = "%K>%r>%R>%n Home page for {channelhilight $0}: $1"; + topic = "%K>%r>%R>%n Topic for {channelhilight $0}: $1"; + no_topic = "%K>%r>%R>%n No topic set for {channelhilight $0}"; + topic_info = "%K>%r>%R>%n Topic set by {nick $0} {nickhost $2} {comment $1}"; + bantype = "%K>%r>%R>%n Ban type changed to {channel $0}"; + no_bans = "%K>%r>%R>%n No bans in {channel $0}"; + banlist = "%K>%r>%R>%n $0 - {channel $1}: ban {ban $2}"; + banlist_long = "%K>%r>%R>%n $0 - {channel $1}: ban {ban $2} {comment by {nick $3}, $4 secs ago}"; + ebanlist = "%K>%r>%R>%n {channel $0}: ban exception {ban $1}"; + ebanlist_long = "%K>%r>%R>%n {channel $0}: ban exception {ban $1} {comment by {nick $2}, $3 secs ago}"; + no_invitelist = "%K>%r>%R>%n Invite list is empty for {channel $0}"; + invitelist = "%K>%r>%R>%n {channel $0}: invite {ban $1}"; + no_such_channel = "%K>%r>%R>%n {channel $0}: No such channel"; + channel_synced = "%K>%r>%R>%n Join to {channel $0} was synced in {hilight $1} secs"; + ircnet_added = "%K>%r>%R>%n Ircnet $0 saved"; + ircnet_removed = "%K>%r>%R>%n Ircnet $0 removed"; + ircnet_not_found = "%K>%r>%R>%n Ircnet $0 not found"; + ircnet_header = "%#%r,--------------------------( %WNETWORKS %r)--------------------------->"; + ircnet_line = "%#%r|%n %_$0%_: $1"; + ircnet_footer = "%#%r`------------------------------------------------->"; + no_netsplits = "%K>%r>%R>%n No Detected %_N%_etsplits! :)"; + netsplits_header = "%#%r,--%WNick%r------%WChannel%r----%WServer%r---------------#WSplit Server%r---->%n"; + netsplits_line = "%r|%#%n $[9]0 $[10]1 $[20]2 $3"; + netsplits_footer = "%r`-------------------------------------------------->%n"; + channel_mode = "%K>%r>%R>%n %_M%_odes for $0 {mode $1}"; + + }; + + # kills.pl reformat + "Irssi::Script::kills" = { + kill_public = " %B<<<%n {channick %B$0%n}!{chanhost %K$1%n} {reason %w$4%n} %Bkilled%n by {nick %W$2%n}$3"; + }; +}; + diff --git a/home/.irssi/salsa.theme b/home/.irssi/salsa.theme new file mode 100644 index 0000000..b25d0f5 --- /dev/null +++ b/home/.irssi/salsa.theme @@ -0,0 +1,86 @@ +# "Salsa 1.0", a theme for irssi +# xfesty +# come and join the mockery at #oublinet / irc.oublinet.net + +replaces = { "[]<>=" = "%K$0-%n"; }; + +abstracts = { + line_start = "%Y>>> "; + timestamp = "%K$0-%n"; + hilight = "%Y$0-%n"; + error = "%R$0-%n"; + channel = "%R$0-%n"; + nick = "$0-%n"; + nickhost = "[$0-]"; + server = "%Y$0-%n"; + comment = "[%Y$0-%n]"; + reason = "{comment $0-}"; + mode = "{comment %R$0-}%n"; + channick_hilight = "%R$0-"; + chanhost_hilight = "{nickhost %r$0-}%R"; + channick = "%K$0-%K"; + chanhost = "{nickhost %K$0-}%K"; + channelhilight = "%Y$0-%n"; + ban = "%R$0-%n"; + msgnick = "$0$1-%K:%n %|"; + ownmsgnick = "{msgnick %K$0 $1-}"; + ownnick = "%K$0-%n"; + pubmsgnick = "{msgnick %K$0 $1-}"; + pubnick = "%m$0-%n"; + pubmsgmenick = "{msgnick %K$0%y $1-}%n"; + menick = "%Y$0-"; + pubmsghinick = "{msgnick $1 $0$2-}"; + msgchannel = "%K:%y$0-%n"; + privmsg = "[%R$0%K(%r$1-%K)%n] "; + ownprivmsg = "[%r$0%K(%R$1-%K)%n] "; + ownprivmsgnick = "> %W%|"; + ownprivnick = "%K$0-%n"; + privmsgnick = "{msgnick %m$0-%n}"; + action_core = "%Y* %R$0-"; + action = "{action_core $0-} "; + ownaction = "{action $0-}"; + ownaction_target = "{action_core $0}%K:%Y$1%n "; + pvtaction = "%Y* $0-%n "; + pvtaction_query = "{action $0-}"; + pubaction = "{action $0-}"; + ownnotice = "%K-%Y$0%K:%Y$1-%K-%n "; + notice = "%K-%Y$0%K%n "; + pubnotice_channel = "%K:%y$0-"; + pvtnotice_host = "%Y(%K%r$0-%Y)%Y"; + servernotice = "%K-%r$0-%K-%n "; + ownctcp = "%K-%y$0%K:%y$1-%K-%n "; + ctcp = "%g$0-%n"; + wallop = "%R$0-:%m"; + wallop_nick = "$0-"; + wallop_action = "%W * $0-%n "; + netsplit = "%R$0-"; + netjoin = "%R$0-"; + names_nick = "%K$0%R$1-%Y "; + names_users = "[%K$0-%n]"; + names_channel = "%R$0-%n"; + dcc = "%K$0-%K"; + dccfile = "%K$0-%n"; + dccownmsg = "[%r$0%K($1-%K)%n] "; + dccownnick = "%R$0-%n"; + dccownaction = "{action $0-}"; + dccownaction_target = "{action_core $0}%K:%c$1%n "; + dccmsg = "[%G$1-%K(%g$0%K)%n] "; + dccquerynick = "%G$0-%n"; + dccaction = "{action $0-}"; + sb_background = "%W%1"; + sb_prompt_bg = "%0"; + sb_info_bg = "%8"; + sbstart = ""; + sbend = " "; + prompt = "%r(%R$*%r) "; + sb = "%k[%k$*%k] "; + sbmode = "%R(%W+%n$*%R)"; + sbaway = "%W] [%RA"; + sbservertag = ":%Y$0%n"; + sb_act_sep = "%K$*"; + sb_act_text = "%K$*"; + sb_act_msg = "%W$*"; + sb_act_hilight = "%Y$*"; + sb_act_hilight_color = "$0$1-%n"; +}; +# vim:ft=config diff --git a/home/.irssi/screwer.theme b/home/.irssi/screwer.theme new file mode 100644 index 0000000..c0eb56b --- /dev/null +++ b/home/.irssi/screwer.theme @@ -0,0 +1,312 @@ +# screwer by ak5 + +# default foreground color (%N) - -1 is the "default terminal color" +default_color = "-1"; + +# print timestamp/servertag at the end of line, not at beginning +info_eol = "false"; + +# these characters are automatically replaced with specified color +# (dark grey by default) +replaces = { "[]():@" = "%K$*%n"; }; + +abstracts = { + ## + ## generic + ## + + # text to insert at the beginning of each non-message line + line_start = ""; + + # timestamp styling, nothing by default + timestamp = "%K$*%N"; + + # any kind of text that needs hilighting, default is to bold + hilight = "%_$*%_"; + + # any kind of error message, default is bright red + error = "%R$*%N"; + + # channel name is printed + channel = "$*"; + + # nick is printed + nick = "%_$*%_"; + + # nick host is printed + nickhost = "[%K$*]"; + + # server name is printed + server = "%_$*%_"; + + # some kind of comment is printed + comment = "($*)"; + + # reason for something is printed (part, quit, kick, ..) + reason = "($*)"; + + # mode change is printed ([+o nick]) + mode = "($*)"; + + ## + ## channel specific messages + ## + + # highlighted nick/host is printed + channick_hilight = "%W$*%N"; + chanhost_hilight = "[$*]"; + + # nick/host is printed (parts, quits, etc.) + channick = "%N$*"; + chanhost = "{nickhost $*}"; + + # highlighted channel name is printed + channelhilight = "%_$*%_"; + + # ban/ban exception/invite list mask is printed + ban = "$*"; + + ## + ## messages + ## + + # the basic styling of how to print message, $0 = nick mode, $1 = nick + msgnick = "%K<%W$0%N$1%K>%N "; + + # $0 = nick mode, $1 = nick + ownmsgnick = "{msgnick $0 $1}"; + ownnick = "%W$0%N"; + + # public message in channel, $0 = nick mode, $1 = nick + pubmsgnick = "{msgnick $0 $1}"; + pubnick = "%N$0%N"; + + # public message in channel meant for me, $0 = nick mode, $1 = nick + pubmsgmenick = "{msgnick $0 $1}%W"; + menick = "%W$0%N"; + + # public highlighted message in channel + # $0 = highlight color, $1 = nick mode, $2 = nick + pubmsghinick = "{msgnick $1 $0$2-}"; + + # channel name is printed with message + msgchannel = "%N:%K$*%N"; + + # private message, $0 = nick, $1 = host + privmsg = "[$0(%K$1-)] "; + + # private message from you, $0 = "msg", $1 = target nick + ownprivmsg = "[%K$0(%N$1-)] "; + + # own private message in query + ownprivmsgnick = "{msgnick $*}"; + ownprivnick = "%W$*%N"; + + # private message in query + privmsgnick = "{msgnick %N$*}"; + + ## + ## Actions (/ME stuff) + ## + + # used internally by this theme + action_core = "%W*%N $*"; + + # generic one that's used by most actions + action = "{action_core $0} "; + + # own action, both private/public + ownaction = "{action_core $0} "; + + # own action with target, both private/public + ownaction_target = "%W*%N $0:%K$1%N "; + + # private action sent by others + pvtaction = " %W*%N $* "; + pvtaction_query = "{action $*}"; + + # public action sent by others + pubaction = "{action $*}"; + + + ## + ## other IRC events + ## + + # whois + whois = "%K$[8]0 :%N $1-"; + + # notices + ownnotice = "[%K$0>%N$1-] "; + notice = "%W-%N$*%W-%N "; + # notice = "%W-%N$0( + pubnotice_channel = "%N:%g$*"; + pvtnotice_host = "(%K$*)"; + servernotice = "%K-%N$*%K-%N "; + + # CTCPs + ownctcp = "[%K$0($1-)] "; + ctcp = "%N$*"; + + # wallops + wallop = "%W$*%N: "; + wallop_nick = "%N$*"; + wallop_action = "%W * $*%N "; + + # netsplits + netsplit = "%w-%W-%K$*%W-%w-%N"; + netjoin = "%w-%W-%K$*%W-%w-%N"; + + # /names list + names_prefix = ""; + names_nick = "%K|%W$0%N$1-%N "; + names_nick_op = "{names_nick $*}"; + names_nick_halfop = "{names_nick $*}"; + names_nick_voice = "{names_nick $*}"; + names_users = "%K|>%w>%W>%N $*"; + names_channel = "$*"; + + # DCC + dcc = "$*%N"; + dccfile = "%_$*%_"; + + # DCC chat, own msg/action + dccownmsg = "[$0($1-)] "; + dccownnick = "$*"; + dccownquerynick = "%W$*%N"; + dccownaction = "%K*%N $* "; + dccownaction_target = "%K*%N $0%K:%K$1%N "; + + # DCC chat, others + dccmsg = "[$1-($0)] "; + dccquerynick = "$*"; + dccaction = "%K*%N $* "; + + ## + ## statusbar + ## + + # default background for all statusbars. You can also give + # the default foreground color for statusbar items. + # sb_background = "%9%W"; + sb_background = "%N%N"; + + # default backround for "default" statusbar group + # sb_default_bg = "%0"; + sb_default_bg = "%N"; + # background for prompt / input line + sb_prompt_bg = "%N"; + # background for info statusbar + sb_info_bg = "%N"; + # background for topicbar (same default) + sb_topic_bg = "%N"; + + # text at the beginning of statusbars. sb-item already puts + # space there,so we don't use anything by default. + # sbstart = "%y»)%N"; + sbstart = ""; + # text at the end of statusbars. Use space so that it's never + # used for anything. + sbend = "%K<«"; + + # topicsbstart = "{sbstart $*}"; + topicsbstart = "%K»>%N"; + # topicsbend = "{sbend $*}"; + topicsbend = ""; + + prompt = "%K>%N$*%K: "; + + sb = "%K[%N$*%K]%N "; + #custom: + sbtime = "%K$*%N "; + #endcustom + sbmode = "%K/%N+$*%N"; + sbaway = " %N(%KzZzZ%N)%N"; + sbservertag = "%K:%N $0 %K(%N^X%K)"; + + # activity in statusbar + + # ',' separator + sb_act_sep = "%K$*"; + # normal text + sb_act_text = "%N$*"; + # public message + sb_act_msg = "%W$*"; + # hilight + sb_act_hilight = "%Y$*"; + + # hilight with specified color, $0 = color, $1 = text + sb_act_hilight_color = "$0$1-%N"; + # sb_act_hilight_color = "$0-%N"; + # sb_act_hilight_color = "%Y$1%N"; + + + # userlist.pl settings + sb_usercount = "{sb $0 %K/ $1-}"; + sb_uc_ircops = "%K*%N$*"; + sb_uc_ops = "%K@%N$*"; + sb_uc_halfops = "%K%%%N$*"; + sb_uc_voices = "%K+%N$*"; + sb_uc_normal = "%N$*"; + sb_uc_space = "%K,"; + + # nact.pl settings + nact_display = "$1%G>%N$0%R>%N$2"; + nact_command = "$1: [DN:$0 UP:$2]"; +}; + + +formats = { + "fe-common/core" = { + join = "%K»»%N$[-9]0%K» {chanhost $1} %Kjoins $2"; + part = "%K««%N$[-9]0%K« {chanhost $1} %Kleaves $2 {reason $3}"; + quit = "%K««%N$[-9]0%K« {chanhost $1} %Kquits {reason $2}"; + kick = "%R«%r«%N$[-9]0%K was kicked from%N {channel $1} %Kby%N $2 {reason $3}"; + own_msg = "{ownmsgnick $2 {ownnick $[0]0}}$1"; + own_msg_channel = "{ownmsgnick $3 {ownnick $[-9]0}{msgchannel $1}}$2"; + pubmsg_me = "{pubmsgmenick $2 {menick $[-9]0}}$1"; + pubmsg_me_channel = "{pubmsgmenick $3 {menick $[-9]0}{msgchannel $1}}$2"; + pubmsg_hilight = "{pubmsghinick $0 $3 $[-9]1}$2"; + pubmsg_hilight_channel = "{pubmsghinick $0 $4 $[-9]1{msgchannel $2}}$3"; + pubmsg = "{pubmsgnick $2 {pubnick \00310$[0]0}}$1"; + pubmsg_channel = "{pubmsgnick $3 {pubnick $[-9]0}{msgchannel $1}}$2"; + set_item = "$0 %K=%N $1"; + line_start_irssi = "{line_start}{hilight %KIrssi:%N }"; + nick_changed = "%K--%N{channick $[-9]0}%K- is now known as%N {nick $1}"; + your_nick_changed = "%K--%N You're now known as {nick $1}"; + daychange = "%K----%N Day changed to %%d %%b %%Y"; + }; + "fe-common/irc" = { + chanmode_change = "%K--modechange {channel $0}:%N {mode $1} %Kby%N $2"; + whois = "%K,-%w-%W-%nWhois%W-%w-%K<%N $0 {chanhost_hilight $1@$2}%n%:%K|%n {whois ircname $3}"; + whois_idle = "%K|%n {whois idle %|$1d $2h $3m $4s}"; + whois_idle_signon = "%K|%n {whois idle %|$1d $2h $3m $4s {comment signon: $5}}"; + whois_server = "%K|%n {whois server %|$1 {comment $2}}"; + whois_oper = "%K|%n {whois {hilight $1}}"; + whois_registered = "%K|%n {whois has registered this nick}"; + whois_help = "%K|%n {whois is available for help}"; + whois_modes = "%K|%n {whois modes $1}"; + whois_realhost = "%K|%n {whois hostname $1-}"; + whois_usermode = "%K|%n {whois usermode $1}"; + whois_channels = "%K|%n {whois channels %|$1}"; + whois_away = "%K|%n {whois away %|$1}"; + whois_special = "%K|%n {whois info %|$1}"; + whois_extra = "%K|%n {whois extra %|$1}"; + end_of_whois = "%K`---------------- -- --- - - -"; + whois_not_found = "%K>%w>%W>%N There is no such nick $0"; + + # very ugly. But recently some EFNet IRCDs send an annoying + # "actually using host" message along with whois. + default_event_server = "%K|%n {whois ircdmsg $1 [$0]}"; + + server_chanmode_change = "{netsplit IRCDMode}%K/$0 %n{mode $1} %Kby%N {nick $2}"; + own_action = "{ownaction $[-9]0}$1"; + action_public = "{pubaction $[-9]0}$1"; + action_public_channel = "{pubaction $[-9]0{msgchannel $1}}$2"; + channel_created = "%K-- Channel%n $0 %Kcreated%n $1"; + topic = "%K-- Topic for %n$0: $1"; + no_topic = "%K-- No topic set for %n$0"; + topic_info = "%K-- Topic set by%n {nick $0} {nickhost $2} {comment $1}"; + channel_synced = "%KJoin to%n {channel $0} %Kwas synced in%n {hilight $1} secs"; + }; +}; diff --git a/home/.irssi/scripts/autorun/adv_windowlist.pl b/home/.irssi/scripts/autorun/adv_windowlist.pl new file mode 100755 index 0000000..acd3d6a --- /dev/null +++ b/home/.irssi/scripts/autorun/adv_windowlist.pl @@ -0,0 +1,2954 @@ +use strict; +use warnings; + +our $VERSION = '1.9'; # 32a6d4807a45e71 +our %IRSSI = ( + authors => 'Nei', + contact => 'Nei @ anti@conference.jabber.teamidiot.de', + url => "http://anti.teamidiot.de/", + name => 'adv_windowlist', + description => 'Adds a permanent advanced window list on the right or in a status bar.', + sbitems => 'awl_shared', + license => 'GNU GPLv2 or later', + ); + +# UPGRADE NOTE +# ============ +# for users of 0.7 or earlier series, please note that appearance +# settings have moved to /format, i.e. inside your theme! +# the fifo (screen) has been replaced by an external viewer script + +# Usage +# ===== +# copy the script to ~/.irssi/scripts/ +# +# In irssi: +# +# /run adv_windowlist +# +# In your shell (for example a tmux split): +# +# perl ~/.irssi/scripts/adv_windowlist.pl +# +# To use sbar mode instead: +# +# /toggle awl_viewer +# +# Hint: to get rid of the old [Act:] display +# /statusbar window remove act +# +# to get it back: +# /statusbar window add -after lag -priority 10 act + +# Options +# ======= +# formats can be cleared with /format -delete +# +# /format awl_display_(no)key(_active|_visible) +# * string : Format String for one window. The following $'s are expanded: +# $C : Name +# $N : Number of the Window +# $Q : meta-Keymap +# $H : Start hilighting +# $S : Stop hilighting +# /+++++++++++++++++++++++++++++++++, +# | **** I M P O R T A N T : **** | +# | | +# | don't forget to use $S if you | +# | used $H before! | +# | | +# '+++++++++++++++++++++++++++++++++/ +# key : a key binding that goes to this window could be detected in /bind +# nokey : no such key binding was detected +# active : window would receive the input you are currently typing +# visible : window is also visible on screen but not active (a split window) +# +# /format awl_name_display +# * string : Format String for window names +# $0 : name as formatted by the settings +# +# /format awl_display_header +# * string : Format String for this header line. The following $'s are expanded: +# $C : network tag +# +# /format awl_separator(2) +# * string : Character to use between the channel entries +# variant 2 can be used for alternating separators (only in status bar +# without block display) +# +# /format awl_abbrev_chars +# * string : Character to use when shortening long names. The second character +# will be used if two blocks need to be filled. +# +# /format awl_title +# * string : Text to display in the title string or title bar +# +# /format awl_viewer_item_bg +# * string : Format String specifying the viewer's item background colour +# +# /set awl_prefer_name +# * this setting decides whether awl will use the active_name (OFF) or the +# window name as the name/caption in awl_display_*. +# That way you can rename windows using /window name myownname. +# +# /set awl_hide_empty +# * if visible windows without items should be hidden from the window list +# set it to 0 to show all windows +# 1 to hide visible windows without items (negative exempt +# active window) +# +# /set awl_detach +# * list of windows that should be hidden from the window list. you +# can also use /awl detach and /awl attach to manage this +# setting. an optional data_level can be specified with ",num" +# +# /set awl_detach_data +# * num : hide the detached window if its data_level is below num +# +# /set awl_detach_aht +# * if enabled, also detach all windows listed in the +# activity_hide_targets setting +# +# /set awl_hide_data +# * num : hide the window if its data_level is below num +# set it to 0 to basically disable this feature, +# 1 if you don't want windows without activity to be shown +# 2 to show only those windows with channel text or hilight +# 3 to show only windows with hilight (negative exempt active window) +# +# /set awl_hide_name_data +# * num : hide the name of the window if its data_level is below num +# (only works in status bar without block display) +# you will want to change your formats to add $H...$S around $Q or $N +# if you plan to use this +# +# /set awl_maxlines +# * num : number of lines to use for the window list (0 to disable, negative +# lock) +# +# /set awl_maxcolumns +# * num : number of columns to use for the window list when using the +# tmux integration (0 to disable) +# +# /set awl_block +# * num : width of a column in viewer mode (negative values = block +# display in status bar mode) +# /+++++++++++++++++++++++++++++++++, +# | ****** W A R N I N G ! ****** | +# | | +# | If your block display looks | +# | DISTORTED, you need to add the | +# | following line to your .theme | +# | file under | +# | abstracts = { : | +# | | +# | sb_act_none = "%K$*"; | +# | | +# '+++++++++++++++++++++++++++++++++/ +# +# /set awl_sbar_maxlength +# * if you enable the maxlength setting, the block width will be used as a +# maximum length for the non-block status bar mode too. +# +# /set awl_height_adjust +# * num : how many lines to leave empty in viewer mode +# +# /set awl_sort <-data_level|-last_line|refnum> +# * you can change the window sort order with this variable +# -data_level : sort windows with hilight first +# -last_line : sort windows in order of activity +# refnum : sort windows by window number +# active/server/tag : sort by server name +# lru : sort windows with the last recently used last +# "-" reverses the sort order +# typechecks are supported via ::, e.g. active::Query or active::Irc::Query +# undefinedness can be checked with ~, e.g. ~active +# string comparison can be done with =, e.g. name=(status) +# to make sort case insensitive, use #i, e.g. name#i +# any key in the window hash can be tested, e.g. active/chat_type=XMPP +# multiple criteria can be separated with , or +, e.g. -data_level+-last_line +# +# /set awl_placement +# /set awl_position +# * these settings correspond to /statusbar because awl will create +# status bars for you +# (see /help statusbar to learn more) +# +# /set awl_all_disable +# * if you set awl_all_disable to ON, awl will also remove the +# last status bar it created if it is empty. +# As you might guess, this only makes sense with awl_hide_data > 0 ;) +# +# /set awl_viewer +# * enable the external viewer script +# +# /set awl_viewer_launch +# * try to auto-launch the viewer under tmux or with a shell command +# /awl restart is required all auto-launch related settings to take +# effect +# +# /set awl_viewer_tmux_position +# * try to split in this direction when using tmux for the viewer +# custom : use custom_command setting +# +# /set awl_viewer_xwin_command +# * custom command to run in order to start the viewer when irssi is +# running under X +# %A - gets replaced by the command to run the viewer +# %qA - additionally quote the command +# +# /set awl_viewer_custom_command +# * custom command to run in order to start the viewer +# +# /set awl_viewer_launch_env +# * specific environment settings for use on viewer auto-launch, +# without the AWL_ prefix +# +# /set awl_shared_sbar +# * share a status bar for the first awl item, you will need to manually +# /statusbar window add -after lag -priority 10 awl_shared +# left : space in cells occupied on the left of status bar +# right : space occupied on the right +# Note: you need to replace "left" AND "right" with the appropriate numbers! +# +# /set awl_path +# * path to the file which the viewer script reads +# +# /set fancy_abbrev +# * how to shorten too long names +# no : shorten in the middle +# head : always cut off the ends +# strict : shorten repeating substrings +# fancy : combination of no+strict +# +# /set awl_custom_xform +# * specify a custom routine to transform window names +# example: s/^#// remove the #-mark of IRC channels +# the special flags $CHANNEL / $TAG / $QUERY / $NAME can be +# tested in conditionals +# +# /set awl_last_line_shade +# * set timeout to shade activity base colours, to enable +# you also need to add +-last_line to awl_sort +# (requires 256 colour support) +# +# /set awl_no_mode_hint +# * whether to show the hint of running the viewer script in the +# status bar +# +# /set awl_mouse +# * enable the terminal mouse in irssi +# (use the awl-patched mouse.pl for gestures and commands if you need +# them and disable mouse_escape) +# +# /set awl_mouse_offset +# * specifies where on the screen is the awl status bar +# (0 = on top/bottom, 1 = one additional line in between, +# e.g. prompt) +# you MUST set this correctly otherwise the mouse coordinates will +# be off +# +# /set mouse_scroll +# * how many lines the mouse wheel scrolls +# +# /set mouse_escape +# * seconds to disable the mouse, when not clicked on the windowlist +# + +# Commands +# ======== +# /awl detach +# * hide the current window from the window list. num specifies the +# data_level (optional) +# +# /awl attach +# * unhide the current window from the window list +# +# /awl ack +# * change to the next window with activity, ignoring detached windows +# +# /awl redraw +# * redraws the windowlist. There may be occasions where the +# windowlist can get destroyed so you can use this command to +# force a redraw. +# +# /awl restart +# * restart the connection to the viewer script. + +# Viewer script +# ============= +# When run from the command line, adv_windowlist acts as the viewer +# script to be used together with the irssi script to display the +# window list in a sidebar/terminal of its own. +# +# One optional parameter is accepted, the awl_path +# +# The viewer can be configured by three environment variables: +# +# AWL_HI9=1 +# * interpret %9 as high-intensity toggle instead of bold. This had +# been the default prior to version 0.9b8 +# +# AWL_AUTOFOCUS=0 +# * disable auto-focus behaviour when activating a window +# +# AWL_NOTITLE=1 +# * disable the title bar + +# Nei =^.^= ( anti@conference.jabber.teamidiot.de ) + +no warnings 'redefine'; +use constant IN_IRSSI => __PACKAGE__ ne 'main' || $ENV{IRSSI_MOCK}; +use constant SCRIPT_FILE => __FILE__; +no if !IN_IRSSI, strict => (qw(subs refs)); +use if IN_IRSSI, Irssi => (); +use if IN_IRSSI, 'Irssi::TextUI' => (); +use v5.10; +use Encode; +use Storable (); +use IO::Socket::UNIX; +use List::Util qw(min max reduce); +use Hash::Util qw(lock_keys); +use Text::ParseWords qw(shellwords); + +BEGIN { + if ($] < 5.012) { + *CORE::GLOBAL::length = *CORE::GLOBAL::length = sub (_) { + defined $_[0] ? CORE::length($_[0]) : undef + }; + } + *Irssi::active_win = {}; # hide incorrect warning +} + +unless (IN_IRSSI) { + local *_ = \@ARGV; + &AwlViewer::main; + exit; +} + + +use constant GLOB_QUEUE_TIMER => 100; + +our $BLOCK_ALL; # localized blocker +my @actString; # status bar texts +my @win_items; +my $currentLines = 0; +my %awins; +my $globTime; # timer to limit remake calls + +my %CHANGED; +my $VIEWER_MODE; +my $MOUSE_ON; +my %mouse_coords; +my %statusbars; +my %S; # settings +my $settings_str = '1'; +my $window_sort_func; +my $custom_xform; +my ($sb_base_width, $sb_base_width_pre, $sb_base_width_post); +my $print_text_activity; +my $shade_line_timer; +my ($screenHeight, $screenWidth); +my %viewer; + +my (%keymap, %nummap, %wnmap, %specialmap, %wnmap_exp, %custom_key_map); +my %banned_channels; +my %detach_map; +my %abbrev_cache; + +use constant setc => 'awl'; + +sub set ($) { + setc . '_' . $_[0] +} + +sub add_statusbar { + for (@_) { + # add subs + my $l = set $_; + { + my $close = $_; + no strict 'refs'; + *{$l} = sub { awl($close, @_) }; + } + Irssi::command("^statusbar $l reset"); + Irssi::command("statusbar $l enable"); + if (lc $S{placement} eq 'top') { + Irssi::command("statusbar $l placement top"); + } + if (my $x = $S{position}) { + Irssi::command("statusbar $l position $x"); + } + Irssi::command("statusbar $l add -priority 100 -alignment left barstart"); + Irssi::command("statusbar $l add $l"); + Irssi::command("statusbar $l add -priority 100 -alignment right barend"); + Irssi::command("statusbar $l disable"); + Irssi::statusbar_item_register($l, '$0', $l); + $statusbars{$_} = 1; + Irssi::command("statusbar $l enable"); + } +} + +sub remove_statusbar { + for (@_) { + my $l = set $_; + Irssi::command("statusbar $l disable"); + Irssi::command("statusbar $l reset"); + Irssi::statusbar_item_unregister($l); + { + no strict 'refs'; + undef &{$l}; + } + delete $statusbars{$_}; + } +} + +my $awl_shared_empty = sub { + return if $BLOCK_ALL; + my ($item, $get_size_only) = @_; + $item->default_handler($get_size_only, '', '', 0); +}; + +sub syncLines { + my $maxLines = $S{maxlines}; + my $newLines = ($maxLines > 0 and @actString > $maxLines) ? + $maxLines : + ($maxLines < 0) ? + -$maxLines : + @actString; + $currentLines = 1 if !$currentLines && $S{shared_sbar}; + if ($S{shared_sbar} && !$statusbars{shared}) { + my $l = set 'shared'; + { + no strict 'refs'; + *{$l} = sub { + return if $BLOCK_ALL; + my ($item, $get_size_only) = @_; + + my $text = $actString[0]; + my $title = _get_format(set 'title'); + if (length $title) { + $title =~ s{\\(.)|(.)}{ + defined $2 ? quotemeta $2 + : $1 eq 'V' ? '\u' + : $1 eq ':' ? quotemeta ':%n' + : $1 =~ /^[uUFQE]$/ ? "\\$1" + : quotemeta "\\$1" + }sge; + $title = eval qq{"$title"}; + $title .= ' '; + } + my $pat = defined $text ? "{sb $title\$*}" : '{sb }'; + $text //= ''; + $item->default_handler($get_size_only, $pat, $text, 0); + }; + } + $statusbars{shared} = 1; + remove_statusbar (0) if $statusbars{0}; + } + elsif ($statusbars{shared} && !$S{shared_sbar}) { + add_statusbar (0) if $currentLines && $newLines; + delete $statusbars{shared}; + my $l = set 'shared'; + { + no strict 'refs'; + *{$l} = $awl_shared_empty; + } + } + if ($currentLines == $newLines) { return; } + elsif ($newLines > $currentLines) { + add_statusbar ($currentLines .. ($newLines - 1)); + } + else { + remove_statusbar (reverse ($newLines .. ($currentLines - 1))); + } + $currentLines = $newLines; +} + +sub awl { + return if $BLOCK_ALL; + my ($line, $item, $get_size_only) = @_; + + my $text = $actString[$line]; + my $pat = defined $text ? '{sb $*}' : '{sb }'; + $text //= ''; + $item->default_handler($get_size_only, $pat, $text, 0); +} + +# remove old statusbars +{ my %killBar; + sub get_old_status { + my ($textDest, $cont, $cont_stripped) = @_; + if ($textDest->{level} == 524288 and $textDest->{target} eq '' and !defined $textDest->{server}) { + my $name = quotemeta(set ''); + if ($cont_stripped =~ m/^$name(\d+)\s/) { $killBar{$1} = 1; } + Irssi::signal_stop; + } + } + sub killOldStatus { + %killBar = (); + Irssi::signal_add_first('print text' => 'get_old_status'); + Irssi::command('statusbar'); + Irssi::signal_remove('print text' => 'get_old_status'); + remove_statusbar(keys %killBar); + } +} + +sub _add_map { + my ($type, $target, $map) = @_; + ($type->{$target}) = sort { length $a <=> length $b || $a cmp $b } + $map, exists $type->{$target} ? $type->{$target} : (); +} + +sub get_keymap { + my ($textDest, undef, $cont_stripped) = @_; + if ($textDest->{level} == 524288 and $textDest->{target} eq '' and !defined $textDest->{server}) { + my $one_meta_or_ctrl_key = qr/((?:meta-)*?)(?:(meta-|\^)(\S)|(\w+))/; + $cont_stripped = as_uni($cont_stripped); + if ($cont_stripped =~ m/((?:$one_meta_or_ctrl_key-)*$one_meta_or_ctrl_key)\s+(.*)$/) { + my ($combo, $command) = ($1, $10); + my $map = ''; + while ($combo =~ s/(?:-|^)$one_meta_or_ctrl_key$//) { + my ($level, $ctl, $key, $nkey) = ($1, $2, $3, $4); + my $numlevel = ($level =~ y/-//); + $ctl = '' if !$ctl || $ctl ne '^'; + $map = ('-' x ($numlevel%2)) . ('+' x ($numlevel/2)) . + $ctl . (defined $key ? $key : "\01$nkey\01") . $map; + } + for ($command) { + last unless length $map; + if (/^change_window (\d+)/i) { + _add_map(\%nummap, $1, $map); + } + elsif (/^(?:command window goto|change_window) (\S+)/i) { + my $window = $1; + if ($window !~ /\D/) { + _add_map(\%nummap, $window, $map); + } + elsif (lc $window eq 'active') { + _add_map(\%specialmap, '_active', $map); + } + else { + _add_map(\%wnmap, $window, $map); + } + } + elsif (/^(?:active_window|command ((awl )?ack))/i) { + _add_map(\%specialmap, '_active', $map); + $viewer{use_ack} = $1; + } + elsif (/^command window last/i) { + _add_map(\%specialmap, '_last', $map); + } + elsif (/^(?:upper_window|command window up)/i) { + _add_map(\%specialmap, '_up', $map); + } + elsif (/^(?:lower_window|command window down)/i) { + _add_map(\%specialmap, '_down', $map); + } + elsif (/^key\s+(\w+)/i) { + $custom_key_map{$1} = $map; + } + } + } + Irssi::signal_stop; + } +} + +sub update_keymap { + %nummap = %wnmap = %specialmap = %custom_key_map = (); + Irssi::signal_remove('command bind' => 'watch_keymap'); + Irssi::signal_add_first('print text' => 'get_keymap'); + Irssi::command('bind'); + Irssi::signal_remove('print text' => 'get_keymap'); + for (keys %custom_key_map) { + if (exists $custom_key_map{$_} && + $custom_key_map{$_} =~ s/\01(\w+)\01/exists $custom_key_map{$1} ? $custom_key_map{$1} : "\02"/ge) { + if ($custom_key_map{$_} =~ /\02/) { + delete $custom_key_map{$_}; + } + else { + redo; + } + } + } + for my $keymap (\(%specialmap, %wnmap, %nummap)) { + for (keys %$keymap) { + if ($keymap->{$_} =~ s/\01(\w+)\01/exists $custom_key_map{$1} ? $custom_key_map{$1} : "\02"/ge) { + if ($keymap->{$_} =~ /\02/) { + delete $keymap->{$_}; + } + } + } + } + Irssi::signal_add('command bind' => 'watch_keymap'); + delete $viewer{client_keymap}; + &wl_changed; +} + +# watch keymap changes +sub watch_keymap { + Irssi::timeout_add_once(1000, 'update_keymap', undef); +} + +{ my %strip_table = ( + # fe-common::core::formats.c:format_expand_styles + # delete format_backs format_fores bold_fores other stuff + (map { $_ => '' } (split //, '04261537' . 'kbgcrmyw' . 'KBGCRMYW' . 'U9_8I:|FnN>#[' . 'pP')), + # escape + (map { $_ => $_ } (split //, '{}%')), + ); + sub ir_strip_codes { # strip %codes + my $o = shift; + $o =~ s/(%(%|Z.{6}|z.{6}|X..|x..|.))/exists $strip_table{$2} ? $strip_table{$2} : + $2 =~ m{x(?:0[a-f]|[1-6][0-9a-z]|7[a-x])|z[0-9a-f]{6}}i ? '' : $1/gex; + $o + } +} +## ir_parse_special -- wrapper around parse_special +## $i - input format +## $args - array ref of arguments to format +## $win - different target window (default current window) +## $flags - different kind of escape flags (default 4|8) +## returns formatted str +sub ir_parse_special { + my $o; + my $i = shift; + my $args = shift // []; + y/ /\177/ for @$args; # hack to escape spaces + my $win = shift || Irssi::active_win; + my $flags = shift // 0x4|0x8; + my @cmd_args = ($i, (join ' ', @$args), $flags); + my $server = Irssi::active_server(); + if (ref $win and ref $win->{active}) { + $o = $win->{active}->parse_special(@cmd_args); + } + elsif (ref $win and ref $win->{active_server}) { + $o = $win->{active_server}->parse_special(@cmd_args); + } + elsif (ref $server) { + $o = $server->parse_special(@cmd_args); + } + else { + $o = &Irssi::parse_special(@cmd_args); + } + $o =~ y/\177/ /; + $o +} + +sub sb_format_expand { # Irssi::current_theme->format_expand wrapper + Irssi::current_theme->format_expand( + $_[0], + ( + Irssi::EXPAND_FLAG_IGNORE_REPLACES + | + ($_[1] ? 0 : Irssi::EXPAND_FLAG_IGNORE_EMPTY) + ) + ) +} + +{ my $term_type = Irssi::version > 20040819 ? 'term_charset' : 'term_type'; + if (Irssi->can('string_width')) { + *screen_length = sub { Irssi::string_width($_[0]) }; + } + else { + local $@; + eval { require Text::CharWidth; }; + unless ($@) { + *screen_length = sub { Text::CharWidth::mbswidth($_[0]) }; + } + else { + my $err = $@; chomp $err; $err =~ s/\sat .* line \d+\.$//; + #Irssi::print("%_$IRSSI{name}: warning:%_ Text::CharWidth module failed to load. Length calculation may be off! Error was:"); + print "%_$IRSSI{name}:%_ $err"; + *screen_length = sub { + my $temp = shift; + if (lc Irssi::settings_get_str($term_type) eq 'utf-8') { + Encode::_utf8_on($temp); + } + length($temp) + }; + } + } + sub as_uni { + no warnings 'utf8'; + Encode::decode(Irssi::settings_get_str($term_type), $_[0], 0) + } + sub as_tc { + Encode::encode(Irssi::settings_get_str($term_type), $_[0], 0) + } +} + +sub sb_length { + screen_length(ir_strip_codes($_[0])) +} + +sub run_custom_xform { + local $@; + eval { + $custom_xform->() + }; + if ($@) { + $@ =~ /^(.*)/; + print '%_'.(set 'custom_xform').'%_ died (disabling): '.$1; + $custom_xform = undef; + } +} + +sub remove_uniform { + my $o = shift; + $o =~ s/^xmpp:(.*?[%@]).+\.[^.]+$/$1/ or + $o =~ s#^psyc://.+\.[^.]+/([@~].*)$#$1#; + if ($custom_xform) { + run_custom_xform() for $o; + } + $o +} + +sub remove_uniform_vars { + my $win = shift; + my $name = __PACKAGE__ . '::custom_xform::' . $win->{active}{type} + if ref $win->{active} && $win->{active}{type}; + no strict 'refs'; + local ${$name} = 1 if $name; + remove_uniform(+shift); +} + +sub lc1459 { + my $x = shift; + $x =~ y/][\\^/}{|~/; + lc $x +} + +sub window_list { + my $i = 0; + map { $_->[1] } sort $window_sort_func map { [ $i++, $_ ] } Irssi::windows; +} + +sub _calculate_abbrev { + my ($wins, $abbrevList) = @_; + if ($S{fancy_abbrev} !~ /^(no|off|head)/i) { + my @nameList = map { ref $_ ? remove_uniform_vars($_, as_uni($_->get_active_name) // '') : '' } @$wins; + for (my $i = 0; $i < @nameList - 1; ++$i) { + my ($x, $y) = ($nameList[$i], $nameList[$i + 1]); + s/^[+#!=]// for $x, $y; + my $res = exists $abbrev_cache{$x}{$y} ? $abbrev_cache{$x}{$y} + : $abbrev_cache{$x}{$y} = string_LCSS($x, $y); + if (defined $res) { + for ($nameList[$i], $nameList[$i + 1]) { + $abbrevList->{$_} //= int((index $_, $res) + (length $res) / 2); + } + } + } + } +} + +my %act_last_line_shades = ( + r => [qw[ 50 40 30 20 ]], + g => [qw[ 1O 1I 1C 16 ]], + y => [qw[ 5O 4I 3C 26 ]], + b => [qw[ 15 14 13 12 ]], + m => [qw[ 54 43 32 21 ]], + c => [qw[ 1S 1L 1E 17 ]], + w => [qw[ 7W 7T 7Q 3E ]], + K => [qw[ 7M 7K 27 7H ]], + R => [qw[ 60 50 40 30 ]], + G => [qw[ 1U 1O 1I 1C ]], + Y => [qw[ 6U 5O 4I 3C ]], + B => [qw[ 2B 2A 29 28 ]], + M => [qw[ 65 54 43 32 ]], + C => [qw[ 1Z 1S 1L 1E ]], + W => [qw[ 6Z 5S 7R 7O ]], + ); + +sub _format_display { + my (undef, $format, $cformat, $hilight, $name, $number, $key, $win) = @_; + if ($print_text_activity && $S{line_shade}) { + my @hilight_code = split /\177/, sb_format_expand("{$hilight \177}"), 2; + my $max_time = max(1, log($S{line_shade}) - log(1000)); + my $time_delta = min(3, min($max_time, log(max(1, time - $win->{last_line}))) / $max_time * 3); + if ($hilight_code[0] =~ /%(.)/ && exists $act_last_line_shades{$1}) { + $hilight = 'sb_act_hilight_color %X'.$act_last_line_shades{$1}[$time_delta]; + } + } + $cformat = '$0' unless length $cformat; + my %map = ('$C' => $cformat, '$N' => '$1', '$Q' => '$2'); + $format =~ s<(\$.)><$map{$1}//$1>ge; + $format =~ s<\$H((?:\$.|[^\$])*?)\$S><{$hilight $1%n}>g; + my @ret = ir_parse_special(sb_format_expand($format), [$name, $number, $key], $win); + @ret +} + +sub _get_format { + Irssi::current_theme->get_format(__PACKAGE__, @_) +} + +sub _is_detached { + my ($win, $active_number) = @_; + my $level = $win->{data_level} // 0; + my $number = $win->{refnum}; + my $name = lc1459( as_uni($win->{name}) ); + my $active = lc1459( as_uni($win->get_active_name) // '' ); + my $tag = $win->{active} && $win->{active}{server} ? lc1459( as_uni($win->{active}{server}{tag}) // '' ) : ''; + my @cond = ($number); + push @cond, "$name" if length $name; + push @cond, "$tag/$active" if length $tag && length $active; + push @cond, "$active" if length $active; + push @cond, "$tag/*", "$tag/::all" if length $tag; + push @cond, "*", "::all"; + for my $cond (@cond) { + if (exists $detach_map{ $cond }) { + my $dd = $detach_map{ $cond } // $S{detach_data}; + return $win->{data_level} < abs $dd + && ($number != $active_number || 0 <= $dd); + } + } + return; +} + +sub _calculate_items { + my ($wins, $abbrevList) = @_; + + my $display_header = _get_format(set 'display_header'); + my $name_format = _get_format(set 'name_display'); + my $abbrev_chars = as_uni(_get_format(set 'abbrev_chars')); + + my %displays; + + my $active = Irssi::active_win; + @win_items = (); + %keymap = (%nummap, %wnmap_exp); + + my ($numPad, $keyPad) = (0, 0); + if ($VIEWER_MODE or $S{block} < 0) { + $numPad = length((sort { length $b <=> length $a } keys %keymap)[0]) // 0; + $keyPad = length((sort { length $b <=> length $a } values %keymap)[0]) // 0; + } + my $last_net; + my ($abbrev1, $abbrev2) = $abbrev_chars =~ /(\X)(.*)/; + my @abbrev_chars = ('~', "\x{301c}"); + unless (defined $abbrev1 && screen_length(as_tc($abbrev1)) == 1) { $abbrev1 = $abbrev_chars[0] } + unless (length $abbrev2) { + $abbrev2 = $abbrev1; + if ($abbrev1 eq $abbrev_chars[0]) { + $abbrev2 = $abbrev_chars[1]; + } + else { + $abbrev2 = $abbrev1; + } + } + if (screen_length(as_tc($abbrev2)) == 1) { + $abbrev2 x= 2; + } + while (screen_length(as_tc($abbrev2)) > 2) { + chop $abbrev2; + } + unless (screen_length(as_tc($abbrev2)) == 2) { + $abbrev2 = $abbrev_chars[1]; + } + for my $win (@$wins) { + my $global_tag_header_mode; + + next unless ref $win; + + my $backup_win = Storable::dclone($win); + delete $backup_win->{active} unless ref $backup_win->{active}; + + $global_tag_header_mode = + $display_header && ($last_net // '') ne ($backup_win->{active}{server}{tag} // ''); + + if ($win->{data_level} < abs $S{hide_data} + && ($win->{refnum} != $active->{refnum} || 0 <= $S{hide_data})) { + next; } + elsif (exists $awins{$win->{refnum}} && $S{hide_empty} && !$win->items + && ($win->{refnum} != $active->{refnum} || 0 <= $S{hide_empty})) { + next; } + elsif (_is_detached($win, $active->{refnum})) { + next; } + + my $colour = $win->{hilight_color} // ''; + my $hilight = do { + if ($win->{data_level} == 0) { 'sb_act_none'; } + elsif ($win->{data_level} == 1) { 'sb_act_text'; } + elsif ($win->{data_level} == 2) { 'sb_act_msg'; } + elsif ($colour ne '') { "sb_act_hilight_color $colour"; } + elsif ($win->{data_level} == 3) { 'sb_act_hilight'; } + else { 'sb_act_special'; } + }; + my $number = $win->{refnum}; + + my ($name, $display, $cdisplay); + if ($global_tag_header_mode) { + $display = $display_header; + $name = as_uni($backup_win->{active}{server}{tag}) // ''; + if ($custom_xform) { + no strict 'refs'; + local ${ __PACKAGE__ . '::custom_xform::TAG' } = 1; + run_custom_xform() for $name; + } + } + else { + my @display = ('display_nokey'); + if (defined $keymap{$number} and $keymap{$number} ne '') { + unshift @display, map { (my $cpy = $_) =~ s/_no/_/; $cpy } @display; + } + if (exists $awins{$number}) { + unshift @display, map { my $cpy = $_; $cpy .= '_visible'; $cpy } @display; + } + if ($active->{refnum} == $number) { + unshift @display, map { my $cpy = $_; $cpy .= '_active'; $cpy } + grep { !/_visible$/ } @display; + } + $display = (grep { length $_ } + map { $displays{$_} //= _get_format(set $_) } + @display)[0]; + $cdisplay = $name_format; + $name = as_uni($win->get_active_name) // ''; + $name = '*' if $S{banned_on} and exists $banned_channels{lc1459($name)}; + $name = remove_uniform_vars($win, $name) if $name ne '*'; + if ($name ne '*' and $win->{name} ne '' and $S{prefer_name}) { + $name = as_uni($win->{name}); + if ($custom_xform) { + no strict 'refs'; + local ${ __PACKAGE__ . '::custom_xform::NAME' } = 1; + run_custom_xform() for $name; + } + } + + if (!$VIEWER_MODE && $S{block} >= 0 && $S{hide_name} + && $win->{data_level} < abs $S{hide_name} + && ($win->{refnum} != $active->{refnum} || 0 <= $S{hide_name})) { + $name = ''; + $cdisplay = ''; + } + } + + $display = "$display%n"; + my $num_ent = (' 'x max(0,$numPad - length $number)) . $number; + my $key_ent = exists $keymap{$number} ? ((' 'x max(0,$keyPad - length $keymap{$number})) . $keymap{$number}) : ' 'x$keyPad; + if ($VIEWER_MODE or $S{sbar_maxlen} or $S{block} < 0) { + my $baseLength = sb_length(_format_display( + '', $display, $cdisplay, $hilight, + 'x', # placeholder + $num_ent, + $key_ent, + $win)) - 1; + my $diff = (abs $S{block}) - (screen_length(as_tc($name)) + $baseLength); + if ($diff < 0) { # too long + my $screen_length = screen_length(as_tc($name)); + if ((abs $diff) >= $screen_length) { $name = '' } # forget it + elsif ((abs $diff) + screen_length(as_tc(substr($name, 0, 1))) >= $screen_length) { $name = substr($name, 0, 1); } + else { + my $ulen = length $name; + my $middle2 = exists $abbrevList->{$name} ? + ($S{fancy_strict}) ? + 2* $abbrevList->{$name} : + (2*($abbrevList->{$name} + $ulen) / 3) : + ($S{fancy_head}) ? + 2*$ulen : + $ulen; + my $first = 1; + while (length $name > 1) { + my $cp = $middle2 >= 0 ? $middle2/2 : -1; # clearing position + my $rm = 2; + # if character at end is wider than 1 cell -> replace it with ~ + if (screen_length(as_tc(substr $name, $cp, 1)) > 1) { + if ($first || $cp < 0) { + $rm = 1; + $first = undef; + } + } + elsif ($cp < 0) { # elsif at end -> replace last 2 characters + --$cp; + } + (substr $name, $cp, $rm) = $abbrev1; + if ($cp > -1 && $rm > 1) { + --$middle2; + } + my $sl = screen_length(as_tc($name)); + if ($sl + $baseLength < abs $S{block}) { + (substr $name, ($middle2+1)/2, 1) = $abbrev2; + last; + } + elsif ($sl + $baseLength == abs $S{block}) { + last; + } + } + } + } + elsif ($VIEWER_MODE or $S{block} < 0) { + $name .= (' ' x $diff); + } + } + + push @win_items, _format_display( + '', $display, $cdisplay, $hilight, + as_tc($name), + $num_ent, + as_tc($key_ent), + $win); + + if ($global_tag_header_mode) { + $last_net = $backup_win->{active}{server}{tag}; + redo; + } + + $mouse_coords{refnum}{$#win_items} = $number; + } +} + +sub _spread_items { + my $width = $screenWidth - $sb_base_width - 1; + my @separator = _get_format(set 'separator'); + if ($S{block} >= 0) { + my $sep2 = _get_format(set 'separator2'); + push @separator, $sep2 if length $sep2 && $sep2 ne $separator[0]; + } + $separator[0] .= '%n'; + my @sepLen = map { sb_length($_) } @separator; + + @actString = (); + my $curLine; + my $curLen = 0; + if ($S{shared_sbar}) { + $curLen += $S{shared_sbar}[0] + 2; + $width -= $S{shared_sbar}[2]; + } + my $mouse_header_check = 0; + for my $it (@win_items) { + my $itemLen = sb_length($it); + if ($curLen) { + if ($curLen + $itemLen + $sepLen[$mouse_header_check % @sepLen] > $width) { + $width += $S{shared_sbar}[2] + if !@actString && $S{shared_sbar}; + push @actString, $curLine; + $curLine = undef; + $curLen = 0; + } + elsif (defined $curLine) { + $curLine .= $separator[$mouse_header_check % @separator]; + $curLen += $sepLen[$mouse_header_check % @sepLen]; + } + } + $curLine .= $it; + if (exists $mouse_coords{refnum}{$mouse_header_check}) { + $mouse_coords{scalar @actString}{ $_ } = $mouse_coords{refnum}{$mouse_header_check} + for $curLen .. $curLen + $itemLen - 1; + } + $curLen += $itemLen; + } + continue { + ++$mouse_header_check; + } + $curLen -= $S{shared_sbar}[0] + if !@actString && $S{shared_sbar}; + push @actString, $curLine if $curLen; +} + +sub remake { + my %abbrevList; + my @wins = window_list(); + if ($VIEWER_MODE or $S{sbar_maxlen} or $S{block} < 0) { + _calculate_abbrev(\@wins, \%abbrevList); + } + + %mouse_coords = ( refnum => +{} ); + _calculate_items(\@wins, \%abbrevList); + + unless ($VIEWER_MODE) { + _spread_items(); + + push @actString, undef unless @actString || $S{all_disable}; + } +} + +sub update_wl { + return if $BLOCK_ALL; + remake(); + + Irssi::statusbar_items_redraw(set $_) for keys %statusbars; + + unless ($VIEWER_MODE) { + Irssi::timeout_add_once(100, 'syncLines', undef); + } + else { + syncViewer(); + } +} + +sub screenFullRedraw { + my ($window) = @_; + if (!ref $window or $window->{refnum} == Irssi::active_win->{refnum}) { + $viewer{fullRedraw} = 1 if $viewer{client}; + $settings_str = ''; + &setup_changed; + } +} + +sub restartViewerServer { + if ($VIEWER_MODE) { + stop_viewer(); + start_viewer(); + } +} + +sub _simple_quote { + my @r = map { + my $x = $_; + $x =~ s/'/'"'"'/g; + $x = "'$x'"; + } @_; + wantarray ? @r : shift @r +} + +sub _viewer_command_replace_format { + my ($ecmd, @args) = @_; + my $file = _simple_quote(SCRIPT_FILE()); + my $path = _simple_quote($viewer{path}); + my @env; + for my $env (shellwords($S{viewer_launch_env})) { + if ($env =~ /^(\w+)(?:=(.*))$/) { + push @env, "AWL_$1=$2" + } + } + my $cmd = join ' ', + (@env ? ('env', _simple_quote(@env)) : ()), + 'perl', $file, '-1', _simple_quote(@args), $path; + $ecmd =~ s{%(%|\w+)}{ + my $sub = $1; + if ($sub eq '%') { + '%' + } + elsif ($sub =~ /^(q*)A(.*)/) { + my $ret = $cmd; + for (1..length $1) { + $ret = _simple_quote($ret); + } + "$ret$2" + } + else { + "%$sub" + } + }gex; + $ecmd +} + +sub start_viewer { + unlink $viewer{path} if -S $viewer{path} || -p _; + + $viewer{server} = IO::Socket::UNIX->new( + Type => SOCK_STREAM, + Local => $viewer{path}, + Listen => 1 + ); + unless ($viewer{server}) { + $viewer{msg} = "Viewer: $!"; + $viewer{retry} = Irssi::timeout_add_once(5000, 'retry_viewer', 1); + return; + } + $viewer{server}->blocking(0); + set_viewer_mode_hint(); + $viewer{server_tag} = Irssi::input_add($viewer{server}->fileno, INPUT_READ, 'vi_connected', undef); + + if ($S{viewer_launch}) { + if (length $ENV{TMUX_PANE} && length $ENV{TMUX} && lc $S{viewer_tmux_position} ne 'custom') { + my $cmd = _viewer_command_replace_format('%qA', '-p', lc $S{viewer_tmux_position}); + Irssi::command("exec - tmux neww -d $cmd 2>&1 &"); + } + elsif (length $ENV{WINDOWID} && length $ENV{DISPLAY} && length $S{viewer_xwin_command} && $S{viewer_xwin_command} =~ /\S/) { + my $cmd = _viewer_command_replace_format($S{viewer_xwin_command}); + Irssi::command("exec - $cmd 2>&1 &"); + } + elsif (length $S{viewer_custom_command} && $S{viewer_custom_command} =~ /\S/) { + my $cmd = _viewer_command_replace_format($S{viewer_custom_command}); + Irssi::command("exec - $cmd 2>&1 &"); + } + } +} + +sub set_viewer_mode_hint { + return unless $viewer{server}; + if ($S{no_mode_hint}) { + $viewer{msg} = undef; + } + else { + my ($name) = __PACKAGE__ =~ /::([^:]+)$/; + $viewer{msg} = "Run $name from the shell or switch to sbar mode"; + } +} + +sub retry_viewer { + start_viewer(); +} + +sub vi_close_client { + Irssi::input_remove(delete $viewer{client_tag}) if exists $viewer{client_tag}; + $viewer{client}->close if $viewer{client}; + delete $viewer{client}; + delete $viewer{client_keymap}; + delete $viewer{client_settings}; + delete $viewer{client_env}; + delete $viewer{fullRedraw}; +} + +sub vi_connected { + vi_close_client(); + $viewer{client} = $viewer{server}->accept or return; + $viewer{client}->blocking(0); + $viewer{client_tag} = Irssi::input_add($viewer{client}->fileno, INPUT_READ, 'vi_clientinput', undef); + syncViewer(); +} + +use constant VIEWER_BLOCK_SIZE => 1024; +sub vi_clientinput { + if ($viewer{client}->read(my $buf, VIEWER_BLOCK_SIZE)) { + $viewer{rcvbuf} .= $buf; + if ($viewer{rcvbuf} =~ s/^(?:(active|\d+)|(last|up|down))\n//igm) { + if (defined $2) { + Irssi::command("window $2"); + } + elsif (lc $1 eq 'active' && $viewer{use_ack}) { + Irssi::command($viewer{use_ack}); + } + else { + Irssi::command("window goto $1"); + } + } + } + else { + vi_close_client(); + Irssi::timeout_add_once(100, 'syncViewer', undef); + } +} + +sub stop_viewer { + Irssi::timeout_remove(delete $viewer{retry}) if exists $viewer{retry}; + vi_close_client(); + Irssi::input_remove(delete $viewer{server_tag}) if exists $viewer{server_tag}; + return unless $viewer{server}; + $viewer{server}->close; + delete $viewer{server}; +} +sub _encode_var { + my $str; + while (@_) { + my ($name, $var) = splice @_, 0, 2; + my $type = ref $var ? $var =~ /HASH/ ? 'map' : $var =~ /ARRAY/ ? 'list' : '' : ''; + $str .= "\n\U$name$type\_begin\n"; + if ($type eq 'map') { + no warnings 'numeric'; + $str .= " $_\n ${$var}{$_}\n" for sort { $a <=> $b || $a cmp $b } keys %$var; + } + elsif ($type eq 'list') { + $str .= " $_\n" for @$var; + } + else { + $str .= " $var\n"; + } + $str .= "\U$name$type\_end\n"; + } + $str +} +sub syncViewer { + if ($viewer{client}) { + @actString = (); + if ($currentLines) { + killOldStatus(); + $currentLines = 0; + } + my $str; + unless ($viewer{client_keymap}) { + $str .= _encode_var('key', +{ %nummap, %specialmap }); + $viewer{client_keymap} = 1; + } + unless ($viewer{client_settings}) { + $str .= _encode_var( + block => $S{block}, + ha => $S{height_adjust}, + mc => $S{maxcolumns}, + ml => $S{maxlines}, + tc => $S{true_colour}, + ); + $viewer{client_settings} = 1; + } + unless ($viewer{client_env}) { + $str .= _encode_var(irssienv => +{ + length $ENV{TMUX_PANE} && length $ENV{TMUX} ? + (tmux_pane => $ENV{TMUX_PANE}, + tmux_srv => $ENV{TMUX}) : (), + length $ENV{WINDOWID} ? + (xwinid => $ENV{WINDOWID}) : (), + }); + $viewer{client_env} = 1; + } + my $separator = _get_format(set 'separator'); + my $sepLen = sb_length($separator); + my $item_bg = _get_format(set 'viewer_item_bg'); + my $title = _get_format(set 'title'); + if (length $title) { + $title =~ s{\\(.)|(.)}{ + defined $2 ? quotemeta $2 + : $1 eq 'V' ? '\U' + : $1 eq ':' ? quotemeta '%N' + : $1 =~ /^[uUFQE]$/ ? "\\$1" + : quotemeta "\\$1" + }sge; + $title = eval qq{"$title"}; + } + $str .= _encode_var(redraw => 1) if delete $viewer{fullRedraw}; + $str .= _encode_var(separator => $separator, + seplen => $sepLen, + itembg => $item_bg, + title => $title, + mouse => $mouse_coords{refnum}, + key2 => \%wnmap_exp, + win => \@win_items); + + my $was = $viewer{client}->blocking(1); + $viewer{client}->print($str); + $viewer{client}->blocking($was); + } + elsif ($viewer{server}) { + if (defined $viewer{msg}) { + @actString = ((uc setc()).": $viewer{msg}"); + } + else { + @actString = (); + } + } + elsif (defined $viewer{msg}) { + @actString = ((uc setc()).": $viewer{msg}"); + } + if (@actString) { + Irssi::timeout_add_once(100, 'syncLines', undef); + } + elsif ($currentLines) { + killOldStatus(); + $currentLines = 0; + } +} + +sub reset_awl { + Irssi::timeout_remove($shade_line_timer) if $shade_line_timer; $shade_line_timer = undef; + my $was_sort = $S{sort} // ''; + my $was_xform = $S{xform} // ''; + my $was_shared = $S{shared_sbar}; + my $was_no_hint = $S{no_mode_hint}; + %S = ( + sort => Irssi::settings_get_str( set 'sort'), + fancy_abbrev => Irssi::settings_get_str('fancy_abbrev'), + xform => Irssi::settings_get_str( set 'custom_xform'), + block => Irssi::settings_get_int( set 'block'), + banned_on => Irssi::settings_get_bool('banned_channels_on'), + prefer_name => Irssi::settings_get_bool(set 'prefer_name'), + hide_data => Irssi::settings_get_int( set 'hide_data'), + hide_name => Irssi::settings_get_int( set 'hide_name_data'), + hide_empty => Irssi::settings_get_int( set 'hide_empty'), + detach => Irssi::settings_get_str( set 'detach'), + detach_data => Irssi::settings_get_int( set 'detach_data'), + detach_aht => Irssi::settings_get_bool(set 'detach_aht'), + sbar_maxlen => Irssi::settings_get_bool(set 'sbar_maxlength'), + placement => Irssi::settings_get_str( set 'placement'), + position => Irssi::settings_get_int( set 'position'), + maxlines => Irssi::settings_get_int( set 'maxlines'), + maxcolumns => Irssi::settings_get_int( set 'maxcolumns'), + all_disable => Irssi::settings_get_bool(set 'all_disable'), + height_adjust => Irssi::settings_get_int( set 'height_adjust'), + mouse_offset => Irssi::settings_get_int( set 'mouse_offset'), + mouse_scroll => Irssi::settings_get_int( 'mouse_scroll'), + mouse_escape => Irssi::settings_get_int( 'mouse_escape'), + line_shade => Irssi::settings_get_time(set 'last_line_shade'), + no_mode_hint => Irssi::settings_get_bool(set 'no_mode_hint'), + true_colour => Irssi::parse_special('$colors_ansi_24bit'), + viewer_launch => Irssi::settings_get_bool(set 'viewer_launch'), + viewer_launch_env => Irssi::settings_get_str(set 'viewer_launch_env'), + viewer_xwin_command => Irssi::settings_get_str(set 'viewer_xwin_command'), + viewer_custom_command => Irssi::settings_get_str(set 'viewer_custom_command'), + viewer_tmux_position => Irssi::settings_get_str(set 'viewer_tmux_position'), + ); + $S{fancy_strict} = $S{fancy_abbrev} =~ /^strict/i; + $S{fancy_head} = $S{fancy_abbrev} =~ /^head/i; + my $shared = Irssi::settings_get_str(set 'shared_sbar'); + if ($shared =~ /^(\d+)([<])(\d+)$/) { + $S{shared_sbar} = [$1, $2, $3]; + } + else { + Irssi::settings_set_str(set 'shared_sbar', 'OFF'); + $S{shared_sbar} = undef; + } + lock_keys(%S); + if ($was_sort ne $S{sort}) { + $print_text_activity = undef; + my @sort_order = grep { @$_ > 4 } map { + s/^\s*//; + my $reverse = s/^\W*\K[-!]//; + my $undef_check = s/^\W*\K~// ? 1 : undef; + my $equal_check = s/=(.*)\s?$// ? $1 : undef; + s/\s*$//; + my $ignore_case = s/#i$// ? 1 : undef; + + $print_text_activity = 1 if $_ eq 'last_line'; + + my @path = split '/'; + my $class_check = @path && $path[-1] =~ s/(::.*)$// ? $1 : undef; + my $lru = "@path" eq 'lru'; + + [ $reverse ? -1 : 1, $undef_check, $equal_check, $class_check, $ignore_case, $lru, @path ] + } "$S{sort}," =~ /([^+,]*|[^+,]*=[^,]*?\s(?=\+)|[^+,]*=[^,]*)[+,]/g; + $window_sort_func = sub { + no warnings qw(numeric uninitialized); + for my $so (@sort_order) { + my @x = map { + my $ret = 0; + $_ = lc1459($_) if defined $_ && !ref $_ && $so->[4]; + $ret = $_ eq ($so->[4] ? lc1459($so->[2]) : $so->[2]) ? 1 : -1 if defined $so->[2]; + $ret = defined $_ ? ($ret || -3) : 3 if $so->[1]; + $ret = ref $_ && $_->isa('Irssi'.$so->[3]) ? 2 : ($ret || -2) if $so->[3]; + -$ret || $_ + } + map { + $so->[5] ? $_->[0] : reduce { return unless ref $a; $a->{$b} } $_->[1], @{$so}[6..$#$so] + } $a, $b; + return ((($x[0] <=> $x[1] || $x[0] cmp $x[1]) * $so->[0]) || next); + } + return ($a->[1]{refnum} <=> $b->[1]{refnum}); + }; + } + if ($was_xform ne $S{xform}) { + if ($S{xform} !~ /\S/) { + $custom_xform = undef; + } + else { + my $script_pkg = __PACKAGE__ . '::custom_xform'; + local $@; + $custom_xform = eval qq{ +package $script_pkg; +use strict; +no warnings; +our (\$QUERY, \$CHANNEL, \$TAG, \$NAME); +return sub { +# line 1 @{[ set 'custom_xform' ]}\n$S{xform}\n}}; + if ($@) { + $@ =~ /^(.*)/; + print '%_'.(set 'custom_xform').'%_ did not compile: '.$1; + } + } + } + + my $new_settings = join "\n", $VIEWER_MODE + ? ("\\", $S{block}, $S{height_adjust}, $S{maxlines}, $S{maxcolumns}, $S{true_colour}) + : ("!", $S{placement}, $S{position}); + + my $first_viewer = $settings_str eq '1'; + if ($settings_str ne $new_settings) { + @actString = (); + %abbrev_cache = (); + $currentLines = 0; + killOldStatus(); + delete $viewer{client_settings}; + $settings_str = $new_settings; + } + + my $was_mouse_mode = $MOUSE_ON; + if ($MOUSE_ON = Irssi::settings_get_bool(set 'mouse') and !$was_mouse_mode) { + install_mouse(); + } + elsif ($was_mouse_mode and !$MOUSE_ON) { + uninstall_mouse(); + } + + unless ($first_viewer) { + my $path = Irssi::settings_get_str(set 'path'); + my $was_viewer_mode = $VIEWER_MODE; + if ($was_viewer_mode && + defined $viewer{path} && $viewer{path} ne $path) { + stop_viewer(); + $was_viewer_mode = 0; + } + elsif ($was_viewer_mode && $S{no_mode_hint} != $was_no_hint + 0) { + set_viewer_mode_hint(); + } + $viewer{path} = $path; + if ($VIEWER_MODE = Irssi::settings_get_bool(set 'viewer') and !$was_viewer_mode) { + start_viewer(); + } + elsif ($was_viewer_mode and !$VIEWER_MODE) { + stop_viewer(); + } + } + + %banned_channels = map { lc1459(as_uni($_)) => undef } + split ' ', Irssi::settings_get_str('banned_channels'); + + %detach_map = ($S{detach_aht} + ? (map { ( lc1459(as_uni($_)) => undef ) } + split ' ', Irssi::settings_get_str('activity_hide_targets')) : (), + (map { my ($k, $v) = (split /(?:,(-?\d+))$/, $_)[0, 1]; + ( lc1459(as_uni($k)) => $v ) } + split ' ', $S{detach})); + + my @sb_base = split /\177/, sb_format_expand("{sbstart}{sb \177}{sbend}"), 2; + $sb_base_width_pre = sb_length($sb_base[0]); + $sb_base_width_post = max 0, sb_length($sb_base[1])-1; + $sb_base_width = $sb_base_width_pre + $sb_base_width_post + 5; + + if ($print_text_activity && $S{line_shade}) { + $shade_line_timer = Irssi::timeout_add(max(10 * GLOB_QUEUE_TIMER, 100*$S{line_shade}**(1/3)), 'wl_changed', undef); + } + + $CHANGED{AWINS} = 1; +} + +sub hide_window { + my ($data) = @_; + my $ent; + + $data =~ s/\s*$//; + my $win = Irssi::active_win; + my $number = $win->{refnum}; + my $name = as_uni($win->{name}); + my $active = as_uni($win->get_active_name) // ''; + my $tag = $win->{active} && $win->{active}{server} ? as_uni($win->{active}{server}{tag}) // '' : ''; + if (length $name) { + $ent = "$name"; + } + elsif (length $tag && length $active) { + $ent = "$tag/$active"; + } + else { + $ent = "$number"; + } + + my $found = 0; + my @setting; + for my $s (split ' ', $S{detach}) { + my ($k, $v) = (split /(?:,(-?\d+))$/, $s)[0, 1]; + if (lc1459(as_uni($k)) eq lc1459($ent)) { + unless ($found) { + if ($data =~ /^(-?\d+)$/) { + $ent .= ",$1"; + } + if (defined $v && 0 == abs $v) { + $win->print("Hiding window $ent"); + } + push @setting, as_tc($ent); + $found = 1; + } + } + else { + push @setting, defined $v ? "$k,$v" : $k; + } + } + unless ($found) { + $win->print("Hiding window $ent"); + if ($data =~ /^(-?\d+)$/) { + $ent .= ",$1"; + } + push @setting, as_tc($ent); + } + + if (@setting) { + Irssi::command("^set ".(set 'detach')." @setting"); + } else { + Irssi::command("^set -clear ".(set 'detach')); + } +} + +sub unhide_window { + my ($data, $server, $witem) = @_; + my $win = Irssi::active_win; + my $number = $win->{refnum}; + my $name = as_uni($win->{name}); + my $active = as_uni($win->get_active_name) // ''; + my $tag = $win->{active} && $win->{active}{server} ? as_uni($win->{active}{server}{tag}) // '' : ''; + + my %detach_aht; + if ($S{detach_aht}) { + %detach_aht = (map { ( lc1459(as_uni($_)) => undef ) } + split ' ', Irssi::settings_get_str('activity_hide_targets')); + } + my @setting; + my @kills = (length $name ? $name : undef, + length $tag && length $active ? "$tag/$active" : undef, + length $active ? $active : undef, + $number); + my @was_unhidden = (0) x @kills; + for my $s (split ' ', $S{detach}) { + my ($k, $v) = (split /(?:,(-?\d+))$/, $s)[0, 1]; + my $k2 = lc1459(as_uni($k)); + my $kill; + for my $ki (0..$#kills) { + if (defined $kills[$ki] && $k2 eq lc1459($kills[$ki])) { + $kill = $ki; + } + } + + if (defined $kill) { + if (defined $v && 0 == abs $v) { + $was_unhidden[$kill] = 1; + push @setting, defined $v ? "$k,$v" : $k; + } else { + $win->print("Unhiding window $kills[$kill]"); + } + } + else { + push @setting, defined $v ? "$k,$v" : $k; + } + } + my @is_hidden = (defined $kills[0] && (exists $detach_map{"*"} || exists $detach_map{"::all"}), + defined $kills[1] && (exists $detach_map{lc1459("$tag/*")} || exists $detach_map{lc1459("$tag/::all")} + || exists $detach_map{"*"} || exists $detach_map{"::all"}), + defined $kills[2] && (exists $detach_map{"*"} || exists $detach_map{"::all"}), + (exists $detach_map{"*"} || exists $detach_map{"::all"}) + ); + for my $ki (1, 2, 0, 3) { + if ($is_hidden[$ki]) { + unless ($was_unhidden[$ki]) { + $win->print("Unhiding window $kills[$ki]"); + push @setting, "$kills[$ki],0"; + $was_unhidden[$ki] = 1; + } + last; + } + } + my @is_hidden_aht = (defined $kills[0] && (exists $detach_aht{lc1459($name)} + || exists $detach_aht{"*"} || exists $detach_aht{"::all"}), + defined $kills[1] && (exists $detach_aht{lc1459("$tag/$active")} + || exists $detach_aht{lc1459($active)} + || exists $detach_aht{lc1459("$tag/*")} || exists $detach_aht{lc1459("$tag/::all")} + || exists $detach_aht{"*"} || exists $detach_aht{"::all"}), + defined $kills[2] && (exists $detach_aht{lc1459($active)} + || exists $detach_aht{"*"} || exists $detach_aht{"::all"}), + (exists $detach_aht{$number} || exists $detach_aht{"*"} || exists $detach_aht{"::all"}) + ); + for my $ki (1, 2, 0, 3) { + if ($is_hidden_aht[$ki]) { + unless ($was_unhidden[$ki]) { + $win->print("Unhiding window $kills[$ki], it is hidden because ".(set 'detach_aht')." is ON"); + push @setting, "$kills[$ki],0"; + $was_unhidden[$ki] = 1; + } + last; + } + } + + if (@setting) { + Irssi::command("^set ".(set 'detach')." @setting"); + } else { + Irssi::command("^set -clear ".(set 'detach')); + } +} + +sub ack_window { + my ($data, $server, $witem) = @_; + my $win = Irssi::active_win; + my $number = $win->{refnum}; + if (grep { $_->{cmd} eq 'ack' } Irssi::commands) { + my $Orig_Irssi_windows = \&Irssi::windows; + local *Irssi::windows = sub () { grep { !_is_detached($_, $number) } $Orig_Irssi_windows->() }; + Irssi::command("ack" . (length $data ? " $data" : "")); + } else { + my $ignore_refnum = Irssi::settings_get_bool('active_window_ignore_refnum'); + my $max_win; + my $max_act = 0; + my $max_ref = 0; + for my $rec (Irssi::windows) { + next if _is_detached($rec, $number); + + # ignore refnum + if ($ignore_refnum && + $rec->{data_level} > 0 && $max_act < $rec->{data_level}) { + $max_act = $rec->{data_level}; + $max_win = $rec; + } + + # windows with lower refnums break ties + elsif (!$ignore_refnum && + $rec->{data_level} > 0 && + ($rec->{data_level} > $max_act || + ($rec->{data_level} == $max_act && $rec->{refnum} < $max_ref))) { + $max_act = $rec->{data_level}; + $max_win = $rec; + $max_ref = $rec->{refnum}; + } + } + $max_win->set_active if defined $max_win; + } +} + +sub refnum_changed { + my ($win, $old_refnum) = @_; + my @old_setting = split ' ', $S{detach}; + my @setting = map { + my ($k, $v) = (split /(?:,(-?\d+))$/, $_)[0, 1]; + if ($k eq $old_refnum) { + $win->{refnum} . (defined $v ? ",$v" : "") + } + else { + $_ + } + } @old_setting; + if ("@old_setting" ne "@setting") { + $S{detach} = "@setting"; + Irssi::settings_set_str(set 'detach', "@setting"); + &setup_changed; + } + else { + &wl_changed; + } +} + +sub window_destroyed { + my ($win) = @_; + my @old_setting = split ' ', $S{detach}; + my @setting = grep { + my ($k, $v) = (split /(?:,(-?\d+))$/, $_)[0, 1]; + if ($k eq $win->{refnum}) { + 0; + } + else { + 1; + } + } @old_setting; + if ("@old_setting" ne "@setting") { + $S{detach} = "@setting"; + Irssi::settings_set_str(set 'detach', "@setting"); + &setup_changed; + } + else { + &awins_changed; + } +} + +sub stop_mouse_tracking { + print STDERR "\e[?1005l\e[?1000l"; +} +sub start_mouse_tracking { + print STDERR "\e[?1000h\e[?1005h"; +} +sub install_mouse { + Irssi::command_bind('mouse_xterm' => 'mouse_xterm'); + Irssi::command('^bind meta-[M command mouse_xterm'); + Irssi::signal_add_first('gui key pressed' => 'mouse_key_hook'); + start_mouse_tracking(); +} +sub uninstall_mouse { + stop_mouse_tracking(); + Irssi::signal_remove('gui key pressed' => 'mouse_key_hook'); + Irssi::command('^bind -delete meta-[M'); + Irssi::command_unbind('mouse_xterm' => 'mouse_xterm'); +} + +sub awl_mouse_event { + return if $VIEWER_MODE; + if ((($_[0] == 3 and $_[3] == 0) + || $_[0] == 64 || $_[0] == 65) and + $_[1] == $_[4] and $_[2] == $_[5]) { + my $top = lc $S{placement} eq 'top'; + my ($pos, $line) = @_[1 .. 2]; + unless ($top) { + $line -= $screenHeight; + $line += $currentLines; + $line += $S{mouse_offset}; + } + else { + $line -= $S{mouse_offset}; + } + $pos -= $sb_base_width_pre; + return if $line < 0 || $line >= $currentLines; + if ($_[0] == 64) { + Irssi::command('window up'); + } + elsif ($_[0] == 65) { + Irssi::command('window down'); + } + elsif (exists $mouse_coords{$line}{$pos}) { + my $win = $mouse_coords{$line}{$pos}; + Irssi::command('window ' . $win); + } + Irssi::signal_stop; + } +} + +sub mouse_scroll_event { + return unless $S{mouse_scroll}; + if (($_[3] == 64 or $_[3] == 65) and + $_[0] == $_[3] and $_[1] == $_[4] and $_[2] == $_[5]) { + my $cmd = 'scrollback goto ' . ($_[3] == 64 ? '-' : '+') . $S{mouse_scroll}; + Irssi::active_win->command($cmd); + Irssi::signal_stop; + } + elsif ($_[0] == 64 or $_[0] == 65) { + Irssi::signal_stop; + } +} + +sub mouse_escape { + return unless $S{mouse_escape} > 0; + if ($_[0] == 3) { + my $tm = $S{mouse_escape}; + $tm *= 1000 if $tm < 1000; + stop_mouse_tracking(); + Irssi::timeout_add_once($tm, 'start_mouse_tracking', undef); + Irssi::signal_stop; + } +} + +sub UNLOAD { + @actString = (); + killOldStatus(); + stop_viewer() if $VIEWER_MODE; + uninstall_mouse() if $MOUSE_ON; +} + +sub addPrintTextHook { # update on print text + return unless defined $^S; + return if $BLOCK_ALL; + return unless $print_text_activity; + return if $_[0]->{level} == 262144 and $_[0]->{target} eq '' + and !defined($_[0]->{server}); + &wl_changed; +} + +sub block_event_window_change { + Irssi::signal_stop; +} + +sub update_awins { + my @wins = Irssi::windows; + local $BLOCK_ALL = 1; + Irssi::signal_add_first('window changed' => 'block_event_window_change'); + my $bwin = + my $awin = Irssi::active_win; + my $lwin; + my $defer_irssi_broken_last; + unless ($wins[0]{refnum} == $awin->{refnum}) { + # special case: more than 1 last win, so /win last; + # /win last doesn't come back to the current window. eg. after + # connect & autojoin; we can't handle this situation, bail out + $defer_irssi_broken_last = 1; + } + else { + $awin->command('window last'); + $lwin = Irssi::active_win; + $lwin->command('window last'); + $defer_irssi_broken_last = $lwin->{refnum} == $bwin->{refnum}; + } + my $awin_counter = 0; + Irssi::signal_remove('window changed' => 'block_event_window_change'); + unless ($defer_irssi_broken_last) { + # we need to keep the fe-windows code running here + Irssi::signal_add_priority('window changed' => 'block_event_window_change', -99); + %awins = %wnmap_exp = (); + do { + Irssi::active_win->command('window up'); + $awin = Irssi::active_win; + $awins{$awin->{refnum}} = undef; + ++$awin_counter; + } until ($awin->{refnum} == $bwin->{refnum} || $awin_counter >= @wins); + Irssi::signal_remove('window changed' => 'block_event_window_change'); + + Irssi::signal_add_first('window changed' => 'block_event_window_change'); + for my $key (keys %wnmap) { + next unless Irssi::window_find_name($key) || Irssi::window_find_item($key); + $awin->command("window goto $key"); + my $cwin = Irssi::active_win; + $wnmap_exp{ $cwin->{refnum} } = $wnmap{$key}; + $cwin->command('window last') + if $cwin->{refnum} != $awin->{refnum}; + } + for my $win (reverse @wins) { # restore original window order + Irssi::active_win->command('window '.$win->{refnum}); + } + $awin->command('window '.$lwin->{refnum}); # restore last win + Irssi::active_win->command('window last'); + Irssi::signal_remove('window changed' => 'block_event_window_change'); + } + $CHANGED{WL} = 1; +} + +sub resizeTerm { + if (defined (my $r = `stty size 2>/dev/null`)) { + ($screenHeight, $screenWidth) = split ' ', $r; + $CHANGED{SETUP} = 1; + } + else { + $CHANGED{SIZE} = 1; + } +} + +sub awl_refresh { + $globTime = undef; + resizeTerm() if delete $CHANGED{SIZE}; + reset_awl() if delete $CHANGED{SETUP}; + update_awins() if delete $CHANGED{AWINS}; + update_wl() if delete $CHANGED{WL}; +} + +sub termsize_changed { $CHANGED{SIZE} = 1; &queue_refresh; } +sub setup_changed { $CHANGED{SETUP} = 1; &queue_refresh; } +sub awins_changed { $CHANGED{AWINS} = 1; &queue_refresh; } +sub wl_changed { $CHANGED{WL} = 1; &queue_refresh; } + +sub window_changed { + &awins_changed if $_[1]; +} + +sub queue_refresh { + return if $BLOCK_ALL; + Irssi::timeout_remove($globTime) + if defined $globTime; # delay the update further + $globTime = Irssi::timeout_add_once(GLOB_QUEUE_TIMER, 'awl_refresh', undef); +} + +sub awl_init { + termsize_changed(); + setup_changed(); + update_keymap(); + Irssi::timeout_remove($globTime) + if defined $globTime; + awl_refresh(); + termsize_changed(); +} + +sub runsub { + my $cmd = shift; + sub { + my ($data, $server, $item) = @_; + Irssi::command_runsub($cmd, $data, $server, $item); + }; +} + +Irssi::signal_register({ + 'gui mouse' => [qw/int int int int int int/], + }); +{ my $broken_expandos = (Irssi::version >= 20081128 && Irssi::version < 20110210) + ? sub { my $x = shift; $x =~ s/\$\{cumode_space\}/ /; $x } : undef; + Irssi::theme_register([ + map { $broken_expandos ? $broken_expandos->($_) : $_ } + set 'display_nokey' => '$N${cumode_space}$H$C$S', + set 'display_key' => '$Q${cumode_space}$H$C$S', + set 'display_nokey_visible' => '%2$N${cumode_space}$H$C$S', + set 'display_key_visible' => '%2$Q${cumode_space}$H$C$S', + set 'display_nokey_active' => '%1$N${cumode_space}$H$C$S', + set 'display_key_active' => '%1$Q${cumode_space}$H$C$S', + set 'display_header' => '%8$C|${N}', + set 'name_display' => '$0', + set 'separator' => ' ', + set 'separator2' => '', + set 'abbrev_chars' => "~\x{301c}", + set 'viewer_item_bg' => sb_format_expand('{sb_background}'), + set 'title' => '\V'.setc().'\:', + ]); +} +Irssi::settings_add_bool(setc, set 'prefer_name', 0); # +Irssi::settings_add_int( setc, set 'hide_empty', 0); # +Irssi::settings_add_int( setc, set 'hide_data', 0); # +Irssi::settings_add_str( setc, set 'detach', ''); # +Irssi::settings_add_int( setc, set 'detach_data', -3); # +Irssi::settings_add_bool(setc, set 'detach_aht', 0); # +Irssi::settings_add_int( setc, set 'hide_name_data', 0); # +Irssi::settings_add_int( setc, set 'maxlines', 9); # +Irssi::settings_add_int( setc, set 'maxcolumns', 4); # +Irssi::settings_add_int( setc, set 'block', 15); # +Irssi::settings_add_bool(setc, set 'sbar_maxlength', 1); # +Irssi::settings_add_int( setc, set 'height_adjust', 2); # +Irssi::settings_add_str( setc, set 'sort', 'refnum'); # +Irssi::settings_add_str( setc, set 'placement', 'bottom'); # +Irssi::settings_add_int( setc, set 'position', 0); # +Irssi::settings_add_bool(setc, set 'all_disable', 1); # +Irssi::settings_add_bool(setc, set 'viewer', 1); # +Irssi::settings_add_str( setc, set 'shared_sbar', 'OFF'); # +Irssi::settings_add_bool(setc, set 'mouse', 0); # +Irssi::settings_add_str( setc, set 'path', Irssi::get_irssi_dir . '/_windowlist'); # +Irssi::settings_add_str( setc, set 'custom_xform', ''); # +Irssi::settings_add_time(setc, set 'last_line_shade', '0'); # +Irssi::settings_add_int( setc, set 'mouse_offset', 1); # +Irssi::settings_add_int( setc, 'mouse_scroll', 3); # +Irssi::settings_add_int( setc, 'mouse_escape', 1); # +Irssi::settings_add_str( setc, 'banned_channels', ''); +Irssi::settings_add_bool(setc, 'banned_channels_on', 1); +Irssi::settings_add_str( setc, 'fancy_abbrev', 'fancy'); # +Irssi::settings_add_bool(setc, set 'no_mode_hint', 0); # +Irssi::settings_add_bool(setc, set 'viewer_launch', 1); # +Irssi::settings_add_str( setc, set 'viewer_launch_env', ''); # +Irssi::settings_add_str( setc, set 'viewer_tmux_position', 'left'); # +Irssi::settings_add_str( setc, set 'viewer_xwin_command', 'xterm +sb -e %A'); # +Irssi::settings_add_str( setc, set 'viewer_custom_command', ''); # + +Irssi::signal_add_last({ + 'setup changed' => 'setup_changed', + 'print text' => 'addPrintTextHook', + 'terminal resized' => 'termsize_changed', + 'setup reread' => 'screenFullRedraw', + 'window hilight' => 'wl_changed', + 'command format' => 'wl_changed', +}); +Irssi::signal_add({ + 'window changed' => 'window_changed', + 'window item changed' => 'wl_changed', + 'window changed automatic' => 'window_changed', + 'window created' => 'awins_changed', + 'window destroyed' => 'window_destroyed', + 'window name changed' => 'wl_changed', + 'window refnum changed' => 'refnum_changed', +}); +Irssi::signal_add_last('gui mouse' => 'mouse_escape'); +Irssi::signal_add_last('gui mouse' => 'mouse_scroll_event'); +Irssi::signal_add_last('gui mouse' => 'awl_mouse_event'); +Irssi::command_bind( setc() => runsub(setc()) ); +Irssi::command_bind( setc() . ' redraw' => 'screenFullRedraw' ); +Irssi::command_bind( setc() . ' restart' => 'restartViewerServer' ); +Irssi::command_bind( setc() . ' attach' => 'unhide_window' ); +Irssi::command_bind( setc() . ' detach' => 'hide_window' ); +Irssi::command_bind( setc() . ' ack' => 'ack_window' ); + +{ + my $l = set 'shared'; + { + no strict 'refs'; + *{$l} = $awl_shared_empty; + } + Irssi::statusbar_item_register($l, '$0', $l); +} + +awl_init(); + +# Mouse script based on irssi mouse patch by mirage +{ my $mouse_status = -1; # -1:off 0,1,2:filling mouse_combo + my @mouse_combo; # 0:button 1:x 2:y + my @mouse_previous; # previous contents of mouse_combo + + sub mouse_xterm_off { + $mouse_status = -1; + } + sub mouse_xterm { + $mouse_status = 0; + Irssi::timeout_add_once(10, 'mouse_xterm_off', undef); + } + + sub mouse_key_hook { + my ($key) = @_; + if ($mouse_status != -1) { + if ($mouse_status == 0) { + @mouse_previous = @mouse_combo; + #if @mouse_combo && $mouse_combo[0] < 64; + } + $mouse_combo[$mouse_status] = $key - 32; + $mouse_status++; + if ($mouse_status == 3) { + $mouse_status = -1; + # match screen coordinates + $mouse_combo[1]--; + $mouse_combo[2]--; + Irssi::signal_emit('gui mouse', @mouse_combo[0 .. 2], @mouse_previous[0 .. 2]); + } + Irssi::signal_stop; + } + } +} + +sub string_LCSS { + my $str = join "\0", @_; + (sort { length $b <=> length $a } $str =~ /(?=(.+).*\0.*\1)/g)[0] +} + +# workaround for issue #271 +{ package Irssi::Nick } + +# workaround for issue #572 +@Irssi::UI::Exec::ISA = 'Irssi::Windowitem' + if Irssi::version >= 20140822 && Irssi::version <= 20161101 && !@Irssi::UI::Exec::ISA; + +UNITCHECK +{ package AwlViewer; + use strict; + use warnings; + no warnings 'redefine'; + use Encode; + use IO::Socket::UNIX; + use IO::Select; + use List::Util qw(max); + use constant BLOCK_SIZE => 1024; + use constant RECONNECT_TIME => 5; + + my $sockpath; + + our $VERSION = '0.8'; + + our ($got_int, $resized, $timeout); + + my %vars; + my (%c2w, @seqlist); + my %mouse_coords; + my (@mouse, @last_mouse); + my ($err, $sock, $loop); + my ($keybuf, $rcvbuf); + my @screen; + my ($screenHeight, $screenWidth); + my ($disp_update, $fs_open, $one_shot_integration, $one_shot_resize); + my $integration_position; + my $show_title_bar; + + sub connect_it { + $sock = IO::Socket::UNIX->new( + Type => SOCK_STREAM, + Peer => $sockpath, + ); + unless ($sock) { + $err = $!; + return; + } + $sock->blocking(0); + $loop->add($sock); + } + + sub remove_conn { + my $fh = shift; + $loop->remove($fh); + $fh->close; + $sock = undef; + %vars = (); + @screen = (); + } + + { package Terminfo; # xterm + sub civis { "\e[?25l" } + sub sc { "\e7" } + sub cup { "\e[" . ($_[0] + 1) . ';' . ($_[1] + 1) . 'H' } + sub el { "\e[K" } + sub rc { "\e8" } + sub cnorm { "\e[?25h" } + sub setab { "\e[4" . $_[0] . 'm' } + sub setaf { "\e[3" . $_[0] . 'm' } + sub setaf16 { "\e[9" . $_[0] . 'm' } + sub setab16 { "\e[10" . $_[0] . 'm' } + sub setaf256 { "\e[38;5;" . $_[0] . 'm' } + sub setab256 { "\e[48;5;" . $_[0] . 'm' } + sub setafrgb { "\e[38;2;" . $_[0] . ';' . $_[1] . ';' . $_[2] . 'm' } + sub setabrgb { "\e[48;2;" . $_[0] . ';' . $_[1] . ';' . $_[2] . 'm' } + sub sgr0 { "\e[0m" } + sub bold { "\e[1m" } + sub it { "\e[3m" } + sub ul { "\e[4m" } + sub blink { "\e[5m" } + sub rev { "\e[7m" } + sub op { "\e[39;49m" } + sub exit_bold { "\e[22m" } + sub exit_it { "\e[23m" } + sub exit_ul { "\e[24m" } + sub exit_blink { "\e[25m" } + sub exit_rev { "\e[27m" } + sub smcup { "\e[?1049h" } + sub rmcup { "\e[?1049l" } + sub smmouse { "\e[?1000h\e[?1005h" } + sub rmmouse { "\e[?1005l\e[?1000l" } + } + + sub init { + $sockpath = shift // "$ENV{HOME}/.irssi/_windowlist"; + STDOUT->autoflush(1); + printf "\r%swaiting for %s...", Terminfo::sc, $::IRSSI{name}; + + `stty -icanon -echo`; + + $loop = IO::Select->new; + STDIN->blocking(0); + $loop->add(\*STDIN); + + $SIG{INT} = sub { + $got_int = 1 + }; + $SIG{WINCH} = sub { + $resized = 1 + }; + + $resized = 3; + + $disp_update = 2; + + $show_title_bar = 1; + } + + sub enter_fs { + return if $fs_open; + safe_print(Terminfo::rc, Terminfo::smcup, Terminfo::civis, Terminfo::smmouse); + $fs_open = 1; + } + + sub leave_fs { + return unless $fs_open; + safe_print(Terminfo::rmmouse, Terminfo::cnorm, Terminfo::rmcup); + safe_print(sprintf "\r%swaiting for %s...", Terminfo::sc, $::IRSSI{name}) if $_[0]; + + $fs_open = 0; + } + + sub end_prog { + leave_fs(); + STDIN->blocking(1); + `stty sane`; + printf "\r%s%sthanks for using %s\n", Terminfo::rc, Terminfo::el, $::IRSSI{name}; + } + + sub safe_print { + my $st = STDIN->blocking(1); + print @_; + STDIN->blocking($st); + } + + sub safe_qx { + my $st = STDIN->blocking(1); + my $ret = `$_[0]`; + STDIN->blocking($st); + $ret + } + + sub safe_print_sock { + return unless $sock; + my $was = $sock->blocking(1); + $sock->print(@_); + $sock->blocking($was); + } + + sub process_recv { + my $need = 0; + while ($rcvbuf =~ s/\n(.+)_BEGIN\n((?: .*\n)*)\1_END\n//) { + my $var = lc $1; + my $data = $2; + my @data = split "\n ", "\n$data ", -1; + shift @data; pop @data; + my $itembg = $vars{itembg}; + if ($var =~ s/list$//) { + $vars{$var} = \@data; + } + elsif ($var =~ s/map$//) { + $vars{$var} = +{ @data }; + } + else { + $vars{$var} = join "\n", @data; + } + $need = 1 if $var eq 'win'; + $need = 1 if $var eq 'redraw' && $vars{$var}; + if (($itembg//'') ne ($vars{itembg}//'')) { + $need = $vars{redraw} = 1; + } + _build_keymap() if $var eq 'key2'; + } + $need + } + + { my %ansi_table; + my ($i, $j, $k) = (0, 0, 0); + my %term_state; + sub reset_term_state { my %old_term = %term_state; %term_state = (); %old_term } + sub set_term_state { my %old_term = %term_state; %term_state = @_; %old_term } + %ansi_table = ( + # fe-common::core::formats.c:format_expand_styles + (map { my $t = $i++; ($_ => sub { my $n = $term_state{hicolor} ? \&Terminfo::setab16 : \&Terminfo::setab; + $n->($t) }) } (split //, '01234567' )), + (map { my $t = $j++; ($_ => sub { my $n = $term_state{hicolor} ? \&Terminfo::setaf16 : \&Terminfo::setaf; + $n->($t) }) } (split //, 'krgybmcw' )), + (map { my $t = $k++; ($_ => sub { my $n = $term_state{hicolor} ? \&Terminfo::setaf : \&Terminfo::setaf16; + $n->($t) }) } (split //, 'KRGYBMCW')), + # reset + n => sub { $term_state{hicolor} = 0; my $r = Terminfo::op; + for (qw(blink rev bold)) { + $r .= Terminfo->can("exit_$_")->() if delete $term_state{$_}; + } + { + local $ansi_table{n} = $ansi_table{N}; + $r .= formats_to_ansi_basic($vars{itembg}); + } + $r + }, + N => sub { reset_term_state(); Terminfo::sgr0 }, + # flash/bright + F => sub { my $n = 'blink'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # reverse + 8 => sub { my $n = 'rev'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # bold + "_" => sub { my $n = 'bold'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # underline + U => sub { my $n = 'ul'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # italic + I => sub { my $n = 'it'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # bold, used as colour modifier if AWL_HI9 is set + 9 => $ENV{AWL_HI9} ? sub { $term_state{hicolor} ^= 1; '' } + : sub { my $n = 'bold'; my $e = ($term_state{$n} ^= 1) ? $n : "exit_$n"; Terminfo->can($e)->() }, + # delete other stuff + (map { $_ => sub { '' } } (split //, ':|>#[')), + # escape + (map { my $close = $_; $_ => sub { $close } } (split //, '{}%')), + ); + for my $base (0 .. 15) { + my $close = $base; + my $idx = ($close&8) | ($close&4)>>2 | ($close&2) | ($close&1)<<2; + $ansi_table{ (sprintf "x0%x", $close) } = + $ansi_table{ (sprintf "x0%X", $close) } = + sub { Terminfo::setab256($idx) }; + $ansi_table{ (sprintf "X0%x", $close) } = + $ansi_table{ (sprintf "X0%X", $close) } = + sub { Terminfo::setaf256($idx) }; + } + for my $plane (1 .. 6) { + for my $coord (0 .. 35) { + my $close = 16 + ($plane-1) * 36 + $coord; + my $ch = $coord < 10 ? $coord : chr( $coord - 10 + ord 'a' ); + $ansi_table{ "x$plane$ch" } = + $ansi_table{ "x$plane\U$ch" } = + sub { Terminfo::setab256($close) }; + $ansi_table{ "X$plane$ch" } = + $ansi_table{ "X$plane\U$ch" } = + sub { Terminfo::setaf256($close) }; + } + } + for my $gray (0 .. 23) { + my $close = 232 + $gray; + my $ch = chr( $gray + ord 'a' ); + $ansi_table{ "x7$ch" } = + $ansi_table{ "x7\U$ch" } = + sub { Terminfo::setab256($close) }; + $ansi_table{ "X7$ch" } = + $ansi_table{ "X7\U$ch" } = + sub { Terminfo::setaf256($close) }; + } + # fe-windows.c:color_24bit_256 + my $cc = sub { + use integer; + + my $cstep_size = 40; + my $cstep_start = 0x5f; + + my $gstep_size = 10; + my $gstep_start = 0x08; + + my @dist = (0) x 3; + my @r; my @gr; + + for (my $i = 0; $i < 3; ++$i) { + my $n = $_[$i]; + $gr[$i] = -1; + if ($n < $cstep_start /2) { + $r[$i] = 0; + $dist[$i] = -$cstep_size/2; + } + else { + $r[$i] = 1+(($n-$cstep_start + $cstep_size /2)/$cstep_size); + $dist[$i] = (($n-$cstep_start + $cstep_size /2)% $cstep_size - $cstep_size/2); + } + if ($n < $gstep_start /2) { + $gr[$i] = -1; + } + else { + $gr[$i] = (($n-$gstep_start + $gstep_size /2)/$gstep_size); + } + } + if ($r[0] == $r[1] && $r[1] == $r[2] && + 4*abs($dist[0]) < $gstep_size && 4*abs($dist[1]) < $gstep_size && 4*abs($dist[2]) < $gstep_size) { + # skip gray detection + } + else { + my $j = $r[1] == $r[2] ? 0 : 1; + if (($r[0] == $r[1] || $r[$j] == $r[2]) && abs($r[$j]-$r[($j+1)% 3]) <= 1) { + my $k = $gr[1] == $gr[2] ? 0 : 1; + if (($gr[0] == $gr[1] || $gr[$k] == $gr[2]) && abs($gr[$k]-$gr[($k+1)% 3]) <= 2) { + if ($gr[$k] < 0) { + $r[0] = $r[1] = $r[2] = 0; + } + elsif ($gr[$k] > 23) { + $r[0] = $r[1] = $r[2] = 5; + } + else { + $r[0] = 6; + $r[1] = ($gr[$k] / 6); + $r[2] = $gr[$k]% 6; + } + } + } + } + return 16 + $r[0]*36 + $r[1] * 6 + $r[2]; + }; + $ansi_table{z} = sub { + my ($r, $g, $b) = map { hex } unpack '(A2)*', $_[0]; + $vars{tc} eq 'ON' ? Terminfo::setabrgb($r, $g, $b) : Terminfo::setab256($cc->($r, $g, $b)); + }; + $ansi_table{Z} = sub { + my ($r, $g, $b) = map { hex } unpack '(A2)*', $_[0]; + $vars{tc} eq 'ON' ? Terminfo::setafrgb($r, $g, $b) : Terminfo::setaf256($cc->($r, $g, $b)); + }; + sub formats_to_ansi_basic { + my $o = shift; + $o =~ s{(%((Z|z)(......)|X..|x..|.))}{ + if ($ansi_table{$2}) { $ansi_table{$2}->() } + elsif ($ansi_table{$3}) { $ansi_table{$3}->($4) } + else { $1 } + }gex; + $o + } + } + + sub _header { + my $str = $vars{title} // uc ::setc(); + my $ccs = qr/%(?:Z(?:[0-9A-F]{6})|X(?:[1-6][0-9A-Z]|7[A-X])|[0-9BCFGIKMNRUWY_])/i; + (my $stripstr = $str) =~ s/($ccs)//g; + my $space = int( ((abs $vars{block}) - length $stripstr) / (1 + length $stripstr)); + if ($space > 0) { + my $ss = ' ' x $space; + my @x = $str =~ /((?:$ccs)*\X(?:(?:$ccs)*$)?)/g; + $str = join $ss, '', @x, ''; + } + ($stripstr = $str) =~ s/($ccs)//g; + my $pad = max 0, (abs $vars{block}) - length $stripstr; + $str = ' ' x ($pad/2) . $str . ' ' x ($pad/2 + $pad%2); + $str + } + + sub _add_item { + my ($i, $j, $c, $wi, $screen, $mouse) = @_; + $screen->[$i][$j] = "%N%n$wi"; + if (exists $vars{mouse}{$c - 1}) { + $mouse->[$i][$j] = $vars{mouse}{$c - 1}; + } + } + sub update_screen { + $disp_update = 0; + unless ($sock && exists $vars{seplen} && exists $vars{block}) { + leave_fs(1); + return; + } + enter_fs(); + @screen = () if delete $vars{redraw}; + %mouse_coords = (); + my $ncols = ($vars{seplen} + abs $vars{block}) ? + int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ) : 0; + my $xenl = ($vars{seplen} + abs $vars{block}) + && $ncols > int( ($screenWidth + $vars{seplen} - 1) / ($vars{seplen} + abs $vars{block}) ); + my $nrows = $screenHeight - $vars{ha}; + my @wi = @{$vars{win}//[]}; + my $max_items = $ncols * $nrows; + my $c = $show_title_bar ? 1 : 0; + my $items = @wi + $c; + my $titems = $items > $max_items ? $max_items : $items; + my $i = 0; + my $j = 0; + my @new_screen; + my @new_mouse; + $new_screen[0][0] = _header() #. ' ' x $vars{seplen} + if $show_title_bar; + unless ($nrows > $ncols) { # line layout + ++$j if $show_title_bar; + for my $wi (@wi) { + if ($j >= $ncols) { + $j = 0; + ++$i; + } + last if $i >= $nrows; + _add_item($i, $j, $show_title_bar ? $c : $c + 1, + $wi, \@new_screen, \@new_mouse); + if ($c + 1 < $titems && $j + 1 < $ncols) { + $new_screen[$i][$j] .= $vars{separator}; + } + ++$j; + ++$c; + } + } + else { # column layout + ++$i if $show_title_bar; + for my $wi (@wi) { + if ($i >= $nrows) { + $i = 0; + ++$j; + } + last if $j >= $ncols; + _add_item($i, $j, $show_title_bar ? $c : $c + 1, + $wi, \@new_screen, \@new_mouse); + if ($c + $nrows < $titems) { + $new_screen[$i][$j] .= $vars{separator}; + } + ++$i; + ++$c; + } + } + my $step = $vars{seplen} + abs $vars{block}; + $i = 0; + my $str = Terminfo::sc . Terminfo::sgr0; + for (my $i = 0; $i < @new_screen; ++$i) { + for (my $j = 0; $j < @{$new_screen[$i]}; ++$j) { + if (defined $new_mouse[$i] && defined $new_mouse[$i][$j]) { + my $from = $j * $step; + $mouse_coords{$i}{$_} = $new_mouse[$i][$j] + for $from .. $from + abs $vars{block}; + } + next if defined $screen[$i] && defined $screen[$i][$j] + && $screen[$i][$j] eq $new_screen[$i][$j]; + $str .= Terminfo::cup($i, $j * $step) + . formats_to_ansi_basic($new_screen[$i][$j]) + . Terminfo::sgr0; + $str .= Terminfo::el if $j == $#{$new_screen[$i]} && (!$xenl || $j + 1 != $ncols); + } + } + for (@new_screen .. $screenHeight - 1) { + if (!@screen || defined $screen[$_]) { + $str .= Terminfo::cup($_, 0) . Terminfo::sgr0 . Terminfo::el; + } + } + $str .= Terminfo::rc; + safe_print $str; + @screen = @new_screen; + } + + sub handle_resize { + if (defined (my $r = safe_qx('stty size'))) { + ($screenHeight, $screenWidth) = split ' ', $r; + $resized = 0; + @screen = (); + $disp_update = 1; + if ($one_shot_integration == 2) { + $one_shot_resize--; + } + } + else { + } + } + + sub _build_keymap { + %c2w = reverse( %{$vars{key}}, %{$vars{key2}} ); + if (!grep { /^[+-]./ } keys %c2w) { + %c2w = (%c2w, map { ("-$_" => $c2w{$_}) } grep { !/^\^./ } keys %c2w); + } + %c2w = map { + my $key = $_; + s{^(-)?(\+)?(\^)?(.)}{ + join '', ( + ($1 ? "\e" : ''), + ($2 ? "\e\e" : ''), + ($3 ? "$4"^"@" : $4) + ) + }e; + $_ => $c2w{$key} + } keys %c2w; + @seqlist = sort { length $b <=> length $a } keys %c2w; + } + + sub _match_tmux { + length $ENV{TMUX} && exists $vars{irssienv}{tmux_srv} && length $vars{irssienv}{tmux_pane} + && $ENV{TMUX} eq $vars{irssienv}{tmux_srv} + } + + sub process_keys { + Encode::_utf8_on($keybuf); + my $win; + my $use_mouse; + my $maybe; + KEY: while (length $keybuf && !$maybe) { + $maybe = 0; + if ($keybuf =~ s/^\e\[M(.)(.)(.)//) { + @last_mouse = @mouse;# if @mouse && $mouse[0] < 64; + @mouse = map { -32 + ord } ($1, $2, $3); + $use_mouse = 1; + next KEY; + } + for my $s (@seqlist) { + if ($keybuf =~ s/^\Q$s//) { + $win = $c2w{$s}; + $use_mouse = 0; + next KEY; + } + elsif (length $keybuf < length $s && $s =~ /^\Q$keybuf/) { + $maybe = 1; + } + } + unless ($maybe) { + substr $keybuf, 0, 1, ''; + } + } + if ($use_mouse && @mouse && @last_mouse && + $mouse[2] == $last_mouse[2] && + $mouse[1] == $last_mouse[1] && + ($mouse[0] == 3 || $mouse[0] == 64 || $mouse[0] == 65)) { + if ($mouse[0] == 64) { + $win = 'up'; + } + elsif ($mouse[0] == 65) { + $win = 'down'; + } + elsif (exists $mouse_coords{$mouse[2] - 1}{$mouse[1] - 1}) { + $win = $mouse_coords{$mouse[2] - 1}{$mouse[1] - 1}; + } + elsif ($mouse[2] == 1 && $mouse[1] <= abs $vars{block}) { + $win = $last_mouse[0] != 0 ? 'last' : 'active'; + } + else { + } + } + if (defined $win) { + $win =~ s/^_//; + safe_print_sock("$win\n"); + if (!exists $ENV{AWL_AUTOFOCUS} || $ENV{AWL_AUTOFOCUS}) { + if (_match_tmux()) { + safe_qx("tmux selectp -t $vars{irssienv}{tmux_pane} 2>&1"); + } + elsif (exists $vars{irssienv}{xwinid}) { + safe_qx("wmctrl -ia $vars{irssienv}{xwinid} 2>/dev/null"); + } + } + } + Encode::_utf8_off($keybuf); + } + + sub check_integration { + return unless $vars{irssienv}; + return unless $sock && exists $vars{seplen} && exists $vars{block}; + if ($one_shot_integration == 1) { + my $nrows = $screenHeight - $vars{ha}; + my $ncols = ($vars{seplen} + abs $vars{block}) ? int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ) : 0; + my $items = ($show_title_bar ? 1 : 0) + @{$vars{win}//[]}; + my $dcols_required = $nrows ? int($items/$nrows) + !!($items%$nrows) : 0; + my $rows_required = $ncols ? int($items/$ncols) + !!($items%$ncols) : 0; + $rows_required = abs $vars{ml} + if ($vars{ml} < 0 || ($vars{ml} > 0 && $rows_required > $vars{ml})); + $dcols_required = abs $vars{mc} + if ($vars{mc} < 0 || ($vars{mc} > 0 && $dcols_required > $vars{mc})); + my $rows = $rows_required + $vars{ha}; + my $cols = ($dcols_required * ($vars{seplen} + abs $vars{block})) - $vars{seplen}; + if (_match_tmux()) { + # int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ); + my ($pos_flag, $before); + if ($integration_position eq 'left') { + $pos_flag = 'h'; + $before = 1; + } + elsif ($integration_position eq 'top') { + $pos_flag = 'v'; + $before = 1; + } + elsif ($integration_position eq 'right') { + $pos_flag = 'h'; + } + else { + $pos_flag = 'v'; + } + my @cmd = "joinp -d$pos_flag -s $ENV{TMUX_PANE} -t $vars{irssienv}{tmux_pane}"; + push @cmd, "swapp -d -t $ENV{TMUX_PANE} -s $vars{irssienv}{tmux_pane}" + if $before; + $cols = max($cols, 2); + $rows = max($rows, 2); + + safe_qx("tmux " . (join " \\\; ", @cmd) . " 2>&1"); + } + else { + $resized = 1; + #safe_qx("resize -s $screenHeight $cols 2>&1") + # if $cols > 0; + } + $one_shot_integration++; + if ($resized == 1) { + handle_resize(); + resize_integration(); + } + } + elsif ($one_shot_integration == 2) { + resize_integration(1); + } + } + + sub resize_integration { + return unless $one_shot_integration; + return unless ($one_shot_resize//0) < 0 || shift; + return if ($one_shot_resize//0) > 0; + + my $nrows = $screenHeight - $vars{ha}; + my $ncols = ($vars{seplen} + abs $vars{block}) ? int( ($screenWidth + $vars{seplen}) / ($vars{seplen} + abs $vars{block}) ) : 0; + my $items = ($show_title_bar ? 1 : 0) + @{$vars{win}//[]}; + my $dcols_required = $nrows ? (int($items/$nrows) + !!($items%$nrows)) : 0; + my $rows_required = $ncols ? int($items/$ncols) + !!($items%$ncols) : 0; + $rows_required = abs $vars{ml} + if ($vars{ml} < 0 || ($vars{ml} > 0 && $rows_required > $vars{ml})); + $dcols_required = abs $vars{mc} + if ($vars{mc} < 0 || ($vars{mc} > 0 && $dcols_required > $vars{mc})); + my $rows = $rows_required + $vars{ha}; + my $cols = ($dcols_required * ($vars{seplen} + abs $vars{block})) - $vars{seplen}; + if (_match_tmux()) { + my $pos_flag; + my $before = 0; + if ($integration_position eq 'left') { + $pos_flag = 'h'; + $before = 1; + } + elsif ($integration_position eq 'top') { + $pos_flag = 'v'; + $before = 1; + } + elsif ($integration_position eq 'right') { + $pos_flag = 'h'; + } + else { + $pos_flag = 'v'; + } + my @cmd; + # hard tmux limits + $cols = max($cols, 2); + $rows = max($rows, 2); + if ($pos_flag eq 'h' && $cols != $screenWidth) { + my $change = $screenWidth - $cols; + my $dir = ($before ^ ($change<0)) ? 'L' : 'R'; + push @cmd, "resizep -$dir -t $ENV{TMUX_PANE} @{[abs $change]}"; + #push @cmd, "resizep -x $cols -t $ENV{TMUX_PANE}"; + $one_shot_resize = 1; + } + if ($pos_flag eq 'v' && $rows != $screenHeight) { + #push @cmd, "resizep -y $rows -t $ENV{TMUX_PANE}"; + my $change = $screenHeight - $rows; + my $dir = ($before ^ ($change<0)) ? 'U' : 'D'; + push @cmd, "resizep -$dir -t $ENV{TMUX_PANE} @{[abs $change]}"; + $one_shot_resize = 1; + } + + safe_qx("tmux " . (join " \\\; ", @cmd) . " 2>&1") + if @cmd; + } + else { + $cols = max($cols, 1); + $rows = max($rows, 1); + unless ($nrows > $ncols) { # line layout + if ($rows != $screenHeight) { + safe_qx("resize -s $rows $screenWidth 2>&1"); + $one_shot_resize = 1; + } + } + else { + if ($cols != $screenWidth) { + safe_qx("resize -s $screenHeight $cols 2>&1"); + $one_shot_resize = 1; + } + } + } + if ($resized == 1) { + handle_resize(); + } + } + + sub init_integration { + return unless $one_shot_integration; + if (_match_tmux()) { + } + else { + } + safe_print("\e]2;".(uc ::setc())."\e\\"); + } + + sub main { + require Getopt::Std; + my %opts; + Getopt::Std::getopts('1p:', \%opts); + my $one_shot = $opts{1}; + $integration_position = $opts{p}; + $one_shot_integration = 0+!!$one_shot; + #shift if @_ && $_[0] eq '--'; + &init; + $show_title_bar = 0 if $ENV{AWL_NOTITLE}; + init_integration(); + until ($got_int) { + $timeout = undef; + if ($resized) { + if ($resized == 1) { + $timeout = 1; + $resized++; + } + else { + handle_resize(); + resize_integration(); + } + } + unless ($sock || $timeout) { + connect_it(); + } + $timeout ||= RECONNECT_TIME unless $sock; + update_screen() if $disp_update; + SELECT: while (my @read = $loop->can_read($timeout)) { + for my $fh (@read) { + if ($fh == \*STDIN) { + if (read STDIN, my $buf, BLOCK_SIZE) { + do { + $keybuf .= $buf; + } while read STDIN, $buf, BLOCK_SIZE; + } + else { + $got_int = 1; + last SELECT; + } + } + else { + if ($fh->read(my $buf, BLOCK_SIZE)) { + do { + $rcvbuf .= $buf; + } while $fh->read($buf, BLOCK_SIZE); + } + else { + $disp_update = 1; + remove_conn($fh); + if ($one_shot) { + $got_int = 1; + last SELECT; + } + $timeout ||= RECONNECT_TIME; + } + } + } + $disp_update |= process_recv() if length $rcvbuf; + process_keys() if length $keybuf; + check_integration() if $one_shot; + update_screen() if $disp_update; + } + continue { + } + } + end_prog(); + } +} + +1; + +# Changelog +# ========= +# 1.9 +# - add %Z support to viewer +# +# 1.8 +# - use string_width in Irssi 1.2.0 +# +# 1.7 +# - fix crash on invalid /set awl_sort, introduced in 1.6, reported by +# tpetazzoni +# - delay viewer initialisation +# - improve race condition on tmux resize integration +# +# 1.6 +# - add detach setting to hide windows +# - fix race condition when loading the script, reported by madduck +# - improve compatibility with irssi 1.2 +# - add special value lru to awl_sort to sort windows by usage +# +# 1.5 +# - improve compat. with sideways splits +# +# 1.4 +# - fix line wrapping in some themes, reported by justanotherbody +# - fix named window key detection, reported by madduck +# - make title (in viewer and shared_sbar) configurable +# +# 1.3 +# - workaround for irssi issue #572 +# +# 1.2 +# - new format to choose abbreviation character +# +# 1.1 +# - infinite loop on shortening certain window names reported by Kalan +# +# 1.0 +# - new awl_viewer_launch setting and an array of related settings +# - fixed regression bug /exec -interactive +# - fixed some warnings in perl 5.10 reported by kl3 +# - workaround for crash due to infinite recursion in irssi's Perl +# error handling +# +# 0.9 +# - fix endless loop in awin detection code! +# - correct colour swap in awl_viewer +# - fix passing of alternate socket path to the viewer +# - potential undefinedness in mouse refnum hinted at by Canopus +# - fixed regression bug /exec -interactive +# - add case-insensitive modifier to awl_sort +# - run custom_xform on awl_prefer_name also +# - avoid inconsistent active window state after awin detection +# reported by ss +# - revert %9-hack in the viewer prompted by discussion with pierrot +# - fix new warning in perl 5.22 +# +# 0.8 +# - replace fifo mode with external viewer script +# - remove bundled cpan modules +# - work around bogus irssi warning +# - improve mouse support +# - workaround for broken cumode in irssi 0.8.15 +# - fix handling of non-meta windows (uninitialized warning) +# - add 256 colour support, strip true colour codes +# - fix totally bogus $N padding reported by Ed S. +# - make /window goto #name mappings work but ignore non-existant ones +# - improve incomplete reads reported by bcode +# - fix single % in awl_viewer reported by bcode +# - add support for key bindings by nike and ferret +# - coerce utf8 key binds +# - add settings: custom_xform, last_line_shade, hide_name_data +# - abbreviations were broken in some cases +# - fix some misuse of / as cmdchar in mouse script reported by bcode +# - add shared status bar mode +# - ${type} variables for custom_xform setting +# - crash if custom_xform had runtime error +# - update sorting documentation +# - fix odd case in size calculation noted by lasers +# - add missing font styles to the viewer reported by ishanyx +# - add italic +# +# 0.7g +# - remove screen support and replace it with fifo support +# - add double-width support to the shortener +# - correct documentation regarding $T vs. display_header +# - add missing refresh for window item changed (thanks vague) +# - add visible windows +# - add exemptions for active window +# - workaround for hiding the window changes from trackbar +# - hack to force 16colours in screen mode +# - remember last window (reported by earthnative) +# - wrong window focus on new queries (reported by emsid) +# - dataloss bug on trying to remember last window +# +# 0.6d+ +# - add support for network headers +# - fixed regression bug /exec -interactive +# +# 0.6ca+ +# - add screen support (from nicklist.pl) +# - names can now have a max length and window names can be used +# - fixed a bug with block display in screen mode and status bar mode +# - added space handling to ir_fe and removed it again +# - now handling formats on my own +# - started to work on $tag display +# - added warning about missing sb_act_none abstract leading to +# - display*active settings +# - added warning about the bug in awl_display_(no)key_active settings +# - mouse hack +# +# 0.5d +# - add setting to also hide the last status bar if empty (awl_all_disable) +# - reverted to old utf8 code to also calculate broken utf8 length correctly +# - simplified dealing with status bars in wlreset +# - added a little tweak for the renamed term_type somewhere after Irssi 0.8.9 +# - fixed bug in handling channel #$$ +# - reset background colour at the beginning of an entry +# +# 0.4d +# - fixed order of disabling status bars +# - several attempts at special chars, without any real success +# and much more weird new bugs caused by this +# - setting to specify sort order +# - reduced timeout values +# - added awl_hide_data +# - make it so the dynamic sub is actually deleted +# - fix a bug with removing of the last separator +# - take into consideration parse_special +# +# 0.3b +# - automatically kill old status bars +# - reset on /reload +# - position/placement settings +# +# 0.2 +# - automated retrieval of key bindings (thanks grep.pl authors) +# - improved removing of status bars +# - got rid of status chop +# +# 0.1 +# - Based on chanact.pl which was apparently based on lightbar.c and +# nicklist.pl with various other ideas from random scripts. diff --git a/home/.irssi/scripts/autorun/bitlbee_tab_completion.pl b/home/.irssi/scripts/autorun/bitlbee_tab_completion.pl new file mode 100644 index 0000000..8d19128 --- /dev/null +++ b/home/.irssi/scripts/autorun/bitlbee_tab_completion.pl @@ -0,0 +1,88 @@ +use strict; +use vars qw($VERSION %IRSSI); + +$VERSION = '1.3'; + +%IRSSI = ( + authors => 'Tijmen "timing" Ruizendaal & Wilmer van der Gaast', + contact => 'tijmen.ruizendaal@gmail.com', + name => 'BitlBee_tab_completion', + description => 'Intelligent Tab-completion for BitlBee commands.', + license => 'GPLv2', + url => 'http://the-timing.nl/stuff/irssi-bitlbee', + changed => '2009-08-11', +); + +my $root_nick = 'root'; +my $bitlbee_channel = '&bitlbee'; +my $bitlbee_server_tag = 'localhost'; +my $get_completions = 0; + +my @commands; + +Irssi::signal_add_last 'channel sync' => sub { + my( $channel ) = @_; + if( $channel->{topic} eq "Welcome to the control channel. Type \x02help\x02 for help information." ){ + $bitlbee_server_tag = $channel->{server}->{tag}; + $bitlbee_channel = $channel->{name}; + request_completions(); + } +}; + +if (get_channel()) { + request_completions(); +} + +sub request_completions { + $get_completions = 1; + Irssi::server_find_tag($bitlbee_server_tag)->send_raw( 'COMPLETIONS' ); +} + +sub get_channel { + my @channels = Irssi::channels(); + foreach my $channel(@channels) { + if ($channel->{topic} eq "Welcome to the control channel. Type \x02help\x02 for help information.") { + $bitlbee_channel = $channel->{name}; + $bitlbee_server_tag = $channel->{server}->{tag}; + return 1; + } + } + return 0; +} + +sub irc_notice { + return unless $get_completions; + my( $server, $msg, $from, $address, $target ) = @_; + + if( $msg =~ s/^COMPLETIONS // ) { + $root_nick = $from; + if( $msg eq 'OK' ) { + @commands = (); + } + elsif( $msg eq 'END' ) { + $get_completions = 0; + } + @commands = ( @commands, $msg ); + + Irssi::signal_stop(); + } +} + +sub complete_word { + my ($complist, $window, $word, $linestart, $want_space) = @_; + my $channel = $window->get_active_name(); + if ($channel eq $bitlbee_channel or $channel eq $root_nick or $linestart =~ /^\/(msg|query) \Q$root_nick\E */i){ + $linestart =~ s/^\/(msg|query) \Q$root_nick\E *//i; + $linestart =~ s/^\Q$root_nick\E[:,] *//i; + foreach my $command(@commands) { + if ($command =~ /^$word/i) { + push @$complist, $command; + } + } + } +} + + +Irssi::signal_add_last('complete word', 'complete_word'); +Irssi::signal_add_first('message irc notice', 'irc_notice'); + diff --git a/home/.irssi/scripts/autorun/go.pl b/home/.irssi/scripts/autorun/go.pl new file mode 100644 index 0000000..cdb5692 --- /dev/null +++ b/home/.irssi/scripts/autorun/go.pl @@ -0,0 +1,115 @@ +use strict; +use vars qw($VERSION %IRSSI); +use Irssi; +use Irssi::Irc; + +# Usage: +# /script load go.pl +# If you are in #irssi you can type /go #irssi or /go irssi or even /go ir ... +# also try /go ir and /go (that's two spaces) +# +# The following settings exist: +# +# /SET go_match_case_sensitive [ON|OFF] +# Match window/item names sensitively (the default). Turning this off +# means e.g. "/go foo" would jump to a window named "Foobar", too. +# +# /SET go_match_anchored [ON|OFF] +# Match window/names only at the start of the word (the default). Turning +# this off will mean that strings can match anywhere in the window/names. +# The leading '#' of channel names is optional either way. +# +# /SET go_complete_case_sensitive [ON|OFF] +# When using tab-completion, match case-insensitively (the default). +# Turning this on means that "/go foo" will *not* suggest "Foobar". +# +# /SET go_complete_anchored [ON|OFF] +# Match window/names only at the start of the word. The default is 'off', +# which causes completion to match anywhere in the window/names during +# completion. The leading '#' of channel names is optional either way. +# + +$VERSION = '1.1.1'; + +%IRSSI = ( + authors => 'nohar', + contact => 'nohar@freenode', + 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 => '2019-02-25' +); + +sub _make_regexp { + my ($name, $ci, $aw) = @_; + my $re = "\Q${name}\E"; + $re = "(?i:$re)" unless $ci; + $re = "^#?$re" if $aw; + return $re; +} + +sub signal_complete_go { + my ($complist, $window, $word, $linestart, $want_space) = @_; + my $channel = $window->get_active_name(); + my $k = Irssi::parse_special('$k'); + + return unless ($linestart =~ /^\Q${k}\Ego\b/i); + + my $re = _make_regexp($word, + Irssi::settings_get_bool('go_complete_case_sensitive'), + Irssi::settings_get_bool('go_complete_anchored')); + @$complist = (); + foreach my $w (Irssi::windows) { + my $name = $w->get_active_name(); + if ($word ne "") { + if ($name =~ $re) { + push(@$complist, $name) + } + } else { + push(@$complist, $name); + } + } + Irssi::signal_stop(); +}; + +sub cmd_go +{ + my($chan,$server,$witem) = @_; + + my $case_sensitive = Irssi::settings_get_bool('go_match_case_sensitive'); + my $match_anchored = Irssi::settings_get_bool('go_match_anchored'); + + $chan =~ s/ *//g; + my $re = _make_regexp($chan, $case_sensitive, $match_anchored); + + my @matches; + foreach my $w (Irssi::windows) { + my $name = $w->get_active_name(); + if (($case_sensitive && $name eq $chan) || + (!$case_sensitive && CORE::fc $name eq CORE::fc $chan)) { + $w->set_active(); + return; + } elsif ($name =~ /$re/) { + push(@matches, $w); + } + } + if (@matches) { + $matches[0]->set_active(); + } +} + +Irssi::command_bind("go", "cmd_go"); +Irssi::signal_add_first('complete word', 'signal_complete_go'); +Irssi::settings_add_bool('go', 'go_match_case_sensitive', 1); +Irssi::settings_add_bool('go', 'go_complete_case_sensitive', 0); +Irssi::settings_add_bool('go', 'go_match_anchored', 1); +Irssi::settings_add_bool('go', 'go_complete_anchored', 0); + +# Changelog +# +# 2017-02-02 1.1 martin f. krafft +# - made case-sensitivity of match configurable +# - made anchoring of search strings configurable +# +# 2019-02-025 1.1.1 dylan lloyd +# - prefer exact channel matches diff --git a/home/.irssi/scripts/autorun/highlite.pl b/home/.irssi/scripts/autorun/highlite.pl new file mode 100644 index 0000000..515d0dd --- /dev/null +++ b/home/.irssi/scripts/autorun/highlite.pl @@ -0,0 +1,113 @@ +use strict; +use Irssi; +use Irssi::Irc; + +use vars qw($VERSION %IRSSI); + +$VERSION = "1.0"; +%IRSSI = ( + "authors" => "Mantis", + "contact" => "mantis\@inta-link.com", + "name" => "highlite", + "description" => "shows events happening in all channels you are in that may concern you", + "url" => "http://www.inta-link.com/", + "license" => "GNU GPL v2", + "changed" => "2003-01-03" +); + +sub msg_join +{ + my ($server, $channame, $nick, $host) = @_; + $channame =~ s/^://; + + my $windowname = Irssi::window_find_name('highlite'); + $windowname->print("%B%0JOIN : " . $nick . " : " . $channame . " : " . $host, MSGLEVEL_CLIENTCRAP) if ($windowname); +} + +sub msg_part +{ + my ($server, $channame, $nick, $host) = @_; + $channame =~ s/^://; + + my $windowname = Irssi::window_find_name('highlite'); + $windowname->print("%b%0PART : " . $nick . " : " . $channame . " : " . $host, MSGLEVEL_CLIENTCRAP) if ($windowname); +} + +sub msg_quit +{ + my ($server, $nick, $host, $quitmsg) = @_; + + if (substr($quitmsg, 0, 14) eq "Read error to ") + { + $quitmsg = "[ General Read Error ]"; + } + if (substr($quitmsg, 0, 17) eq "Ping timeout for ") + { + $quitmsg = "[ General Ping Timeout Error ]"; + } + + my $windowname = Irssi::window_find_name('highlite'); + $windowname->print("%R%0QUIT : " . $nick . " : " . $host . " : " . $quitmsg, MSGLEVEL_CLIENTCRAP) if ($windowname); + + $quitmsg = ""; +} + +sub msg_topic +{ + my ($server, $channame, $topicmsg, $nick, $host) = @_; + $channame =~ s/^://; + + my $windowname = Irssi::window_find_name('highlite'); + $windowname->print("%G%0TOPIC : " . $nick . " : " . $channame . " : " . $topicmsg, MSGLEVEL_CLIENTCRAP) if ($windowname); +} + +sub msg_nick +{ + my ($server, $nick, $old_nick, $host) = @_; + + my $windowname = Irssi::window_find_name('highlite'); + $windowname->print("%m%0NICK : " . $old_nick . " : " . $nick . " : " . $host, MSGLEVEL_CLIENTCRAP) if ($windowname); +} + +sub msg_kick +{ + my ($server, $channame, $kicked, $nick, $host, $reason) = @_; + $channame =~ s/^://; + + my $windowname = Irssi::window_find_name('highlite'); + $windowname->print("%Y%0KICK : " . $kicked . " : " . $channame . " : " . $nick . " : " . $reason, MSGLEVEL_CLIENTCRAP) if ($windowname); +} + +sub sig_printtext { + my ($dest, $text, $stripped) = @_; + + if (($dest->{level} & (MSGLEVEL_HILIGHT|MSGLEVEL_MSGS)) && ($dest->{level} & MSGLEVEL_NOHILIGHT) == 0) + { + if ($dest->{level} & MSGLEVEL_PUBLIC) + { + my $windowname = Irssi::window_find_name('highlite'); + + $windowname->print("%W%0HIGHLITE : " . $dest->{target} . " : " . $text, MSGLEVEL_CLIENTCRAP) if ($windowname); + } + } +} + +my $windowname = Irssi::window_find_name('highlite'); +if (!$windowname) +{ + Irssi::command("window new hidden"); + Irssi::command("window name highlite"); +} + +Irssi::signal_add( +{ + 'message join' => \&msg_join, + 'message part' => \&msg_part, + 'message quit' => \&msg_quit, + 'message topic' => \&msg_topic, + 'print text', 'sig_printtext', + 'message nick' => \&msg_nick, + 'message kick' => \&msg_kick +} +); + diff --git a/home/.irssi/scripts/autorun/ido_switcher.pl b/home/.irssi/scripts/autorun/ido_switcher.pl new file mode 100644 index 0000000..17a8db8 --- /dev/null +++ b/home/.irssi/scripts/autorun/ido_switcher.pl @@ -0,0 +1,1165 @@ +=pod + +=head1 NAME + +ido_switcher.pl + +=head1 DESCRIPTION + +Search and select windows similar to ido-mode for emacs + +=head1 INSTALLATION + +This script requires that you have first installed and loaded F + +Uberprompt can be downloaded from: + +L + +and follow the instructions at the top of that file or its README for installation. + +If uberprompt.pl is available, but not loaded, this script will make one +attempt to load it before giving up. This eliminates the need to precisely +arrange the startup order of your scripts. + +=head2 SETUP + +C + +Where C<^G> is a key of your choice. + +=head2 USAGE + +C (or whatever you've set the above bind to), enters IDO window switching mode. +You can then type either a search string, or use one of the additional key-bindings +to change the behaviour of the search. C provides online help regarding +the possible interactive options. + +=head3 EXTENDED USAGE: + +It is possible to pass arguments to the C command, which +correspond to some of the interactively settable parameters listed below. + +The following options are available: + +=over 4 + +=item C<-channels> + +Search through only channels. + +=item C<-queries> + +Search through only queries. + +=item C<-all> + +search both queries and channels (Default). + +=item C<-active> + +Lmit search to only window items with activity. + +=item C<-exact> + +Enable exact-substring matching + +=item C<-flex> + +Enable flex-string matching + +=back + +I, C<-flex> or C<-regex> are given, the default is the value of +C> + +=head4 EXAMPLE + +=over 2 + +=item C + +=item C + +=back + +B When entering window switching mode, the contents of your input line will +be saved and cleared, to avoid visual clutter whilst using the switching +interface. It will be restored once you exit the mode using either C, C, +or C. + +=head3 INTERACTIVE COMMANDS + +The following key-bindings are available only once the mode has been +activated: + +=over 4 + +=item C + + Exit the mode without changing windows. + +=item C + +Exit, as above. + +=item C + +Rotate the list of window candidates forward by one item + +=item C + +Rotate the list of window candidates backward by one item + +=item C + +Toggle 'Active windows only' filter + +=item C + +Switch between 'Flex' and 'Exact' matching. + +=item C + +Select a network or server to filter candidates by + +=item C + +Clear the current search string + +=item C + +Cycle between showing only queries, channels, or all. + +=item C + +Filter candidates by current search string, and then reset +the search string + +=item C + +Select the current head of the candidate list (the green one) + +=item C + +Select the current head of the list, without exiting the +switching mode. The head is then moved one place to the right, +allowing one to cycle through channels by repeatedly pressing space. + +=item C + +B<[currently in development]> displays all possible completions +at the bottom of the current window. + +=item I (C, etc) + +Add that character to the current search string. + +=back + +=head3 USAGE NOTES + +=over 4 + +=item * + +Using C-e (show actives), followed by repeatedly pressing space will cycle +through all your currently active windows. + +=item * + +If you enter a search string fragment, and realise that more than one candidate +is still presented, rather than delete the whole string and modify it, you +can use C-SPC to 'lock' the current matching candidates, but allow you to +search through those matches alone. + +=back + +=head1 AUTHORS + +Based originally on L script Copyright 2007 Wouter Coekaerts +Ccoekie@irssi.orgE>. + +Primary functionality Copyright 2010-2011 Tom Feist +Cshabble+irssi@metavore.orgE>. + +=head1 LICENCE + +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 St, Fifth Floor, Boston, MA 02110-1301 USA + +=head1 BUGS: + +=over 4 + +=item B Sometimes selecting a channel with the same name on a different + network will take you to the wrong channel. + +=back + +=head1 TODO + +=over 4 + +=item B C-g - cancel + +=item B C-spc - narrow + +=item B flex matching (on by default, but optional) + +=item TODO server/network narrowing + +=item B colourised output (via uberprompt) + +=item B C-r / C-s rotate matches + +=item B toggle queries/channels + +=item B remove inputline content, restore it afterwards. + +=item TODO tab - display all possibilities in window (clean up afterwards) +how exactly will this work? + +=item B sort by recent activity/recently used windows (separate commands?) + +=item B need to be able to switch ordering of active ones (numerical, or most +recently active, priority to PMs/hilights, etc?) + +=item B should space auto-move forward to next window for easy stepping + through sequential/active windows? + +=back + +=cut + +use strict; +use warnings; + +use Irssi; +use Irssi::TextUI; +use Data::Dumper; + + +our $VERSION = '2.4'; # 7c9e42560a2f4a1 +our %IRSSI = + ( + authors => 'Tom Feist, Wouter Coekaerts', + contact => 'shabble+irssi@metavore.org, shabble@#irssi/freenode', + name => 'ido_switcher', + description => 'Select window[-items] using an ido-mode like search interface', + license => 'GPLv2 or later', + url => 'http://github.com/shabble/irssi-scripts/tree/master/ido-mode/', + ); + + + +my $CMD_NAME = 'ido_switch_start'; +my $CMD_OPTS = '-channels -queries -all -active -exact -flex -regex'; + + +my $input_copy = ''; +my $input_pos_copy = 0; + +my $ido_switch_active = 0; # for intercepting keystrokes + +my @window_cache = (); +my @search_matches = (); + +my $match_index = 0; +my $search_str = ''; +my $active_only = 0; +my $regex_valid = 1; + +my $mode_type = 'ALL'; +my @mode_cache; +my $showing_help = 0; + +my $need_clear = 0; + +my $sort_ordering = "start-asc"; +my $sort_active_first = 0; + +# /set configurable settings +my $ido_show_count; +my $ido_use_flex; + +my $DEBUG_ENABLED = 0; +sub DEBUG () { $DEBUG_ENABLED } + + +sub MODE_WIN () { 0 } # windows +sub MODE_NET () { 1 } # chatnets +#sub MODE_C () { 2 } # channels +#sub MODE_S () { 3 } # select server +#sub MODE_W () { 4 } # select window + +my $MODE = MODE_WIN; + +# check we have uberprompt loaded. + +my %need_clear; + +sub _print { + my $win = Irssi::active_win; + my $str = join('', @_); + $need_clear = 1; + $win->print($str, MSGLEVEL_NEVER); + push @{ $need_clear{ $win->{_irssi} } }, $win->view->{buffer}{cur_line}; +} + +sub _debug_print { + return unless DEBUG; + my $win = Irssi::active_win; + my $str = join('', @_); + $win->print($str, MSGLEVEL_CLIENTCRAP); +} + +sub _print_clear { + return unless $need_clear; + for my $win (Irssi::windows) { + if (my $lines = delete $need_clear{ $win->{_irssi} }) { + my $view = $win->view; + my $bottom = $view->{bottom}; + for my $line (@$lines) { + $view->remove_line($line); + } + $win->command('^scrollback end') if $bottom && !$win->view->{bottom}; + $view->redraw; + } + } + %need_clear=(); +} + +# TODO: use the code from rl_history_search to put this into a disposable +# split win. +# TODO: create formats for this. +sub display_help { + + my @message = + ('%_IDO Window Switching Help:%_', + '', + '%_Ctrl-g%_ %|- cancel out of the mode without changing windows.', + '%_Esc%_ %|- cancel out, as above.', + '%_Ctrl-s%_ %|- rotate the list of window candidates forward by 1', + '%_Ctrl-r%_ %|- rotate the list of window candidates backward by 1', + '%_Ctrl-e%_ %|- Toggle \'Active windows only\' filter', + '%_Ctrl-f%_ %|- Switch between \'Regex\', \'Flex\' and \'Exact\' matching.', +# '%_Ctrl-d%_ %|- Select a network or server to filter candidates by', + '%_Ctrl-u%_ %|- Clear the current search string', + '%_Ctrl-q%_ %|- Cycle between showing only queries, channels, or all.', + '%_Ctrl-SPC%_ %|- Filter candidates by current search string, and then reset the search string', + '%_RET%_ %|- Select the current head of the candidate list (the %_green%n one)', + '%_SPC%_ %|- Select the current head of the list, without exiting switching mode. The head ' + .'is then moved one place to the right, allowing one to cycle through channels by repeatedly ' + .'pressing space.', + '%_TAB%_ %|- displays all possible completions at the bottom of the current window.', + '', + ' %_All other keys (a-z, A-Z, etc) - Add that character to the', + ' %_current search string.', + '', + '%_Press Any Key to return%_', + ); + + _print($_) for @message; + $showing_help = 1; +} + +sub print_all_matches { + my $message_header = "Windows:"; + my $win = Irssi::active_win(); + my $win_width = $win->{width} || 80; + + # TODO: needs to prefix ambig things with chatnet, or maybe order in groups + # by chatnet with newlines. + + # Also, colourise the channel list. + + my $col_width = 1; + + for (@search_matches) { + my $len = length($_->{num} . ':' . _format_display_tag($_) . $_->{name}); + $col_width = $len if $len > $col_width; + } + + my $cols = int($win_width / $col_width); + + my @lines; + my $i = 0; + my @line; + + for my $item (@search_matches) { + ++$i; + my $name = $item->{name}; + push @line, sprintf('%*s', -(10+$col_width), "\cD4/".$item->{num}.":\cD3/"._format_display_tag($item)."\cD4/".$name); + if ($i == $cols) { + push @lines, join ' ', @line; + @line = (); + $i = 0; + } + } + # flush rest out. + push @lines, join ' ', @line; + + _print($message_header); + _print($_) for (@lines); + #_print("Longtest name: $longest_name"); +} + +unless ("Irssi::Script::uberprompt"->can('init')) { + + print "Warning, this script requires '\%_uberprompt.pl\%_' in order to work. "; + +} + +sub ido_switch_init { + #Irssi::settings_add_bool('ido_switch', 'ido_switch_debug', 0); + Irssi::settings_add_str('ido_switch', 'ido_use_flex', 'flex'); + Irssi::settings_add_bool('ido_switch', 'ido_show_active_first', 1); + Irssi::settings_add_int ('ido_switch', 'ido_show_count', 5); + + + Irssi::command_bind($CMD_NAME, \&ido_switch_start); + Irssi::command_set_options($CMD_NAME, $CMD_OPTS); + + Irssi::signal_add ('setup changed' => \&setup_changed); + Irssi::signal_add_first('gui key pressed' => \&handle_keypress); + + setup_changed(); +} + +sub setup_changed { + #$DEBUG_ENABLED = Irssi::settings_get_bool('ido_switch_debug'); + $ido_show_count = Irssi::settings_get_int ('ido_show_count'); + $ido_use_flex = _flex_mode(Irssi::settings_get_str('ido_use_flex')); + $sort_active_first = Irssi::settings_get_bool('ido_show_active_first'); +} + +sub ido_switch_start { + + my ($args, $server, $witem) = @_; + + # store copy of input line to restore later. + $input_copy = Irssi::parse_special('$L'); + $input_pos_copy = Irssi::gui_input_get_pos(); + + Irssi::gui_input_set(''); + + my $options = {}; + my @opts = Irssi::command_parse_options($CMD_NAME, $args); + if (@opts and ref($opts[0]) eq 'HASH') { + $options = $opts[0]; + #print "Options: " . Dumper($options); + } + + # clear / initialise match variables. + $ido_switch_active = 1; + $search_str = ''; + $match_index = 0; + @mode_cache = (); + + # configure settings from provided arguments. + + # use provided options first, or fall back to /setting. + $ido_use_flex = _flex_mode(exists $options->{exact} + ? 'exact' + : exists $options->{flex} + ? 'flex' + : exists $options->{regex} + ? 'regex' + : Irssi::settings_get_str('ido_use_flex')); + + # only select active items + $active_only = exists $options->{active}; + + # what type of items to search. + $mode_type = exists $options->{queries} + ? 'QUERY' + : exists $options->{channels} + ? 'CHANNEL' + : 'ALL'; + + _debug_print "Win cache: " . join(", ", map { $_->{name} } @window_cache); + + _update_cache(); + + update_matches(); + update_window_select_prompt(); +} + +sub _flex_mode { + if ($_[0] =~ /flex/i) { + 'Flex' + } elsif ($_[0] =~ /regex/i) { + 'Regex' + } else { + 'Exact' + } +} + +sub _update_cache { + @window_cache = get_all_windows(); +} + +sub _build_win_obj { + my ($win, $win_item) = @_; + + my @base = ( + b_pos => -1, + e_pos => -1, + hilight_field => 'name', + active => $win->{data_level} > 0, + num => $win->{refnum}, + server => $win->{active_server}, + + ); + + if (defined($win_item)) { + return ( + @base, + name => $win_item->{visible_name}, + type => $win_item->{type}, + itemname => $win_item->{name}, + active => $win_item->{data_level} > 0, + server => $win_item->{server}, + + ) + } else { + return ( + @base, + name => $win->{name}, + type => 'WIN', + ); + } +} + +sub get_all_windows { + my @ret; + + foreach my $win (Irssi::windows()) { + my @items = $win->items(); + + if ($win->{name} ne '') { + _debug_print "Adding window: " . $win->{name}; + push @ret, { _build_win_obj($win, undef) }; + } + if (scalar @items) { + foreach my $item (@items) { + _debug_print "Adding windowitem: " . $item->{visible_name}; + push @ret, { _build_win_obj($win, $item) }; + } + } else { + if (not grep { $_->{num} == $win->{refnum} } @ret) { + my $item = { _build_win_obj($win, undef) }; + $item->{name} = "Unknown"; + push @ret, $item; + } + #_debug_print "Error occurred reading info from window: $win"; + #_debug_print Dumper($win); + } + } + @ret = _sort_windows(\@ret); + + return @ret; + +} + +sub _sort_windows { + my $list_ref = shift; + my @ret = @$list_ref; + + @ret = sort { $a->{num} <=> $b->{num} } @ret; + if ($sort_active_first) { + my @active = grep { $_->{active} } @ret; + my @inactive = grep { not $_->{active} } @ret; + + return (@active, @inactive); + } else { + return @ret; + } +} + +sub ido_switch_select { + my ($selected, $tag) = @_; + if (!$selected) { + _debug_print "Error, selection invalid"; + return; + } + _debug_print sprintf("Selecting window: %s (%d)", + $selected->{name}, $selected->{num}); + + Irssi::command("WINDOW GOTO " . $selected->{num}); + + if ($selected->{type} ne 'WIN') { + _debug_print "Selecting window item: " . $selected->{itemname}; + my $i = 1; my $found; + for (Irssi::active_win->items) { + if ($_->{name} eq $selected->{itemname}) { + if (!defined $selected->{server} && !defined $_->{server}) { + $found = 1; + last; + } + if (defined $selected->{server} && defined $_->{server} + && $selected->{server}{tag} eq $_->{server}{tag}) { + $found = 1; + last; + } + } + ++$i; + } + Irssi::command("WINDOW ITEM GOTO " . ($found ? $i : $selected->{itemname})); + } + + update_matches(); +} + +sub ido_switch_exit { + $ido_switch_active = 0; + + _print_clear(); + + Irssi::gui_input_set($input_copy); + Irssi::gui_input_set_pos($input_pos_copy); + Irssi::signal_emit('change prompt', '', 'UP_INNER'); +} + +sub _order_matches { + return @_[$match_index .. $#_, + 0 .. $match_index - 1] +} + +sub update_window_select_prompt { + + # take the top $ido_show_count entries and display them. + my $match_count = scalar @search_matches; + my $show_count = $ido_show_count; + my $match_string = '[No matches]'; + + $show_count = $match_count if $match_count < $show_count; + + if ($show_count > 0) { # otherwise, default message above. + _debug_print "Showing: $show_count matches"; + + my @ordered_matches = _order_matches(@search_matches); + + my @display = @ordered_matches[0..$show_count - 1]; + + # determine which items are non-unique, if any. + + my %uniq; + + foreach my $res (@display) { + my $name = $res->{name}; + + if (!exists $uniq{$name}) { + $uniq{$name} = []; + } + push @{$uniq{$name}}, $res; + } + + # and set a flag to ensure they have their network tag applied + # to them when drawn. + foreach my $name (keys %uniq) { + my @values = @{$uniq{$name}}; + if (@values > 1) { + $_->{display_net} = 1 for @values; + } + } + + # show the first entry in green + + my $first = shift @display; + my $formatted_first = _format_display_entry($first, '%g'); + unshift @display, $formatted_first; + + # and array-slice-map the rest to be red. + # or yellow, if they have unviewed activity + + @display[1..$#display] + = map + { + _format_display_entry($_, $_->{active}?'%y':'%r') + + } @display[1..$#display]; + + # join em all up + $match_string = join ', ', @display; + } + + my @indicators; + + # indicator if flex mode is being used (C-f to toggle) + push @indicators, $ido_use_flex; + push @indicators, 'Active' if $active_only; + push @indicators, ucfirst(lc($mode_type)); + + my $flex = sprintf(' %%b[%%n%s%%b]%%n ', join ', ', @indicators); + + my $search = ''; + $search = (sprintf '`%s\': ', $search_str) if length $search_str; + $search = (sprintf '`%%R%s%%n\': ', $search_str) if (length $search_str && !$regex_valid); + + Irssi::signal_emit('change prompt', $flex . $search . $match_string, + 'UP_INNER'); +} + + + +sub _format_display_entry { + my ($obj, $colour) = @_; + + my $field = $obj->{hilight_field}; + my $hilighted = { netname => _format_display_tag($obj).$obj->{name}, + name => $obj->{name}, num => $obj->{num} }; + my $show_tag = $obj->{display_net} || 0; + + if ($obj->{b_pos} >= 0 && $obj->{e_pos} > $obj->{b_pos}) { + substr($hilighted->{$field}, $obj->{e_pos}, 0) = '%_'; + substr($hilighted->{$field}, $obj->{b_pos}, 0) = '%_'; + _debug_print "Showing $field as: " . $hilighted->{$field} + } + + return sprintf('%s%s:%s%%n', + $colour, + $hilighted->{num}, + $hilighted->{netname}) + if $field eq 'netname'; + + return sprintf('%s%s:%s%s%%n', + $colour, + $hilighted->{num}, + $show_tag ? _format_display_tag($obj) : '', + $hilighted->{name}); +} + +sub _format_display_tag { + my $obj = shift; + if (defined $obj->{server}) { + my $server = $obj->{server}; + my $tag = $server->{tag}; + return $tag . '/' if length $tag; + } + return ''; +} + +sub _check_active { + my ($obj) = @_; + return 1 unless $active_only; + return $obj->{active}; +} + +sub update_matches { + my $current_match = get_window_match(); + + _update_cache() unless $search_str; + + if ($mode_type ne 'ALL') { + @mode_cache = @window_cache; + @window_cache = grep { $_->{type} eq $mode_type } @window_cache; + } else { + @window_cache = @mode_cache if @mode_cache; + } + + my $field = 'name'; + my $search_str2; + if ($search_str =~ m:^(.*)/(.*?)$:) { + $field = 'netname'; + $search_str2 = "$2/$1"; + } + + $regex_valid = 1; + if ($search_str =~ m/^\d+$/) { + + @search_matches = + grep { + _check_active($_) and regex_match($search_str, $_, 'num') + } @window_cache; + + } elsif ($ido_use_flex eq 'Flex') { + + @search_matches = + grep { + _check_active($_) and (flex_match($search_str, $_, $field) >= 0 + || (defined $search_str2 && flex_match($search_str2, $_, $field) >= 0)) + } @window_cache; + + } elsif ($ido_use_flex eq 'Regex') { + my $regex = do { local $@; + my $ret = eval { qr/$search_str/ } || qr/\Q$search_str/; + if ($@) { $regex_valid = 0 } + $ret; + }; + my $regex2 = defined $search_str2 ? + do { local $@; eval { qr/$search_str2/ } || qr/\Q$search_str2/ } + : undef; + @search_matches = + grep { + _check_active($_) and (regex_match($regex, $_, $field) + || (defined $regex2 && regex_match($regex2, $_, $field))) + } @window_cache; + } else { + @search_matches = + grep { + _check_active($_) and (regex_match(qr/\Q$search_str/, $_, $field) + || (defined $search_str2 && regex_match(qr/\Q$search_str2/, $_, $field))) + } @window_cache; + } + + $match_index = 0; + if ($current_match) { + for my $idx (0..$#search_matches) { + if ($search_matches[$idx]{num} == $current_match->{num} + && $search_matches[$idx]{type} eq $current_match->{type}) { + $match_index = $idx; + if ($current_match->{type} eq 'WIN') { + last; + } elsif ($search_matches[$idx]{itemname} eq $current_match->{itemname}) { + last; + } + } + } + } + +} + +sub regex_match { + my ($regex, $obj, $field) = @_; + my $data = $field eq 'netname' + ? _format_display_tag($obj).$obj->{name} : $obj->{$field}; + if ($data =~ m/$regex/i) { + $obj->{hilight_field} = $field; + $obj->{b_pos} = $-[0]; + $obj->{e_pos} = $+[0]; + return 1; + } + return 0; +} + +sub flex_match { + my ($search_str, $obj, $field) = @_; + + my $pattern = $search_str; + my $source = $field eq 'netname' + ? _format_display_tag($obj).$obj->{name} : $obj->{$field}; + + _debug_print "Flex match: $pattern / $source"; + + # default to matching everything if we don't have a pattern to compare + # against. + + return 0 unless $pattern; + + my @chars = split '', lc($pattern); + my $ret = -1; + my $first = 0; + + my $lc_source = lc($source); + + $obj->{hilight_field} = $field; + + foreach my $char (@chars) { + my $pos = index($lc_source, $char, $ret); + if ($pos > -1) { + + # store the beginning of the match + $obj->{b_pos} = $pos unless $first; + $first = 1; + + _debug_print("matched: $char at $pos in $source"); + $ret = $pos + 1; + + } else { + + $obj->{b_pos} = $obj->{e_pos} = -1; + _debug_print "Flex returning: -1"; + + return -1; + } + } + + _debug_print "Flex returning: $ret"; + + #store the end of the match. + $obj->{e_pos} = $ret; + + return $ret; +} + +sub prev_match { + + $match_index++; + if ($match_index > $#search_matches) { + $match_index = 0; + } + + _debug_print "index now: $match_index"; +} + +sub next_match { + + $match_index--; + if ($match_index < 0) { + $match_index = $#search_matches; + } + _debug_print "index now: $match_index"; +} + +sub get_window_match { + return $search_matches[$match_index]; +} + +sub handle_keypress { + my ($key) = @_; + + return unless $ido_switch_active; + + if ($showing_help) { + _print_clear(); + $showing_help = 0; + Irssi::signal_stop(); + } + + if ($key == 0) { # C-SPC? + _debug_print "\%_Ctrl-space\%_"; + + $search_str = ''; + @window_cache = @search_matches; + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + } + + if ($key == 3) { # C-c + _print_clear(); + Irssi::signal_stop(); + return; + } + if ($key == 4) { # C-d +# update_network_select_prompt(); + Irssi::signal_stop(); + return; + } + + if ($key == 5) { # C-e + $active_only = not $active_only; + Irssi::signal_stop(); + update_matches(); + update_window_select_prompt(); + return; + } + + if ($key == 6) { # C-f + + $ido_use_flex = ($ido_use_flex eq 'Regex' ? 'Flex' + : $ido_use_flex eq 'Flex' ? 'Exact' + : 'Regex'); + _update_cache(); + + update_matches(); + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + } + if ($key == 9) { # TAB + _debug_print "Tab complete"; + _print_clear(); + print_all_matches(); + Irssi::signal_stop(); + } + + if ($key == 10 || $key == 13) { # enter + _debug_print "selecting history and quitting"; + my $selected_win = get_window_match(); + ido_switch_select($selected_win); + + ido_switch_exit(); + Irssi::signal_stop(); + return; + } + if ($key == 11) { # Ctrl-K + my $sel = get_window_match(); + _debug_print("deleting entry: " . $sel->{num}); + Irssi::command("window close " . $sel->{num}); + _update_cache(); + update_matches(); + update_window_select_prompt(); + Irssi::signal_stop(); + + } + + if ($key == 18) { # Ctrl-R + _debug_print "skipping to prev match"; + #update_matches(); + next_match(); + + update_window_select_prompt(); + Irssi::signal_stop(); # prevent the bind from being re-triggered. + return; + } + + if ($key == 17) { # Ctrl-q + if ($mode_type eq 'CHANNEL') { + $mode_type = 'QUERY'; + } elsif ($mode_type eq 'QUERY') { + $mode_type = 'ALL'; + } else { # ALL + $mode_type = 'CHANNEL'; + } + update_matches(); + update_window_select_prompt(); + Irssi::signal_stop(); + } + + if ($key == 19) { # Ctrl-s + _debug_print "skipping to next match"; + prev_match(); + + #update_matches(); + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + } + + if ($key == 7) { # Ctrl-g + _debug_print "aborting search"; + ido_switch_exit(); + Irssi::signal_stop(); + return; + } + + if ($key == 8) { # Ctrl-h + display_help(); + Irssi::signal_stop(); + return; + } + + if ($key == 21) { # Ctrl-u + $search_str = ''; + update_matches(); + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + + } + + if ($key == 127) { # DEL + + if (length $search_str) { + $search_str = substr($search_str, 0, -1); + _debug_print "Deleting char, now: $search_str"; + } + + update_matches(); + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + } + + # TODO: handle esc- sequences and arrow-keys? + + if ($key == 27) { # Esc + ido_switch_exit(); + return; + } + + if ($key == 32) { # space + my $selected_win = get_window_match(); + ido_switch_select($selected_win); + + prev_match(); + update_window_select_prompt(); + + Irssi::signal_stop(); + + return; + } + + if ($key > 32) { # printable + $search_str .= chr($key); + + update_matches(); + update_window_select_prompt(); + + Irssi::signal_stop(); + return; + } + + # ignore all other keys. + Irssi::signal_stop(); +} + +ido_switch_init(); + +sub update_network_select_prompt { + + my @servers = map + { + { + name => $_->{tag}, + type => 'SERVER', + active => 0, + e_pos => -1, + b_pos => -1, + hilight_field => 'name', + } + } Irssi::servers(); + + my $match_count = scalar @servers; + my $show_count = $ido_show_count; + my $match_string = '(no matches) '; + + $show_count = $match_count if $match_count < $show_count; + + if ($show_count > 0) { + _debug_print "Showing: $show_count matches"; + + my @ordered_matches = _order_matches(@servers); + my @display = @ordered_matches[0..$show_count - 1]; + + # show the first entry in green + + unshift(@display, _format_display_entry(shift(@display), '%g')); + + # and array-slice-map the rest to be red (or yellow for active) + @display[1..$#display] + = map + { + _format_display_entry($_, $_->{active}?'%y':'%r') + + } @display[1..$#display]; + + # join em all up + $match_string = join ', ', @display; + } + + my @indicators; + + # indicator if flex mode is being used (C-f to toggle) + push @indicators, $ido_use_flex; + push @indicators, 'Active' if $active_only; + + my $flex = sprintf(' %%k[%%n%s%%k]%%n ', join ',', @indicators); + + my $search = ''; + $search = (sprintf '`%s\': ', $search_str) if length $search_str; + $search = (sprintf '`%%R%s%%n\': ', $search_str) if (length $search_str && !$regex_valid); + + Irssi::signal_emit('change prompt', $flex . $search . $match_string, + 'UP_INNER'); + +} diff --git a/home/.irssi/scripts/autorun/nickcolor.pl b/home/.irssi/scripts/autorun/nickcolor.pl new file mode 100644 index 0000000..95b7b63 --- /dev/null +++ b/home/.irssi/scripts/autorun/nickcolor.pl @@ -0,0 +1,253 @@ +use strict; +use Irssi 20020101.0250 (); +use vars qw($VERSION %IRSSI); +$VERSION = "2.1"; +%IRSSI = ( + authors => "Timo Sirainen, Ian Peters, David Leadbeater, Bruno Cattáneo", + contact => "tss\@iki.fi", + name => "Nick Color", + description => "assign a different color for each nick", + license => "Public Domain", + url => "http://irssi.org/", + changed => "Mon 08 Jan 21:28:53 BST 2018", +); + +# Settings: +# nickcolor_colors: List of color codes to use. +# e.g. /set nickcolor_colors 2 3 4 5 6 7 9 10 11 12 13 +# (avoid 8, as used for hilights in the default theme). +# +# nickcolor_enable_prefix: Enables prefix for same nick. +# +# nickcolor_enable_truncate: Enables nick truncation. +# +# nickcolor_prefix_text: Prefix text for succesive messages. +# e.g. /set nickcolor_prefix_text - +# +# nickcolor_truncate_value: Truncate nick value. +# e.g. /set nickcolor_truncate_value -7 +# This will truncate nicknames at 7 characters and make them right aligned + +my %saved_colors; +my %session_colors = {}; +my %saved_nicks; # To store each channel's last nickname + +sub load_colors { + open my $color_fh, "<", "$ENV{HOME}/.irssi/saved_colors"; + while (<$color_fh>) { + chomp; + my($nick, $color) = split ":"; + $saved_colors{$nick} = $color; + } +} + +sub save_colors { + open COLORS, ">", "$ENV{HOME}/.irssi/saved_colors"; + + foreach my $nick (keys %saved_colors) { + print COLORS "$nick:$saved_colors{$nick}\n"; + } + + close COLORS; +} + +# If someone we've colored (either through the saved colors, or the hash +# function) changes their nick, we'd like to keep the same color associated +# with them (but only in the session_colors, ie a temporary mapping). + +sub sig_nick { + my ($server, $newnick, $nick, $address) = @_; + my $color; + + $newnick = substr ($newnick, 1) if ($newnick =~ /^:/); + + if ($color = $saved_colors{$nick}) { + $session_colors{$newnick} = $color; + } elsif ($color = $session_colors{$nick}) { + $session_colors{$newnick} = $color; + } +} + +# This gave reasonable distribution values when run across +# /usr/share/dict/words + +sub simple_hash { + my ($string) = @_; + chomp $string; + my @chars = split //, $string; + my $counter; + + foreach my $char (@chars) { + $counter += ord $char; + } + + my @colors = split / /, Irssi::settings_get_str('nickcolor_colors'); + $counter = $colors[$counter % @colors]; + + return $counter; +} + +# process public (others) messages +sub sig_public { + my ($server, $msg, $nick, $address, $target) = @_; + + my $enable_prefix = Irssi::settings_get_bool('nickcolor_enable_prefix'); + my $enable_truncate = Irssi::settings_get_bool('nickcolor_enable_truncate'); + my $prefix_text = Irssi::settings_get_str('nickcolor_prefix_text'); + my $truncate_value = Irssi::settings_get_int('nickcolor_truncate_value'); + + # Reference for server/channel + my $tagtarget = "$server->{tag}/$target"; + + # Set default nick truncate value to 0 if option is disabled + $truncate_value = 0 if (!$enable_truncate); + + # Has the user assigned this nick a color? + my $color = $saved_colors{$nick}; + + # Have -we- already assigned this nick a color? + if (!$color) { + $color = $session_colors{$nick}; + } + + # Let's assign this nick a color + if (!$color) { + $color = simple_hash $nick; + $session_colors{$nick} = $color; + } + + $color = sprintf "\003%02d", $color; + + # Optional: We check if it's the same nickname for current target + if ($saved_nicks{$tagtarget} eq $nick && $enable_prefix) + { + # Grouped message + Irssi::command('/^format pubmsg ' . $prefix_text . '$1'); + } + else + { + # Normal message + Irssi::command('/^format pubmsg {pubmsgnick $2 {pubnick ' . $color . '$[' . $truncate_value . ']0}}$1'); + + # Save nickname for next message + $saved_nicks{$tagtarget} = $nick; + } + +} + +# process public (me) messages +sub sig_me { + my ($server, $msg, $target) = @_; + my $nick = $server->{nick}; + + my $enable_prefix = Irssi::settings_get_bool('nickcolor_enable_prefix'); + my $enable_truncate = Irssi::settings_get_bool('nickcolor_enable_truncate'); + my $prefix_text = Irssi::settings_get_str('nickcolor_prefix_text'); + my $truncate_value = Irssi::settings_get_int('nickcolor_truncate_value'); + + # Reference for server/channel + my $tagtarget = "$server->{tag}/$target"; + + # Set default nick truncate value to 0 if option is disabled + $truncate_value = 0 if (!$enable_truncate); + + # Optional: We check if it's the same nickname for current target + if ($saved_nicks{$tagtarget} eq $nick && $enable_prefix) + { + # Grouped message + Irssi::command('/^format own_msg ' . $prefix_text . '$1'); + } + else + { + # Normal message + Irssi::command('/^format own_msg {ownmsgnick $2 {ownnick $[' . $truncate_value . ']0}}$1'); + + # Save nickname for next message + $saved_nicks{$tagtarget} = $nick; + } + +} + +# process public (others) actions +sub sig_action_public { + my ($server, $msg, $nick, $address, $target) = @_; + + my $enable_prefix = Irssi::settings_get_bool('nickcolor_enable_prefix'); + + # Reference for server/channel + my $tagtarget = "$server->{tag}/$target"; + + # Empty current target nick if prefix option is enabled + $saved_nicks{$tagtarget} = '' if ($enable_prefix); + +} + +# process public (me) actions +sub sig_action_me { + my ($server, $msg, $target) = @_; + my $nick = $server->{nick}; + + my $enable_prefix = Irssi::settings_get_bool('nickcolor_enable_prefix'); + + # Reference for server/channel + my $tagtarget = "$server->{tag}/$target"; + + # Empty current target nick if prefix option is enabled + $saved_nicks{$tagtarget} = '' if ($enable_prefix); + +} + +sub cmd_color { + my ($data, $server, $witem) = @_; + my ($op, $nick, $color) = split " ", $data; + + $op = lc $op; + + if (!$op) { + Irssi::print ("No operation given (save/set/clear/list/preview)"); + } elsif ($op eq "save") { + save_colors; + } elsif ($op eq "set") { + if (!$nick) { + Irssi::print ("Nick not given"); + } elsif (!$color) { + Irssi::print ("Color not given"); + } elsif ($color < 2 || $color > 14) { + Irssi::print ("Color must be between 2 and 14 inclusive"); + } else { + $saved_colors{$nick} = $color; + } + } elsif ($op eq "clear") { + if (!$nick) { + Irssi::print ("Nick not given"); + } else { + delete ($saved_colors{$nick}); + } + } elsif ($op eq "list") { + Irssi::print ("\nSaved Colors:"); + foreach my $nick (keys %saved_colors) { + Irssi::print (chr (3) . sprintf("%02d", $saved_colors{$nick}) . "$nick" . + chr (3) . "1 ($saved_colors{$nick})"); + } + } elsif ($op eq "preview") { + Irssi::print ("\nAvailable colors:"); + foreach my $i (2..14) { + Irssi::print (chr (3) . "$i" . "Color #$i"); + } + } +} + +load_colors; + +Irssi::settings_add_str('misc', 'nickcolor_colors', '2 3 4 5 6 7 9 10 11 12 13'); +Irssi::settings_add_bool('misc', 'nickcolor_enable_prefix', 0); +Irssi::settings_add_bool('misc', 'nickcolor_enable_truncate', 0); +Irssi::settings_add_str('misc', 'nickcolor_prefix_text' => '- '); +Irssi::settings_add_int('misc', 'nickcolor_truncate_value' => 0); +Irssi::command_bind('color', 'cmd_color'); + +Irssi::signal_add('message public', 'sig_public'); +Irssi::signal_add('message own_public', 'sig_me'); +Irssi::signal_add('message irc action', 'sig_action_public'); +Irssi::signal_add('message irc own_action', 'sig_action_me'); +Irssi::signal_add('event nick', 'sig_nick'); diff --git a/home/.irssi/scripts/autorun/screen_away.pl b/home/.irssi/scripts/autorun/screen_away.pl new file mode 100644 index 0000000..199fc1a --- /dev/null +++ b/home/.irssi/scripts/autorun/screen_away.pl @@ -0,0 +1,248 @@ +use Irssi; +use strict; +use FileHandle; + +use vars qw($VERSION %IRSSI); + +$VERSION = "0.9.8.2"; +%IRSSI = ( + authors => 'Andreas \'ads\' Scherbaum ', + name => 'screen_away', + description => 'set (un)away, if screen is attached/detached', + license => 'GPL v2', + url => 'none', +); + +# screen_away irssi module +# +# written by Andreas 'ads' Scherbaum +# +# 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 +# 24.08.2004 fixing bug where the away nick was not storedcorrectly +# thanks for Harald Wurpts for help debugging this one +# 17.09.2004 rewrote init part to use $ENV{'STY'} +# 05.12.2004 add patch for remember away state +# thanks to Jilles Tjoelker +# change "chatnet" to "tag" +# 18.05.2007 fix '-one' for SILC networks +# +# +# usage: +# +# put this script into your autorun directory and/or load it with +# /SCRIPT LOAD +# +# there are 5 settings available: +# +# /set screen_away_active ON/OFF/TOGGLE +# /set screen_away_repeat +# /set screen_away_message +# /set screen_away_window +# /set screen_away_nick +# +# active means, that you will be only set away/unaway, if this +# flag is set, default is ON +# repeat is the number of seconds, after the script will check the +# screen status again, default is 5 seconds +# message is the away message sent to the server, default: not here ... +# window is a window number or name, if set, the script will switch +# to this window, if it sets you away, default is '1' +# nick is the new nick, if the script goes away +# will only be used it not empty +# +# normal you should be able to rename the script to something other +# than 'screen_away' (as example, if you dont like the name) by simple +# changing the 'name' parameter in the %IRSSI hash at the top of this script + + +# variables +my $timer_name = undef; +my $away_status = 0; +my %old_nicks = (); +my %away = (); + +# Register formats +Irssi::theme_register( +[ + 'screen_away_crap', + '{line_start}{hilight ' . $IRSSI{'name'} . ':} $0' +]); + +# if we are running +my $screen_away_used = 0; + +# try to find out, if we are running in a screen +# (see, if $ENV{STY} is set +if (!defined($ENV{STY})) { + # just return, we will never be called again + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'screen_away_crap', + "could not open status file for parent process (pid: " . getppid() . "): $!"); + return; +} + +my ($socket_pid, $socket_name, $socket_path); + +# search for socket +# normal we could search the socket file, ... if we know the path +# but so we have to call one time the screen executable +# disable locale +# the quotes around C force perl 5.005_03 to use the shell +# thanks to Jilles Tjoelker for pointing this out +my $socket = `LC_ALL="C" screen -ls`; + + + +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 + # $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]+)\..*$/$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) + $screen_away_used = 1; + } else { + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'screen_away_crap', + "error reading screen informations from:"); + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'screen_away_crap', + "$socket"); + return; + } +} + +# last check +if ($screen_away_used == 0) { + # we will never be called again + return; +} + +# build complete socket name +$socket = $socket_path . "/" . $socket_name; + +# register config variables +Irssi::settings_add_bool('misc', $IRSSI{'name'} . '_active', 1); +Irssi::settings_add_int('misc', $IRSSI{'name'} . '_repeat', 5); +Irssi::settings_add_str('misc', $IRSSI{'name'} . '_message', "not here ..."); +Irssi::settings_add_str('misc', $IRSSI{'name'} . '_window', "1"); +Irssi::settings_add_str('misc', $IRSSI{'name'} . '_nick', ""); + +# init process +screen_away(); + +# screen_away() +# +# check, set or reset the away status +# +# parameter: +# none +# return: +# 0 (OK) +sub screen_away { + my ($away, @screen, $screen); + + # only run, if activated + if (Irssi::settings_get_bool($IRSSI{'name'} . '_active') == 1) { + if ($away_status == 0) { + # display init message at first time + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'screen_away_crap', + "activating $IRSSI{'name'} (interval: " . Irssi::settings_get_int($IRSSI{'name'} . '_repeat') . " seconds)"); + } + # get actual screen status + my @screen = stat($socket); + # 00100 is the mode for "user has execute permissions", see stat.h + if (($screen[2] & 00100) == 0) { + # no execute permissions, Detached + $away = 1; + } else { + # execute permissions, Attached + $away = 2; + } + + # check if status has changed + if ($away == 1 and $away_status != 1) { + # set away + if (length(Irssi::settings_get_str($IRSSI{'name'} . '_window')) > 0) { + # if length of window is greater then 0, make this window active + Irssi::command('window goto ' . Irssi::settings_get_str($IRSSI{'name'} . '_window')); + } + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'screen_away_crap', + "Set away"); + my $message = Irssi::settings_get_str($IRSSI{'name'} . '_message'); + if (length($message) == 0) { + # we have to set a message or we wouldnt go away + $message = "not here ..."; + } + my ($server); + foreach $server (Irssi::servers()) { + if (!$server->{usermode_away}) { + # user isnt yet away + $away{$server->{'tag'}} = 0; + $server->command("AWAY " . (($server->{chat_type} ne 'SILC') ? "-one " : "") . "$message") if (!$server->{usermode_away}); + if (length(Irssi::settings_get_str($IRSSI{'name'} . '_nick')) > 0) { + # only change, if actual nick isnt already the away nick + if (Irssi::settings_get_str($IRSSI{'name'} . '_nick') ne $server->{nick}) { + # keep old nick + $old_nicks{$server->{'tag'}} = $server->{nick}; + # set new nick + $server->command("NICK " . Irssi::settings_get_str($IRSSI{'name'} . '_nick')); + } + } + } else { + # user is already away, remember this + $away{$server->{'tag'}} = 1; + } + } + $away_status = $away; + } elsif ($away == 2 and $away_status != 2) { + # unset away + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'screen_away_crap', + "Reset away"); + my ($server); + foreach $server (Irssi::servers()) { + if ($away{$server->{'tag'}} == 1) { + # user was already away, dont reset away + $away{$server->{'tag'}} = 0; + next; + } + $server->command("AWAY" . (($server->{chat_type} ne 'SILC') ? " -one" : "")) if ($server->{usermode_away}); + if (defined($old_nicks{$server->{'tag'}}) and length($old_nicks{$server->{'tag'}}) > 0) { + # set old nick + $server->command("NICK " . $old_nicks{$server->{'tag'}}); + $old_nicks{$server->{'tag'}} = ""; + } + } + $away_status = $away; + } + } + # but everytimes install a new timer + register_screen_away_timer(); + return 0; +} + +# register_screen_away_timer() +# +# remove old timer and install a new one +# +# parameter: +# none +# return: +# none +sub register_screen_away_timer { + if (defined($timer_name)) { + # remove old timer, if defined + Irssi::timeout_remove($timer_name); + } + # add new timer with new timeout (maybe the timeout has been changed) + $timer_name = Irssi::timeout_add(Irssi::settings_get_int($IRSSI{'name'} . '_repeat') * 1000, 'screen_away', ''); +} + diff --git a/home/.irssi/scripts/autorun/tmux-nicklist-portable.pl b/home/.irssi/scripts/autorun/tmux-nicklist-portable.pl new file mode 100755 index 0000000..f3aad1a --- /dev/null +++ b/home/.irssi/scripts/autorun/tmux-nicklist-portable.pl @@ -0,0 +1,433 @@ +# based on the nicklist.pl script +################################################################################ +# tmux_nicklist.pl +# This script integrates tmux and irssi to display a list of nicks in a +# vertical right pane with 20% width. Right now theres no configuration +# or setup, simply initialize the script with irssi and by default you +# will get the nicklist for every channel(customize by altering +# the regex in /set nicklist_channel_re) +# +# /set nicklist_channel_re +# * only show on channels matching this regular expression +# +# /set nicklist_max_users +# * only show when the channel has so many users or less (0 = always) +# +# /set nicklist_smallest_main +# * only show when main window is larger than this (0 = always) +# +# /set nicklist_pane_width +# * width of the nicklist pane +# +# /set nicklist_color +# * colourise the nicks in the nicklist (required nickcolor script +# with get_nick_color2 and debug_ansicolour functions) +# +# /set nicklist_gone_sort +# * sort away people below +# +# It supports mouse scrolling and the following keys: +# k/up arrow: up one line +# j/down arrow: down one line +# u/pageup: up 50% lines +# d/pagedown: down 50% lines +# gg: go to top +# G: go to bottom +# +# For better integration, unrecognized sequences will be sent to irssi and +# its pane will be focused. +# +# to toggle the nicklist if it is in the way you can make a key binding: +# /bind meta-Z /script exec Irssi::Script::tmux_nicklist_portable::toggle_nicklist +################################################################################ + +use strict; +use warnings; +use IO::Handle; +use IO::Select; +use POSIX; +use File::Temp qw/ :mktemp /; +use File::Basename; +our $VERSION = '0.1.8'; +our %IRSSI = ( + authors => 'Thiago de Arruda', + contact => 'tpadilha84@gmail.com', + name => 'tmux-nicklist', + description => 'displays a list of nicks in a separate tmux pane', + license => 'WTFPL', +); + +# "other" prefixes by danielg4 +# added 'd' and 'u' navigation as in vim, by @gerardbm (github) + +{ package Irssi::Nick } + +if ($#ARGV == -1) { +require Irssi; + +my $enabled = 0; +my $nicklist_toggle = 1; +my $script_path = __FILE__; +my $tmpdir; +my $fifo_path; +my $fifo; +my $just_launched; +my $resize_timer; + +sub enable_nicklist { + return if ($enabled); + $tmpdir = mkdtemp Irssi::get_irssi_dir()."/nicklist-XXXXXXXX"; + $fifo_path = "$tmpdir/fifo"; + POSIX::mkfifo($fifo_path, 0600) or die "can't mkfifo $fifo_path: $!"; + my $cmd = "perl $script_path $fifo_path $ENV{TMUX_PANE}"; + #my $width = Irssi::settings_get_int('nicklist_pane_width'); + my $width = 20; + system('tmux', 'split-window', '-dh', '-l', $width, '-t', $ENV{TMUX_PANE}, $cmd); + open_fifo(); + Irssi::timeout_remove($just_launched) if defined $just_launched; + $just_launched = Irssi::timeout_add_once(300, sub { $just_launched = undef; }, ''); +} + +sub open_fifo { + # The next system call will block until the other pane has opened the pipe + # for reading, so synchronization is not an issue here. + open $fifo, ">", $fifo_path or do { + if ($! == 4) { + Irssi::timeout_add_once(300, \&open_fifo, ''); + $enabled = -1 unless $enabled; + return; + } + die "can't open $fifo_path: $!"; + }; + $fifo->autoflush(1); + if ($enabled < -1) { + $enabled = 1; + disable_nicklist(); + } elsif ($enabled == -1) { + $enabled = 1; + reset_nicklist("enabled"); + } else { + $enabled = 1; + } +} + +sub disable_nicklist { + return unless ($enabled); + if ($enabled > 0) { + print $fifo "EXIT\n"; + close $fifo; + $fifo = undef; + unlink $fifo_path; + rmdir $tmpdir; + } + $enabled--; +} + +sub reset_nicklist { + my $event = shift; + my $active = Irssi::active_win(); + my $channel = $active->{active}; + return disable_nicklist unless $channel && ref $channel; + if ($event =~ /^nick/) { + # check if that nick event is for the current channel/nicklist + my ($event_channel) = @_; + return unless $channel->{_irssi} == $event_channel->{_irssi}; + } + my ($colourer, $ansifier); + if (Irssi::settings_get_bool('nicklist_color')) { + for my $script (sort map { my $z = $_; $z =~ s/::$//; $z } grep { /^nickcolor|nm/ } keys %Irssi::Script::) { + if ($colourer = "Irssi::Script::$script"->can('get_nick_color2')) { + $ansifier = "Irssi::Script::$script"->can('debug_ansicolour'); + last; + } + } + } + my $channel_pattern = Irssi::settings_get_str('nicklist_channel_re'); + { local $@; + $channel_pattern = eval { qr/$channel_pattern/ }; + $channel_pattern = qr/(?!)/ if $@; + } + my $smallest_main = Irssi::settings_get_int('nicklist_smallest_main'); + if (!$nicklist_toggle + || !$channel || !ref($channel) + || !$channel->isa('Irssi::Channel') + || !$channel->{'names_got'} + || $channel->{'name'} !~ $channel_pattern + || ($smallest_main && $channel->window->{width} < $smallest_main)) { + disable_nicklist; + } else { + my %colour; + my @nicks = $channel->nicks(); + my $max_nicks = Irssi::settings_get_int('nicklist_max_users'); + if ($max_nicks && @nicks > $max_nicks) { + disable_nicklist; + } else { + enable_nicklist; + return unless $enabled > 0; + foreach my $nick (sort { $a->{_irssi} <=> $b->{_irssi} } @nicks) { + $colour{$nick->{nick}} = ($ansifier && $colourer) ? $ansifier->($colourer->($active->{active}{server}{tag}, $channel->{name}, $nick->{nick}, 0)) : ''; + } + print($fifo "BEGIN\n"); + my $gone_sort = Irssi::settings_get_bool('nicklist_gone_sort'); + my $prefer_real; + if (exists $Irssi::Script::{'realnames::'}) { + my $code = "Irssi::Script::realnames"->can('use_realnames'); + $prefer_real = $code && $code->($channel); + } + my $_real = sub { + my $nick = shift; + $prefer_real && length $nick->{'realname'} ? $nick->{'realname'} : $nick->{'nick'} + }; + foreach my $nick (sort {($a->{'op'}?'1':$a->{'halfop'}?'2':$a->{'voice'}?'3':$a->{'other'}>32?'0':'4').($gone_sort?($a->{'gone'}?1:0):'').lc($_real->($a)) + cmp ($b->{'op'}?'1':$b->{'halfop'}?'2':$b->{'voice'}?'3':$b->{'other'}>32?'0':'4').($gone_sort?($b->{'gone'}?1:0):'').lc($_real->($b))} @nicks) { + my $colour = $colour{$nick->{nick}} || "\e[39m"; + $colour = "\e[37m" if $nick->{'gone'}; + print($fifo "NICK"); + if ($nick->{'op'}) { + print($fifo "\e[32m\@$colour".$_real->($nick)."\e[39m"); + } elsif ($nick->{'halfop'}) { + print($fifo "\e[34m%$colour".$_real->($nick)."\e[39m"); + } elsif ($nick->{'voice'}) { + print($fifo "\e[33m+$colour".$_real->($nick)."\e[39m"); + } elsif ($nick->{'other'}>32) { + print($fifo "\e[31m".(chr $nick->{'other'})."$colour".$_real->($nick)."\e[39m"); + } else { + print($fifo " $colour".$_real->($nick)."\e[39m"); + } + print($fifo "\n"); + } + print($fifo "END\n"); + } + } +} + +sub toggle_nicklist { + if ($enabled) { + $nicklist_toggle = undef + } else { + $nicklist_toggle = 1; + } + reset_nicklist("toggle"); +} + +sub switch_channel { + print $fifo "SWITCH_CHANNEL\n" if $fifo; + &reset_nicklist; +} + +sub resized_timed { + Irssi::timeout_remove($resize_timer) if defined $resize_timer; + return if defined $just_launched; + $resize_timer = Irssi::timeout_add_once(1100, \&resized, ''); + #resized(); +} +sub resized { + $resize_timer = undef; + return if defined $just_launched; + return unless $enabled >= 0; + disable_nicklist; + Irssi::timeout_add_once(200, sub{reset_nicklist("terminal resized")}, ''); +} +sub UNLOAD { + disable_nicklist; +} + +Irssi::settings_add_str('tmux_nicklist', 'nicklist_channel_re', '.*'); +Irssi::settings_add_int('tmux_nicklist', 'nicklist_max_users', 0); +Irssi::settings_add_int('tmux_nicklist', 'nicklist_smallest_main', 0); +Irssi::settings_add_int('tmux_nicklist', 'nicklist_pane_width', 13); +Irssi::settings_add_bool('tmux_nicklist', 'nicklist_color', 1); +Irssi::settings_add_bool('tmux_nicklist', 'nicklist_gone_sort', 0); +Irssi::signal_add_last('window item changed', sub{switch_channel("window item changed",@_)}); +Irssi::signal_add_last('window changed', sub{switch_channel("window changed",@_)}); +Irssi::signal_add_last('channel joined', sub{switch_channel("channel joined",@_)}); +Irssi::signal_add('nicklist new', sub{reset_nicklist("nicklist new",@_)}); +Irssi::signal_add('nicklist remove', sub{reset_nicklist("nicklist remove",@_)}); +Irssi::signal_add('nicklist changed', sub{reset_nicklist("nicklist changed",@_)}); +Irssi::signal_add_first('nick mode changed', sub{reset_nicklist("nick mode changed",@_)}); +Irssi::signal_add('gui exit', \&disable_nicklist); +Irssi::signal_add_last('terminal resized', \&resized_timed); + +} else { +my $fifo_path = $ARGV[0]; +my $irssi_pane = $ARGV[1]; +# array to store the current channel nicknames +my @nicknames = (); + +# helper functions for manipulating the terminal +# escape sequences taken from +# http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html +sub enable_mouse { print "\e[?1000h"; } +# recognized sequences +my $MOUSE_SCROLL_DOWN="\e[Ma"; +my $MOUSE_SCROLL_UP="\e[M`"; +my $ARROW_DOWN="\e[B"; +my $ARROW_UP="\e[A"; +my $DOWN="j"; +my $UP="k"; +my $PAGE_DOWN="\e[6~"; +my $PAGE_UP="\e[5~"; +my $PAGE_DOWN_D="d"; +my $PAGE_UP_U="u"; +my $GO_TOP="gg"; +my $GO_BOTTOM="G"; + +my $current_line = 0; +my $sequence = ''; +my ($rows, $cols); + +sub term_size { + split ' ', `stty size`; +} + +sub redraw { + my $last_nick_idx = @nicknames; + my $last_idx = $current_line + $rows; + # normalize last visible index + if ($last_idx > ($last_nick_idx)) { + $last_idx = $last_nick_idx; + } + # redraw visible nicks + for my $i (reverse 1..$rows) { + print "\e[$i;1H\e[K"; + my $idx = $current_line + $i - 1; + if ($idx < $last_idx) { + my $z = 0; my $col = $cols; + for (split /(\e\[(?:\d|;|:|\?|\s)*.)/, $nicknames[$idx]) { + if ($z ^= 1) { + print +(substr $_, 0, $col) if $col > 0; + $col -= length; + } else { + print + } + } + } + } +} + +sub move_down { + $sequence = ''; + my $count = int $_[0]; + my $nickcount = $#nicknames; + return if ($nickcount <= $rows); + if ($count == -1) { + $current_line = $nickcount - $rows + 1; + redraw; + return; + } + my $visible = $nickcount - $current_line - $count + 1; + if ($visible > $rows) { + $current_line += $count; + redraw; + } elsif (($visible + $count) > $rows) { + # scroll the maximum we can + $current_line = $nickcount - $rows + 1; + redraw; + } +} + +sub move_up { + $sequence = ''; + my $count = int $_[0]; + if ($count == -1) { + $current_line = 0; + redraw; + return; + } + return if ($current_line == 0); + $count = 1 if $count == 0; + $current_line -= $count; + $current_line = 0 if $current_line < 0; + redraw; +} + +$SIG{INT} = 'IGNORE'; + +STDOUT->autoflush(1); +# setup terminal so we can listen for individual key presses without echo +`stty -icanon -echo`; + +# open named pipe and setup the 'select' wrapper object for listening on both +# fds(fifo and sdtin) +open my $fifo, "<", $fifo_path or die "can't open $fifo_path: $!"; +my $select = IO::Select->new(); +my @ready; +$select->add($fifo); +$select->add(\*STDIN); + +enable_mouse; +system('tput', 'smcup'); +print "\e[?7l"; #system('tput', 'rmam'); +system('tput', 'civis'); +MAIN: { + while (@ready = $select->can_read) { + foreach my $fd (@ready) { + ($rows, $cols) = term_size; + if ($fd == $fifo) { + while (<$fifo>) { + my $line = $_; + if ($line =~ /^BEGIN/) { + @nicknames = (); + } elsif ($line =~ /^SWITCH_CHANNEL/) { + $current_line = 0; + } elsif ($line =~ /^NICK(.+)$/) { + push @nicknames, $1; + } elsif ($line =~ /^END$/) { + redraw; + last; + } elsif ($line =~ /^EXIT$/) { + last MAIN; + } + } + } else { + my $key = ''; + sysread(STDIN, $key, 1); + $sequence .= $key; + if ($MOUSE_SCROLL_DOWN =~ /^\Q$sequence\E/) { + if ($MOUSE_SCROLL_DOWN eq $sequence) { + move_down 3; + # mouse scroll has two more bytes that I dont use here + # so consume them now to avoid sending unwanted bytes to + # irssi + sysread(STDIN, $key, 2); + } + } elsif ($MOUSE_SCROLL_UP =~ /^\Q$sequence\E/) { + if ($MOUSE_SCROLL_UP eq $sequence) { + move_up 3; + sysread(STDIN, $key, 2); + } + } elsif ($ARROW_DOWN =~ /^\Q$sequence\E/) { + move_down 1 if ($ARROW_DOWN eq $sequence); + } elsif ($ARROW_UP =~ /^\Q$sequence\E/) { + move_up 1 if ($ARROW_UP eq $sequence); + } elsif ($DOWN =~ /^\Q$sequence\E/) { + move_down 1 if ($DOWN eq $sequence); + } elsif ($UP =~ /^\Q$sequence\E/) { + move_up 1 if ($UP eq $sequence); + } elsif ($PAGE_DOWN =~ /^\Q$sequence\E/) { + move_down $rows/2 if ($PAGE_DOWN eq $sequence); + } elsif ($PAGE_UP =~ /^\Q$sequence\E/) { + move_up $rows/2 if ($PAGE_UP eq $sequence); + } elsif ($PAGE_DOWN_D =~ /^\Q$sequence\E/) { + move_down $rows/2 if ($PAGE_DOWN_D eq $sequence); + } elsif ($PAGE_UP_U =~ /^\Q$sequence\E/) { + move_up $rows/2 if ($PAGE_UP_U eq $sequence); + } elsif ($GO_BOTTOM =~ /^\Q$sequence\E/) { + move_down -1 if ($GO_BOTTOM eq $sequence); + } elsif ($GO_TOP =~ /^\Q$sequence\E/) { + move_up -1 if ($GO_TOP eq $sequence); + } else { + # Unrecognized sequences will be send to irssi and its pane + # will be focused + system('tmux', 'send-keys', '-t', $irssi_pane, $sequence); + system('tmux', 'select-pane', '-t', $irssi_pane); + $sequence = ''; + } + } + } + } +} + +close $fifo; + +} diff --git a/home/.irssi/scripts/autorun/trackbar.pl b/home/.irssi/scripts/autorun/trackbar.pl new file mode 100644 index 0000000..8814cab --- /dev/null +++ b/home/.irssi/scripts/autorun/trackbar.pl @@ -0,0 +1,606 @@ +## trackbar.pl +# +# 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 +# been reading that window :) It also removes the previous drawn line, so you +# don't see double lines. +# +# redraw trackbar only works on irssi 0.8.17 or higher. +# +## + +## Usage: +# +# The script works right out of the box, but if you want you can change +# the working by /set'ing the following variables: +# +# Setting: trackbar_style +# Description: This setting will be the color of your trackbar line. +# By default the value will be '%K', only Irssi color +# formats are allowed. If you don't know the color formats +# by heart, you can take a look at the formats documentation. +# You will find the proper docs on http://www.irssi.org/docs. +# +# Setting: trackbar_string +# Description: This is the string that your line will display. This can +# be multiple characters or just one. For example: '~-~-' +# The default setting is '-'. +# Here are some unicode characters you can try: +# "───" => U+2500 => a line +# "═══" => U+2550 => a double line +# "━━━" => U+2501 => a wide line +# "▭ " => U+25ad => a white rectangle +# +# Setting: trackbar_use_status_window +# Description: If this setting is set to OFF, Irssi won't print a trackbar +# in the statuswindow +# +# Setting: trackbar_ignore_windows +# Description: A list of windows where no trackbar should be printed +# +# Setting: trackbar_print_timestamp +# Description: If this setting is set to ON, Irssi will print the formatted +# timestamp in front of the trackbar. +# +# Setting: trackbar_require_seen +# Description: Only clear the trackbar if it has been scrolled to. +# +# Setting: trackbar_all_manual +# Description: Never clear the trackbar until you do /mark. +# +# /mark is a command that will redraw the line at the bottom. +# +# Command: /trackbar, /trackbar goto +# Description: Jump to where the trackbar is, to pick up reading +# +# Command: /trackbar keep +# Description: Keep this window's trackbar where it is the next time +# you switch windows (then this flag is cleared again) +# +# Command: /mark, /trackbar mark +# Description: Remove the old trackbar and mark the bottom of this +# window with a new trackbar +# +# Command: /trackbar markvisible +# Description: Like mark for all visible windows +# +# Command: /trackbar markall +# Description: Like mark for all windows +# +# Command: /trackbar remove +# Description: Remove this window's trackbar +# +# Command: /trackbar removeall +# Description: Remove all windows' trackbars +# +# Command: /trackbar redraw +# Description: Force redraw of trackbars +# +## + +## +# +# For bugreports and other improvements contact one of the authors. +# +# 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 script; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +## + +use strict; +use warnings; +use vars qw($VERSION %IRSSI); + +$VERSION = "2.9"; # a4c78e85092a271 + +%IRSSI = ( + authors => "Peter 'kinlo' Leurs, Uwe Dudenhoeffer, " . + "Michiel Holtkamp, Nico R. Wohlgemuth, " . + "Geert Hauwaerts", + contact => 'peter@pfoe.be', + patchers => 'Johan Kiviniemi (UTF-8), Uwe Dudenhoeffer (on-upgrade-remove-line)', + name => 'trackbar', + description => 'Shows a bar where you have last read a window.', + license => 'GNU General Public License', + url => 'http://www.pfoe.be/~peter/trackbar/', + commands => 'trackbar', +); + +## Comments and remarks. +# +# This script uses settings. +# Use /SET to change the value or /TOGGLE to switch it on or off. +# +# +# Tip: The command 'trackbar' is very useful if you bind that to a key, +# so you can easily jump to the trackbar. Please see 'help bind' for +# more information about keybindings in Irssi. +# +# Command: /BIND meta2-P key F1 +# /BIND F1 command trackbar +# +## + +## Bugfixes and new items in this rewrite. +# +# * Remove all the trackbars before upgrading. +# * New setting trackbar_use_status_window to control the statuswindow trackbar. +# * New setting trackbar_print_timestamp to print a timestamp or not. +# * New command 'trackbar' to scroll up to the trackbar. +# * When resizing your terminal, Irssi will update all the trackbars to the new size. +# * When changing trackbar settings, change all the trackbars to the new settings. +# * New command 'trackbar mark' to draw a new trackbar (The old '/mark'). +# * New command 'trackbar markall' to draw a new trackbar in each window. +# * New command 'trackbar remove' to remove the trackbar from the current window. +# * New command 'trackbar removeall' to remove all the trackbars. +# * Don't draw a trackbar in empty windows. +# * Added a version check to prevent Irssi redraw errors. +# * Fixed a bookmark NULL versus 0 bug. +# * Fixed a remove-line bug in Uwe Dudenhoeffer his patch. +# * New command 'help trackbar' to display the trackbar commands. +# * Fixed an Irssi startup bug, now processing each auto-created window. +# +## + +## Known bugs and the todolist. +# +# Todo: * Instead of drawing a line, invert the line. +# +## + +## 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: +# +# 2.9: - fix crash on /mark in empty window +# 2.8: - fix /^join bug +# 2.7: - add /set trackbar_all_manual option +# 2.5: - merge back on scripts.irssi.org +# - fix /trackbar redraw broken in 2.4 +# - fix legacy encodings +# - add workaround for irssi issue #271 +# 2.4: - add support for horizontal splits +# 2.3: - add some features for seen tracking using other scripts +# 2.0: - big rewrite based on 1.4 +# * removed /tb, you can have it with /alias tb trackbar if you want +# * subcommand and settings changes: +# /trackbar vmark => /trackbar markvisible +# /trackbar scroll => /trackbar goto (or just /trackbar) +# /trackbar help => /help trackbar +# /set trackbar_hide_windows => /set trackbar_ignore_windows +# /set trackbar_timestamp => /set trackbar_print_timestamp +# * magic line strings were removed, just paste the unicode you want! +# * trackbar_timestamp_styled is not currently supported +# 1.9: - add version guard +# 1.8: - sub draw_bar +# 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 +# and everything *should* work. However, this script is being plagued by +# irssi internal bugs. The function Irssi::settings_get_str does NOT handle +# unicode strings properly, hence you will notice problems when setting the bar +# to a unicode char. For changing your bar to utf-8 symbols, read the line sub. +# 1.3: - Upgrade now removes the trackbars. +# - Some code cleanups, other defaults +# - /mark sets the line to the bottom +# 1.2: - Support for utf-8 +# - How the bar looks can now be configured with trackbar_string +# and trackbar_style +# 1.1: - Fixed bug when closing window +# 1.0: - Initial release +# +## + +use Irssi; +use Irssi::TextUI; +use Encode; + +use POSIX qw(strftime); + +sub cmd_help { + my ($args) = @_; + if ($args =~ /^trackbar *$/i) { + print CLIENTCRAP <>%n %_Scriptinfo:%_ Loaded $0 version $1 by $2.', + 'trackbar_wrong_version', '%R>>%n %_Trackbar:%_ Please upgrade your client to 0.8.17 or above if you would like to use this feature of trackbar.', + 'trackbar_all_removed', '%R>>%n %_Trackbar:%_ All the trackbars have been removed.', + 'trackbar_not_found', '%R>>%n %_Trackbar:%_ No trackbar found in this window.', +]); + +my $old_irssi = Irssi::version < 20140701; +sub check_version { + if ($old_irssi) { + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_wrong_version'); + return; + } else { + return 1; + } +} + +sub is_utf8 { + lc Irssi::settings_get_str('term_charset') eq 'utf-8' +} + +my (%config, %keep_trackbar, %unseen_trackbar); + +sub remove_one_trackbar { + my $win = shift; + my $view = shift || $win->view; + my $line = $view->get_bookmark('trackbar'); + if (defined $line) { + my $bottom = $view->{bottom}; + $view->remove_line($line); + $win->command('^scrollback end') if $bottom && !$win->view->{bottom}; + $view->redraw; + } +} + +sub add_one_trackbar_pt1 { + my $win = shift; + my $view = shift || $win->view; + + my $last_cur_line = ($view->{buffer}{cur_line}||+{})->{_irssi}; + $win->print(line($win->{width}), MSGLEVEL_NEVER); + + my $cur_line = ($win->view->{buffer}{cur_line}||+{})->{_irssi}; # get a fresh buffer + + ($last_cur_line//'') ne ($cur_line//'') # printing was successful +} + +sub add_one_trackbar_pt2 { + my $win = shift; + my $view = $win->view; + + $view->set_bookmark_bottom('trackbar'); + $unseen_trackbar{ $win->{_irssi} } = 1; + Irssi::signal_emit("window trackbar added", $win); + $view->redraw; +} + +sub update_one_trackbar { + my $win = shift; + my $view = shift || $win->view; + my $force = shift; + my $ignored = win_ignored($win, $view); + my $success; + + $success = add_one_trackbar_pt1($win, $view) ? 1 : 0 + if $force || !$ignored; + + remove_one_trackbar($win, $view) + if ( $success || !defined $success ) && ( $force || !defined $force || !$ignored ); + + add_one_trackbar_pt2($win) + if $success; +} + +sub win_ignored { + my $win = shift; + my $view = shift || $win->view; + return 1 unless $view->{buffer}{lines_count}; + return 1 if $win->{name} eq '(status)' && !$config{use_status_window}; + no warnings 'uninitialized'; + return 1 if grep { $win->{name} eq $_ || $win->{refnum} eq $_ + || $win->get_active_name eq $_ } @{ $config{ignore_windows} }; + return 0; +} + +sub sig_window_changed { + my ($newwindow, $oldwindow) = @_; + return unless $oldwindow; + redraw_one_trackbar($newwindow) unless $old_irssi; + trackbar_update_seen($newwindow); + return if delete $keep_trackbar{ $oldwindow->{_irssi} }; + trackbar_update_seen($oldwindow); + return if $config{require_seen} && $unseen_trackbar{ $oldwindow->{_irssi } }; + return if $config{all_manual}; + update_one_trackbar($oldwindow, undef, 0); +} + +sub trackbar_update_seen { + my $win = shift; + return unless $win; + return unless $unseen_trackbar{ $win->{_irssi} }; + + my $view = $win->view; + my $line = $view->get_bookmark('trackbar'); + unless ($line) { + delete $unseen_trackbar{ $win->{_irssi} }; + Irssi::signal_emit("window trackbar seen", $win); + return; + } + my $startline = $view->{startline}; + return unless $startline; + + if ($startline->{info}{time} < $line->{info}{time} + || $startline->{_irssi} == $line->{_irssi}) { + delete $unseen_trackbar{ $win->{_irssi} }; + Irssi::signal_emit("window trackbar seen", $win); + } +} + +sub screen_length; +{ local $@; + eval { require Text::CharWidth; }; + unless ($@) { + *screen_length = sub { Text::CharWidth::mbswidth($_[0]) }; + } + else { + *screen_length = sub { + my $temp = shift; + Encode::_utf8_on($temp) if is_utf8(); + length($temp) + }; + } +} + +{ my %strip_table = ( + (map { $_ => '' } (split //, '04261537' . 'kbgcrmyw' . 'KBGCRMYW' . 'U9_8I:|FnN>#[' . 'pP')), + (map { $_ => $_ } (split //, '{}%')), + ); + sub c_length { + my $o = Irssi::strip_codes($_[0]); + $o =~ s/(%(%|Z.{6}|z.{6}|X..|x..|.))/exists $strip_table{$2} ? $strip_table{$2} : + $2 =~ m{x(?:0[a-f]|[1-6][0-9a-z]|7[a-x])|z[0-9a-f]{6}}i ? '' : $1/gex; + screen_length($o) + } +} + +sub line { + my ($width, $time) = @_; + my $string = $config{string}; + $string = ' ' unless length $string; + $time ||= time; + + Encode::_utf8_on($string) if is_utf8(); + my $length = c_length($string); + + my $format = ''; + if ($config{print_timestamp}) { + $format = $config{timestamp_str}; + $format =~ y/%/\01/; + $format =~ s/\01\01/%/g; + $format = strftime($format, localtime $time); + $format =~ y/\01/%/; + } + + my $times = $width / $length; + $times += 1 if $times != int $times; + my $style = "$config{style}"; + Encode::_utf8_on($style) if is_utf8(); + $format .= $style; + $width -= c_length($format); + $string x= $times; + chop $string while length $string && c_length($string) > $width; + return $format . $string; +} + +sub remove_all_trackbars { + for my $window (Irssi::windows) { + next unless ref $window; + remove_one_trackbar($window); + } +} + +sub UNLOAD { + remove_all_trackbars(); +} + +sub redraw_one_trackbar { + my $win = shift; + my $view = $win->view; + my $line = $view->get_bookmark('trackbar'); + return unless $line; + my $bottom = $view->{bottom}; + $win->print_after($line, MSGLEVEL_NEVER, line($win->{width}, $line->{info}{time}), + $line->{info}{time}); + $view->set_bookmark('trackbar', $win->last_line_insert); + $view->remove_line($line); + $win->command('^scrollback end') if $bottom && !$win->view->{bottom}; + $view->redraw; +} + +sub redraw_trackbars { + return unless check_version(); + for my $win (Irssi::windows) { + next unless ref $win; + redraw_one_trackbar($win); + } +} + +sub goto_trackbar { + my $win = Irssi::active_win; + my $line = $win->view->get_bookmark('trackbar'); + + if ($line) { + $win->command("scrollback goto ". strftime("%d %H:%M:%S", localtime($line->{info}{time}))); + } else { + $win->printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_not_found'); + } +} + +sub cmd_mark { + update_one_trackbar(Irssi::active_win, undef, 1); +} + +sub cmd_markall { + for my $window (Irssi::windows) { + next unless ref $window; + update_one_trackbar($window); + } +} + +sub signal_stop { + Irssi::signal_stop; +} + +sub cmd_markvisible { + my @wins = Irssi::windows; + my $awin = + my $bwin = Irssi::active_win; + my $awin_counter = 0; + Irssi::signal_add_priority('window changed' => 'signal_stop', -99); + do { + Irssi::active_win->command('window up'); + $awin = Irssi::active_win; + update_one_trackbar($awin); + ++$awin_counter; + } until ($awin->{refnum} == $bwin->{refnum} || $awin_counter >= @wins); + Irssi::signal_remove('window changed' => 'signal_stop'); +} + +sub cmd_trackbar_remove_one { + remove_one_trackbar(Irssi::active_win); +} + +sub cmd_remove_all_trackbars { + remove_all_trackbars(); + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_all_removed'); +} + +sub cmd_keep_once { + $keep_trackbar{ Irssi::active_win->{_irssi} } = 1; +} + +sub trackbar_runsub { + my ($data, $server, $item) = @_; + $data =~ s/\s+$//g; + + if ($data) { + Irssi::command_runsub('trackbar', $data, $server, $item); + } else { + goto_trackbar(); + } +} + +sub update_config { + my $was_status_window = $config{use_status_window}; + $config{style} = Irssi::settings_get_str('trackbar_style'); + $config{string} = Irssi::settings_get_str('trackbar_string'); + $config{require_seen} = Irssi::settings_get_bool('trackbar_require_seen'); + $config{all_manual} = Irssi::settings_get_bool('trackbar_all_manual'); + $config{ignore_windows} = [ split /[,\s]+/, Irssi::settings_get_str('trackbar_ignore_windows') ]; + $config{use_status_window} = Irssi::settings_get_bool('trackbar_use_status_window'); + $config{print_timestamp} = Irssi::settings_get_bool('trackbar_print_timestamp'); + if (defined $was_status_window && $was_status_window != $config{use_status_window}) { + if (my $swin = Irssi::window_find_name('(status)')) { + if ($config{use_status_window}) { + update_one_trackbar($swin); + } + else { + remove_one_trackbar($swin); + } + } + } + if ($config{print_timestamp}) { + my $ts_format = Irssi::settings_get_str('timestamp_format'); + my $ts_theme = Irssi::current_theme->get_format('fe-common/core', 'timestamp'); + my $render_str = Irssi::current_theme->format_expand($ts_theme); + (my $ts_escaped = $ts_format) =~ s/([%\$])/$1$1/g; + $render_str =~ s/(?|\$(.)(?!\w)|\$\{(\w+)\})/$1 eq 'Z' ? $ts_escaped : $1/ge; + $config{timestamp_str} = $render_str; + } + redraw_trackbars() unless $old_irssi; +} + +Irssi::settings_add_str('trackbar', 'trackbar_string', is_utf8() ? "\x{2500}" : '-'); +Irssi::settings_add_str('trackbar', 'trackbar_style', '%K'); +Irssi::settings_add_str('trackbar', 'trackbar_ignore_windows', ''); +Irssi::settings_add_bool('trackbar', 'trackbar_use_status_window', 1); +Irssi::settings_add_bool('trackbar', 'trackbar_print_timestamp', 0); +Irssi::settings_add_bool('trackbar', 'trackbar_require_seen', 0); +Irssi::settings_add_bool('trackbar', 'trackbar_all_manual', 0); + +update_config(); + +Irssi::signal_add_last( 'mainwindow resized' => 'redraw_trackbars') + unless $old_irssi; + +Irssi::signal_register({'window trackbar added' => [qw/Irssi::UI::Window/]}); +Irssi::signal_register({'window trackbar seen' => [qw/Irssi::UI::Window/]}); +Irssi::signal_register({'gui page scrolled' => [qw/Irssi::UI::Window/]}); +Irssi::signal_add_last('gui page scrolled' => 'trackbar_update_seen'); + +Irssi::signal_add('setup changed' => 'update_config'); +Irssi::signal_add_priority('session save' => 'remove_all_trackbars', Irssi::SIGNAL_PRIORITY_HIGH-1); + +Irssi::signal_add('window changed' => 'sig_window_changed'); + +Irssi::command_bind('trackbar goto' => 'goto_trackbar'); +Irssi::command_bind('trackbar keep' => 'cmd_keep_once'); +Irssi::command_bind('trackbar mark' => 'cmd_mark'); +Irssi::command_bind('trackbar markvisible' => 'cmd_markvisible'); +Irssi::command_bind('trackbar markall' => 'cmd_markall'); +Irssi::command_bind('trackbar remove' => 'cmd_trackbar_remove_one'); +Irssi::command_bind('trackbar removeall' => 'cmd_remove_all_trackbars'); +Irssi::command_bind('trackbar redraw' => 'redraw_trackbars'); +Irssi::command_bind('trackbar' => 'trackbar_runsub'); +Irssi::command_bind('mark' => 'cmd_mark'); +Irssi::command_bind_last('help' => 'cmd_help'); + +Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_loaded', $IRSSI{name}, $VERSION, $IRSSI{authors}); + +# workaround for issue #271 +{ package Irssi::Nick } diff --git a/home/.irssi/scripts/autorun/uberprompt.pl b/home/.irssi/scripts/autorun/uberprompt.pl new file mode 100644 index 0000000..b54244c --- /dev/null +++ b/home/.irssi/scripts/autorun/uberprompt.pl @@ -0,0 +1,774 @@ +=pod + +=head1 NAME + +uberprompt.pl + +=head1 DESCRIPTION + +This script replaces the default prompt status-bar item with one capable of +displaying additional information, under either user control or via scripts. + +=head1 INSTALLATION + +Copy into your F<~/.irssi/scripts/> directory and load with +C>. + +It is recommended that you make it autoload in one of the +L. + +=head1 SETUP + +If you have a custom prompt format, you may need to copy it to the +uberprompt_format setting. See below for details. + +=head1 USAGE + +Although the script is designed primarily for other scripts to set +status information into the prompt, the following commands are available: + +=over 4 + +=item * CmsgE> + +Sets the prompt to the given argument. Any use of C<$p> in the argument will +be replaced by the original prompt content. + +A parameter corresponding to the C constants listed below is required, in +the format C + +=item * C + +Clears the additional data provided to the prompt. + +=item * C + +Eenables the uberprompt (things may get confused if this is used +whilst the prompt is already enabled) + +=item * C + +Restore the original irssi prompt and prompt_empty statusbars. unloading the +script has the same effect. + +=item * C + +show help for uberprompt commands + +=back + +=head1 SETTINGS + +=head2 UBERPROMPT FORMAT + +CformatE> + +The default is C<[$*$uber]>, which is the same as the default provided in +F. + +Changing this setting will update the prompt immediately, unlike editing your theme. + +An additional variable available within this format is C<$uber>, which expands to +the content of prompt data provided with the C or C +placement argument. + +For all other placement arguments, it will expand to the empty string. + +B This setting completely overrides the C line in your +.theme file, and may cause unexpected behaviour if your theme wishes to set a +different form of prompt. It can be simply copied from the theme file into the +above setting. + +=head2 OTHER SETTINGS + +=over 4 + +=item * C + +Boolean value, which determines if uberprompt should enable itself automatically +upon loading. If Off, it must be enabled manually with C. Defaults to On. + +=item * C + +Boolean value, which determines if uberprompt should print debugging information. +Defaults to Off, and should probably be left that way unless requested for bug-tracing +purposes. + +=item * C + +String value containing the format-string which uberprompt uses to display the +prompt. Defaults to "C<[$*$uber] >", where C<$*> is the content the prompt would +normally display, and C<$uber> is a placeholder variable for dynamic content, as +described in the section above. + +=item * C + +String value which can contain one or more commands to be run whenever the uberprompt +is enabled, either via autostart, or C. Defaults to the empty string, in +which case no commands are run. Some examples include: + +C or + +C for those using vim_mode.pl who want +the command status indicator on the prompt line. + +=item * C + +String value, defaulting to the empty string, which can contain commands which +are executed when the uberprompt is disabled, either by unloading the script, +or by the command C. + +=item * C + +Boolean value, defaults to Off. If enabled, the format string for the prompt +will be subject to the I section of the theme. The most obvious +effect of this is that bracket characters C<[ ]> are displayed in a different +colour, typically quite dark. + +=back + +B For both C settings above, multiple commands can +be chained together in the form C. The C<^> prevents +any output from the commands (such as error messages) being displayed. + +=head2 SCRIPTING USAGE + +The primary purpose of uberprompt is to be used by other scripts to +display information in a way that is not possible by printing to the active +window or using statusbar items. + +The content of the prompt can be set from other scripts via the C<"change prompt"> +signal. + +For Example: + + signal_emit 'change prompt' 'some_string', UberPrompt::UP_INNER; + +will set the prompt to include that content, by default 'C<[$* some_string]>' + +The possible position arguments are the following strings: + +=over 4 + +=item * C - place the provided string before the prompt - C<$string$prompt> + +=item * C - place the provided string inside the prompt - C<{prompt $* $string}> + +=item * C - place the provided string after the prompt - C<$prompt$string> + +=item * C - replace the prompt with the provided string - C<$string> + +=back + +All strings may use the special variable 'C<$prompt>' to include the prompt +verbatim at that position in the string. It is probably only useful for +the C mode however. '$C' will include the prompt, minus any +trailing whitespace. + +=head2 CHANGE NOTIFICATIONS + +You can also be notified when the prompt changes in response to the previous +signal or manual C commands via: + + signal_add 'prompt changed', sub { my ($text, $len) = @_; ... do something ... }; + +This callback will occur whenever the contents of the prompt is changed. + + +=head2 NOTES FOR SCRIPT WRITERS: + +The following code snippet can be used within your own script as a preamble +to ensure that uberprompt is loaded before your script to avoid +any issues with loading order. It first checks if uberprompt is loaded, and +if not, attempts to load it. If the load fails, the script will die +with an error message, otherwise it will call your app_init() function. + +I<---- start of snippet ----> + + my $DEBUG_ENABLED = 0; + sub DEBUG () { $DEBUG_ENABLED } + + # check we have uberprompt loaded. + + sub script_is_loaded { + return exists($Irssi::Script::{$_[0] . '::'}); + } + + if (not script_is_loaded('uberprompt')) { + + print "This script requires 'uberprompt.pl' in order to work. " + . "Attempting to load it now..."; + + Irssi::signal_add('script error', 'load_uberprompt_failed'); + Irssi::command("script load uberprompt.pl"); + + unless(script_is_loaded('uberprompt')) { + load_uberprompt_failed("File does not exist"); + } + app_init(); + } else { + app_init(); + } + + sub load_uberprompt_failed { + Irssi::signal_remove('script error', 'load_uberprompt_failed'); + + print "Script could not be loaded. Script cannot continue. " + . "Check you have uberprompt.pl installed in your path and " + . "try again."; + + die "Script Load Failed: " . join(" ", @_); + } + +I<---- end of snippet ----> + +=head1 AUTHORS + +Copyright E 2011 Tom Feist Cshabble+irssi@metavore.orgE> + +=head1 LICENCE + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +=head1 BUGS + +=over 4 + +=item * + +Resizing the terminal rapidly whilst using this script in debug mode may cause +irssi to crash. See bug report at http://bugs.irssi.org/index.php?do=details&task_id=772 for details. + +=back + +=head1 TODO + +=over 4 + +=item * report failure (somehow) to clients if hte prompt is disabled. + +=item * fix issue at autorun startup with sbar item doesn't exist. + +=back + +=cut + +use strict; +use warnings; + +use Irssi; +use Irssi::TextUI; +use Data::Dumper; + +{ package Irssi::Nick } # magic. + +our $VERSION = "0.2"; +our %IRSSI = + ( + authors => "shabble", + contact => 'shabble+irssi@metavore.org, shabble@#irssi/Freenode', + name => "uberprompt", + description => "Helper script for dynamically adding text " + . "into the input-bar prompt.", + license => "MIT", + changed => "24/7/2010" + ); + + +my $DEBUG_ENABLED = 0; +sub DEBUG { $DEBUG_ENABLED } + +my $prompt_data = ''; +my $prompt_data_pos = 'UP_INNER'; + +my $prompt_last = ''; +my $prompt_format = ''; +my $prompt_format_empty = ''; + +# flag to indicate whether rendering of hte prompt should allow the replaces +# theme formats to be applied to the content. +my $use_replaces = 0; +my $trim_data = 0; + +my $emit_request = 0; + +my $expando_refresh_timer; +my $expando_vars = {}; + +my $init_callbacks = {load => '', unload => ''}; + +pre_init(); + +sub pre_init { + Irssi::command('statusbar prompt reset'); + init(); +} + +sub prompt_subcmd_handler { + my ($data, $server, $item) = @_; + #$data =~ s/\s+$//g; # strip trailing whitespace. + Irssi::command_runsub('prompt', $data, $server, $item); +} + +sub _error($) { + my ($msg) = @_; + Irssi::active_win->print($msg, MSGLEVEL_CLIENTERROR); +} + +sub _debug_print($) { + return unless DEBUG; + my ($msg) = @_; + Irssi::active_win->print($msg, MSGLEVEL_CLIENTCRAP); +} + +sub _print_help { + my ($args) = @_; + if ($args =~ m/^\s*prompt/i) { + my @help_lines = + ( + "", + "PROMPT ON", + "PROMPT OFF", + "PROMPT CLEAR", + "PROMPT SET { -pre | -post | -only | -inner } ", + "", + "Commands for manipulating the UberPrompt.", + "", + "/PROMPT ON enables uberprompt, replacing the existing prompt ", + " statusbar-item", + "/PROMPT OFF disables it, and restores the original prompt item", + "/PROMPT CLEAR resets the value of any additional data set by /PROMPT SET", + " or a script", + "/PROMPT SET changes the contents of the prompt, according to the mode", + " and content provided.", + " { -inner sets the value of the \$uber psuedo-variable in the", + " /set uberprompt_format setting.", + " | -pre places the content before the current prompt string", + " | -post places the content after the prompt string", + " | -only replaces the entire prompt contents with the given string }", + "", + "See Also:", + '', + '/SET uberprompt_format -- defaults to "[$*$uber] "', + '/SET uberprompt_format_empty -- defaults to "[$*] "', + "/SET uberprompt_autostart -- determines whether /PROMPT ON is run", + " automatically when the script loads", + "/SET uberprompt_use_replaces -- toggles the use of the current theme", + " \"replaces\" setting. Especially", + " noticeable on brackets \"[ ]\" ", + "/SET uberprompt_trim_data -- defaults to off. Removes whitespace from", + " both sides of the \$uber result.", + "", + ); + + Irssi::print($_, MSGLEVEL_CLIENTCRAP) for @help_lines; + Irssi::signal_stop; + } +} + +sub UNLOAD { + deinit(); +} + +sub exp_lbrace() { '{' } +sub exp_rbrace() { '}' } + +sub deinit { + Irssi::expando_destroy('lbrace'); + Irssi::expando_destroy('rbrace'); + + if (Irssi::settings_get_bool('uberprompt_restore_on_exit')) { + # remove uberprompt and return the original ones. + print "Removing uberprompt and restoring original"; + restore_prompt_items(); + } +} + +sub init { + Irssi::statusbar_item_register('uberprompt', 0, 'uberprompt_draw'); + + # TODO: flags to prevent these from being recomputed? + Irssi::expando_create('lbrace', \&exp_lbrace, {}); + Irssi::expando_create('rbrace', \&exp_rbrace, {}); + + Irssi::settings_add_str ('uberprompt', 'uberprompt_format', '[$*$uber] '); + Irssi::settings_add_str ('uberprompt', 'uberprompt_format_empty', '[$*] '); + + Irssi::settings_add_str ('uberprompt', 'uberprompt_load_hook', ''); + Irssi::settings_add_str ('uberprompt', 'uberprompt_unload_hook', ''); + + Irssi::settings_add_bool('uberprompt', 'uberprompt_debug', 0); + Irssi::settings_add_bool('uberprompt', 'uberprompt_autostart', 1); + Irssi::settings_add_bool ('uberprompt', 'uberprompt_restore_on_exit', 1); + + Irssi::settings_add_bool('uberprompt', 'uberprompt_use_replaces', 0); + Irssi::settings_add_bool('uberprompt', 'uberprompt_trim_data', 0); + + Irssi::command_bind("prompt", \&prompt_subcmd_handler); + Irssi::command_bind('prompt on', \&replace_prompt_items); + Irssi::command_bind('prompt off', \&restore_prompt_items); + Irssi::command_bind('prompt set', \&cmd_prompt_set); + Irssi::command_bind('prompt clear', + sub { + Irssi::signal_emit 'change prompt', '$p', 'UP_POST'; + }); + + my $prompt_set_args_format = "inner pre post only"; + Irssi::command_set_options('prompt set', $prompt_set_args_format); + + Irssi::command_bind('help', \&_print_help); + + Irssi::signal_add('setup changed', \&reload_settings); + + # intialise the prompt format. + reload_settings(); + + # make sure we redraw when necessary. + Irssi::signal_add('window changed', \&uberprompt_refresh); + Irssi::signal_add('window name changed', \&uberprompt_refresh); + Irssi::signal_add('window changed automatic', \&uberprompt_refresh); + Irssi::signal_add('window item changed', \&uberprompt_refresh); + Irssi::signal_add('window item server changed', \&uberprompt_refresh); + Irssi::signal_add('window server changed', \&uberprompt_refresh); + Irssi::signal_add('server nick changed', \&uberprompt_refresh); + + Irssi::signal_add('nick mode changed', \&refresh_if_me); + + # install our statusbars if required. + if (Irssi::settings_get_bool('uberprompt_autostart')) { + replace_prompt_items(); + } + + # API signals + + Irssi::signal_register({'change prompt' => [qw/string string/]}); + Irssi::signal_add('change prompt' => \&change_prompt_handler); + + # other scripts (specifically overlay/visual) can subscribe to + # this event to be notified when the prompt changes. + # arguments are new contents (string), new length (int) + Irssi::signal_register({'prompt changed' => [qw/string int/]}); + Irssi::signal_register({'prompt length request' => []}); + + Irssi::signal_add('prompt length request', \&length_request_handler); +} + +sub cmd_prompt_set { + my $args = shift; + my @options_list = Irssi::command_parse_options "prompt set", $args; + if (@options_list) { + my ($options, $rest) = @options_list; + + my @opt_modes = keys %$options; + if (@opt_modes != 1) { + _error '%_/prompt set%_ must specify exactly one mode of' + . ' {-inner, -only, -pre, -post}'; + return; + } + + my $mode = 'UP_' . uc($opt_modes[0]); + + Irssi::signal_emit 'change prompt', $rest, $mode; + } else { + _error ('%_/prompt set%_ must specify a mode {-inner, -only, -pre, -post}'); + } +} + +sub refresh_if_me { + my ($channel, $nick) = @_; + + return unless $channel and $nick; + + my $server = Irssi::active_server; + my $window = Irssi::active_win; + + return unless $server and $window; + + my $my_chan = $window->{active}->{name}; + my $my_nick = $server->parse_special('$N'); + + return unless $my_chan and $my_nick; + + _debug_print "Chan: $channel->{name}, " + . "nick: $nick->{nick}, " + . "me: $my_nick, chan: $my_chan"; + + if ($my_chan eq $channel->{name} and $my_nick eq $nick->{nick}) { + uberprompt_refresh(); + } +} + +sub length_request_handler { + $emit_request = 1; + uberprompt_render_prompt(); + $emit_request = 0; +} + +sub reload_settings { + + $use_replaces = Irssi::settings_get_bool('uberprompt_use_replaces'); + $DEBUG_ENABLED = Irssi::settings_get_bool('uberprompt_debug'); + + $init_callbacks = { + load => Irssi::settings_get_str('uberprompt_load_hook'), + unload => Irssi::settings_get_str('uberprompt_unload_hook'), + }; + + if (DEBUG) { + Irssi::signal_add 'prompt changed', 'debug_prompt_changed'; + } else { + Irssi::signal_remove 'prompt changed', 'debug_prompt_changed'; + } + + my $new = Irssi::settings_get_str('uberprompt_format'); + + if ($prompt_format ne $new) { + _debug_print("Updated prompt format"); + $prompt_format = $new; + $prompt_format =~ s/\$uber/\$\$uber/; + Irssi::abstracts_register(['uberprompt', $prompt_format]); + + $expando_vars = {}; + + # TODO: something clever here to check if we need to schedule + # an update timer or something, rather than just refreshing on + # every possible activity in init() + while ($prompt_format =~ m/(?{$var_name} = Irssi::parse_special($1); + } + } + + $new = Irssi::settings_get_str('uberprompt_format_empty'); + + if ($prompt_format_empty ne $new) { + _debug_print("Updated prompt format"); + $prompt_format_empty = $new; + $prompt_format_empty =~ s/\$uber/\$\$uber/; + Irssi::abstracts_register(['uberprompt_empty', $prompt_format_empty]); + + $expando_vars = {}; + + # TODO: something clever here to check if we need to schedule + # an update timer or something, rather than just refreshing on + # every possible activity in init() + while ($prompt_format_empty =~ m/(?{$var_name} = Irssi::parse_special($1); + } + } + + $trim_data = Irssi::settings_get_bool('uberprompt_trim_data'); +} + +sub debug_prompt_changed { + my ($text, $len) = @_; + + $text =~ s/%/%%/g; + + print "DEBUG_HANDLER: Prompt Changed to: \"$text\", length: $len"; +} + +sub change_prompt_handler { + my ($text, $pos) = @_; + + # fix for people who used prompt_info and hence the signal won't + # pass the second argument. + $pos = 'UP_INNER' unless defined $pos; + _debug_print("Got prompt change signal with: $text, $pos"); + + my ($changed_text, $changed_pos); + $changed_text = defined $prompt_data ? $prompt_data ne $text : 1; + $changed_pos = defined $prompt_data_pos ? $prompt_data_pos ne $pos : 1; + + $prompt_data = $text; + $prompt_data_pos = $pos; + + if ($changed_text || $changed_pos) { + _debug_print("Redrawing prompt"); + uberprompt_refresh(); + } +} + +sub _escape_prompt_special { + my $str = shift; + $str =~ s/\$/\$\$/g; + $str =~ s/\\/\\\\/g; + #$str =~ s/%/%%/g; + $str =~ s/{/\${lbrace}/g; + $str =~ s/}/\${rbrace}/g; + + return $str; +} + +sub uberprompt_render_prompt { + + my $window = Irssi::active_win; + my $prompt_arg = ''; + + # default prompt sbar arguments (from config) + if (scalar( () = $window->items )) { + $prompt_arg = '$[.15]{itemname}'; + } else { + $prompt_arg = '${winname}'; + } + + my $prompt = ''; # rendered content of the prompt. + my $theme = Irssi::current_theme; + + my $arg = $use_replaces ? 0 : Irssi::EXPAND_FLAG_IGNORE_REPLACES; + + if ($prompt_data && (!$trim_data || trim($prompt_data))) { + $prompt = $theme->format_expand("{uberprompt $prompt_arg}", $arg); + } + else { + $prompt = $theme->format_expand("{uberprompt_empty $prompt_arg}", $arg); + } + + if ($prompt_data_pos eq 'UP_ONLY') { + $prompt =~ s/\$\$uber//; # no need for recursive prompting, I hope. + + # TODO: only compute this if necessary? + my $prompt_nt = $prompt; + $prompt_nt =~ s/\s+$//; + + my $pdata_copy = $prompt_data; + + $pdata_copy =~ s/\$prompt_nt/$prompt_nt/; + $pdata_copy =~ s/\$prompt/$prompt/; + + $prompt = $pdata_copy; + + } elsif ($prompt_data_pos eq 'UP_INNER' && defined $prompt_data) { + + my $esc = _escape_prompt_special($prompt_data); + $esc = $trim_data ? trim($esc) : $esc; + $prompt =~ s/\$\$uber/$esc/; + + } else { + # remove the $uber marker + $prompt =~ s/\$\$uber//; + + # and add the additional text at the appropriate position. + if ($prompt_data_pos eq 'UP_PRE') { + $prompt = $prompt_data . $prompt; + } elsif ($prompt_data_pos eq 'UP_POST') { + $prompt .= $prompt_data; + } + } + + _debug_print("rendering with: $prompt"); + + + if (($prompt ne $prompt_last) or $emit_request) { + + # _debug_print("Emitting prompt changed signal"); + # my $exp = Irssi::current_theme()->format_expand($text, 0); + my $ps = Irssi::parse_special($prompt); + + Irssi::signal_emit('prompt changed', $ps, length($ps)); + $prompt_last = $prompt; + } + return $prompt; +} + +sub uberprompt_draw { + my ($sb_item, $get_size_only) = @_; + + my $prompt = uberprompt_render_prompt(); + + my $ret = $sb_item->default_handler($get_size_only, $prompt, '', 0); + _debug_print("redrawing with: $prompt"); + return $ret; +} + +sub uberprompt_refresh { + Irssi::statusbar_items_redraw('uberprompt'); +} + +sub replace_prompt_items { + # remove existing ones. + _debug_print("Removing original prompt"); + + _sbar_command('prompt', 'remove', 'prompt'); + _sbar_command('prompt', 'remove', 'prompt_empty'); + + # add the new one. + + _sbar_command('prompt', 'add', 'uberprompt', + qw/-alignment left -before input -priority '-1'/); + + _sbar_command('prompt', 'position', '100'); + + my $load_hook = $init_callbacks->{load}; + if (defined $load_hook and length $load_hook) { + eval { + Irssi::command($load_hook); + }; + if ($@) { + _error("Uberprompt user load-hook command ($load_hook) failed: $@"); + } + } + +} + +sub restore_prompt_items { + + _sbar_command('prompt', 'remove', 'uberprompt'); + + _debug_print("Restoring original prompt"); + + _sbar_command('prompt', 'reset'); + + my $unload_hook = $init_callbacks->{unload}; + + if (defined $unload_hook and length $unload_hook) { + eval { + Irssi::command($unload_hook); + }; + if ($@) { + _error("Uberprompt user unload-hook command ($unload_hook) failed: $@"); + } + } +} + +sub _sbar_command { + my ($bar, $cmd, $item, @args) = @_; + + my $args_str = join ' ', @args; + + $args_str .= ' ' if length $args_str && defined $item; + + my $command = sprintf 'STATUSBAR %s %s %s%s', + $bar, $cmd, $args_str, defined $item ? $item : ''; + + _debug_print("Running command: $command"); + Irssi::command($command); +} + +sub trim { + my $string = shift; + + $string =~ s/^\s*//; + $string =~ s/\s*$//; + + return $string; +} diff --git a/home/.irssi/scripts/clones_scanner.pl b/home/.irssi/scripts/clones_scanner.pl new file mode 100755 index 0000000..09e6bfb --- /dev/null +++ b/home/.irssi/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 reset to defaults with /set -default +# +# /set clones_scanner_maxtime