Notificações do irssi no macOS: menções, ligação perdida e falta de autenticação
Como receber notificações nativas no macOS quando o irssi, a correr num servidor remoto dentro de um screen, te menciona, perde a ligação ou fica por autenticar. Inclui a definição que faz o highlight apanhar o nick em qualquer posição da frase.
Um irssi que corre num servidor remoto, dentro de um screen, não consegue mostrar notificações no teu ecrã. Quando alguém te menciona ou a ligação cai, só dás por isso ao abrir o terminal.
Este tutorial monta, no macOS, notificações nativas para três situações: uma menção ao teu nick, a perda de ligação ao servidor IRC e uma ligação restabelecida sem autenticação no NickServ. Não usa serviços externos, o que evita enviar o texto das conversas para terceiros.
Requisitos: irssi com suporte Perl no servidor, acesso ssh por chave a partir do Mac, e macOS.
Como funciona
irssi (servidor) --escreve--> ~/.irssi/fnotify
|
Mac: ssh + tail -F <------lê---------+
|
+--> osascript --> notificação nativa do macOS
Um script Perl no irssi escreve uma linha por aviso num ficheiro. No Mac, um agente launchd mantém um ssh ... tail -F aberto sobre esse ficheiro e transforma cada linha numa notificação. A limitação é que só recebes avisos com o Mac ligado e com rede: o que acontece com ele desligado perde-se.
O script do irssi
Cria ~/.irssi/scripts/fnotify.pl no servidor:
# fnotify.pl - escreve avisos (highlights, ligacao perdida, autenticacao em falta)
# num ficheiro, para serem lidos por outro processo (ex.: tail -F por ssh).
#
# Formato, uma linha por aviso, campos separados por TAB:
# <tipo> TAB <titulo> TAB <texto>
# tipos: hilight | disconnect | auth
use strict;
use warnings;
use Irssi;
use vars qw($VERSION %IRSSI);
$VERSION = '0.1';
%IRSSI = (
name => 'fnotify',
description => 'Escreve highlights, desligacoes e falhas de autenticacao num ficheiro',
license => 'GPLv2',
);
my %last; # "tipo|servidor" => time do ultimo aviso (evita duplicados)
my $MAX_SIZE = 100_000;
sub clean {
my ($s, $max) = @_;
$s = '' unless defined $s;
$s =~ s/[\x00-\x1f\x7f]+/ /g; # tabs, newlines e controlos nunca saem do script
$s =~ s/^\s+|\s+$//g;
$s = substr($s, 0, $max - 3) . '...' if length($s) > $max;
return $s;
}
sub notify {
my ($type, $title, $text, $dedupe_key, $window) = @_;
$window = 60 unless defined $window;
if (defined $dedupe_key) {
my $k = "$type|$dedupe_key";
return if $last{$k} && time() - $last{$k} < $window;
$last{$k} = time();
}
my $file = Irssi::settings_get_str('fnotify_file');
$file =~ s{^~}{$ENV{HOME}};
# ficheiro pequeno: corta quando passa o limite
if (-s $file && -s $file > $MAX_SIZE) {
if (open(my $t, '>', $file)) { close($t); }
}
my $old = umask(0077);
if (open(my $fh, '>>', $file)) {
print $fh join("\t", $type, clean($title, 80), clean($text, 300)), "\n";
close($fh);
} else {
Irssi::print("fnotify: nao consegui escrever em $file: $!");
}
umask($old);
}
# 1. highlight: o que o irssi marca como HILIGHT (o teu nick e palavras em /hilight)
Irssi::signal_add('print text', sub {
my ($dest, $text, $stripped) = @_;
my $level = $dest->{level};
return unless $level & Irssi::MSGLEVEL_HILIGHT();
return if $level & Irssi::MSGLEVEL_NOHILIGHT();
my $where = defined $dest->{target} && length $dest->{target} ? $dest->{target} : 'irssi';
notify('hilight', $where, $stripped, "$where|$stripped", 2);
});
# 2. ligacao perdida
Irssi::signal_add('server disconnected', sub {
my ($server) = @_;
my $tag = $server->{tag} // '?';
notify('disconnect', $tag, "Ligacao perdida a $tag", $tag);
});
# 3. ligado mas sem autenticacao: confirma o modo de utilizador passado uns segundos
Irssi::signal_add('event connected', sub {
my ($server) = @_;
my $tag = $server->{tag} // '?';
my $wait = Irssi::settings_get_int('fnotify_auth_wait');
$wait = 30 if $wait < 5;
Irssi::timeout_add_once($wait * 1000, sub {
my $srv = Irssi::server_find_tag($tag) or return;
return unless $srv->{connected};
my $mode = Irssi::settings_get_str('fnotify_auth_mode');
return unless length $mode;
my $um = $srv->{usermode} // '';
return if index($um, $mode) >= 0;
notify('auth', $tag, "Ligado a $tag mas sem autenticar (modo +$mode em falta)", $tag);
}, undef);
});
# 3b. o NickServ a pedir identificacao
Irssi::signal_add('message irc notice', sub {
my ($server, $msg, $nick, $addr, $target) = @_;
return unless defined $nick && lc($nick) eq 'nickserv';
return unless $msg =~ /\b(identify|identif|register(ed)?|registad|password)\b/i;
my $tag = $server->{tag} // '?';
notify('auth', $tag, "NickServ: $msg", $tag);
});
Irssi::settings_add_str('fnotify', 'fnotify_file', '~/.irssi/fnotify');
Irssi::settings_add_str('fnotify', 'fnotify_auth_mode', 'r');
Irssi::settings_add_int('fnotify', 'fnotify_auth_wait', 30);
O bloco do highlight escuta o sinal print text e só avisa quando a linha tem o nível HILIGHT, o que quer dizer que respeita tudo o que o irssi já considera um highlight, incluindo as palavras que definires com /hilight. Para a falta de autenticação o script espera 30 segundos depois de ligar e verifica se o teu modo de utilizador tem a letra r, que na maioria das redes significa nick registado e identificado. Isto varia de rede para rede, por isso é configurável com /set fnotify_auth_mode.
Duas decisões que valem a pena explicar. O texto das mensagens vem de estranhos, por isso o script remove tabs, newlines e caracteres de controlo antes de escrever, e o ficheiro é criado com permissões 600. E o mesmo aviso do mesmo tipo e rede é suprimido durante 60 segundos, senão uma desligação seguida de três tentativas de reconexão dava três notificações.
Carrega-o e deixa-o em autorun:
/script load fnotify.pl
ln -s ../fnotify.pl ~/.irssi/scripts/autorun/fnotify.pl
O leitor no Mac
Cria ~/.local/bin/irssi-notify e dá-lhe chmod 755:
#!/bin/zsh
# Le os avisos do irssi no servidor (~/.irssi/fnotify) e mostra notificacoes no macOS.
# O texto vem do IRC (nao confiavel): e passado ao osascript como argumento, nunca interpolado.
notify() { # $1=titulo $2=texto $3=som
osascript -e 'on run argv' \
-e 'display notification (item 2 of argv) with title (item 1 of argv) sound name (item 3 of argv)' \
-e 'end run' -- "$1" "$2" "$3" >/dev/null 2>&1
}
ssh -o BatchMode=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ConnectTimeout=15 meuservidor \
'tail -F -n0 ~/.irssi/fnotify 2>/dev/null' |
while IFS=$'\t' read -r type title text; do
case $type in
hilight) notify "IRC $title" "$text" "Glass" ;;
disconnect) notify "IRC: ligação perdida" "$text" "Basso" ;;
auth) notify "IRC: autenticar $title" "$text" "Sosumi" ;;
esac
done
exit 1 # o ssh terminou: o launchd volta a arrancar
Troca meuservidor pelo alias do teu ~/.ssh/config. O ssh tem de entrar sem pedir password, por isso uma chave sem passphrase ou carregada num agente.
O ponto crítico é o osascript. O texto das mensagens IRC vem de estranhos, por isso é passado como argumento (item 2 of argv) e nunca colado dentro do código AppleScript. Se o texto fosse interpolado numa string, uma mensagem bem escolhida num canal podia executar comandos no teu Mac.
Manter o leitor sempre a correr
Cria ~/Library/LaunchAgents/pt.exemplo.irssi-notify.plist, trocando o caminho pela tua pasta pessoal:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>pt.exemplo.irssi-notify</string>
<key>ProgramArguments</key><array><string>/Users/O_TEU_UTILIZADOR/.local/bin/irssi-notify</string></array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>ThrottleInterval</key><integer>30</integer>
<key>StandardErrorPath</key><string>/Users/O_TEU_UTILIZADOR/Library/Logs/irssi-notify.log</string>
</dict></plist>
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/pt.exemplo.irssi-notify.plist
O ThrottleInterval de 30 segundos é deliberado. Se a rede cair, o ssh falha e o launchd tenta de novo. Sem espera, isso resultaria em dezenas de ligações falhadas por minuto, e um fail2ban no servidor pode acabar por banir o teu IP.
Para testar sem tocar no irssi, escreve uma linha de teste diretamente no ficheiro:
ssh meuservidor "printf 'hilight\t#teste\t<eu> isto é um teste\n' >> ~/.irssi/fnotify"
A notificação “IRC #teste” devia aparecer no Mac. Se não aparecer, vê as permissões em Definições do Sistema, Notificações: o osascript aparece como Script Editor.
Highlight com o nick em qualquer posição
Por omissão, o irssi só considera uma mensagem como highlight quando o teu nick aparece no início da frase: “nick, tudo bem?” conta, “oi nick” não conta. Como o fnotify só reage ao que o irssi marca como highlight, uma menção a meio da frase não gera notificação. O sintoma visível é o irssi não pintar de outra cor essas linhas.
A definição que controla este comportamento vem desligada:
/set hilight_nick_matches_everywhere ON
/save
Com ela ativa, o nick conta em qualquer posição da frase, a linha é pintada com a cor de highlight e o fnotify apanha-a.
Detetar o nick no próprio script (por exemplo no sinal message public) também funciona, mas duplica avisos sempre que o irssi marca a mesma mensagem como highlight, porque o texto dos dois caminhos difere ligeiramente e a deduplicação não os reconhece. Por isso o fnotify usa uma só fonte: o que o irssi mostra como highlight é o que gera notificação, o que também respeita /hilight e o nível NOHILIGHT.
Como testar
As mensagens que tu próprio escreves nunca são highlight, por isso escrever o teu nick num canal não serve de teste. Força um highlight a partir do próprio irssi:
/script exec Irssi::active_win->print("o_teu_nick teste do fnotify", Irssi::MSGLEVEL_HILIGHT)
Ou usa um segundo cliente com outro nick (um webchat serve). Para ver o que o irssi registou:
ssh meuservidor 'tail ~/.irssi/fnotify'
Cada mensagem tem de gerar uma só linha. Se houver duas, a deduplicação não está a reconhecer uma diferença de formato entre elas.
Limitações
A deteção de ligação perdida e a de falta de autenticação dependem da rede. A de autenticação assume que a rede marca os nicks identificados com o modo +r, e o padrão do NickServ (identify, register, password) é largo, por isso pode disparar com avisos que não são pedidos de autenticação. A deduplicação limita o ruído, mas convém ajustar o padrão à tua rede. Estes dois avisos foram verificados apenas com uma versão simulada do módulo Irssi, não com uma desligação real, por isso confirma-os da primeira vez que a ligação cair.
Também não há avisos para mensagens privadas, e nada chega ao telemóvel.
Para desfazer: launchctl bootout gui/$(id -u)/pt.exemplo.irssi-notify, apaga os dois ficheiros do Mac e, no servidor, /script unload fnotify e o link em autorun.
Próximo passo
Como o servidor só escreve num ficheiro de texto, o mesmo ficheiro serve para outros destinos. Para receber os avisos também no telemóvel, basta trocar o leitor do Mac por um que envie cada linha para um serviço de push. O script do irssi não muda.
Partilhar
Comentários 0
Ainda sem comentários — sê o primeiro.