source: perl/modules/IRC/lib/BarnOwl/Module/IRC.pm @ cd12307

release-1.10release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since cd12307 was cd12307, checked in by Alex Vandiver <alexmv@mit.edu>, 14 years ago
Minor space and formatting nitpicks
  • Property mode set to 100644
File size: 15.3 KB
RevLine 
[b38b0b2]1use strict;
2use warnings;
3
4package BarnOwl::Module::IRC;
5
6=head1 NAME
7
[2c40dc0]8BarnOwl::Module::IRC
[b38b0b2]9
10=head1 DESCRIPTION
11
[2c40dc0]12This module implements IRC support for barnowl.
[b38b0b2]13
14=cut
15
16use BarnOwl;
17use BarnOwl::Hooks;
18use BarnOwl::Message::IRC;
[380b1ab]19use BarnOwl::Module::IRC::Connection qw(is_private);
[ab9cd8f]20use BarnOwl::Module::IRC::Completion;
[b38b0b2]21
22use Net::IRC;
23use Getopt::Long;
24
[2c40dc0]25our $VERSION = 0.02;
[b38b0b2]26
27our $irc;
28
29# Hash alias -> BarnOwl::Module::IRC::Connection object
30our %ircnets;
[fe8cad8]31our %channels;
[3b4ba7d]32our %reconnect;
[b38b0b2]33
34sub startup {
[b10f340]35    BarnOwl::new_variable_string('irc:nick', {
36        default     => $ENV{USER},
37        summary     => 'The default IRC nickname',
38        description => 'By default, irc-connect will use this nick '  .
39        'when connecting to a new server. See :help irc-connect for ' .
40        'more information.'
41       });
42
43    BarnOwl::new_variable_string('irc:user', {
44        default => $ENV{USER},
45        summary => 'The IRC "username" field'
46       });
47        BarnOwl::new_variable_string('irc:name', {
48        default => "",
49        summary     => 'A short name field for IRC',
50        description => 'A short (maybe 60 or so chars) piece of text, ' .
51        'originally intended to display your real name, which people '  .
52        'often use for pithy quotes and URLs.'
53       });
[cd12307]54
[b10f340]55    BarnOwl::new_variable_bool('irc:spew', {
56        default     => 0,
57        summary     => 'Show unhandled IRC events',
58        description => 'If set, display all unrecognized IRC events as ' .
[cd12307]59        'admin messages. Intended for debugging and development use only.'
[b10f340]60       });
61   
[b38b0b2]62    register_commands();
63    register_handlers();
[96f7b07]64    BarnOwl::filter(qw{irc type ^IRC$ or ( type ^admin$ and adminheader ^IRC$ )});
[b38b0b2]65}
66
67sub shutdown {
68    for my $conn (values %ircnets) {
[ba2ca66]69        $conn->conn->disconnect();
[b38b0b2]70    }
71}
72
[f17bb2c0]73sub quickstart {
74    return <<'END_QUICKSTART';
75@b[IRC:]
76Use ':irc-connect @b[server]' to connect to an IRC server, and
77':irc-join @b[#channel]' to join a channel. ':irc-msg @b[#channel]
78@b[message]' sends a message to a channel.
79END_QUICKSTART
80}
81
[da554da]82sub buddylist {
83    my $list = "";
84
85    for my $net (sort keys %ircnets) {
86        my $conn = $ircnets{$net};
87        my ($nick, $server) = ($conn->nick, $conn->server);
88        $list .= BarnOwl::Style::boldify("IRC channels for $net ($nick\@$server)\n");
89
90        for my $chan (keys %channels) {
91            next unless grep $_ eq $conn, @{$channels{$chan}};
92            $list .= "  $chan\n";
93        }
94        $list .= "\n";
95    }
96
97    return $list;
98}
99
[9c7a701]100#sub mainloop_hook {
101#    return unless defined $irc;
102#    eval {
103#        $irc->do_one_loop();
104#    };
105#    return;
106#}
107
108sub OwlProcess {
[b38b0b2]109    return unless defined $irc;
110    eval {
111        $irc->do_one_loop();
112    };
113    return;
114}
115
[9c7a701]116
[b38b0b2]117sub register_handlers {
118    if(!$irc) {
119        $irc = Net::IRC->new;
120        $irc->timeout(0);
121    }
122}
123
[330c55a]124use constant OPTIONAL_CHANNEL => 1;
125use constant REQUIRE_CHANNEL => 2;
126
[b38b0b2]127sub register_commands {
[f17bb2c0]128    BarnOwl::new_command(
129        'irc-connect' => \&cmd_connect,
130        {
131            summary => 'Connect to an IRC server',
132            usage =>
133'irc-connect [-a ALIAS ] [-s] [-p PASSWORD] [-n NICK] SERVER [port]',
[cd12307]134            description => <<END_DESCR
135Connect to an IRC server. Supported options are:
[f17bb2c0]136
137 -a <alias>          Define an alias for this server
138 -s                  Use SSL
139 -p <password>       Specify the password to use
140 -n <nick>           Use a non-default nick
141
142The -a option specifies an alias to use for this connection. This
143alias can be passed to the '-a' argument of any other IRC command to
144control which connection it operates on.
145
146For servers with hostnames of the form "irc.FOO.{com,org,...}", the
147alias will default to "FOO"; For other servers the full hostname is
148used.
149END_DESCR
150        }
151    );
152
153    BarnOwl::new_command(
154        'irc-disconnect' => mk_irc_command( \&cmd_disconnect ),
155        {
156            summary => 'Disconnect from an IRC server',
157            usage   => 'irc-disconnect [-a ALIAS]',
158
159            description => <<END_DESCR
160Disconnect from an IRC server. You can specify a specific server with
161"-a SERVER-ALIAS" if necessary.
162END_DESCR
163        }
164    );
165
166    BarnOwl::new_command(
[e0fba58]167        'irc-msg' => mk_irc_command( \&cmd_msg ),
[f17bb2c0]168        {
169            summary => 'Send an IRC message',
170            usage   => 'irc-msg [-a ALIAS] DESTINATION MESSAGE',
171
172            description => <<END_DESCR
[cd12307]173Send an IRC message.
[f17bb2c0]174END_DESCR
175        }
176    );
177
178    BarnOwl::new_command(
179        'irc-mode' => mk_irc_command( \&cmd_mode, OPTIONAL_CHANNEL ),
180        {
181            summary => 'Change an IRC channel or user mode',
182            usage   => 'irc-mode [-a ALIAS] TARGET [+-]MODE OPTIONS',
183
184            description => <<END_DESCR
185Change the mode of an IRC user or channel.
186END_DESCR
187        }
188    );
189
190    BarnOwl::new_command(
191        'irc-join' => mk_irc_command( \&cmd_join ),
192        {
193            summary => 'Join an IRC channel',
[1b62a55]194            usage   => 'irc-join [-a ALIAS] #channel [KEY]',
[f17bb2c0]195
196            description => <<END_DESCR
197Join an IRC channel.
198END_DESCR
199        }
200    );
201
202    BarnOwl::new_command(
203        'irc-part' => mk_irc_command( \&cmd_part, REQUIRE_CHANNEL ),
204        {
205            summary => 'Leave an IRC channel',
206            usage   => 'irc-part [-a ALIAS] #channel',
207
208            description => <<END_DESCR
209Part from an IRC channel.
210END_DESCR
211        }
212    );
213
214    BarnOwl::new_command(
215        'irc-nick' => mk_irc_command( \&cmd_nick ),
216        {
217            summary => 'Change your IRC nick on an existing connection.',
218            usage   => 'irc-nick [-a ALIAS] NEW-NICK',
219
220            description => <<END_DESCR
221Set your IRC nickname on an existing connect. To change it prior to
222connecting, adjust the `irc:nick' variable.
223END_DESCR
224        }
225    );
226
227    BarnOwl::new_command(
228        'irc-names' => mk_irc_command( \&cmd_names, REQUIRE_CHANNEL ),
229        {
230            summary => 'View the list of users in a channel',
231            usage   => 'irc-names [-a ALIAS] #channel',
232
233            description => <<END_DESCR
234`irc-names' displays the list of users in a given channel in a pop-up
235window.
236END_DESCR
237        }
238    );
239
240    BarnOwl::new_command(
241        'irc-whois' => mk_irc_command( \&cmd_whois ),
242        {
243            summary => 'Displays information about a given IRC user',
244            usage   => 'irc-whois [-a ALIAS] NICK',
245
246            description => <<END_DESCR
247Pops up information about a given IRC user.
248END_DESCR
249        }
250    );
251
252    BarnOwl::new_command(
253        'irc-motd' => mk_irc_command( \&cmd_motd ),
254        {
255            summary => 'Displays an IRC server\'s MOTD (Message of the Day)',
256            usage   => 'irc-motd [-a ALIAS]',
257
258            description => <<END_DESCR
259Displays an IRC server's message of the day.
260END_DESCR
261        }
262    );
263
264    BarnOwl::new_command(
265        'irc-list' => \&cmd_list,
266        {
267            summary => 'Show all the active IRC connections.',
268            usage   => 'irc-list',
269
270            description => <<END_DESCR
271Show all the currently active IRC connections with their aliases and
272server names.
273END_DESCR
274        }
275    );
276
277    BarnOwl::new_command( 'irc-who'   => mk_irc_command( \&cmd_who ) );
278    BarnOwl::new_command( 'irc-stats' => mk_irc_command( \&cmd_stats ) );
279
280    BarnOwl::new_command(
281        'irc-topic' => mk_irc_command( \&cmd_topic, REQUIRE_CHANNEL ),
282        {
283            summary => 'View or change the topic of an IRC channel',
284            usage   => 'irc-topic [-a ALIAS] #channel [TOPIC]',
285
286            description => <<END_DESCR
287Without extra arguments, fetches and displays a given channel's topic.
288
289With extra arguments, changes the target channel's topic string. This
290may require +o on some channels.
291END_DESCR
292        }
293    );
294
295    BarnOwl::new_command(
296        'irc-quote' => mk_irc_command( \&cmd_quote ),
297        {
298            summary => 'Send a raw command to the IRC servers.',
299            usage   => 'irc-quote [-a ALIAS] TEXT',
300
301            description => <<END_DESCR
302Send a raw command line to an IRC server.
303
304This can be used to perform some operation not yet supported by
305BarnOwl, or to define new IRC commands.
306END_DESCR
307        }
308    );
[b38b0b2]309}
310
[f17bb2c0]311
[167044b]312$BarnOwl::Hooks::startup->add('BarnOwl::Module::IRC::startup');
313$BarnOwl::Hooks::shutdown->add('BarnOwl::Module::IRC::shutdown');
[f17bb2c0]314$BarnOwl::Hooks::getQuickstart->add('BarnOwl::Module::IRC::quickstart');
[da554da]315$BarnOwl::Hooks::getBuddyList->add("BarnOwl::Module::IRC::buddylist");
[b38b0b2]316
317################################################################################
318######################## Owl command handlers ##################################
319################################################################################
320
321sub cmd_connect {
322    my $cmd = shift;
323
[2c40dc0]324    my $nick = BarnOwl::getvar('irc:nick');
325    my $username = BarnOwl::getvar('irc:user');
326    my $ircname = BarnOwl::getvar('irc:name');
[b38b0b2]327    my $host;
328    my $port;
329    my $alias;
330    my $ssl;
331    my $password = undef;
332
333    {
334        local @ARGV = @_;
335        GetOptions(
336            "alias=s"    => \$alias,
337            "ssl"        => \$ssl,
[2c40dc0]338            "password=s" => \$password,
[b10f340]339            "nick=s"     => \$nick,
[2c40dc0]340        );
[b38b0b2]341        $host = shift @ARGV or die("Usage: $cmd HOST\n");
342        if(!$alias) {
[f094fc4]343            if($host =~ /^(?:irc[.])?([\w-]+)[.]\w+$/) {
[b0c8011]344                $alias = $1;
345            } else {
346                $alias = $host;
347            }
[b38b0b2]348        }
349        $ssl ||= 0;
[f094fc4]350        $port = shift @ARGV || ($ssl ? 6697 : 6667);
[b38b0b2]351    }
352
[b0c8011]353    if(exists $ircnets{$alias}) {
354        die("Already connected to a server with alias '$alias'. Either disconnect or specify an alias with -a.\n");
355    }
356
[b38b0b2]357    my $conn = BarnOwl::Module::IRC::Connection->new($irc, $alias,
358        Nick      => $nick,
359        Server    => $host,
360        Port      => $port,
361        Username  => $username,
362        Ircname   => $ircname,
363        Port      => $port,
364        Password  => $password,
365        SSL       => $ssl
366       );
367
[cab045b]368    if ($conn->conn->connected) {
[3b4ba7d]369        $conn->connected("Connected to $alias as $nick");
[5ff830a]370    } else {
371        die("IRC::Connection->connect failed: $!");
372    }
373
[b38b0b2]374    return;
375}
376
377sub cmd_disconnect {
378    my $cmd = shift;
[33db995]379    my $conn = shift;
[ba2ca66]380    $conn->conn->disconnect;
[48f7d12]381    return;
[b38b0b2]382}
383
384sub cmd_msg {
[330c55a]385    my $cmd  = shift;
386    my $conn = shift;
[e0fba58]387    my $to = shift or die("Usage: $cmd [NICK|CHANNEL]\n");
[2c40dc0]388    # handle multiple recipients?
[b38b0b2]389    if(@_) {
390        process_msg($conn, $to, join(" ", @_));
391    } else {
[744769e]392        BarnOwl::start_edit_win(BarnOwl::quote('/msg', '-a', $conn->alias, $to), sub {process_msg($conn, $to, @_)});
[b38b0b2]393    }
[48f7d12]394    return;
[b38b0b2]395}
396
397sub process_msg {
398    my $conn = shift;
399    my $to = shift;
400    my $body = shift;
401    # Strip whitespace. In the future -- send one message/line?
402    $body =~ tr/\n\r/  /;
[919535f]403    if ($body =~ /^\/me (.*)/) {
[0c4a190]404        $conn->conn->me($to, Encode::encode('utf-8', $1));
[56d0189]405        $body = '* '.$conn->nick.' '.$1;
[919535f]406    } else {
[0c4a190]407        $conn->conn->privmsg($to, Encode::encode('utf-8', $body));
[919535f]408    }
[b38b0b2]409    my $msg = BarnOwl::Message->new(
410        type        => 'IRC',
[6858d2d]411        direction   => is_private($to) ? 'out' : 'in',
[b38b0b2]412        server      => $conn->server,
413        network     => $conn->alias,
414        recipient   => $to,
415        body        => $body,
416        sender      => $conn->nick,
[2c40dc0]417        is_private($to) ?
[0e52069]418          (isprivate  => 'true') : (channel => $to),
[744769e]419        replycmd    => BarnOwl::quote('irc-msg',  '-a', $conn->alias, $to),
420        replysendercmd => BarnOwl::quote('irc-msg', '-a', $conn->alias, $to),
[b38b0b2]421       );
422    BarnOwl::queue_message($msg);
[48f7d12]423    return;
[b38b0b2]424}
425
[e625b5e]426sub cmd_mode {
427    my $cmd = shift;
428    my $conn = shift;
429    my $target = shift;
430    $target ||= shift;
431    $conn->conn->mode($target, @_);
[48f7d12]432    return;
[e625b5e]433}
434
[2c40dc0]435sub cmd_join {
436    my $cmd = shift;
[330c55a]437    my $conn = shift;
[2c40dc0]438    my $chan = shift or die("Usage: $cmd channel\n");
[fe8cad8]439    $channels{$chan} ||= [];
440    push @{$channels{$chan}}, $conn;
[1b62a55]441    $conn->conn->join($chan, @_);
[48f7d12]442    return;
[2c40dc0]443}
[b38b0b2]444
[6858d2d]445sub cmd_part {
446    my $cmd = shift;
[330c55a]447    my $conn = shift;
448    my $chan = shift;
[fe8cad8]449    $channels{$chan} = [grep {$_ ne $conn} @{$channels{$chan} || []}];
[ba2ca66]450    $conn->conn->part($chan);
[48f7d12]451    return;
[6858d2d]452}
453
[6286f26]454sub cmd_nick {
455    my $cmd = shift;
[330c55a]456    my $conn = shift;
[b0c8011]457    my $nick = shift or die("Usage: $cmd <new nick>\n");
[ba2ca66]458    $conn->conn->nick($nick);
[48f7d12]459    return;
[6286f26]460}
461
[6858d2d]462sub cmd_names {
463    my $cmd = shift;
[330c55a]464    my $conn = shift;
465    my $chan = shift;
[d264c6d]466    $conn->names_tmp([]);
[ba2ca66]467    $conn->conn->names($chan);
[48f7d12]468    return;
[6858d2d]469}
470
[b0c8011]471sub cmd_whois {
472    my $cmd = shift;
[330c55a]473    my $conn = shift;
[b0c8011]474    my $who = shift || die("Usage: $cmd <user>\n");
[ba2ca66]475    $conn->conn->whois($who);
[48f7d12]476    return;
[b0c8011]477}
478
[56e72d5]479sub cmd_motd {
480    my $cmd = shift;
[330c55a]481    my $conn = shift;
[ba2ca66]482    $conn->conn->motd;
[48f7d12]483    return;
[56e72d5]484}
485
[f094fc4]486sub cmd_list {
487    my $cmd = shift;
488    my $message = BarnOwl::Style::boldify('Current IRC networks:') . "\n";
489    while (my ($alias, $conn) = each %ircnets) {
490        $message .= '  ' . $alias . ' => ' . $conn->nick . '@' . $conn->server . "\n";
491    }
492    BarnOwl::popless_ztext($message);
[48f7d12]493    return;
[f094fc4]494}
495
496sub cmd_who {
497    my $cmd = shift;
[330c55a]498    my $conn = shift;
[f094fc4]499    my $who = shift || die("Usage: $cmd <user>\n");
[330c55a]500    BarnOwl::error("WHO $cmd $conn $who");
[f094fc4]501    $conn->conn->who($who);
[48f7d12]502    return;
[f094fc4]503}
504
505sub cmd_stats {
506    my $cmd = shift;
[330c55a]507    my $conn = shift;
[f094fc4]508    my $type = shift || die("Usage: $cmd <chiklmouy> [server] \n");
509    $conn->conn->stats($type, @_);
[48f7d12]510    return;
[f094fc4]511}
512
[3ad15ff]513sub cmd_topic {
514    my $cmd = shift;
[330c55a]515    my $conn = shift;
516    my $chan = shift;
517    $conn->conn->topic($chan, @_ ? join(" ", @_) : undef);
[48f7d12]518    return;
[3ad15ff]519}
520
[af9de56]521sub cmd_quote {
522    my $cmd = shift;
523    my $conn = shift;
524    $conn->conn->sl(join(" ", @_));
[48f7d12]525    return;
[af9de56]526}
527
[b38b0b2]528################################################################################
529########################### Utilities/Helpers ##################################
530################################################################################
531
[330c55a]532sub mk_irc_command {
533    my $sub = shift;
534    my $use_channel = shift || 0;
535    return sub {
536        my $cmd = shift;
537        my $conn;
538        my $alias;
539        my $channel;
540        my $getopt = Getopt::Long::Parser->new;
541        my $m = BarnOwl::getcurmsg();
[b38b0b2]542
[330c55a]543        local @ARGV = @_;
544        $getopt->configure(qw(pass_through permute no_getopt_compat prefix_pattern=-|--));
545        $getopt->getoptions("alias=s" => \$alias);
546
547        if(defined($alias)) {
548            $conn = get_connection_by_alias($alias);
549        }
[ecee82f]550        if($use_channel) {
[e625b5e]551            $channel = $ARGV[0];
[330c55a]552            if(defined($channel) && $channel =~ /^#/) {
553                if($channels{$channel} && @{$channels{$channel}} == 1) {
[e625b5e]554                    shift @ARGV;
[ecee82f]555                    $conn = $channels{$channel}[0] unless $conn;
[330c55a]556                }
[ecee82f]557            } elsif ($m && $m->type eq 'IRC' && !$m->is_private) {
558                $channel = $m->channel;
559            } else {
560                undef $channel;
[330c55a]561            }
562        }
[ecee82f]563
[330c55a]564        if(!$channel && $use_channel == REQUIRE_CHANNEL) {
565            die("Usage: $cmd <channel>\n");
566        }
567        if(!$conn) {
568            if($m && $m->type eq 'IRC') {
569                $conn = get_connection_by_alias($m->network);
570            }
571        }
572        if(!$conn && scalar keys %ircnets == 1) {
573            $conn = [values(%ircnets)]->[0];
574        }
575        if(!$conn) {
576            die("You must specify an IRC network using -a.\n");
577        }
578        if($use_channel) {
579            $sub->($cmd, $conn, $channel, @ARGV);
580        } else {
581            $sub->($cmd, $conn, @ARGV);
582        }
583    };
[6858d2d]584}
585
[b38b0b2]586sub get_connection_by_alias {
[2c40dc0]587    my $key = shift;
588    die("No such ircnet: $key\n") unless exists $ircnets{$key};
[b38b0b2]589    return $ircnets{$key};
590}
591
5921;
Note: See TracBrowser for help on using the repository browser.