summaryrefslogtreecommitdiffstats
path: root/scripts/fix_slackirc.pl
blob: b2856449e23f9c902ac1286d0ee58edf9fa111f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use strict;
use warnings;

our $VERSION = '0.3'; # e43de0fd9100921
our %IRSSI = (
    authors     => 'Nei',
    contact     => 'Nei @ anti@conference.jabber.teamidiot.de',
    url         => "http://anti.teamidiot.de/",
    name        => 'fix_slackirc',
    description => 'Some workarounds to improve irssi experience on the Slack IRC gateway',
    license     => 'ISC',
   );

# Usage
# =====
# Load and enjoy

use Irssi;

my $SLACK_ADDRESS = 'irc.tinyspeck.com';

# Work-around for 2 bugs in the Slack IRC bridge
# * Format of 315 is broken - the nick is missing
# * No Reply to the MODE b command
# * Empty real names are given as string "null" instead
# these issues will stop channel sync from working properly

my %timer;
my %chanlist;

Irssi::signal_register({
    'chanquery who end' => [qw[iobject string]],
});

# Accessor for the Rawlog
sub _s2r {
    my $ref = shift;
    if ($ref =~ /=SCALAR/) {
	bless +{ _irssi => $$ref } => ref $ref
    } else {
	$ref
    }
}

sub queue_ban_end {
    my ($server, $raw) = @_;
    return unless $server && $server->{real_address} eq $SLACK_ADDRESS;
    my (@p) = split ' :', $raw, 2;
    unshift @p, (split / /, shift @p);
    my $tag = $server->{tag};
    push @{ $chanlist{$tag} }, split /,/, $p[1];
    if (my $t = delete $timer{$tag}) {
	Irssi::timeout_remove($t);
    }
    # Gross hack due to Irssi missing a signal. We expect that irssi will ask for bans "soon" and in order
    $timer{$tag} = Irssi::timeout_add_once(5_000, 'send_end_of_bans', $tag);
}

sub send_end_of_bans {
    my ($tag) = @_;
    delete $timer{$tag};
    return unless $chanlist{$tag} && @{$chanlist{$tag}};
    my $chan = shift @{ $chanlist{$tag} // [] } || return;
    if (my $server = Irssi::server_find_tag($tag)) {
	my $raw = ':'.$server->{real_address}.' 368 '.$server->{nick}.' '.$chan.' :Slack has no Ban List';
	_s2r($server->{rawlog})->input("[FAKED] $raw") if $server->{rawlog};
	Irssi::signal_emit('server incoming', $server, $raw);
	if (@{$chanlist{$tag}}) {
	    # We expect that irssi will ask for the "next" ban soon
	    $timer{$tag} = Irssi::timeout_add_once(2_000, 'send_end_of_bans', $tag);
	}
	else {
	    delete $chanlist{$tag};
	}
    }
}

sub fix_incoming {
    my ($server, $raw) = @_;
    my $nick = $server->{nick};
    my $continue;
    # fix end of wholist
    if ($raw =~ s/:$SLACK_ADDRESS 315 \K /$nick /) {
	$continue = 1;
    }
    # fix "null" realnames
    elsif ($raw =~ s/:$SLACK_ADDRESS 352 .* :\d+ \Knull$//) {
	$continue = 1;
    }
    # support self msgs
    elsif ($raw =~ s/^:(?:(\S+)!\S+(\@$SLACK_ADDRESS)) PRIVMSG ([^#\s]\S*) :\[\3\] /:$3!$3$2 PRIVMSG $1 :/) {
	$continue = 1;
    }
    if ($continue) {
	_s2r($server->{rawlog})->input("[FIXED] $raw") if $server->{rawlog};
	Irssi::signal_continue($server, $raw);
    }
}

Irssi::signal_add_last('chanquery who end' => 'queue_ban_end');
Irssi::signal_add_first('server incoming' => 'fix_incoming');