aboutsummaryrefslogblamecommitdiffstats
path: root/auto-server/auto_server.pl
blob: 0046340dc5feb7efb54f1321ced8bacaf33795a5 (plain) (tree)
1
2
3


                                                                              














                                                                             



                                    
 
























                                                               




                                                                                   



                                                      
                     
                  




                                                                     
                                                             











                                                                  


























                                                                                       
                       

                                                                
                        







                                                                                 












                                                       







                                         
                                          





























































                                                                            

                                                                     










                                                                               

                                                     
 
                                                                       
 


                                                  
 


                   
# 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');
    my $data = retrieve_channels();
    unbind_completion();
    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;
        # This comes tumbling down if the server doesn't have a MOTD.
        # is that RFC required?
        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;
    if ($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();