xget 2.1.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +5 -5
  2. data/xget.rb +757 -0
  3. metadata +11 -11
  4. data/bin/xget +0 -710
data/bin/xget DELETED
@@ -1,710 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- # xget.rb - xget
4
- # Created by Rusty Shackleford on 2013/05/19
5
- # Copyright (c) 2013, Rusty Shackleford
6
- # All rights reserved.
7
-
8
- begin
9
- %w(socket thread slop timeout).each { |r| require r }
10
- rescue LoadError
11
- abort "#{$0} Requires slop - Please run 'gem install slop'"
12
- end
13
-
14
- # Why isn't this enabled by default?
15
- Thread.abort_on_exception = true
16
- # Put standard output into syncronised mode
17
- $stdout.sync = true
18
-
19
- # Version values
20
- $ver_maj, $ver_min, $ver_rev = 2, 1, 1
21
- $ver_str = "#{$ver_maj}.#{$ver_min}.#{$ver_rev}"
22
-
23
- config = {
24
- "out-dir" => './',
25
- "skip-existing" => false,
26
- "servers" => {},
27
- "sleep-interval" => 5,
28
- "allow-queueing" => false
29
- }
30
-
31
- def puts_error msg
32
- puts "! \e[31mERROR\e[0m: #{msg}"
33
- end
34
-
35
- def puts_abort msg
36
- abort "! \e[31mERROR\e[0m: #{msg}"
37
- end
38
-
39
- def puts_warning msg
40
- puts "! \e[33mWARNING:\e[0m: #{msg}"
41
- end
42
-
43
- # Extend IO to readlines without blocking
44
- class IO
45
- def gets_nonblock
46
- @rlnb_buffer ||= ""
47
- ch = nil
48
- while ch = self.read_nonblock(1)
49
- @rlnb_buffer += ch
50
- if ch == "\n" then
51
- res = @rlnb_buffer
52
- @rlnb_buffer = ""
53
- return res
54
- end
55
- end
56
- end
57
- end
58
-
59
- # Extend Array to get averages
60
- class Array
61
- def average
62
- inject(:+) / count
63
- end
64
- end
65
-
66
- # Class to hold XDCC requests
67
- class XDCC_REQ
68
- attr_accessor :serv, :chan, :bot, :pack, :info
69
-
70
- def initialize serv, chan, bot, pack, info = "*"
71
- @serv = serv
72
- @chan = chan
73
- @bot = bot
74
- @pack = pack
75
- @info = info
76
- end
77
-
78
- def eql? other
79
- self.serv == other.serv and self.chan == other.chan and self.bot == other.bot and self.pack == other.pack
80
- end
81
-
82
- def to_s
83
- "[ #{self.serv}, #{self.chan}, #{self.bot}, #{self.pack}, #{self.info} ]"
84
- end
85
- end
86
-
87
- # Class to hold DCC SEND info for when waiting for DCC ACCEPT
88
- class XDCC_SEND
89
- attr_accessor :fname, :fsize, :ip, :port
90
-
91
- def initialize fname, fsize, ip, port
92
- @fname = fname
93
- @fsize = fsize
94
- @ip = ip
95
- @port = port
96
- end
97
-
98
- def to_s
99
- "[ #{self.fname}, #{self.fsize}, #{self.ip}, #{self.port} ]"
100
- end
101
- end
102
-
103
- # Class to emit events
104
- module Emitter
105
- def callbacks
106
- @callbacks ||= Hash.new { |h, k| h[k] = [] }
107
- end
108
-
109
- def on type, &block
110
- callbacks[type] << block
111
- self
112
- end
113
-
114
- def emit type, *args
115
- callbacks[type].each do |block|
116
- block.call(*args)
117
- end
118
- end
119
- end
120
-
121
- # Class to handle IRC stream and emit events
122
- class Stream
123
- include Emitter
124
- attr_accessor :io, :buf
125
-
126
- def initialize serv
127
- @buf = []
128
- Timeout.timeout(5) { @io = TCPSocket.new serv, 6667 }
129
- rescue SocketError => e
130
- puts_abort "Failed to connect to #{serv}! #{e.message}"
131
- rescue Timeout::Error
132
- puts_abort "Connection to #{serv} timed out!"
133
- end
134
-
135
- def disconnect
136
- @io.puts 'QUIT'
137
- rescue Errno::EPIPE
138
- end
139
-
140
- def << data
141
- @buf << data
142
- end
143
-
144
- def write
145
- @buf.each do |x|
146
- @io.puts x
147
- emit :WROTE, x
148
- end
149
- @buf = []
150
- rescue EOFError, Errno::ECONNRESET
151
- emit :CLOSED
152
- end
153
-
154
- def read
155
- read = @io.gets_nonblock
156
- emit :READ, read
157
- rescue IO::WaitReadable
158
- emit :WAITING
159
- rescue EOFError, Errno::ECONNRESET
160
- emit :CLOSED
161
- end
162
- end
163
-
164
- # Class to handle IRC stream
165
- class Bot
166
- attr_reader :stream
167
-
168
- def initialize stream
169
- @stream = stream
170
- stream.on :CLOSED do stop; end
171
- end
172
-
173
- def start
174
- @running = true
175
- tick while @running
176
- end
177
-
178
- def stop
179
- @running = false
180
- end
181
-
182
- def tick
183
- stream.read
184
- stream.write
185
- sleep 0.001
186
- end
187
- end
188
-
189
- # Get relative size from bytes
190
- def bytes_to_closest bytes
191
- fsize_arr = [ 'B', 'KB', 'MB', 'GB', 'TB' ]
192
- exp = (Math.log(bytes) / Math.log(1024)).to_i
193
- exp = fsize_arr.length if exp > fsize_arr.length
194
- bytes /= 1024.0 ** exp
195
- return "#{bytes.round(2)}#{fsize_arr[exp]}"
196
- end
197
-
198
- # Loop until there is no file with the same name
199
- def safe_fname fname
200
- return fname unless File.exists? fname
201
-
202
- ext = File.extname fname
203
- base = File.basename fname, ext
204
- dir = File.dirname fname
205
-
206
- cur = 2
207
- while true
208
- test = "#{dir}/#{base} (#{cur})#{ext}"
209
- return test unless File.exists? test
210
- cur += 1
211
- end
212
- end
213
-
214
- # Get a close relative time remaining, in words
215
- def time_distance t
216
- if t < 60
217
- case t
218
- when 0 then "- nevermind, done!"
219
- when 1..4 then "in a moment!"
220
- when 5..9 then "less than 10 seconds"
221
- when 10..19 then "less than 20 seconds"
222
- when 20..39 then "half a minute"
223
- else "less than a minute"
224
- end
225
- else # Use minutes, to aovid big numbers
226
- t = t / 60.0
227
- case t.to_i
228
- when 1 then "about a minute"
229
- when 2..45 then "#{t.round} minutes"
230
- when 45..90 then "about an hour"
231
- when 91..1440 then "about #{(t / 60.0).round} hours"
232
- when 1441..2520 then "about a day"
233
- when 2521..86400 then "about #{(t / 1440.0).round} days"
234
- else "about #{(t/ 43200.0).round} months"
235
- end
236
- end
237
- end
238
-
239
- # Get elapsed time in words
240
- def time_elapsed t
241
- return "instantly!" if t <= 0
242
-
243
- # Get the GMTime from seconds and split
244
- ta = Time.at(t).gmtime.strftime('%S|%M|%H|%-d|%-m|%Y').split('|', 6).collect { |i| i.to_i }
245
- ta[-1] -= 1970 # fuck the police
246
- ta[-2] -= 1 # fuck, fuck
247
- ta[-3] -= 1 # fuck the police
248
-
249
- # Remove the 0 digets
250
- i = 0
251
- ta.reverse.each do |x|
252
- break if x != 0
253
- i += 1
254
- end
255
-
256
- # Unit suffixes
257
- suffix = [ "seconds", "minutes", "hours", "days", "months", "years" ]
258
- # Don't use plural if x is 1
259
- plural = ->(x, y) { x == 1 ? y[0..-2] : y }
260
- # Format string to "value unit"
261
- format_str = ->(x) { "#{ta[x]} #{plural[ta[x], suffix[x]]}, " }
262
-
263
- # Form the string
264
- ta = ta.take(ta.length - i)
265
- str = ""
266
- (ta.length - 1).downto(0) { |x| str += format_str[x] }
267
- "in #{str[0..-3]}"
268
- end
269
-
270
- # DCC download handler
271
- def dcc_download ip, port, fname, fsize, read = 0
272
- sock = nil
273
- begin
274
- Timeout.timeout(5) { sock = TCPSocket.new ip, port }
275
- rescue Timeout::Error
276
- puts_abort "Connection to #{ip} timed out!"
277
- end
278
- puts_abort "Failed to connect to \"#{ip}:#{port}\": #{e}" if sock.nil?
279
-
280
- fsize_clean = bytes_to_closest fsize
281
- avgs, last_check, start_time = [], Time.now - 2, Time.now
282
- fh = File.open fname, (read == 0 ? "w" : "a") # Write or append
283
-
284
- # Form the status bar
285
- print_bar = ->() {
286
- print "\r\e[0K> [ \e[1;37m"
287
- pc = read.to_f / fsize.to_f * 100.0
288
- bars = (pc / 10).to_i
289
- bars.times { print "#" }
290
- (10 - bars).times { print " " }
291
- avg = avgs.average * 1024.0
292
- time_rem = time_distance ((fsize - read) / avg) * 8.0
293
- print "\e[0m ] #{pc.round(2)}% #{bytes_to_closest read}/#{fsize_clean} \e[1;37m@\e[0m #{bytes_to_closest avg}/s \e[1;37min\e[0m #{time_rem}"
294
-
295
- last_check = Time.now
296
- avgs.clear
297
- }
298
-
299
- while buf = sock.readpartial(8192)
300
- read += buf.bytesize
301
- avgs << buf.bytesize
302
- print_bar[] if (Time.now - last_check) > 1 and not avgs.empty?
303
-
304
- begin
305
- sock.write_nonblock [read].pack('N')
306
- rescue Errno::EWOULDBLOCK
307
- rescue Errno::EAGAIN => e
308
- puts_error "#{File.basename fname} timed out! #{e}"
309
- end
310
-
311
- fh << buf
312
- break if read >= fsize
313
- end
314
- print_bar.call unless avgs.empty?
315
- elapsed_time = time_elapsed (Time.now - start_time).to_i
316
-
317
- sock.close
318
- fh.close
319
-
320
- puts "\n! \e[1;32mSUCCESS\e[0m: downloaded #{File.basename fname} #{elapsed_time}"
321
- rescue EOFError, SocketError => e
322
- puts "\n! ERROR: #{File.basename fname} failed to download! #{e}"
323
- end
324
-
325
- opts = Slop.parse do |o|
326
- o.banner = " Usage: #{$0} [options] [value] [links] [--files] [file1:file2:file3]\n"
327
- o.bool '-h', '--help', 'Prints help'
328
-
329
- o.on '-v', '--version', 'Print version' do
330
- puts "#{$0}: v#{$ver_str}"
331
- exit
332
- end
333
-
334
- o.string '--config', 'Config file location'
335
- o.string '--user', 'IRC \'USER\' for Ident'
336
- o.string '--nick', 'IRC nick'
337
- o.string '--pass', 'IRC \'PASS\' for Ident'
338
- o.string '--realname', 'Realname for \'USER\' Ident'
339
- o.string '--nickserv', 'Password for Nickserv'
340
- o.array '--files', 'Pass list of files to parse for links', as: Array, delimiter: ':'
341
- o.string '--out-dir', 'Output directory to save fiels to', :default => "./"
342
- o.bool '--skip-existing', 'Don\' download files that already exist'
343
- o.bool '--allow-queueing', 'Wait for pack to start downloading rather than fail immediately when queued'
344
- o.int '--sleep-interval', 'Time in seconds to sleep before requesting next pack. Zero for no sleep.'
345
- end
346
-
347
- if opts.help?
348
- puts opts
349
- puts "\n Examples"
350
- puts " \txget.rb --config config.conf --nick test"
351
- puts " \txget.rb --files test1.txt:test2.txt:test3.txt"
352
- puts " \txget.rb #news@irc.rizon.net/ginpachi-sensei/1"
353
- puts " \txget.rb #news@irc.rizon.net/ginpachi-sensei/41..46"
354
- puts " \txget.rb #news@irc.rizon.net/ginpachi-sensei/41..46|2"
355
- puts " \txget.rb #news@irc.rizon.net/ginpachi-sensei/41..46&49..52|2&30"
356
- exit
357
- end
358
-
359
- # Get the config location
360
- config_loc = opts["config"]
361
- config_loc = File.expand_path config_loc unless config_loc.nil?
362
- if config_loc.nil? or not File.exists? config_loc
363
- config_loc = File.expand_path "~/.xget.conf"
364
- config_loc = ".xget.conf" unless File.exists? config_loc
365
-
366
- unless File.exists? config_loc
367
- puts "ERROR! Invalid config path '#{config_loc}''. Exiting!"
368
- exit
369
- end
370
- end
371
-
372
- # Insert config settings from arguments into config hash
373
- cur_block = "*"
374
- config["servers"][cur_block] = {}
375
- %w(user nick pass real nserv).each do |x|
376
- config["servers"][cur_block][x.to_sym] = opts[x] unless opts[x].nil?
377
- end
378
-
379
- # Check if specified output directory actually exists
380
- puts_abort "Out directory, \"#{opts["out-dir"]}\" doesn't exist!" unless Dir.exists? opts["out-dir"]
381
- config["out-dir"] = opts["out-dir"].dup
382
- config["out-dir"] << "/" unless config["out-dir"][-1] == "/"
383
-
384
- # Parse config
385
- config_copies = {}
386
- File.open(config_loc, "r").each_line do |line|
387
- next if line.length <= 1 or line[0] == '#'
388
-
389
- if line =~ /^\[(\S+)\]$/ # Check if header
390
- cur_block = $1
391
- if cur_block.include? ',' # Check if header contains more than one server
392
- tmp_split = cur_block.split(",")
393
- next unless tmp_split[0] =~ /^(\w+?).(\w+?).(\w+?)$/
394
- config_copies[tmp_split[0]] = []
395
- tmp_split.each do |x| # Add all copies to copies hash
396
- next if x == tmp_split[0] or not x =~ /^(\w+?).(\w+?).(\w+?)$/
397
- config_copies[tmp_split[0]].push x unless config_copies[tmp_split[0]].include? x
398
- end
399
- cur_block = tmp_split[0]
400
- end
401
-
402
- # Set current block to the new header
403
- config["servers"][cur_block] = {} unless config["servers"].has_key? cur_block
404
- elsif line =~ /^(\S+)=(.*+?)$/
405
- # Check if current line is specifying out directory
406
- case $1
407
- when "out-dir"
408
- t_out_dir = File.expand_path $2
409
- puts_abort "Out directory, \"#{t_out_dir}\" doesn't exist!" unless Dir.exists? t_out_dir
410
- config[$1] = t_out_dir
411
- config[$1] << "/" unless config[$1][-1] == "/"
412
- next
413
- when "sleep-interval" then config[$1] = $2.to_i
414
- when "skip-existing" then config[$1] = ($2 == "true")
415
- when "allow-queueing" then config[$1] = ($2 == "true")
416
- else
417
- # Add value to current header, default is *
418
- t_sym = $1.downcase.to_sym
419
- config["servers"][cur_block][t_sym] = $2 unless config["servers"][cur_block].has_key? t_sym
420
- end
421
- end
422
- end
423
-
424
- # Go through each and make copies of the original
425
- unless config_copies.empty?
426
- config_copies.each do |k,v|
427
- v.each { |x| config["servers"][x] = config["servers"][k] }
428
- end
429
- end
430
-
431
- # Set the set the command line config options if specified
432
- config["skip-existing"] = opts["skip-existing"] if opts["skip-existing"]
433
- config["allow-queueing"] = opts["allow-queueing"] if opts["allow-queueing"]
434
- config["sleep-interval"] = opts["sleep-interval"] unless opts["sleep-interval"].nil?
435
-
436
- # Take remaining arguments and all lines from --files arg and put into array
437
- to_check = opts.arguments
438
- if opts['files'] != nil and not opts['files'].empty?
439
- opts['files'].each do |x|
440
- File.open(x, "r").each_line { |y| to_check << y.chomp } if File.exists? x
441
- end
442
- end
443
-
444
- if to_check.empty?
445
- puts opts
446
- abort "\n No jobs, nothing to do!"
447
- end
448
-
449
- # Parse to_check array for valid XDCC links, irc.serv.org/#chan/bot/pack
450
- tmp_requests = []
451
- to_check.each do |x|
452
- if x =~ /^(#\S+)@(irc.\w+.\w+{2,3})\/(\S+)\/([\.&\|\d]+)$/
453
- chan = $1
454
- serv = $2
455
- bot = $3
456
- info = config["servers"].has_key?(serv) ? serv : "*"
457
- $4.split('&').each do |y|
458
- if y =~ /^(\d+)(\.\.\d+(\|\d+)?)?$/
459
- pack = $1.to_i
460
- if $2.nil?
461
- tmp_requests.push XDCC_REQ.new serv, chan, bot, pack, info
462
- else
463
- step = $3.nil? ? 1 : $3[1..-1].to_i
464
- range = $2[2..-1].to_i
465
-
466
- puts_abort "Invalid range #{pack} to #{range} in \"#{x}\"" if pack > range or pack == range
467
-
468
- (pack..range).step(step).each do |z|
469
- tmp_requests.push XDCC_REQ.new serv, chan, bot, z, info
470
- end
471
- end
472
- end
473
- end
474
- else
475
- puts_abort "#{x} is not a valid XDCC address\n XDCC Address format: #chan@irc.serv.com/bot/pack(s) or ^\/msg irc.serv.com bot xdcc send #id$"
476
- end
477
- end
478
-
479
- # Remove duplicate entries from requests
480
- i = j = 0
481
- to_pop = []
482
- tmp_requests.each do |x|
483
- tmp_requests.each do |y|
484
- to_pop << j if x.eql? y if i != j
485
- j += 1
486
- end
487
- i += 1
488
- end
489
- to_pop.each { |x| tmp_requests.delete_at(x) }
490
-
491
- # Sort requests array to hash, serv {} -> chan {} -> requests []
492
- requests = {}
493
- tmp_requests.each do |x|
494
- requests[x.serv] = [] unless requests.has_key? x.serv
495
- requests[x.serv] << x
496
- end
497
-
498
- if requests.empty?
499
- puts opts
500
- abort "\n No jobs, nothing to do!"
501
- end
502
-
503
- # Sort requests by pack
504
- requests.each do |k,v|
505
- puts "#{k} \e[1;37m->\e[0m"
506
- v.sort_by { |x| [x.chan, x.bot, x.pack] }.each { |x| puts " #{x}" }
507
- end
508
- puts
509
-
510
- # H-h-here we g-go...
511
- requests.each do |k, v|
512
- req, info = v[0], config["servers"][v[0].info]
513
- last_chan, cur_req, motd = "", -1, false
514
- nick_sent, nick_check, nick_valid = false, false, false
515
- xdcc_sent, xdcc_accepted, xdcc_queued = false, false, false
516
- xdcc_accept_time, xdcc_ret, req_send_time = nil, nil, nil
517
-
518
- stream = Stream.new req.serv
519
- bot = Bot.new stream
520
- stream << "NICK #{info[:nick]}"
521
- stream << "USER #{info[:user]} 0 * #{info[:real]}"
522
- stream << "PASS #{info[:pass]}" unless info[:pass].nil?
523
-
524
- # Handle read data
525
- stream.on :READ do |data|
526
- /^(?:[:](?<prefix>\S+) )?(?<type>\S+)(?: (?!:)(?<dest>.+?))?(?: [:](?<msg>.+))?$/ =~ data
527
- #puts "\e[1;37m>>\e[0m #{prefix} | #{type} | #{dest} | #{msg}"
528
-
529
- case type
530
- when 'NOTICE'
531
- if dest == 'AUTH'
532
- if msg =~ /erroneous nickname/i
533
- puts_error 'Login failed'
534
- stream.disconnect
535
- end
536
- #puts "> \e[1;32m#{msg}\e[0m"
537
- else
538
- if prefix =~ /^NickServ!/
539
- if not nick_sent and info[:nserv] != nil
540
- stream << "PRIVMSG NickServ :IDENTIFY #{info[:nserv]}"
541
- nick_sent = true
542
- elsif nick_sent and not nick_check
543
- case msg
544
- when /password incorrect/i
545
- nick_valid = false
546
- nick_check = true
547
- when /password accepted/i
548
- nick_valid = true
549
- nick_check = true
550
- end
551
- end
552
- #puts "> \e[1;33m#{msg}\e[0m"
553
- elsif prefix =~ /^#{Regexp.escape req.bot}!(.*)$/i
554
- case msg
555
- when /already requested that pack/i, /closing connection/i, /you have a dcc pending/i
556
- puts_error msg
557
- stream << "PRIVMSG #{req.bot} :XDCC CANCEL"
558
- stream << 'QUIT'
559
- when /you can only have (\d+?) transfer at a time/i
560
- if config["allow-queueing"]
561
- puts "! #{prefix}: #{msg}"
562
- puts_warning "Pack queued, waiting for transfer to start..."
563
- xdcc_queued = true
564
- else
565
- puts_error msg
566
- stream << "PRIVMSG #{req.bot} :XDCC CANCEL"
567
- stream << 'QUIT'
568
- end
569
- else
570
- puts "! #{prefix}: #{msg}"
571
- end
572
- end
573
- end
574
- when 'PRIVMSG'
575
- if xdcc_sent and not xdcc_accepted and prefix =~ /#{Regexp.escape req.bot}!(.*)$/i
576
- /^\001DCC SEND (?<fname>((".*?").*?|(\S+))) (?<ip>\d+) (?<port>\d+) (?<fsize>\d+)\001\015$/ =~ msg
577
- unless $~.nil?
578
- req_send_time = nil
579
-
580
- tmp_fname = fname
581
- fname = $1 if tmp_fname =~ /^"(.*)"$/
582
- puts "Preparing to download: \e[36m#{fname}\e[0m"
583
- fname = (config["out-dir"].dup << fname)
584
- xdcc_ret = XDCC_SEND.new fname, fsize.to_i, [ip.to_i].pack('N').unpack('C4') * '.', port.to_i
585
-
586
- # Check if the for unfinished download amd try to resume
587
- if File.exists? xdcc_ret.fname and File.stat(xdcc_ret.fname).size < xdcc_ret.fsize
588
- stream << "PRIVMSG #{req.bot} :\001DCC RESUME #{tmp_fname} #{xdcc_ret.port} #{File.stat(xdcc_ret.fname).size}\001"
589
- xdcc_accepted = true
590
- print "! Incomplete file detected. Attempting to resume..."
591
- next # Skip and wait for "DCC ACCEPT"
592
- elsif File.exists? xdcc_ret.fname
593
- if config["skip-existing"]
594
- puts_warning "File already exists, skipping..."
595
- stream << "PRIVMSG #{req.bot} :XDCC CANCEL"
596
-
597
- xdcc_sent, xdcc_accepted, xdcc_queued = false, false, false
598
- xdcc_accept_time, xdcc_ret = nil, nil
599
- next
600
- else
601
- puts_warnings "File already existing, using a safe name..."
602
- xdcc_ret.fname = safe_fname xdcc_ret.fname
603
- end
604
- end
605
-
606
- # It's a new download, start from beginning
607
- Thread.new do
608
- pid = fork do
609
- puts "Connecting to: #{req.bot} @ #{xdcc_ret.ip}:#{xdcc_ret.port}"
610
- dcc_download xdcc_ret.ip, xdcc_ret.port, xdcc_ret.fname, xdcc_ret.fsize
611
- end
612
-
613
- Process.wait pid
614
- xdcc_sent, xdcc_accepted, xdcc_queued = false, false, false
615
- xdcc_accept_time, xdcc_ret = nil, nil
616
- end
617
- end
618
- elsif xdcc_accepted and xdcc_ret != nil and msg =~ /^\001DCC ACCEPT ((".*?").*?|(\S+)) (\d+) (\d+)\001\015$/
619
- # DCC RESUME request accepted, continue the download!
620
- xdcc_accept_time = nil
621
- xdcc_accepted = false
622
- puts "\e[1;32mSUCCESS\e[0m!"
623
-
624
- Thread.new do
625
- pid = fork do
626
- puts "Connecting to: #{req.bot} @ #{xdcc_ret.ip}:#{xdcc_ret.port}"
627
- dcc_download xdcc_ret.ip, xdcc_ret.port, xdcc_ret.fname, xdcc_ret.fsize, File.stat(xdcc_ret.fname).size
628
- end
629
-
630
- Process.wait pid
631
- xdcc_sent, xdcc_accepted, xdcc_queued = false, false, false
632
- xdcc_accept_time, xdcc_ret = nil, nil
633
- end
634
- end
635
- when /^\d+?$/
636
- type_i = type.to_i
637
- case type_i
638
- # when 1 # Print welcome message, because it's nice
639
- # msg.sub!(/#{Regexp.escape info[:nick]}/, "\e[34m#{info[:nick]}\e[0m")
640
- # puts "! #{msg}"
641
- when 400..533 # Handle errors, except a few
642
- next if [439, 462, 477].include? type_i
643
- puts_error "#{msg}"
644
- stream.disconnect
645
- when 376 then motd = true # Mark the end of the MOTD
646
- end
647
- when 'PING' then stream << "PONG :#{msg}"
648
- when 'ERROR' then (msg =~ /closing link/i ? puts(msg) : puts_error(msg))
649
- end
650
- end
651
-
652
- # Handle things while waiting for data
653
- stream.on :WAITING do
654
- unless xdcc_accepted
655
- if motd and not xdcc_sent
656
- cur_req += 1
657
- if cur_req >= v.length
658
- stream.disconnect
659
- next
660
- end
661
- req = v[cur_req]
662
-
663
- if req.chan != last_chan
664
- stream << "PART #{last_chan}" unless last_chan == ""
665
- last_chan = req.chan
666
- stream << "JOIN #{req.chan}"
667
- end
668
-
669
- # Cooldown between downloads
670
- if cur_req > 0
671
- puts "Sleeping for #{config["sleep-interval"]} seconds before requesting the next pack"
672
- sleep(config["sleep-interval"])
673
- end
674
-
675
- stream << "PRIVMSG #{req.bot} :XDCC SEND #{req.pack}"
676
- req_send_time = Time.now
677
- xdcc_sent = true
678
- end
679
-
680
- # Wait 3 seconds for DCC SEND response, if there isn't one, abort
681
- if xdcc_sent and not req_send_time.nil? and not xdcc_accepted
682
- if config["allow-queueing"] and xdcc_queued
683
- next
684
- end
685
- if (Time.now - req_send_time).floor > 10
686
- puts_error "#{req.bot} took too long to respond, are you sure it's a bot?"
687
- stream.disconnect
688
- bot.stop
689
- end
690
- end
691
-
692
- # Wait 3 seconds for a DCC ACCEPT response, if there isn't one, don't resume
693
- if xdcc_sent and xdcc_accepted and not xdcc_accept_time.nil?
694
- if (Time.now - xdcc_accept_time).floor > 10
695
- puts "FAILED! Bot client doesn't support resume!"
696
- puts "Connecting to: #{req.bot} @ #{xdcc_ret.ip}:#{xdcc_ret.port}"
697
- dcc_download xdcc_ret.ip, xdcc_ret.port, xdcc_ret.fname, xdcc_ret.fsize
698
- end
699
- end
700
- end
701
- end
702
-
703
- # Print sent data, for debugging only really
704
- stream.on :WROTE do |data|
705
- #puts "\e[1;37m<<\e[0m #{data}"
706
- end
707
-
708
- # Start the bot
709
- bot.start
710
- end