1# Original version by Stefan "tommie" Tomanek <stefan@kann-nix.org>
2# Enhanced with max. download limits, retries, pausing, searching and other neat stuff by Obfuscoder (obfuscoder@obfusco.de)
3# You can find the script on GitHub: https://github.com/obfuscoder/irssi_scripts
4# Please report bugs to https://github.com/obfuscoder/irssi_scripts/issues
5
6use strict;
7use warnings;
8
9use vars qw($VERSION %IRSSI);
10$VERSION = "20141016";
11%IRSSI = (
12    authors     => "Stefan 'tommie' Tomanek, Obfuscoder",
13    contact     => "obfuscoder\@obfusco.de",
14    name        => "xdccget",
15    description => "enhanced downloading, queing, searching from XDCC bots",
16    license     => "GPLv2",
17    changed     => "$VERSION",
18    commands    => "xdccget"
19);
20
21use Irssi 20020324;
22
23use vars qw(@queue @completed %offers $timer $debug %lists);
24
25Irssi::settings_add_str($IRSSI{'name'}, 'xdccget_config_path', "$ENV{HOME}/.irssi");
26Irssi::settings_add_int($IRSSI{'name'}, 'xdccget_max_downloads', 2);
27Irssi::settings_add_int($IRSSI{'name'}, 'xdccget_retry_time', 5);
28
29$debug=0;
30my $config_path = Irssi::settings_get_str('xdccget_config_path');
31my $max_downloads = Irssi::settings_get_int('xdccget_max_downloads');
32my $queue_file = "$config_path/xdccget.queue";
33sub saveQueue {
34    if (!open (QUEUE, q{>}, $queue_file)) {
35        print CLIENTCRAP "XDCCGET - ERROR! Cannot open queue file for saving at $queue_file. $!";
36        return;
37    }
38    foreach (@queue) {
39        print QUEUE $_->{net}."\t".$_->{nick}."\t".$_->{pack}."\t".$_->{filename}."\t".$_->{xdcc}."\n";
40    }
41    if (!close QUEUE) {
42        print CLIENTCRAP "XDCCGET - ERROR! Could not close queue file after saving at $queue_file. $!";
43    }
44}
45
46sub loadQueue {
47    @queue = ();
48    if (!open (QUEUE, q{<}, $queue_file)) {
49        print CLIENTCRAP "XDCCGET - Warning: Could open queue file for loading from $queue_file: $!";
50        return;
51    }
52    while (<QUEUE>) {
53        chomp;
54        my ($net, $nick, $pack, $desc, $xdcc) = split (/\t/);
55        if ($xdcc eq "") {
56            $xdcc = "xdcc";
57        }
58        my %transfer = (
59            'nick'     => $nick,
60            'pack'     => $pack,
61            'status'   => 'waiting',
62            'net'      => $net,
63            'pos'      => 0,
64            'try'      => 0,
65            'etr'      => 0,
66            'timer'    => undef,
67            'xdcc'     => $xdcc,
68            'filename' => $desc,
69        );
70        push @queue, \%transfer;
71    }
72    if (!close QUEUE) {
73        print CLIENTCRAP "XDCCGET - ERROR! Could not close queue file after loading at $queue_file. $!";
74    }
75}
76
77sub debugFunc {
78    return unless ($debug);
79    my $funcname = shift (@_);
80    print CLIENTCRAP "XDCC-DEBUG - $funcname (". join(",",@_).")\n";
81}
82
83sub show_help() {
84    my $help="XDCCget $VERSION
85  Commands:
86
87  /xdccget queue <nickname> <number> [[-xdcc=<method>] <description>]
88      Queue the specified pack of the currently selected server and 'Nickname'.
89      The description will be shown in the queue.
90      With -xdcc=<method> it is possible to specify the method to be used in the request.
91      Default is 'xdcc' as in /msg <nickname> xdcc send <number>
92
93  /xdccget stat
94  /xdccget
95  /xdccget -l
96      List the download queue
97
98  /xdccget list <nickname>
99      Request the XDCC list of <nickname>
100
101  /xdccget cancel <number>
102      Cancel the download if currently downloading, request to being removed from queue
103      if queued by the XDCC offerer, or remove pack <number> from the download queue
104
105  /xdccget pause <number>
106      Pause pack <number> within the local queue. Resume with reset
107
108  /xdccget reset <number>
109      Reset pack <number> so that it is unpaused and --
110      if download slots are still available -- triggers a download request
111
112  /xdccget offers [<options>] <description search pattern>
113      Display all the announced offers matching the given pattern or options.
114      The announcements are continuously monitored by this script throughout all joined channels.
115
116      Options:
117      One or more of the following options can be used. Each option must start with a '-':
118
119      -server=<server pattern>     only show offers announced by bots on servers matching this pattern
120      -channel=<channel pattern>   only show offers announced by bots on channels matching this pattern
121      -nick=<nick pattern>         only show offers announced by bots with nicks matching this pattern
122
123      Examples:
124         /xdccget offers iron.*bluray
125         /xdccget offers -nick=bot iron.*bluray
126         /xdccget offers -channel=beast-xdcc iron.*bluray
127
128      Regular expressions are used to match each of the parameters.
129
130  /xdccget help
131      Display this help
132
133  You can also simply use /x instead of /xdccget ;-)
134
135  Configuration:
136
137  xdccget_config_path
138      Path where this script is storing its files (queue and finished downloads).
139      Default is '\$HOME/.irssi'.
140
141  xdccget_max_downloads
142      Maximum number of parallel downloads. Default is 2. A download request which is queued
143      by the XDCC offer bot does not count against the limit. The next item in the download queue
144      is being requested as long as download slots are available. Also other downloads not controlled
145      by this script do not count either. It is also possible to exceed this limit if a bot sends the
146      previously requested and queued file while there are downloads running already.
147
148  xdccget_retry_time
149      Time in minutes between retries. Default is 5. Retries are necessary for full
150      offer queues of bots, bots being/becoming offline, or not getting the requested download or any
151      understandable message regarding the request. Please DO NOT set this value to less than 300
152      (5 minutes) or risk being banned from the channels for spamming the bots.
153
154  Please report bugs to https://github.com/obfuscoder/irssi_scripts/issues
155";
156    my $text = '';
157    foreach (split(/\n/, $help)) {
158        $_ =~ s/^\/(.*)$/%9\/$1%9/;
159        $text .= $_."\n";
160    }
161    print CLIENTCRAP &draw_box("XDCCget", $text, "help", 1);
162}
163
164sub draw_box ($$$$) {
165    my ($title, $text, $footer, $colour) = @_;
166    my $box = '';
167    my $exp_flags = Irssi::EXPAND_FLAG_IGNORE_EMPTY | Irssi::EXPAND_FLAG_IGNORE_REPLACES;
168    $box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n";
169    if (defined($text)) {
170        foreach (split(/\n/, $text)) {
171            $box .= '%R|%n '.$_."\n";
172        }
173    }
174    $box .= '%R`--<%n'.$footer.'%R>->%n';
175    $box =~ s/%.//g unless $colour;
176    return $box;
177}
178
179sub contains {
180    my ($item, @list) = @_;
181    foreach (@list) {
182        ($item eq $_) && return 1;
183    }
184    return 0;
185}
186
187sub event_message_irc_notice {
188    debugFunc ("event_message_irc_notice", @_);
189    my ($server, $msg, $nick, $address, $target) = @_;
190    my $i;
191    $_ = $msg;
192    for ($i=0; $i<= $#queue; $i++) {
193        if ($queue[$i] && lc($nick) eq lc($queue[$i]->{'nick'})) {
194            if (/Closing Connection/) {
195                print CLIENTCRAP "%R>>%n XDCC-Transfer closed";
196                # Is it a canceled transfer?
197                if ($queue[$i]->{'status'} eq 'canceling') {
198                    $queue[$i]->{'status'} = 'cancelled';
199                } elsif ($queue[$i]->{'status'} ne 'paused') {
200                    # We should try again unless we paused the queue item
201                    $queue[$i]->{'status'} = 'waiting';
202                }
203            } elsif (/Transfer Completed/i) {
204                print CLIENTCRAP "%R>>%n XDCC-Transfer completed";
205                # Mark the transfer as completed
206                $queue[$i]->{'status'} = 'completed';
207            } elsif (/You already requested that pack/i) {
208                $queue[$i]->{'status'} = 'transferring';
209            } elsif (/You already have that item queued/i) {
210                $queue[$i]->{'status'} = 'queued';
211            } elsif (/Sending (?:You|u) (?:Your Queued )?Pack/i) {
212                $queue[$i]->{'status'} = 'transferring';
213                print CLIENTCRAP "%R>>%n XDCC-Transfer starting";
214            } elsif (/All Slots Full, Added (|you to the main )queue in position ([0-9]*)/i) {
215                $queue[$i]->{'pos'} = $2;
216                $queue[$i]->{'etr'} = 0;
217                $queue[$i]->{'status'} = 'queued';
218            } elsif (/You have been queued for ([0-9]*?) hr ([0-9]*?) min, currently in main queue position ([0-9]*?) of ([0-9]*?)\.  Estimated remaining time is ([0-9]*?) hr ([0-9]*?) min or (less|more)\./i) {
219                $queue[$i]->{'pos'} = $3;
220                $queue[$i]->{'etr'} = time() + (($5*60)+$6)*60;
221                $queue[$i]->{'status'} = 'queued';
222            } elsif (/You have been queued for ([0-9]*?) hours ([0-9]*?) minutes, currently in main queue position ([0-9]*?) of ([0-9]*?)\./i) {
223                $queue[$i]->{'pos'} = $3;
224                $queue[$i]->{'status'} = 'queued';
225            } elsif (/You have been queued for ([0-9]*?) minutes, currently in main queue position ([0-9]*?) of ([0-9]*?)\./i) {
226                $queue[$i]->{'status'} = 'queued';
227                # FIXME unite somehow with regexp above
228                $queue[$i]->{'pos'} = $2;
229            } elsif (/It has been placed in queue slot #(\d+), it will send when sends are available/i) {
230                $queue[$i]->{'pos'} = $1;
231                $queue[$i]->{'status'} = 'queued';
232            } elsif (/Invalid Pack Number/i) {
233                $queue[$i]->{'status'} = 'invalid';
234            } elsif (/The Owner Has Requested That No New Connections Are Made/i ||
235                /All Slots Full,( Main)? queue of size [0-9]* is Full, Try Again Later/i ||
236                /You can only have 1 transfer at a time/i ||
237                /you must be on a known channel/i) {
238                print CLIENTCRAP "Retrying ....\n";
239                my $retry = Irssi::settings_get_int('xdccget_retry_time')*60000;
240                $queue[$i]->{'status'} = 'retrying';
241                $queue[$i]->{'timer'} = Irssi::timeout_add($retry, 'retry_transfer', $i);
242                $queue[$i]->{'etr'} = time()+$retry/1000;
243            } elsif (/must be on a known channel/i) {
244                $server->command("WHOIS $nick");
245                $queue[$i]->{'status'} = 'joining';
246            } else { Irssi::print($_) if ($debug); }
247            process_queue();
248            last;
249        }
250    }
251    if (/#(\d+).+?\d+x \[ *(<?\d+.*?)\] +(.*)$/) {
252        my ($pack, $size, $name) = ($1, $2, $3);
253        if (defined $lists{lc $server->{tag}}{lc $nick}) {
254            $lists{lc $server->{tag}}{lc $nick}{$pack} = $name;
255        }
256        foreach (@queue) {
257            next unless lc $nick eq lc $_->{nick};
258            next unless lc $server->{tag} eq lc $_->{net};
259            next unless $_->{pack} eq $pack;
260            $_->{filename} = $name;
261        }
262    }
263}
264
265sub process_queue {
266    debugFunc ("process_queue", @_);
267    my ($i, $j, $numdls);
268    $numdls = 0;
269    my $process;
270    unless (scalar(@queue) > 0) {return 0};
271    for ($i=0; $i<= $#queue; $i++) {
272        debugFunc (" - Item: $i -> ".$queue[$i]{'status'});
273        if ($queue[$i]{'status'} eq 'completed' ||
274            $queue[$i]{'status'} eq 'cancelled') {
275            push (@completed, $queue[$i]);
276            my $done_file = "$config_path/xdccdone.txt";
277            if (!open (DONEFILE, q{>>}, $done_file)) {
278                print CLIENTCRAP "XDCCGET - Warning: Could not open file for appending done queue entry at $done_file. $!";
279            } else {
280                print DONEFILE $queue[$i]{net}."\t".$queue[$i]{nick}."\t".$queue[$i]{pack}."\t".$queue[$i]{filename}."\t".$queue[$i]{'status'}."\n";
281                if (!close (DONEFILE)) {
282                    print CLIENTCRAP "XDCCGET - Warning: Could not close file after appending done queue entry at $done_file. $!";
283                }
284            }
285            splice (@queue, $i, 1);
286        } else {
287            if ($queue[$i]{'status'} eq 'waiting') {
288                $process = 1;
289                for ($j=0; $j<$i; $j++) {
290                    if ($queue[$i]{'nick'} eq $queue[$j]{'nick'}) {
291                        $process = 0;
292                    }
293                }
294                if ($numdls >= $max_downloads) {
295                    $process = 0;
296                }
297                if ($process) {
298                    my $server = Irssi::server_find_tag($queue[$i]{'net'});
299                    if (defined($server)) {
300                        $server->command('MSG '.$queue[$i]{'nick'}.' '.$queue[$i]{'xdcc'}.' send '.$queue[$i]{'pack'});
301                        print CLIENTCRAP "%R>>%n XDCC Requesting queue item ".($i+1);
302                        $queue[$i]->{'try'}++;
303                        $queue[$i]->{'status'} = 'requested';
304                    }
305                }
306            }
307            if ($queue[$i]{'status'} eq 'requested' ||
308                $queue[$i]{'status'} eq 'transferring') {
309                $numdls ++;
310            }
311        }
312    }
313    saveQueue();
314}
315
316sub retry_transfer {
317    my ($numdls,$i);
318    $numdls = 0;
319    for ($i=0; $i<= $#queue; $i++) {
320        if ($queue[$i]{'status'} eq 'requested' ||
321            $queue[$i]{'status'} eq 'transferring') {
322            $numdls ++;
323        }
324        if (defined ($queue[$i]->{'timer'})) {
325            Irssi::timeout_remove($queue[$i]->{'timer'});
326            undef ($queue[$i]->{'timer'});
327            if ($queue[$i]->{'status'} eq 'retrying' && $numdls < $max_downloads) {
328                $queue[$i]->{'status'} = 'waiting';
329                process_queue();
330            }
331        }
332    }
333}
334
335sub queue_pack {
336    debugFunc ("queue_pack", @_);
337    my ($args, $server, $witem) = @_;
338    my @args = split(/ /, $args, 3);
339    my $xdcc = "xdcc";
340    my ($nick, $pack, $desc);
341    if (ref $witem && $witem->{type} eq 'QUERY' && $args[0] =~ /^\d+$/) {
342        ($nick, $pack, $desc) = ($witem->{name}, $args[0], $args[1]);
343    } else {
344        ($nick, $pack, $desc) = @args;
345    }
346    if ($desc =~ /^-xdcc=(.+?) (.+)$/) {
347        $xdcc = $1;
348        $desc = $2;
349    }
350    my $status = 'waiting';
351    my $chatnet = $server->{tag};
352    my %transfer = ('nick'    => $nick,
353        'pack'    => $pack,
354        'status'  => $status,
355        'net'     => $chatnet,
356        'pos'     => 0,
357        'try'     => 0,
358        'etr'     => 0,
359        'timer'   => undef,
360        'xdcc'    => $xdcc
361    );
362    if (defined $server->{tag} && defined $lists{lc $server->{tag}} && defined $lists{lc $server->{tag}}{lc $nick}{$_}) {
363        $transfer{filename} = $lists{lc $server->{tag}}{lc $nick}{$_};
364    }
365    if (defined ($desc)) {
366        $transfer{filename} = $desc;
367    }
368    push @queue, \%transfer;
369    process_queue();
370}
371
372sub list_xdcc_queue {
373    my $text;
374    my $i = 1;
375    foreach (@queue) {
376        my $current = $_;
377        my $botname = $current->{'nick'};
378        my $ircnet = $current->{'net'};
379        my $pack = $current->{'pack'};
380        my $status = $current->{'status'};
381        my $info = '';
382        my $etr = '';
383        if ($current->{'status'} eq 'queued') {
384            my $time = $current->{'etr'}-time();
385            my $hours = int($time / (60*60));
386            my $minutes = int( ($time-($hours*60*60))/60 );
387            my $seconds = int( ($time-($hours*60*60)-($minutes*60))  );
388
389            $etr = '('.$hours.' hours, '.$minutes.' minutes and '.$seconds.' seconds remaining)' if ($current->{'etr'} > 0);
390            $info = "[".$current->{'pos'}."]".' '.$etr;
391        } elsif ($current->{'status'} eq 'retrying') {
392            my $time = $current->{'etr'}-time();
393            my $hours = int($time / (60*60));
394            my $minutes = int( ($time-($hours*60*60))/60 );
395            my $seconds = int( ($time-($hours*60*60)-($minutes*60))  );
396
397            $etr = '('.$hours.' hours, '.$minutes.' minutes and '.$seconds.' seconds remaining)' if ($current->{'etr'} > 0);
398            $info = '['.$current->{'try'}.']'.' '.$etr;
399        }
400        $text .= "%9".$i."%9 ".$botname."<".$ircnet.">: Pack ".$pack;
401        $text .= " (".$current->{filename}.")" if defined $current->{filename};
402        $text .= " => ".$status.' '.$info;
403        $text .= "\n";
404        $i++;
405    }
406    print CLIENTCRAP draw_box("XDCCget", $text, "queued packs", 1);
407}
408
409sub cancel_pack {
410    my (@numbers) = @_;
411    @numbers = sort {$b cmp $a} @numbers;
412    foreach (@numbers) {
413        my $item = $queue[$_-1];
414        next if (!defined($item));
415
416        if ($item->{'status'} eq 'queued') {
417            # Remove the request from the bots queue
418            my $server = Irssi::server_find_tag($item->{'net'});
419            $server->command('MSG '.$item->{'nick'}.' xdcc remove');
420            print CLIENTCRAP "%R>>>%n Removing pack ".$_." from server queue";
421            $item->{'status'} = 'canceling';
422            #splice(@queue, $_,$_+1);
423        } elsif ($item->{'status'} eq 'transferring') {
424            $item->{'status'} = 'cancelled';
425            Irssi::command('dcc close get '.$item->{'nick'});
426            print CLIENTCRAP "%R>>>%n Transfer aborted";
427
428        } else {
429            debugFunc ("splice", $_);
430            splice(@queue, $_-1,1);
431        }
432        process_queue();
433    }
434}
435
436sub reset_pack {
437    foreach (@_) {
438        next if ($#queue < $_ || $_ < 0);
439        $queue[$_-1]->{'status'} = 'waiting';
440    }
441    process_queue();
442}
443
444sub pause_pack {
445    my $server = shift;
446    foreach (@_) {
447        next if ($#queue < $_ || $_ < 0);
448        if ($queue[$_-1]->{'status'} eq 'queued') {
449            $server->command('msg '.$queue[$_-1]->{'nick'}.' xdcc remove');
450        } elsif ($queue[$_-1]->{'status'} eq 'transferring') {
451            Irssi::command('dcc close get '.$queue[$_-1]->{'nick'});
452        }
453        $queue[$_-1]->{'status'} = 'paused';
454    }
455    process_queue();
456}
457
458sub list_packs ($$) {
459    my ($server, $bot) = @_;
460    $server->command('MSG '.$bot.' xdcc list');
461    $lists{lc $server->{tag}}{lc $bot} = {};
462}
463
464sub cmd_xdccget {
465    my ($args, $server, $witem) = @_;
466    my @arg = split(/ /, $args);
467
468    if ((scalar(@arg) == 0) or ($arg[0] eq '-l') or ($arg[0] eq 'stat')) {
469        list_xdcc_queue();
470    } elsif ($arg[0] eq 'queue') {
471        # queue files
472        shift @arg;
473        queue_pack("@arg", $server, $witem);
474    } elsif ($arg[0] eq 'list' && defined $arg[1]) {
475        list_packs($server, $arg[1]);
476    } elsif ($arg[0] eq 'cancel') {
477        shift @arg;
478        cancel_pack(@arg);
479    } elsif ($arg[0] eq 'reset') {
480        shift @arg;
481        reset_pack(@arg);
482    } elsif ($arg[0] eq 'pause') {
483        shift @arg;
484        pause_pack($server, @arg);
485    } elsif ($arg[0] eq 'help') {
486        show_help();
487    } elsif ($arg[0] eq 'offers') {
488        shift @arg;
489        show_offers(@arg);
490    }
491}
492
493sub event_private_message {
494    debugFunc ("event_private_message", @_);
495    my ($server, $text, $nick, $address) = @_;
496    event_message_irc_notice($server, $text, $nick, $address, undef);
497}
498
499sub event_no_such_nick {
500    debugFunc ("event_private_message", @_);
501    my ($server, $args, $sender_nick, $sender_address) = @_;
502    my ($myself, $nick) = split(/ /, $args, 3);
503
504    my $i;
505    for ($i=0; $i<= $#queue; $i++) {
506        if ($nick eq $queue[$i]->{'nick'}) {
507            if ($queue[$i]->{'status'} eq 'requested' || $queue[$i]->{'status'} eq 'joining') {
508                my $retry = Irssi::settings_get_int('xdccget_retry_time')*60000;
509                $queue[$i]->{'status'} = 'retrying';
510                $queue[$i]->{'timer'} = Irssi::timeout_add($retry, 'retry_transfer', $i);
511                $queue[$i]->{'etr'} = time()+$retry/1000;
512            }
513        }
514        process_queue();
515    }
516}
517
518sub event_server_connected {
519    my ($server) = @_;
520    debugFunc ("SERVER CONNECTED: " . $server->{'tag'});
521
522    my $i;
523    for ($i=0; $i<= $#queue; $i++) {
524        $queue[$i]->{'status'} = 'waiting' if (lc($queue[$i]->{'net'}) eq lc($server->{'tag'}));
525    }
526    process_queue();
527}
528
529sub event_server_disconnected {
530    my ($server) = @_;
531    debugFunc ("SERVER DISCONNECTED: " . $server->{'tag'});
532
533    my $i;
534    for ($i=0; $i<= $#queue; $i++) {
535        $queue[$i]->{'status'} = 'waiting' if (lc($queue[$i]->{'net'}) eq lc($server->{'tag'}));
536    }
537    process_queue();
538}
539
540sub show_offers {
541    my $server;
542    my $channel;
543    my $nick;
544    my $desc;
545    foreach (@_) {
546        if (/^-server=(.*?)$/) {
547            $server = $1;
548        } elsif (/^-channel=(.*?)$/) {
549            $channel = $1;
550        } elsif (/^-nick=(.*?)$/) {
551            $nick = $1;
552        } else {
553            $desc = $_;
554        }
555    }
556    my $text;
557    $text = "";
558    foreach my $s (keys %offers) {
559        next unless (!defined($server) || $s =~ /$server/i);
560        foreach my $c (keys %{$offers{$s}}) {
561            next unless (!defined($channel) || $c =~ /$channel/i);
562            foreach my $n (keys %{$offers{$s}{$c}}) {
563                next unless (defined($offers{$s}{$c}{$n}{'numpacks'}));
564                next unless (!defined($nick) || $n =~ /$nick/i);
565                my $text1 = "";
566                $text1 .= "$s $c $n - #$offers{$s}{$c}{$n}{'numpacks'}, Slots: $offers{$s}{$c}{$n}{'freeslots'}/$offers{$s}{$c}{$n}{'numslots'}";
567                $text1 .= ", Q: $offers{$s}{$c}{$n}{'posqueue'}/$offers{$s}{$c}{$n}{'numqueue'}" if (defined($offers{$s}{$c}{$n}{'posqueue'}));
568                $text1 .= ", Min: $offers{$s}{$c}{$n}{'minspeed'}" if (defined($offers{$s}{$c}{$n}{'minspeed'}));
569                $text1 .= ", Max: $offers{$s}{$c}{$n}{'maxspeed'}" if (defined($offers{$s}{$c}{$n}{'maxspeed'}));
570                $text1 .= ", Rec: $offers{$s}{$c}{$n}{'recspeed'}" if (defined($offers{$s}{$c}{$n}{'recspeed'}));
571                $text1 .= ", BW: $offers{$s}{$c}{$n}{'bwcurrent'}" if (defined($offers{$s}{$c}{$n}{'bwcurrent'}));
572                $text1 .= ", Rec: $offers{$s}{$c}{$n}{'bwrecord'}" if (defined($offers{$s}{$c}{$n}{'bwrecord'}));
573                $text1 .= ", Cap: $offers{$s}{$c}{$n}{'bwcapacity'}" if (defined($offers{$s}{$c}{$n}{'bwcapacity'}));
574                $text1 .= "\n";
575                my $text2 = "";
576                foreach my $p (sort {$a <=> $b} keys %{$offers{$s}{$c}{$n}{'packs'}}) {
577                    next unless (!defined($desc) || $offers{$s}{$c}{$n}{'packs'}{$p}{'desc'} =~ /$desc/i);
578                    $text2 .= "    #$p x$offers{$s}{$c}{$n}{'packs'}{$p}{'numdl'} [$offers{$s}{$c}{$n}{'packs'}{$p}{'size'}] $offers{$s}{$c}{$n}{'packs'}{$p}{'desc'}\n";
579                }
580                next if (length($text2) == 0);
581                $text .= $text1.$text2;
582            }
583        }
584    }
585    print CLIENTCRAP draw_box("XDCCget", $text, "offers", 1);
586}
587
588sub event_message_public {
589    my ($server, $msg, $nick, $address, $target) = @_;
590
591    if ($msg =~ /.*?\*\*.*? (\d+) packs? .*?\*\*.*?  (\d+) of (\d+) slots? open(?:, Queue: (\d+)\/(\d+))?(?:, Min: ((?:\d+|\.)+KB\/s))?(?:, Max: ((?:\d+|\.)+KB\/s))?(?:, Record: ((?:\d|\.)+KB\/s))?/i) {
592        $offers{$server->{'tag'}} = {} unless (defined($offers{$server->{'tag'}}));
593        $offers{$server->{'tag'}}{$target} = {} unless (defined($offers{$server->{'tag'}}{$target}));
594        $offers{$server->{'tag'}}{$target}{$nick} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}));
595        $offers{$server->{'tag'}}{$target}{$nick}{'packs'} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}{'packs'}));
596        $offers{$server->{'tag'}}{$target}{$nick}{'numpacks'} = $1 if (defined($1));
597        $offers{$server->{'tag'}}{$target}{$nick}{'freeslots'} = $2 if (defined($2));
598        $offers{$server->{'tag'}}{$target}{$nick}{'numslots'} = $3 if (defined($3));
599        $offers{$server->{'tag'}}{$target}{$nick}{'posqueue'} = $4 if (defined($4));
600        $offers{$server->{'tag'}}{$target}{$nick}{'numqueue'} = $5 if (defined($5));
601        $offers{$server->{'tag'}}{$target}{$nick}{'minspeed'} = $6 if (defined($6));
602        $offers{$server->{'tag'}}{$target}{$nick}{'maxspeed'} = $7 if (defined($7));
603        $offers{$server->{'tag'}}{$target}{$nick}{'recspeed'} = $8 if (defined($8));
604    }
605    if ($msg =~ /.*?\*\*.*? Bandwidth Usage .*?\*\*.*? Current: ((?:\d|\.)+KB\/s)(?:, Cap: ((?:\d|\.)+KB\/s))?(?:, Record: ((?:\d|\.)+KB\/s))?/) {
606        $offers{$server->{'tag'}} = {} unless (defined($offers{$server->{'tag'}}));
607        $offers{$server->{'tag'}}{$target} = {} unless (defined($offers{$server->{'tag'}}{$target}));
608        $offers{$server->{'tag'}}{$target}{$nick} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}));
609        $offers{$server->{'tag'}}{$target}{$nick}{'packs'} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}{'packs'}));
610        $offers{$server->{'tag'}}{$target}{$nick}{'bwcurrent'} = $1 if (defined($1));
611        $offers{$server->{'tag'}}{$target}{$nick}{'bwcapacity'} = $2 if (defined($2));
612        $offers{$server->{'tag'}}{$target}{$nick}{'bwrecord'} = $3 if (defined($3));
613    }
614
615    if ($msg =~ /\.*?#(\d+).*?\s+(\d+)x\s+\[\s*?((?:\d|\.)*(?:M|K|G))\]\s+(.+)/) {
616        $offers{$server->{'tag'}} = {} unless (defined($offers{$server->{'tag'}}));
617        $offers{$server->{'tag'}}{$target} = {} unless (defined($offers{$server->{'tag'}}{$target}));
618        $offers{$server->{'tag'}}{$target}{$nick} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}));
619        $offers{$server->{'tag'}}{$target}{$nick}{'packs'} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}{'packs'}));
620        $offers{$server->{'tag'}}{$target}{$nick}{'packs'}{$1} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}{'packs'}{$1}));
621        $offers{$server->{'tag'}}{$target}{$nick}{'packs'}{$1}{'numdl'} = $2;
622        $offers{$server->{'tag'}}{$target}{$nick}{'packs'}{$1}{'size'} = $3;
623        $offers{$server->{'tag'}}{$target}{$nick}{'packs'}{$1}{'desc'} = $4;
624    }
625}
626
627Irssi::command_bind('xdccget', \&cmd_xdccget);
628
629loadQueue();
630
631foreach my $cmd ('queue', 'cancel', 'list', 'help', 'stat','reset','offers', 'pause') {
632    Irssi::command_bind('xdccget '.$cmd => sub {
633        cmd_xdccget("$cmd ".$_[0], $_[1], $_[2]);
634    });
635}
636
637Irssi::signal_add('message public', 'event_message_public');
638Irssi::signal_add('message irc notice', 'event_message_irc_notice');
639Irssi::signal_add("message private", "event_private_message");
640Irssi::signal_add("event 401", "event_no_such_nick");
641Irssi::signal_add("event connected", "event_server_connected");
642Irssi::signal_add("server disconnected", "event_server_disconnected");
643
644print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded: /xdccget help for help';
645
646print "Configuration files are stored in $config_path";
647
648if ($#queue >= 0) {
649    process_queue();
650}
651