- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
perl/modules/IRC/lib/BarnOwl/Module/IRC.pm
rb8a3e00 r926c721 21 21 22 22 use AnyEvent::IRC; 23 use Encode; 24 use File::Spec; 23 25 use Getopt::Long; 24 use Encode;26 use Text::Wrap; 25 27 26 28 our $VERSION = 0.02; 29 30 our $IRC_SUBS_FILENAME = "ircchannels"; 27 31 28 32 our $irc; … … 67 71 }); 68 72 73 BarnOwl::new_variable_int('irc:max-message-length', { 74 default => 450, 75 summary => 'Split messages to at most this many characters.' . 76 "If non-positive, don't split messages", 77 description => 'If set to a positive number, any paragraph in an ' . 78 'IRC message will be split after this many characters.' 79 }); 80 69 81 register_commands(); 70 82 BarnOwl::filter(qw{irc type ^IRC$ or ( type ^admin$ and adminheader ^IRC$ )}); … … 129 141 argument listed above, and die if no channel argument can be found. 130 142 143 =item C<CHANNEL_OR_USER> 144 145 Pass the channel argument, but accept it if it's a username (e.g. 146 has no hash). Only relevant with C<CHANNEL_ARG>. 147 131 148 =item C<CHANNEL_OPTIONAL> 132 149 … … 145 162 use constant CHANNEL_ARG => 1; 146 163 use constant CHANNEL_OPTIONAL => 2; 147 148 use constant ALLOW_DISCONNECTED => 4; 164 use constant CHANNEL_OR_USER => 4; 165 166 use constant ALLOW_DISCONNECTED => 8; 149 167 150 168 sub register_commands { … … 154 172 summary => 'Connect to an IRC server', 155 173 usage => 156 'irc-connect [-a ALIAS 174 'irc-connect [-a ALIAS] [-s] [-p PASSWORD] [-n NICK] SERVER [port]', 157 175 description => <<END_DESCR 158 176 Connect to an IRC server. Supported options are: … … 188 206 189 207 BarnOwl::new_command( 190 'irc-msg' => mk_irc_command( \&cmd_msg ),208 'irc-msg' => mk_irc_command( \&cmd_msg, CHANNEL_OR_USER|CHANNEL_ARG|CHANNEL_OPTIONAL ), 191 209 { 192 210 summary => 'Send an IRC message', … … 215 233 { 216 234 summary => 'Join an IRC channel', 217 usage => 'irc-join [-a ALIAS] #channel [KEY]', 218 219 description => <<END_DESCR 220 Join an IRC channel. 235 usage => 'irc-join [-a ALIAS] [-t] #channel [KEY]', 236 237 description => <<END_DESCR 238 Join an IRC channel. If the -t option is present the subscription will only be 239 temporary, i.e., it will not be written to the subscription file and will 240 therefore not be present the next time BarnOwl is started, and will disappear 241 if the connection is lost. 221 242 END_DESCR 222 243 } … … 227 248 { 228 249 summary => 'Leave an IRC channel', 229 usage => 'irc-part [-a ALIAS] #channel', 230 231 description => <<END_DESCR 232 Part from an IRC channel. 250 usage => 'irc-part [-a ALIAS] [-t] #channel', 251 252 description => <<END_DESCR 253 Part from an IRC channel. If the -t option is present the unsubscription will 254 only be temporary, i.e., it will not be updated in the subscription file and 255 will therefore not be in effect the next time BarnOwl is started, or if the 256 connection is lost. 233 257 END_DESCR 234 258 } … … 327 351 This can be used to perform some operation not yet supported by 328 352 BarnOwl, or to define new IRC commands. 353 END_DESCR 354 } 355 ); 356 357 BarnOwl::new_command( 358 'irc-loadchannels' => \&cmd_loadchannels, 359 { 360 summary => 'Reload persistent channels', 361 usage => 'irc-loadchannels [-a ALIAS] [<file>]', 362 363 description => <<END_DESCR 364 Load persistent channels from a file. The file defaults to 365 \$HOME/.owl/$IRC_SUBS_FILENAME. If the ALIAS is present, only channels 366 on the given alias are loaded. The ALIAS is case-sensitive. 367 368 Each line of the file should describe a single channel, in the format 369 '\$alias \$channel' (without quotes). 329 370 END_DESCR 330 371 } … … 341 382 ######################## Owl command handlers ################################## 342 383 ################################################################################ 384 385 sub make_autoconnect_filename { 386 # can't use ||, or else we'll treat '0' as invalid. We could check for eq "" ... 387 # TODO(jgross): When we move to requiring perl 5.10, combine the 388 # following two lines using // 389 my $filename = shift; 390 $filename = File::Spec->catfile(BarnOwl::get_config_dir(), $IRC_SUBS_FILENAME) unless defined $filename; 391 if (!File::Spec->file_name_is_absolute($filename)) { 392 $filename = File::Spec->catfile($ENV{HOME}, $filename); 393 } 394 return $filename; 395 } 396 397 sub _get_autoconnect_lines { 398 my $filename = shift; 399 400 # TODO(jgross): Write a C-side function to do this, asynchronously; 401 # AIUI, perl doesn't do asynchronous I/O in any useful way 402 if (open (my $subsfile, "<:encoding(UTF-8)", $filename)) { 403 my @lines = <$subsfile>; 404 close($subsfile); 405 406 # strip trailing newlines 407 local $/ = ""; 408 chomp(@lines); 409 410 return @lines; 411 } 412 413 return (); 414 } 415 416 sub get_autoconnect_channels { 417 my $filename = make_autoconnect_filename(shift); 418 my %channel_hash = (); 419 420 # Load the subs from the file 421 my @lines = _get_autoconnect_lines($filename); 422 423 foreach my $line (@lines) { 424 my @parsed_args = split(' ', $line); 425 if (scalar @parsed_args == 2) { 426 push @{$channel_hash{$parsed_args[0]}}, $parsed_args[1]; 427 } else { 428 warn "Trouble parsing irc configuration file '$filename' line '$line'; the format is '\$alias \$channel', with no spaces in either\n"; 429 } 430 } 431 432 return %channel_hash; 433 } 434 435 sub add_autoconnect_channel { 436 my $conn = shift; 437 my $channel = shift; 438 my $alias = $conn->alias; 439 my $filename = make_autoconnect_filename(shift); 440 441 # we already checked for spaces in $channel in cmd_join, but we still need 442 # to check $alias 443 die "Alias name '$alias' contains a space; parsing will fail. Use the -t flag.\n" unless index($alias, " ") == -1; 444 445 my $line = "$alias $channel"; 446 447 my @lines = _get_autoconnect_lines($filename); 448 449 # We don't want to be noisy about duplicated joins. For example, some 450 # people might have :irc-join in startup files, even though that doesn't 451 # work correctly anymore because connect is asynchronous and so join on 452 # startup races with connect. Regardless, just fail silently if the line 453 # already exists. 454 return if grep { $_ eq $line } @lines; 455 456 open (my $subsfile, ">>:encoding(UTF-8)", make_autoconnect_filename($filename)) 457 or die "Cannot open $filename for writing: $!\n"; 458 local $, = ""; 459 local $/ = ""; 460 print $subsfile "$line\n"; 461 close($subsfile); 462 } 463 464 sub remove_autoconnect_channel { 465 my $conn = shift; 466 my $channel = shift; 467 my $alias = $conn->alias; 468 my $filename = make_autoconnect_filename(shift); 469 470 BarnOwl::Internal::file_deleteline($filename, "$alias $channel", 1); 471 } 472 473 sub cmd_loadchannels { 474 my $cmd = shift; 475 my $alias; 476 my $getopt = Getopt::Long::Parser->new; 477 478 local @ARGV = @_; 479 $getopt->configure(qw(pass_through permute no_getopt_compat prefix_pattern=-|--)); 480 $getopt->getoptions("alias=s" => \$alias); 481 482 my %channel_hash = get_autoconnect_channels(@ARGV); 483 484 my $aliases = (defined $alias) ? [$alias] : [keys %channel_hash]; 485 486 foreach my $cur_alias (@$aliases) { 487 # get_connection_by_alias might die, and we don't want to 488 eval { 489 my $conn = get_connection_by_alias($cur_alias, 1); 490 my %existing_channels = map { $_ => 1 } @{$conn->autoconnect_channels}, @{$channel_hash{$cur_alias}}; 491 $conn->autoconnect_channels([keys %existing_channels]); 492 }; 493 foreach my $channel (@{$channel_hash{$cur_alias}}) { 494 if ($cur_alias eq "") { 495 BarnOwl::command("irc-join", "-t", $channel); 496 } else { 497 BarnOwl::command("irc-join", "-t", "-a", $cur_alias, $channel); 498 } 499 } 500 } 501 } 343 502 344 503 sub cmd_connect { … … 378 537 } 379 538 539 my %channel_hash = get_autoconnect_channels; 540 380 541 my $conn = BarnOwl::Module::IRC::Connection->new($alias, $host, $port, { 381 nick => $nick, 382 user => $username, 383 real => $ircname, 384 password => $password, 385 SSL => $ssl, 386 timeout => sub {0} 542 nick => $nick, 543 user => $username, 544 real => $ircname, 545 password => $password, 546 SSL => $ssl, 547 timeout => sub {0}, 548 autoconnect_channels => $channel_hash{$alias} 387 549 }); 388 550 $ircnets{$alias} = $conn; … … 427 589 @msgs = split "\n\n", $fullbody; 428 590 map { tr/\n/ / } @msgs; 591 # split each body at irc:max-message-length characters, if that number 592 # is positive. Only split at space boundaries. Start counting a-fresh 593 # at the beginning of each paragraph 594 my $max_len = BarnOwl::getvar('irc:max-message-length'); 595 if ($max_len > 0) { 596 local($Text::Wrap::columns) = $max_len; 597 @msgs = split "\n", wrap("", "", join "\n", @msgs); 598 } 429 599 for my $body (@msgs) { 430 600 if ($body =~ /^\/me (.*)/) { … … 463 633 sub cmd_join { 464 634 my $cmd = shift; 465 my $conn = shift; 466 my $chan = shift or die("Usage: $cmd channel\n"); 467 $conn->conn->send_msg(join => $chan, @_); 635 my $is_temporary; 636 637 my $getopt = Getopt::Long::Parser->new; 638 639 local @ARGV = @_; 640 $getopt->configure(qw(pass_through permute no_getopt_compat prefix_pattern=-|--)); 641 $getopt->getoptions("temporary" => \$is_temporary); 642 643 my $conn = shift @ARGV; 644 my $chan = shift @ARGV or die("Usage: $cmd channel\n"); 645 646 die "Channel name '$chan' contains a space. As per RFC 2812, IRC channel names may not contain spaces.\n" unless index($chan, " ") == -1; 647 648 $conn->conn->send_msg(join => $chan, @ARGV); 649 650 # regardless of whether or not this is temporary, we want to persist it 651 # across reconnects. 652 653 # check if the channel is already in the list 654 if (!grep { $_ eq $chan } @{$conn->autoconnect_channels}) { 655 push @{$conn->autoconnect_channels}, $chan; 656 } 657 658 if (!$is_temporary) { 659 # add the line to the subs file 660 add_autoconnect_channel($conn, $chan); 661 } 662 468 663 return; 469 664 } … … 471 666 sub cmd_part { 472 667 my $cmd = shift; 473 my $conn = shift; 474 my $chan = shift; 668 my $is_temporary; 669 670 my $getopt = Getopt::Long::Parser->new; 671 672 local @ARGV = @_; 673 $getopt->configure(qw(pass_through permute no_getopt_compat prefix_pattern=-|--)); 674 $getopt->getoptions("temporary" => \$is_temporary); 675 676 my $conn = shift @ARGV; 677 my $chan = shift @ARGV or die("Usage: $cmd channel\n"); 678 475 679 $conn->conn->send_msg(part => $chan); 680 681 # regardless of whether or not this is temporary, we want to persist it 682 # across reconnects 683 my %existing_channels = map { $_ => 1 } @{$conn->autoconnect_channels}; 684 delete $existing_channels{$chan}; 685 $conn->autoconnect_channels([keys %existing_channels]); 686 687 if (!$is_temporary) { 688 # remove the line from the subs file 689 remove_autoconnect_channel($conn, $chan); 690 } 691 476 692 return; 477 693 } … … 573 789 my $alias; 574 790 my $channel; 791 my $is_temporary; 575 792 my $getopt = Getopt::Long::Parser->new; 576 793 my $m = BarnOwl::getcurmsg(); … … 578 795 local @ARGV = @_; 579 796 $getopt->configure(qw(pass_through permute no_getopt_compat prefix_pattern=-|--)); 580 $getopt->getoptions("alias=s" => \$alias); 797 $getopt->getoptions("alias=s" => \$alias, 798 "temporary" => \$is_temporary); 581 799 582 800 if(defined($alias)) { … … 591 809 $conn ||= $c; 592 810 } 811 } elsif (defined($channel) && ($flags & CHANNEL_OR_USER)) { 812 shift @ARGV; 593 813 } elsif ($m && $m->type eq 'IRC' && !$m->is_private) { 594 814 $channel = $m->channel; … … 598 818 } 599 819 600 if(! $channel&&820 if(!defined($channel) && 601 821 ($flags & CHANNEL_ARG) && 602 822 !($flags & CHANNEL_OPTIONAL)) { … … 615 835 die("You must specify an IRC network using -a.\n"); 616 836 } 837 push @ARGV, "-t" if $is_temporary; 617 838 if($flags & CHANNEL_ARG) { 618 839 $sub->($cmd, $conn, $channel, @ARGV);
Note: See TracChangeset
for help on using the changeset viewer.