aboutsummaryrefslogtreecommitdiffstats
path: root/auto-server/auto_server.pl
blob: 447a13198c92b10bea7091e2d85834cd895655b6 (plain) (blame)
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# This script was initially written by shabble, this fork (and molestation) is
# built upon his original work.
#
# USAGE:
#
# the primary command used is /join+ #channelname
#
# Mappings for channels to servers is accomplished with the
# joinplus_server_maps setting.
#
# Within this setting, space separated pairs denote channel, server pairs.
# Spaces also separate individual pairs, for example:
#
# /set joinplus_server_maps #foo Freenode #bar irc.somewhere.tld #meep DALNet
#
# Then use /join+ #foo, and if you are not already connected to freenode, it
# will connect you, and then join that channel.

# TODO:
# Autocompletion for channel names
# address conflict resolution
# fix that disgusting race condition

use strict;
use warnings;


use Irssi;
use Irssi::Irc;
use Irssi::TextUI;

use Data::Dumper;


my $DEBUG_ENABLED = 0;
sub DEBUG () { $DEBUG_ENABLED }

sub _debug_print {
    return unless DEBUG;
    return unless scalar (grep { defined && length } @_) == @_;
    my $win = Irssi::active_win;
    my $str = join('', @_);
    $win->print($str, Irssi::MSGLEVEL_CLIENTCRAP);
}

our $VERSION = '0.1';

our %IRSSI = (
              authors     => 'shabble, richo',
              contact     => 'richo@psych0tik.net',
              name        => 'auto-join-ng',
              description => 'connects to a specified server in order to connect' .
                             ' to a channel there, without having first to' .
                             ' connect to the server',
              license     => 'Public Domain',
             );

my $channel_map = {};
my @hack_channels;
my $pending_joins;

sub auto_server_init {
    Irssi::command_bind('join+', \&join_plus);
    Irssi::settings_add_str('join_plus', 'joinplus_server_maps', '');
    Irssi::signal_add_last('setup changed', \&setup_changed);
    Irssi::settings_add_bool('join_plus', 'join_plus_debug', 0);

    setup_changed();
    $pending_joins = {};

}

sub setup_changed {
    $DEBUG_ENABLED  = Irssi::settings_get_bool('join_plus_debug');
    parse_channel_map();
}

# This is a tremendous kludge.
# If anyone knows a better way to get this listing, I'd like to hear it.
# This has so many race condition bugs I just don't even know where to start.
sub retrieve_channels {
    @hack_channels = ();
    Irssi::signal_add_first('print text', 'haxy_print_hook');
    Irssi::command("CHANNEL LIST");
    Irssi::signal_remove('print text', 'haxy_print_hook');
    return join(" ", @hack_channels);
}


# The idea for how to do this courtesy of http://wouter.coekaerts.be/site/irssi/aliases
sub haxy_print_hook {
    Irssi::signal_remove('print text', 'haxy_print_hook');
    Irssi::signal_stop();
    my $data = $_[1];
    # Hose control characters
    $data =~ s/\x04.//g;
    if ($data =~ m/^#/) {
        my @items = split /\s+/, $data;
        push(@hack_channels, $items[0]);
        push(@hack_channels, $items[1]);
    }
    Irssi::signal_add_first('print text', 'haxy_print_hook');
}

sub parse_channel_map {
    #my $data = Irssi::settings_get_str('joinplus_server_maps');
    unbind_completion();
    my $data = retrieve_channels();
    my @items = split /\s+/, $data;
    if (@items % 2 == 0) {
        $channel_map = { @items }; # risky?
    } else {
        Irssi::active_win->print("Could not process channel => server mappings");
        $channel_map = {};
    }
    _debug_print Dumper($channel_map);
    bind_completion();
}

sub bind_completion {
    foreach(%$channel_map) {
        Irssi::command_bind("join+ $_", \&join_plus);
    }
}

sub unbind_completion {
    foreach(%$channel_map) {
        Irssi::command_unbind("join+ $_", \&join_plus);
    }
}

sub join_plus {
    my ($args, $cmd_server, $witem) = @_;
    #print Dumper($cmd, "moo", $win);

    # parse out channel name from args:
    my $channel;
    if ($args =~ m/^(#?[#a-zA-Z0-9-]+)/) {
        $channel = $1;
        _debug_print ("Channel is: $channel");
    }

    unless ($channel) {
        Irssi::active_win()->print("Channel $args not recognised");
        return;
    }

    # lookup server
    my $server_id = $channel_map->{$channel};
    _debug_print($server_id);

    unless ($server_id) {
        Irssi::active_win()->print("Channel $channel does not have an"
                                   . " appropriate server mapping");
            return;
    }
    # TODO: search values() and give a 'did you mean' for closest channel

    # check if we're connected to that server
    my $server = Irssi::server_find_tag($server_id);

    if (not defined $server) {
        $server = Irssi::server_find_chatnet($server_id);
    }

    if (not defined $server) {
        # still no server, walk the server list looking for address matches.
        my @servers = Irssi::servers();
        foreach my $srv (@servers) {
            if (($srv->{address} eq $server_id) or
                ($srv->{real_address} eq $server_id)) {
                $server = $srv;
                last;
            }
        }
    }

    if (defined $server) {

        _debug_print ("Already connected to server: " . $server->{tag} );

        # check if we're already on the required channel
        my $on_channel = $server->channel_find($channel);

        if (defined $channel && ref($channel) eq 'Irssi::Irc::Channel') {
            Irssi::active_win()->print("You are already connected to "
                                       . " $channel on " . $server->{tag});
            return;
        } else {
            _debug_print ("joining channel: $channel");
            $server->command("JOIN $channel");
        }
    } else {
        # not connected to server.
        _debug_print ("connecting to server: $server_id");

        Irssi::command("CONNECT $server_id");
        _debug_print ("awaiting connection for join");

        $pending_joins->{$server_id} = $channel;
        Irssi::signal_add_last("event 376", 'do_channel_join');
    }
}

sub do_channel_join {
    my ($serv) = @_;
    #_debug_print("server is " . Dumper($serv));
    _debug_print(sprintf("server is %s (%s)", $serv->{address}, $serv->{tag}));

    my $channel = $pending_joins->{$serv->{address}};
    $channel = $pending_joins->{$serv->{tag}} unless $channel;

    _debug_print ("attempting to join $channel");

    Irssi::server_find_tag($serv->{tag})->command("JOIN $channel");

    delete $pending_joins->{$serv->{address}};
    delete $pending_joins->{$serv->{tag}};

}

auto_server_init();