]> git.99rst.org Git - irssi-scripts.irssi.org.git/commitdiff
Update xdccget.pl with queue management and search
authorobfuscoder <redacted>
Thu, 16 Oct 2014 12:47:21 +0000 (14:47 +0200)
committerObfuscoder <redacted>
Sun, 3 May 2015 19:32:51 +0000 (21:32 +0200)
Update xdccget.pl with some queue management and search capabilities. (pausing, retries, max parallel downloads).
Rework of help output.

scripts/xdccget.pl

index 9f16bda6b23716dcac30cf921a5c033482b16450..b6d396ca6473c0525476148811158ae3fce69bda 100644 (file)
-#
-# by Stefan "tommie" Tomanek <stefan@kann-nix.org>
-#
-# History:
-#
-# 26.02.2002
-# *first release, report bugs :)
-#
-# 01.03.2002
-# *CHanged to GPL
-#
-# 08.03.200
-# *some bugfixes
-#
-# 13.04.2002
-# *major improvements
-# *cosmetic changes
-# 
-# 14.04.2002
-# *internal redesign
-# *feature enhancements
-#
-# 17.04.2002
-# * improved queuing code
-# * changed to $server->{tag}
-# * improved communication with server
-#
-# 21.04.2002
-# *improved ETA listing
-#
-# 27.04.2002
-# *handling of gone bots added
-#
-# 28.04.2002
-# *fixed handling of servers that are not in an ircnet
+# Original version by Stefan "tommie" Tomanek <stefan@kann-nix.org>
+# Enhanced with max. download limits, retries, pausing, searching and other neat stuff by Obfuscoder (obfuscoder@obfusco.de)
+# You can find the script on GitHub: https://github.com/obfuscoder/irssi_scripts
+# Please report bugs to https://github.com/obfuscoder/irssi_scripts/issues
 
 use strict;
-#use warnings;
+use warnings;
 
 use vars qw($VERSION %IRSSI);
-$VERSION = "20040509";
+$VERSION = "20141016";
 %IRSSI = (
-    authors     => "Stefan 'tommie' Tomanek",
-    contact     => "stefan\@pico.ruhr.de",
-    name        => "XDCCget",
-    description => "advances downloading from XDCC bots",
+    authors     => "Stefan 'tommie' Tomanek, Obfuscoder",
+    contact     => "obfuscoder\@obfusco.de",
+    name        => "xdccget",
+    description => "enhanced downloading, queing, searching from XDCC bots",
     license     => "GPLv2",
     changed     => "$VERSION",
-    commands   => "xdccget"
+    commands    => "xdccget"
 );
 
 use Irssi 20020324;
 
-use vars qw(@queue $timer $debug %lists);
+use vars qw(@queue @completed %offers $timer $debug %lists);
+
+Irssi::settings_add_str($IRSSI{'name'}, 'xdccget_config_path', "$ENV{HOME}/.irssi");
+Irssi::settings_add_int($IRSSI{'name'}, 'xdccget_max_downloads', 2);
+Irssi::settings_add_int($IRSSI{'name'}, 'xdccget_retry_time', 5);
 
 $debug=0;
+my $config_path = Irssi::settings_get_str('xdccget_config_path');
+my $max_downloads = Irssi::settings_get_int('xdccget_max_downloads');
+my $queue_file = "$config_path/xdccget.queue";
+sub saveQueue {
+    if (!open (QUEUE, q{>}, $queue_file)) {
+        print CLIENTCRAP "XDCCGET - ERROR! Cannot open queue file for saving at $queue_file. $!";
+        return;
+    }
+    foreach (@queue) {
+        print QUEUE $_->{net}."\t".$_->{nick}."\t".$_->{pack}."\t".$_->{filename}."\t".$_->{xdcc}."\n";
+    }
+    if (!close QUEUE) {
+        print CLIENTCRAP "XDCCGET - ERROR! Could not close queue file after saving at $queue_file. $!";
+    }
+}
+
+sub loadQueue {
+    @queue = ();
+    if (!open (QUEUE, q{<}, $queue_file)) {
+        print CLIENTCRAP "XDCCGET - Warning: Could open queue file for loading from $queue_file: $!";
+        return;
+    }
+    while (<QUEUE>) {
+        chomp;
+        my ($net, $nick, $pack, $desc, $xdcc) = split (/\t/);
+        if ($xdcc eq "") {
+            $xdcc = "xdcc";
+        }
+        my %transfer = (
+            'nick'     => $nick,
+            'pack'     => $pack,
+            'status'   => 'waiting',
+            'net'      => $net,
+            'pos'      => 0,
+            'try'      => 0,
+            'etr'      => 0,
+            'timer'    => undef,
+            'xdcc'     => $xdcc,
+            'filename' => $desc,
+        );
+        push @queue, \%transfer;
+    }
+    if (!close QUEUE) {
+        print CLIENTCRAP "XDCCGET - ERROR! Could not close queue file after loading at $queue_file. $!";
+    }
+}
+
+sub debugFunc {
+    return unless ($debug);
+    my $funcname = shift (@_);
+    print CLIENTCRAP "XDCC-DEBUG - $funcname (". join(",",@_).")\n";
+}
 
 sub show_help() {
     my $help="XDCCget $VERSION
-/xdccget queue Nickname <number> <number>...
-    Queue the specified packs of the server 'Nickname'
-/xdccget list
-    List the download queue
-/xdccget cancel <number>
-    Remove pack <number> from the local queue
-/xdccget help
-    Display this help
+  Commands:
+
+  /xdccget queue <nickname> <number> [[-xdcc=<method>] <description>]
+      Queue the specified pack of the currently selected server and 'Nickname'.
+      The description will be shown in the queue.
+      With -xdcc=<method> it is possible to specify the method to be used in the request.
+      Default is 'xdcc' as in /msg <nickname> xdcc send <number>
+
+  /xdccget stat
+  /xdccget
+  /xdccget -l
+      List the download queue
+
+  /xdccget list <nickname>
+      Request the XDCC list of <nickname>
+
+  /xdccget cancel <number>
+      Cancel the download if currently downloading, request to being removed from queue
+      if queued by the XDCC offerer, or remove pack <number> from the download queue
+
+  /xdccget pause <number>
+      Pause pack <number> within the local queue. Resume with reset
+
+  /xdccget reset <number>
+      Reset pack <number> so that it is unpaused and --
+      if download slots are still available -- triggers a download request
+
+  /xdccget offers [<options>] <description search pattern>
+      Display all the announced offers matching the given pattern or options.
+      The announcements are continuously monitored by this script throughout all joined channels.
+
+      Options:
+      One or more of the following options can be used. Each option must start with a '-':
+
+      -server=<server pattern>     only show offers announced by bots on servers matching this pattern
+      -channel=<channel pattern>   only show offers announced by bots on channels matching this pattern
+      -nick=<nick pattern>         only show offers announced by bots with nicks matching this pattern
+
+      Examples:
+         /xdccget offers iron.*bluray
+         /xdccget offers -nick=bot iron.*bluray
+         /xdccget offers -channel=beast-xdcc iron.*bluray
+
+      Regular expressions are used to match each of the parameters.
+
+  /xdccget help
+      Display this help
+
+  You can also simply use /x instead of /xdccget ;-)
+
+  Configuration:
+
+  xdccget_config_path
+      Path where this script is storing its files (queue and finished downloads).
+      Default is '\$HOME/.irssi'.
+
+  xdccget_max_downloads
+      Maximum number of parallel downloads. Default is 2. A download request which is queued
+      by the XDCC offer bot does not count against the limit. The next item in the download queue
+      is being requested as long as download slots are available. Also other downloads not controlled
+      by this script do not count either. It is also possible to exceed this limit if a bot sends the
+      previously requested and queued file while there are downloads running already. 
+
+  xdccget_retry_time
+      Time in minutes between retries. Default is 5. Retries are necessary for full
+      offer queues of bots, bots being/becoming offline, or not getting the requested download or any
+      understandable message regarding the request. Please DO NOT set this value to less than 300
+      (5 minutes) or risk being banned from the channels for spamming the bots.
+
+  Please report bugs to https://github.com/obfuscoder/irssi_scripts/issues
 ";
     my $text = '';
     foreach (split(/\n/, $help)) {
@@ -79,8 +166,12 @@ sub draw_box ($$$$) {
     my $box = '';
     my $exp_flags = Irssi::EXPAND_FLAG_IGNORE_EMPTY | Irssi::EXPAND_FLAG_IGNORE_REPLACES;
     $box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n";
-    foreach (split(/\n/, $text)) {                                                      $box .= '%R|%n '.$_."\n";
-    }                                                                               $box .= '%R`--<%n'.$footer.'%R>->%n';
+    if (defined($text)) {
+        foreach (split(/\n/, $text)) {
+            $box .= '%R|%n '.$_."\n";
+        }
+    }
+    $box .= '%R`--<%n'.$footer.'%R>->%n';
     $box =~ s/%.//g unless $colour;
     return $box;
 }
@@ -88,178 +179,229 @@ sub draw_box ($$$$) {
 sub contains {
     my ($item, @list) = @_;
     foreach (@list) {
-           ($item eq $_) && return 1;
+        ($item eq $_) && return 1;
     }
     return 0;
 }
 
 sub event_message_irc_notice {
+    debugFunc ("event_message_irc_notice", @_);
     my ($server, $msg, $nick, $address, $target) = @_;
+    my $i;
     $_ = $msg;
-    if ($queue[0] &&  $nick eq $queue[0]->{'nick'}) {
-       if (/\*\*\* Closing Connection:/) {
-           print CLIENTCRAP "%R>>%n XDCC-Transfer closed";
-           # Is it a canceled transger?
-           if ($queue[0]->{'status'} == 5) {
-               $queue[0]->{'status'} = 4;
-           } else {
-               # We should try again
-               $queue[0]->{'status'} = 0;
-           }
-       } elsif (/\*\*\* Transfer Completed/i) {
-           print CLIENTCRAP "%R>>%n XDCC-Transfer completed";
-           # Mark the transfer as completed
-           $queue[0]->{'status'} = 4;
-       } elsif (/\*\*\* You already requested that pack/i) {
-           $queue[0]->{'status'} = 4;
-       } elsif (/\*\*\* Sending You Pack/i || /\*\*\* Sending You Your Queued Pack|DCC Send .*? \(.*?\)/i) {
-           $queue[0]->{'status'} = 3;
-           print CLIENTCRAP "%R>>%n XDCC-Transfer starting";
-       } elsif (/\*\*\* All Slots Full, Added (|you to the main )queue in position ([0-9]*)/i) {
-           $queue[0]->{'pos'} = $2;
-           $queue[0]->{'etr'} = 0;
-           $queue[0]->{'status'} = 2;
-       } 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) {
-           $queue[0]->{'pos'} = $3;
-           $queue[0]->{'etr'} = time() + (($5*60)+$6)*60;
-           $queue[0]->{'status'} = 2;
-       } elsif (/You have been queued for ([0-9]*?) hours ([0-9]*?) minutes, currently in main queue position ([0-9]*?) of ([0-9]*?)\./i) {
-           $queue[0]->{'pos'} = $3;
-           $queue[0]->{'status'} = 2;
-       } elsif (/You have been queued for ([0-9]*?) minutes, currently in main queue position ([0-9]*?) of ([0-9]*?)\./) {
-           $queue[0]->{'status'} = 2;
-           # FIXME unite somehow with regexp above
-           $queue[0]->{'pos'} = $2;
-       } elsif (/It has been placed in queue slot #(\d+), it will send when sends are available/) {
-           $queue[0]->{'pos'} = $1;
-           $queue[0]->{'status'} = 2;
-       } elsif (/\*\*\* Invalid Pack Number/) {
-           $queue[0]->{'status'} = 4;
-       } elsif (/\*\*\* The Owner Has Requested That No New Connections Are Made/) {
-           $queue[0]->{'status'} = 4;
-       } elsif (/\*\*\* All Slots Full,(| Main) queue of size [0-9]* is Full, Try Again Later/i || /\*\*\* You can only have 1 transfer at a time/i) {
-           if (Irssi::settings_get_int('xdccget_retry_time') > 0) {
-               my $retry = Irssi::settings_get_int('xdccget_retry_time')*1000;
-               $queue[0]->{'status'} = 6;
-               $queue[0]->{'timer'} = Irssi::timeout_add($retry, 'retry_transfer', undef);
-               $queue[0]->{'etr'} = time()+$retry/1000;
-           } else {
-               $queue[0]->{'status'} = 4;
-           }
-       } elsif (/Removed you from the queue/) {
-           $queue[0]->{'status'} = 4;
-       } else { Irssi::print($_) if ($debug); }
-       
-       process_queue();
+    for ($i=0; $i<= $#queue; $i++) {
+        if ($queue[$i] && lc($nick) eq lc($queue[$i]->{'nick'})) {
+            if (/Closing Connection/) {
+                print CLIENTCRAP "%R>>%n XDCC-Transfer closed";
+                # Is it a canceled transfer?
+                if ($queue[$i]->{'status'} eq 'canceling') {
+                    $queue[$i]->{'status'} = 'cancelled';
+                } elsif ($queue[$i]->{'status'} ne 'paused') {
+                    # We should try again unless we paused the queue item
+                    $queue[$i]->{'status'} = 'waiting';
+                }
+            } elsif (/Transfer Completed/i) {
+                print CLIENTCRAP "%R>>%n XDCC-Transfer completed";
+                # Mark the transfer as completed
+                $queue[$i]->{'status'} = 'completed';
+            } elsif (/You already requested that pack/i) {
+                $queue[$i]->{'status'} = 'transferring';
+            } elsif (/You already have that item queued/i) {
+                $queue[$i]->{'status'} = 'queued';
+            } elsif (/Sending (?:You|u) (?:Your Queued )?Pack/i) {
+                $queue[$i]->{'status'} = 'transferring';
+                print CLIENTCRAP "%R>>%n XDCC-Transfer starting";
+            } elsif (/All Slots Full, Added (|you to the main )queue in position ([0-9]*)/i) {
+                $queue[$i]->{'pos'} = $2;
+                $queue[$i]->{'etr'} = 0;
+                $queue[$i]->{'status'} = 'queued';
+            } 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) {
+                $queue[$i]->{'pos'} = $3;
+                $queue[$i]->{'etr'} = time() + (($5*60)+$6)*60;
+                $queue[$i]->{'status'} = 'queued';
+            } elsif (/You have been queued for ([0-9]*?) hours ([0-9]*?) minutes, currently in main queue position ([0-9]*?) of ([0-9]*?)\./i) {
+                $queue[$i]->{'pos'} = $3;
+                $queue[$i]->{'status'} = 'queued';
+            } elsif (/You have been queued for ([0-9]*?) minutes, currently in main queue position ([0-9]*?) of ([0-9]*?)\./i) {
+                $queue[$i]->{'status'} = 'queued';
+                # FIXME unite somehow with regexp above
+                $queue[$i]->{'pos'} = $2;
+            } elsif (/It has been placed in queue slot #(\d+), it will send when sends are available/i) {
+                $queue[$i]->{'pos'} = $1;
+                $queue[$i]->{'status'} = 'queued';
+            } elsif (/Invalid Pack Number/i) {
+                $queue[$i]->{'status'} = 'invalid';
+            } elsif (/The Owner Has Requested That No New Connections Are Made/i ||
+                /All Slots Full,( Main)? queue of size [0-9]* is Full, Try Again Later/i ||
+                /You can only have 1 transfer at a time/i ||
+                /you must be on a known channel/i) {
+                print CLIENTCRAP "Retrying ....\n";
+                my $retry = Irssi::settings_get_int('xdccget_retry_time')*60000;
+                $queue[$i]->{'status'} = 'retrying';
+                $queue[$i]->{'timer'} = Irssi::timeout_add($retry, 'retry_transfer', $i);
+                $queue[$i]->{'etr'} = time()+$retry/1000;
+            } elsif (/must be on a known channel/i) {
+                $server->command("WHOIS $nick");
+                $queue[$i]->{'status'} = 'joining';
+            } else { Irssi::print($_) if ($debug); }
+            process_queue();
+            last;
+        }
     }
     if (/#(\d+).+?\d+x \[ *(<?\d+.*?)\] +(.*)$/) {
-       my ($pack, $size, $name) = ($1, $2, $3);
-       if (defined $lists{lc $server->{tag}}{lc $nick}) {
-           $lists{lc $server->{tag}}{lc $nick}{$pack} = $name;
-       }
-       foreach (@queue) {
-           next unless lc $nick eq lc $_->{nick};
-           next unless lc $server->{tag} eq lc $_->{net};
-           next unless $_->{pack} eq $pack;
-           $_->{filename} = $name;
-       }
+        my ($pack, $size, $name) = ($1, $2, $3);
+        if (defined $lists{lc $server->{tag}}{lc $nick}) {
+            $lists{lc $server->{tag}}{lc $nick}{$pack} = $name;
+        }
+        foreach (@queue) {
+            next unless lc $nick eq lc $_->{nick};
+            next unless lc $server->{tag} eq lc $_->{net};
+            next unless $_->{pack} eq $pack;
+            $_->{filename} = $name;
+        }
     }
 }
 
 sub process_queue {
+    debugFunc ("process_queue", @_);
+    my ($i, $j, $numdls);
+    $numdls = 0;
+    my $process;
     unless (scalar(@queue) > 0) {return 0};
-    my %current = %{$queue[0]};
-    shift @queue if ( $current{'status'} == 4 );
-    unless (scalar(@queue) > 0) {return 0};
-    %current = %{$queue[0]};
-    if ( $current{'status'} == 0 ) {
-       my $server = Irssi::server_find_tag($current{'net'});
-       $server->command('MSG '.$current{'nick'}.' xdcc send '.$current{'pack'});
-       $queue[0]->{'try'}++;
-       $queue[0]->{'status'} = 1;
+    for ($i=0; $i<= $#queue; $i++) {
+        debugFunc (" - Item: $i -> ".$queue[$i]{'status'});
+        if ($queue[$i]{'status'} eq 'completed' ||
+            $queue[$i]{'status'} eq 'cancelled') {
+            push (@completed, $queue[$i]);
+            my $done_file = "$config_path/xdccdone.txt";
+            if (!open (DONEFILE, q{>>}, $done_file)) {
+                print CLIENTCRAP "XDCCGET - Warning: Could not open file for appending done queue entry at $done_file. $!";
+            } else {
+                print DONEFILE $queue[$i]{net}."\t".$queue[$i]{nick}."\t".$queue[$i]{pack}."\t".$queue[$i]{filename}."\t".$queue[$i]{'status'}."\n";
+                if (!close (DONEFILE)) {
+                    print CLIENTCRAP "XDCCGET - Warning: Could not close file after appending done queue entry at $done_file. $!";
+                }
+            }
+            splice (@queue, $i, 1);
+        } else {
+            if ($queue[$i]{'status'} eq 'waiting') {
+                $process = 1;
+                for ($j=0; $j<$i; $j++) {
+                    if ($queue[$i]{'nick'} eq $queue[$j]{'nick'}) {
+                        $process = 0;
+                    }
+                }
+                if ($numdls >= $max_downloads) {
+                    $process = 0;
+                }
+                if ($process) {
+                    my $server = Irssi::server_find_tag($queue[$i]{'net'});
+                    if (defined($server)) {
+                        $server->command('MSG '.$queue[$i]{'nick'}.' '.$queue[$i]{'xdcc'}.' send '.$queue[$i]{'pack'});
+                        print CLIENTCRAP "%R>>%n XDCC Requesting queue item ".($i+1);
+                        $queue[$i]->{'try'}++;
+                        $queue[$i]->{'status'} = 'requested';
+                    }
+                }
+            }
+            if ($queue[$i]{'status'} eq 'requested' ||
+                $queue[$i]{'status'} eq 'transferring') {
+                $numdls ++;
+            }
+        }
     }
+    saveQueue();
 }
 
 sub retry_transfer {
-    if (defined $queue[0] && $queue[0]->{'status'} == 6) {
-       Irssi::timeout_remove($queue[0]->{'timer'});
-       #print CLIENTCRAP "%R>>%n Retrying XDCC-transfer...";
-       $queue[0]->{'status'} = 0;
-       process_queue();
+    my ($numdls,$i);
+    $numdls = 0;
+    for ($i=0; $i<= $#queue; $i++) {
+        if ($queue[$i]{'status'} eq 'requested' ||
+            $queue[$i]{'status'} eq 'transferring') {
+            $numdls ++;
+        }
+        if (defined ($queue[$i]->{'timer'})) {
+            Irssi::timeout_remove($queue[$i]->{'timer'});
+            undef ($queue[$i]->{'timer'});
+            if ($queue[$i]->{'status'} eq 'retrying' && $numdls < $max_downloads) {
+                $queue[$i]->{'status'} = 'waiting';
+                process_queue();
+            }
+        }
     }
 }
 
 sub queue_pack {
+    debugFunc ("queue_pack", @_);
     my ($args, $server, $witem) = @_;
-    my @args = split(/ /, $args, 2);
-    my ($nick, $packs);
+    my @args = split(/ /, $args, 3);
+    my $xdcc = "xdcc";
+    my ($nick, $pack, $desc);
     if (ref $witem && $witem->{type} eq 'QUERY' && $args[0] =~ /^\d+$/) {
-       ($nick, $packs) = ($witem->{name}, $args[0]);
+        ($nick, $pack, $desc) = ($witem->{name}, $args[0], $args[1]);
     } else {
-       ($nick, $packs) = @args;
+        ($nick, $pack, $desc) = @args;
+    }
+    if ($desc =~ /^-xdcc=(.+?) (.+)$/) {
+        $xdcc = $1;
+        $desc = $2;
     }
-    my @packs = split(/ /, $packs);
-    foreach (@packs) {
-       # 0: Waiting, 1: Processing, 2: Doenloading
-       my $status = 0;
-       my $chatnet = $server->{tag};
-       my %transfer = ('nick'    => $nick,
-                       'pack'    => $_,
-                       'status'  => $status,
-                       'net'     => $chatnet,
-                       'pos'     => 0,
-                       'try'     => 0,
-                       'etr'     => 0,
-                       'timer'   => undef,
-                       );
-       if (defined $lists{lc $server->{tag}}{lc $nick}{$_}) {
-           $transfer{filename} = $lists{lc $server->{tag}}{lc $nick}{$_};
-       }
-       push @queue, \%transfer;
+    my $status = 'waiting';
+    my $chatnet = $server->{tag};
+    my %transfer = ('nick'    => $nick,
+        'pack'    => $pack,
+        'status'  => $status,
+        'net'     => $chatnet,
+        'pos'     => 0,
+        'try'     => 0,
+        'etr'     => 0,
+        'timer'   => undef,
+        'xdcc'    => $xdcc
+    );
+    if (defined $server->{tag} && defined $lists{lc $server->{tag}} && defined $lists{lc $server->{tag}}{lc $nick}{$_}) {
+        $transfer{filename} = $lists{lc $server->{tag}}{lc $nick}{$_};
     }
-    process_queue()
+    if (defined ($desc)) {
+        $transfer{filename} = $desc;
+    }
+    push @queue, \%transfer;
+    process_queue();
 }
 
 sub list_xdcc_queue {
     my $text;
-    my %progress = (0=>'waiting',
-                   1=>'requesting',
-                   2=>'queued',
-                   3=>'transferring',
-                   4=>'completed',
-                   5=>'canceling',
-                   6=>'retrying');
     my $i = 1;
     foreach (@queue) {
-       my $current = $_;
-       my $botname = $current->{'nick'};
-       my $ircnet = $current->{'net'};
-       my $pack = $current->{'pack'};
-       my $status = $progress{$current->{'status'}};
-       my $info = '';
-       my $etr = '';
-       if ($current->{'status'}==2) {
-           my $time = $current->{'etr'}-time();
+        my $current = $_;
+        my $botname = $current->{'nick'};
+        my $ircnet = $current->{'net'};
+        my $pack = $current->{'pack'};
+        my $status = $current->{'status'};
+        my $info = '';
+        my $etr = '';
+        if ($current->{'status'} eq 'queued') {
+            my $time = $current->{'etr'}-time();
+            my $hours = int($time / (60*60));
+            my $minutes = int( ($time-($hours*60*60))/60 );
+            my $seconds = int( ($time-($hours*60*60)-($minutes*60))  );
+
+            $etr = '('.$hours.' hours, '.$minutes.' minutes and '.$seconds.' seconds remaining)' if ($current->{'etr'} > 0);
+            $info = "[".$current->{'pos'}."]".' '.$etr;
+        } elsif ($current->{'status'} eq 'retrying') {
+            my $time = $current->{'etr'}-time();
             my $hours = int($time / (60*60));
             my $minutes = int( ($time-($hours*60*60))/60 );
             my $seconds = int( ($time-($hours*60*60)-($minutes*60))  );
 
             $etr = '('.$hours.' hours, '.$minutes.' minutes and '.$seconds.' seconds remaining)' if ($current->{'etr'} > 0);
-           $info = "[".$current->{'pos'}."]".' '.$etr;
-       } elsif ($current->{'status'}==6) {
-           my $time = $current->{'etr'}-time();
-           my $hours = int($time / (60*60));
-           my $minutes = int( ($time-($hours*60*60))/60 );
-           my $seconds = int( ($time-($hours*60*60)-($minutes*60))  );
-           
-           $etr = '('.$hours.' hours, '.$minutes.' minutes and '.$seconds.' seconds remaining)' if ($current->{'etr'} > 0);
-           $info = '['.$current->{'try'}.']'.' '.$etr;
-       }
-       $text .= "%9".$i."%9 ".$botname."<".$ircnet.">: Pack ".$pack;
-       $text .= " (".$current->{filename}.")" if defined $current->{filename};
-       $text .= " => ".$status.' '.$info;
-       $text .= "\n";
-       $i++;
+            $info = '['.$current->{'try'}.']'.' '.$etr;
+        }
+        $text .= "%9".$i."%9 ".$botname."<".$ircnet.">: Pack ".$pack;
+        $text .= " (".$current->{filename}.")" if defined $current->{filename};
+        $text .= " => ".$status.' '.$info;
+        $text .= "\n";
+        $i++;
     }
     print CLIENTCRAP draw_box("XDCCget", $text, "queued packs", 1);
 }
@@ -268,24 +410,49 @@ sub cancel_pack {
     my (@numbers) = @_;
     @numbers = sort {$b cmp $a} @numbers;
     foreach (@numbers) {
-       my $item = @queue->[$_-1];
-
-       if ($item->{'status'} == 2) {
-           # Remove the order from the bots queue
-           my $server = Irssi::server_find_tag($item->{'net'});
-           $server->command('MSG '.$item->{'nick'}.' xdcc remove');
-           print CLIENTCRAP "%R>>>%n Removing pack ".$_." from server queue";
-           $item->{'status'} = 5;
-           #splice(@queue, $_,$_+1);
-       } elsif ($item->{'status'} == 3) {
-           $item->{'status'} = 5;
-           Irssi::command('DCC close get '.$item->{'nick'});
-           print CLIENTCRAP "%R>>>%n Transfer aborted, waiting for acknowledgement";
-       } else {
-           splice(@queue, $_-1, $_);
-       }
-       process_queue();
+        my $item = $queue[$_-1];
+        next if (!defined($item));
+
+        if ($item->{'status'} eq 'queued') {
+            # Remove the request from the bots queue
+            my $server = Irssi::server_find_tag($item->{'net'});
+            $server->command('MSG '.$item->{'nick'}.' xdcc remove');
+            print CLIENTCRAP "%R>>>%n Removing pack ".$_." from server queue";
+            $item->{'status'} = 'canceling';
+            #splice(@queue, $_,$_+1);
+        } elsif ($item->{'status'} eq 'transferring') {
+            $item->{'status'} = 'cancelled';
+            Irssi::command('dcc close get '.$item->{'nick'});
+            print CLIENTCRAP "%R>>>%n Transfer aborted";
+
+        } else {
+            debugFunc ("splice", $_);
+            splice(@queue, $_-1,1);
+        }
+        process_queue();
+    }
+}
+
+sub reset_pack {
+    foreach (@_) {
+        next if ($#queue < $_ || $_ < 0);
+        $queue[$_-1]->{'status'} = 'waiting';
+    }
+    process_queue();
+}
+
+sub pause_pack {
+    my $server = shift;
+    foreach (@_) {
+        next if ($#queue < $_ || $_ < 0);
+        if ($queue[$_-1]->{'status'} eq 'queued') {
+            $server->command('msg '.$queue[$_-1]->{'nick'}.' xdcc remove');
+        } elsif ($queue[$_-1]->{'status'} eq 'transferring') {
+            Irssi::command('dcc close get '.$queue[$_-1]->{'nick'});
+        }
+        $queue[$_-1]->{'status'} = 'paused';
     }
+    process_queue();
 }
 
 sub list_packs ($$) {
@@ -298,53 +465,186 @@ sub cmd_xdccget {
     my ($args, $server, $witem) = @_;
     my @arg = split(/ /, $args);
 
-    if ((scalar(@arg) == 0) or ($arg[0] eq '-l')) {
-       list_xdcc_queue();
+    if ((scalar(@arg) == 0) or ($arg[0] eq '-l') or ($arg[0] eq 'stat')) {
+        list_xdcc_queue();
     } elsif ($arg[0] eq 'queue') {
-       # queue files
-       shift @arg;
-       queue_pack("@arg", $server, $witem);
+        # queue files
+        shift @arg;
+        queue_pack("@arg", $server, $witem);
     } elsif ($arg[0] eq 'list' && defined $arg[1]) {
-       list_packs($server, $arg[1]);
+        list_packs($server, $arg[1]);
     } elsif ($arg[0] eq 'cancel') {
-       shift @arg;
-       cancel_pack(@arg);
+        shift @arg;
+        cancel_pack(@arg);
+    } elsif ($arg[0] eq 'reset') {
+        shift @arg;
+        reset_pack(@arg);
+    } elsif ($arg[0] eq 'pause') {
+        shift @arg;
+        pause_pack($server, @arg);
     } elsif ($arg[0] eq 'help') {
-       show_help();
+        show_help();
+    } elsif ($arg[0] eq 'offers') {
+        shift @arg;
+        show_offers(@arg);
     }
 }
 
 sub event_private_message {
+    debugFunc ("event_private_message", @_);
     my ($server, $text, $nick, $address) = @_;
     event_message_irc_notice($server, $text, $nick, $address, undef);
 }
 
 sub event_no_such_nick {
+    debugFunc ("event_private_message", @_);
     my ($server, $args, $sender_nick, $sender_address) = @_;
     my ($myself, $nick) = split(/ /, $args, 3);
-    
-    unless (scalar(@queue) == 0) {
-       if ($nick eq $queue[0]->{'nick'}) {
-           if ($queue[0]->{'status'} == 1 || $queue[0]->{'status'} == 5) {
-               $queue[0]->{'status'} = 4;
-           }
-       }
-       process_queue();
+
+    my $i;
+    for ($i=0; $i<= $#queue; $i++) {
+        if ($nick eq $queue[$i]->{'nick'}) {
+            if ($queue[$i]->{'status'} eq 'requested' || $queue[$i]->{'status'} eq 'joining') {
+                my $retry = Irssi::settings_get_int('xdccget_retry_time')*60000;
+                $queue[$i]->{'status'} = 'retrying';
+                $queue[$i]->{'timer'} = Irssi::timeout_add($retry, 'retry_transfer', $i);
+                $queue[$i]->{'etr'} = time()+$retry/1000;
+            }
+        }
+        process_queue();
+    }
+}
+
+sub event_server_connected {
+    my ($server) = @_;
+    debugFunc ("SERVER CONNECTED: " . $server->{'tag'});
+
+    my $i;
+    for ($i=0; $i<= $#queue; $i++) {
+        $queue[$i]->{'status'} = 'waiting' if (lc($queue[$i]->{'net'}) eq lc($server->{'tag'}));
+    }
+    process_queue();
+}
+
+sub event_server_disconnected {
+    my ($server) = @_;
+    debugFunc ("SERVER DISCONNECTED: " . $server->{'tag'});
+
+    my $i;
+    for ($i=0; $i<= $#queue; $i++) {
+        $queue[$i]->{'status'} = 'waiting' if (lc($queue[$i]->{'net'}) eq lc($server->{'tag'}));
     }
+    process_queue();
 }
 
+sub show_offers {
+    my $server;
+    my $channel;
+    my $nick;
+    my $desc;
+    foreach (@_) {
+        if (/^-server=(.*?)$/) {
+            $server = $1;
+        } elsif (/^-channel=(.*?)$/) {
+            $channel = $1;
+        } elsif (/^-nick=(.*?)$/) {
+            $nick = $1;
+        } else {
+            $desc = $_;
+        }
+    }
+    my $text;
+    $text = "";
+    foreach my $s (keys %offers) {
+        next unless (!defined($server) || $s =~ /$server/i);
+        foreach my $c (keys %{$offers{$s}}) {
+            next unless (!defined($channel) || $c =~ /$channel/i);
+            foreach my $n (keys %{$offers{$s}{$c}}) {
+                next unless (defined($offers{$s}{$c}{$n}{'numpacks'}));
+                next unless (!defined($nick) || $n =~ /$nick/i);
+                my $text1 = "";
+                $text1 .= "$s $c $n - #$offers{$s}{$c}{$n}{'numpacks'}, Slots: $offers{$s}{$c}{$n}{'freeslots'}/$offers{$s}{$c}{$n}{'numslots'}";
+                $text1 .= ", Q: $offers{$s}{$c}{$n}{'posqueue'}/$offers{$s}{$c}{$n}{'numqueue'}" if (defined($offers{$s}{$c}{$n}{'posqueue'}));
+                $text1 .= ", Min: $offers{$s}{$c}{$n}{'minspeed'}" if (defined($offers{$s}{$c}{$n}{'minspeed'}));
+                $text1 .= ", Max: $offers{$s}{$c}{$n}{'maxspeed'}" if (defined($offers{$s}{$c}{$n}{'maxspeed'}));
+                $text1 .= ", Rec: $offers{$s}{$c}{$n}{'recspeed'}" if (defined($offers{$s}{$c}{$n}{'recspeed'}));
+                $text1 .= ", BW: $offers{$s}{$c}{$n}{'bwcurrent'}" if (defined($offers{$s}{$c}{$n}{'bwcurrent'}));
+                $text1 .= ", Rec: $offers{$s}{$c}{$n}{'bwrecord'}" if (defined($offers{$s}{$c}{$n}{'bwrecord'}));
+                $text1 .= ", Cap: $offers{$s}{$c}{$n}{'bwcapacity'}" if (defined($offers{$s}{$c}{$n}{'bwcapacity'}));
+                $text1 .= "\n";
+                my $text2 = "";
+                foreach my $p (sort {$a <=> $b} keys %{$offers{$s}{$c}{$n}{'packs'}}) {
+                    next unless (!defined($desc) || $offers{$s}{$c}{$n}{'packs'}{$p}{'desc'} =~ /$desc/i);
+                    $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";
+                }
+                next if (length($text2) == 0);
+                $text .= $text1.$text2;
+            }
+        }
+    }
+    print CLIENTCRAP draw_box("XDCCget", $text, "offers", 1);
+}
+
+sub event_message_public {
+    my ($server, $msg, $nick, $address, $target) = @_;
+
+    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) {
+        $offers{$server->{'tag'}} = {} unless (defined($offers{$server->{'tag'}}));
+        $offers{$server->{'tag'}}{$target} = {} unless (defined($offers{$server->{'tag'}}{$target}));
+        $offers{$server->{'tag'}}{$target}{$nick} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}));
+        $offers{$server->{'tag'}}{$target}{$nick}{'packs'} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}{'packs'}));
+        $offers{$server->{'tag'}}{$target}{$nick}{'numpacks'} = $1 if (defined($1));
+        $offers{$server->{'tag'}}{$target}{$nick}{'freeslots'} = $2 if (defined($2));
+        $offers{$server->{'tag'}}{$target}{$nick}{'numslots'} = $3 if (defined($3));
+        $offers{$server->{'tag'}}{$target}{$nick}{'posqueue'} = $4 if (defined($4));
+        $offers{$server->{'tag'}}{$target}{$nick}{'numqueue'} = $5 if (defined($5));
+        $offers{$server->{'tag'}}{$target}{$nick}{'minspeed'} = $6 if (defined($6));
+        $offers{$server->{'tag'}}{$target}{$nick}{'maxspeed'} = $7 if (defined($7));
+        $offers{$server->{'tag'}}{$target}{$nick}{'recspeed'} = $8 if (defined($8));
+    }
+    if ($msg =~ /.*?\*\*.*? Bandwidth Usage .*?\*\*.*? Current: ((?:\d|\.)+KB\/s)(?:, Cap: ((?:\d|\.)+KB\/s))?(?:, Record: ((?:\d|\.)+KB\/s))?/) {
+        $offers{$server->{'tag'}} = {} unless (defined($offers{$server->{'tag'}}));
+        $offers{$server->{'tag'}}{$target} = {} unless (defined($offers{$server->{'tag'}}{$target}));
+        $offers{$server->{'tag'}}{$target}{$nick} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}));
+        $offers{$server->{'tag'}}{$target}{$nick}{'packs'} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}{'packs'}));
+        $offers{$server->{'tag'}}{$target}{$nick}{'bwcurrent'} = $1 if (defined($1));
+        $offers{$server->{'tag'}}{$target}{$nick}{'bwcapacity'} = $2 if (defined($2));
+        $offers{$server->{'tag'}}{$target}{$nick}{'bwrecord'} = $3 if (defined($3));
+    }
+
+    if ($msg =~ /\.*?#(\d+).*?\s+(\d+)x\s+\[\s*?((?:\d|\.)*(?:M|K|G))\]\s+(.+)/) {
+        $offers{$server->{'tag'}} = {} unless (defined($offers{$server->{'tag'}}));
+        $offers{$server->{'tag'}}{$target} = {} unless (defined($offers{$server->{'tag'}}{$target}));
+        $offers{$server->{'tag'}}{$target}{$nick} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}));
+        $offers{$server->{'tag'}}{$target}{$nick}{'packs'} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}{'packs'}));
+        $offers{$server->{'tag'}}{$target}{$nick}{'packs'}{$1} = {} unless (defined($offers{$server->{'tag'}}{$target}{$nick}{'packs'}{$1}));
+        $offers{$server->{'tag'}}{$target}{$nick}{'packs'}{$1}{'numdl'} = $2;
+        $offers{$server->{'tag'}}{$target}{$nick}{'packs'}{$1}{'size'} = $3;
+        $offers{$server->{'tag'}}{$target}{$nick}{'packs'}{$1}{'desc'} = $4;
+    }
+}
 
 Irssi::command_bind('xdccget', \&cmd_xdccget);
-foreach my $cmd ('queue', 'cancel', 'list', 'help', 'list') {
+
+loadQueue();
+
+foreach my $cmd ('queue', 'cancel', 'list', 'help', 'stat','reset','offers', 'pause') {
     Irssi::command_bind('xdccget '.$cmd => sub {
-                        cmd_xdccget("$cmd ".$_[0], $_[1], $_[2]); });
+        cmd_xdccget("$cmd ".$_[0], $_[1], $_[2]);
+    });
 }
 
-
+Irssi::signal_add('message public', 'event_message_public');
 Irssi::signal_add('message irc notice', 'event_message_irc_notice');
 Irssi::signal_add("message private", "event_private_message");
 Irssi::signal_add("event 401", "event_no_such_nick");
-
-Irssi::settings_add_int($IRSSI{'name'}, 'xdccget_retry_time', 30);
+Irssi::signal_add("event connected", "event_server_connected");
+Irssi::signal_add("server disconnected", "event_server_disconnected");
 
 print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded: /xdccget help for help';
+
+print "Configuration files are stored in $config_path";
+
+if ($#queue >= 0) {
+    process_queue();
+}
git clone https://git.99rst.org/PROJECT