#!/usr/bin/perl use strict; use URI; use AnyEvent::Impl::Perl; use JSON::Syck; use JSONConnection; use Net::IRC3::Client::Connection; $Net::IRC3::Client::Connection::DEBUG = 1; our $CFG; our %ALIASES; our %CONNS; our $JS; sub load_cfg { $CFG ||= {}; return unless -e "$ENV{HOME}/.jsonircrc"; open CFGH, "<", "$ENV{HOME}/.jsonircrc" or die "Couldn't open ~/.jsonircrc: $!"; $::CFG = JSON::Syck::Load (do { local $/; }); } sub save_cfg { open CFGH, ">", "$ENV{HOME}/.jsonircrc" or die "Couldn't open for writing ~/.jsonircrc: $!"; print CFGH (JSON::Syck::Dump ($::CFG)); close CFGH; } sub to_dest_id { my ($irccon, $targ) = @_; $irccon = $ALIASES{$irccon} || $irccon; my $uri = new URI; $uri->scheme ("jsirc"); $uri->authority ($irccon); $uri->path ($targ); "$uri" } sub unalias { my ($id) = @_; for (keys %ALIASES) { if ($id eq $ALIASES{$_}) { return $_; } } } sub lookup_connection { my ($id) = @_; my $alias = unalias ($id); return $CONNS{$alias || $id} } sub from_dest_id { my ($dest_id) = @_; my $uri = URI->new ($dest_id); my $path = ($uri->path_segments ())[1]; return (lookup_connection ($uri->authority), unalias ($uri->authority) || $uri->authority, $path); } sub connect_irc { my ($host, $port, $alias) = @_; my $irccon = "$host:$port"; my $pc = $CONNS{$irccon} = Net::IRC3::Client::Connection->new; $ALIASES{$irccon} = $alias if defined $alias; $pc->reg_cb ( publicmsg => sub { my ($pc, $chan, $msg) = @_; my ($nick) = Net::IRC3::Util::split_prefix ($msg->{prefix}); my $dest = to_dest_id ($irccon, $chan); my $nickdest = to_dest_id ($irccon, $nick); $JS->broadcast ({ src => $dest, type => "message", msg_type => "public", message => $msg->{trailing}, timestamp => time (), to => { nick => $pc->nick, id => to_dest_id ($irccon, $pc->nick) }, from => { nick => $nick, id => $nickdest }, }); 1; }, privatemsg => sub { my ($pc, $dsgnick, $msg) = @_; my ($nick) = Net::IRC3::Util::split_prefix ($msg->{prefix}); my $dest = to_dest_id ($irccon, $nick); $JS->broadcast ({ src => $dest, type => "message", msg_type => "private", message => $msg->{trailing}, timestamp => time (), to => { nick => $pc->nick, id => to_dest_id ($irccon, $pc->nick) }, from => { nick => $nick, id => $dest }, }); 1; }, connect => sub { info_reply ($JS, undef, undef, "Connected to $host:$port " . (defined $alias ? "(aka $alias)" : "") ); }, disconnect => sub { error_reply ($JS, undef, undef, "Lost connection to $host:$port " . (defined $alias ? "(aka $alias)" : "") ); delete $CONNS{"$host:$port"}; delete $ALIASES{"$host:$port"}; } ); eval { $pc->connect ($host, $port); }; if ($@) { error_reply ($JS, undef, undef, "Couldn't connect to $host:$port " . (defined $alias ? "(aka $alias)" : "") . ": $@" ); delete $CONNS{"$host:$port"}; delete $ALIASES{"$host:$port"}; return; } $pc->register (qw/elmex2 elmex2 elmex2/); for (@{$CFG->{channels}->{$alias || "$host:$port"}}) { $pc->send_srv (JOIN => undef => $_); } } sub update_connections { for (map { /^(\S+):(\d+)/ ? [$1, $2, $_] : [] } keys %CONNS) { my ($h, $p) = @$_; unless ( grep { ($_->{host} eq $h) && (($_->{port} || 6667) == $p) && ($_->{connect}) } @{$CFG->{servers}}) { $CONNS{$_->[2]}->disconnect; } } for my $con (@{$CFG->{servers}}) { my ($host, $port) = ($con->{host}, $con->{port} || 6667); if ($con->{connect} and not $CONNS{"$host:$port"}) { connect_irc ($host, $port, $con->{alias}); } } } sub info_reply { my ($con, $lid, $infodata, $string) = @_; if (defined $lid) { $con->send_data ($lid, { type => 'info', info_packet => $infodata, message => $string }); } else { $JS->broadcast ({ type => 'info', info_packet => $infodata, message => $string }); } } sub error_reply { my ($con, $lid, $errdata, $string) = @_; if (defined $lid) { $con->send_data ($lid, { type => 'error', error_packet => $errdata, message => $string }); } else { $JS->broadcast ({ type => 'error', error_packet => $errdata, message => $string }); } } load_cfg; my $c = AnyEvent->condvar; $JS = JSONConnection->new ( packet_cb => sub { my ($JS, $lid, $data) = @_; if ($data->{type} eq 'message') { my ($con, $irccon, $dest) = from_dest_id ($data->{dest}); unless (defined $con) { error_reply ($JS, $lid, $data, "No such ID found: '$data->{dest}'"); return } $con->send_srv (PRIVMSG => $data->{message} => $dest); } elsif ($data->{type} eq 'command') { if ($data->{command} eq 'reload') { load_cfg; update_connections; } elsif ($data->{command} eq 'list_connections') { } elsif ($data->{command} eq 'list_ids') { } } else { error_reply ($JS, $lid, $data, "Did not understand this packet"); } 1 }, connect_cb => sub { my ($JS, $lid) = @_; $JS->send_data ($lid, { type => "hello" }); }); update_connections; $JS->start_listener; $c->wait;