rbot 0.9.12 → 0.9.13

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.
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ task :default => [:buildext]
6
6
 
7
7
  spec = Gem::Specification.new do |s|
8
8
  s.name = 'rbot'
9
- s.version = '0.9.12'
9
+ s.version = '0.9.13'
10
10
  s.summary = <<-EOF
11
11
  A modular ruby IRC bot.
12
12
  EOF
data/bin/rbot CHANGED
@@ -47,7 +47,7 @@ require 'etc'
47
47
  require 'getoptlong'
48
48
  require 'fileutils'
49
49
 
50
- $version ||= '0.9.12'
50
+ $version ||= '0.9.13'
51
51
  $version_timestamp ||= 0
52
52
  $opts = Hash.new
53
53
 
@@ -0,0 +1,161 @@
1
+ #-- vim:sw=2:et
2
+ #++
3
+ #
4
+ # :title: Geo IP Plugin
5
+ #
6
+ # Author:: Raine Virta <rane@kapsi.fi>
7
+ # Copyright:: (C) 2008 Raine Virta
8
+ # License:: GPL v2
9
+ #
10
+ # Resolves the geographic locations of users (network-wide) and IP addresses
11
+
12
+ module GeoIP
13
+ class InvalidHostError < RuntimeError; end
14
+
15
+ GEO_IP_PRIMARY = "http://lakka.kapsi.fi:40086/lookup.yaml?host="
16
+ GEO_IP_SECONDARY = "http://www.geoiptool.com/en/?IP="
17
+ HOST_NAME_REGEX = /^[a-z0-9\-]+(?:\.[a-z0-9\-]+)*\.[a-z]{2,4}/i
18
+
19
+ REGEX = {
20
+ :country => %r{Country:.*?<a href=".*?" target="_blank"> (.*?)</a>}m,
21
+ :region => %r{Region:.*?<a href=".*?" target="_blank">(.*?)</a>}m,
22
+ :city => %r{City:.*?<td align="left" class="arial_bold">(.*?)</td>}m
23
+ }
24
+
25
+ def self.valid_host?(hostname)
26
+ hostname =~ HOST_NAME_REGEX ||
27
+ hostname =~ Resolv::IPv4::Regex && (hostname.split(".").map { |e| e.to_i }.max <= 255)
28
+ end
29
+
30
+ def self.resolve(hostname)
31
+ raise InvalidHostError unless valid_host?(hostname)
32
+
33
+ yaml = Irc::Utils.bot.httputil.get(GEO_IP_PRIMARY+hostname)
34
+
35
+ if yaml
36
+ return YAML::load(yaml)
37
+ else
38
+ res = {}
39
+ raw = Irc::Utils.bot.httputil.get_response(GEO_IP_SECONDARY+hostname)
40
+ raw = raw.decompress_body(raw.raw_body)
41
+
42
+ REGEX.each { |key, regex| res[key] = Iconv.conv('utf-8', 'ISO-8859-1', raw.scan(regex).to_s) }
43
+
44
+ return res
45
+ end
46
+ end
47
+ end
48
+
49
+ class Stack
50
+ def initialize
51
+ @hash = {}
52
+ end
53
+
54
+ def [](nick)
55
+ @hash[nick] = [] unless @hash[nick]
56
+ @hash[nick]
57
+ end
58
+
59
+ def has_nick?(nick)
60
+ @hash.has_key?(nick)
61
+ end
62
+
63
+ def clear(nick)
64
+ @hash.delete(nick)
65
+ end
66
+ end
67
+
68
+ class GeoIpPlugin < Plugin
69
+ def help(plugin, topic="")
70
+ "geoip [<user|hostname|ip>] => returns the geographic location of whichever has been given -- note: user can be anyone on the network"
71
+ end
72
+
73
+ def initialize
74
+ super
75
+
76
+ @stack = Stack.new
77
+ end
78
+
79
+ def whois(m)
80
+ nick = m.whois[:nick].downcase
81
+
82
+ # need to see if the whois reply was invoked by this plugin
83
+ return unless @stack.has_nick?(nick)
84
+
85
+ if m.target
86
+ msg = host2output(m.target.host, m.target.nick)
87
+ else
88
+ msg = "no such user on "+@bot.server.hostname.split(".")[-2]
89
+ end
90
+ @stack[nick].each do |source|
91
+ @bot.say source, msg
92
+ end
93
+
94
+ @stack.clear(nick)
95
+ end
96
+
97
+ def geoip(m, params)
98
+ if params.empty?
99
+ m.reply host2output(m.source.host, m.source.nick)
100
+ else
101
+ if m.replyto.class == Channel
102
+
103
+ # check if there is an user on the channel with nick same as input given
104
+ user = m.replyto.users.find { |user| user.nick == params[:input] }
105
+
106
+ if user
107
+ m.reply host2output(user.host, user.nick)
108
+ return
109
+ end
110
+ end
111
+
112
+ # input is a host name or an IP
113
+ if GeoIP::valid_host?(params[:input])
114
+ m.reply host2output(params[:input])
115
+
116
+ # assume input is a nick
117
+ elsif params[:input] !~ /\./
118
+ nick = params[:input].downcase
119
+
120
+ @stack[nick] << m.replyto
121
+ @bot.whois(nick)
122
+ else
123
+ m.reply "invalid input"
124
+ end
125
+ end
126
+ end
127
+
128
+ def host2output(host, nick=nil)
129
+ return "127.0.0.1 could not be res.. wait, what?" if host == "127.0.0.1"
130
+
131
+ begin
132
+ geo = GeoIP::resolve(host)
133
+
134
+ raise if geo[:country].empty?
135
+ rescue GeoIP::InvalidHostError, RuntimeError
136
+ return _("#{nick ? "#{nick}'s location" : host} could not be resolved")
137
+ end
138
+
139
+ res = _("%{thing} is #{nick ? "from" : "located in"}") % {
140
+ :thing => (nick ? nick : Resolv::getaddress(host)),
141
+ :country => geo[:country]
142
+ }
143
+
144
+ res << " %{city}," % {
145
+ :city => geo[:city]
146
+ } unless geo[:city].to_s.empty?
147
+
148
+ res << " %{country}" % {
149
+ :country => geo[:country]
150
+ }
151
+
152
+ res << " (%{region})" % {
153
+ :region => geo[:region]
154
+ } unless geo[:region].to_s.empty? || geo[:region] == geo[:city]
155
+
156
+ return res
157
+ end
158
+ end
159
+
160
+ plugin = GeoIpPlugin.new
161
+ plugin.map "geoip [:input]", :action => 'geoip', :thread => true
@@ -188,20 +188,21 @@ class IPLookupPlugin < Plugin
188
188
  end
189
189
 
190
190
  def iplookup(m, params)
191
- debug params
191
+ reply = ""
192
192
  if params[:domain].match(/^#{Regexp::Irc::HOSTADDR}$/)
193
193
  ip = params[:domain]
194
194
  else
195
195
  begin
196
196
  ip = Resolv.getaddress(params[:domain])
197
- reply += "#{params[:domain]} | "
197
+ reply << "#{params[:domain]} | "
198
198
  rescue => e
199
199
  m.reply "#{e.message}"
200
200
  return
201
201
  end
202
202
  end
203
203
 
204
- reply += ArinWhois.lookup_info(ip)
204
+ reply << ArinWhois.lookup_info(ip)
205
+
205
206
  m.reply reply
206
207
  end
207
208
 
@@ -52,17 +52,25 @@ class NickRecoverPlugin < Plugin
52
52
  end
53
53
 
54
54
  def stop_recovery
55
- @bot.timer.remove(@recovery) if @recovery
55
+ begin
56
+ @bot.timer.remove(@recovery) if @recovery
57
+ ensure
58
+ @recovery = nil
59
+ end
56
60
  end
57
61
 
58
62
  def start_recovery(time=self.poll_time)
59
63
  if @recovery
60
- @bot.timer.reschedule(@recovery, poll_time)
61
- else
62
- @recovery = @bot.timer.add(time) do
63
- has_nick? ? stop_recovery : recover
64
+ begin
65
+ @bot.timer.reschedule(@recovery, poll_time)
66
+ return
67
+ rescue
68
+ @recovery=nil
64
69
  end
65
70
  end
71
+ @recovery = @bot.timer.add(time) do
72
+ has_nick? ? stop_recovery : recover
73
+ end
66
74
  end
67
75
 
68
76
  def initialize
@@ -35,6 +35,7 @@ class QPlugin < Plugin
35
35
  val
36
36
  end
37
37
  end
38
+ @source = nil
38
39
  end
39
40
 
40
41
  def set(m, params)
@@ -44,11 +45,11 @@ class QPlugin < Plugin
44
45
  end
45
46
 
46
47
  def connect
47
- identify(nil, nil)
48
+ identify(nil, nil) if on_quakenet?
48
49
  end
49
50
 
50
51
  def identify(m, params)
51
- @source = m.replyto
52
+ @source = m.replyto if m
52
53
  @registry['quakenet.auth'] = params[:password] if params[:password]
53
54
 
54
55
  if @registry.has_key?('quakenet.user') && @registry.has_key?('quakenet.auth')
@@ -272,10 +272,39 @@ class IrcLogModule < CoreBotModule
272
272
  end
273
273
  end
274
274
  fp = logfilepath(where_str, now)
275
- FileUtils.mkdir_p File.dirname(fp)
276
- f = File.new(fp, "a")
277
- f.sync = true
278
- f.puts "[#{stamp}] @ Log started by #{@bot.myself.nick}"
275
+ begin
276
+ dir = File.dirname(fp)
277
+ # first of all, we check we're not trying to build a directory structure
278
+ # where one of the components exists already as a file, so we
279
+ # backtrack along dir until we come across the topmost existing name.
280
+ # If it's a file, we rename to filename.old.filedate
281
+ up = dir.dup
282
+ until File.exist? up
283
+ up.replace File.dirname up
284
+ end
285
+ unless File.directory? up
286
+ backup = up.dup
287
+ backup << ".old." << File.atime(up).strftime('%Y%m%d%H%M%S')
288
+ debug "#{up} is not a directory! renaming to #{backup}"
289
+ File.rename(up, backup)
290
+ end
291
+ FileUtils.mkdir_p(dir)
292
+ # conversely, it may happen that fp exists and is a directory, in
293
+ # which case we rename the directory instead
294
+ if File.directory? fp
295
+ backup = fp.dup
296
+ backup << ".old." << File.atime(fp).strftime('%Y%m%d%H%M%S')
297
+ debug "#{fp} is not a file! renaming to #{backup}"
298
+ File.rename(fp, backup)
299
+ end
300
+ # it should be fine to create the file now
301
+ f = File.new(fp, "a")
302
+ f.sync = true
303
+ f.puts "[#{stamp}] @ Log started by #{@bot.myself.nick}"
304
+ rescue Exception => e
305
+ error e
306
+ next
307
+ end
279
308
  @logs[where_str] = [now, f]
280
309
  end
281
310
  @logs[where_str][1].puts "[#{stamp}] #{message}"
@@ -922,7 +922,7 @@ module Irc
922
922
  class User < Netmask
923
923
  alias :to_s :nick
924
924
 
925
- attr_accessor :real_name
925
+ attr_accessor :real_name, :idle_since, :signon
926
926
 
927
927
  # Create a new IRC User from a given Netmask (or anything that can be converted
928
928
  # into a Netmask) provided that the given Netmask does not have globs.
@@ -934,6 +934,8 @@ module Irc
934
934
  raise ArgumentError, "#{str.inspect} must not have globs (unescaped * or ?)" if host.has_irc_glob? && host != "*"
935
935
  @away = false
936
936
  @real_name = String.new
937
+ @idle_since = nil
938
+ @signon = nil
937
939
  end
938
940
 
939
941
  # The nick of a User may be changed freely, but it must not contain glob patterns.
@@ -1299,12 +1301,15 @@ module Irc
1299
1301
  include ServerOrCasemap
1300
1302
  attr_reader :name, :topic, :mode, :users
1301
1303
  alias :to_s :name
1304
+ attr_accessor :creation_time, :url
1302
1305
 
1303
1306
  def inspect
1304
1307
  str = self.__to_s__[0..-2]
1305
1308
  str << " on server #{server}" if server
1306
1309
  str << " @name=#{@name.inspect} @topic=#{@topic.text.inspect}"
1307
1310
  str << " @users=[#{user_nicks.sort.join(', ')}]"
1311
+ str << " (created on #{creation_time})" if creation_time
1312
+ str << " (URL #{url})" if url
1308
1313
  str << ">"
1309
1314
  end
1310
1315
 
@@ -1368,6 +1373,12 @@ module Irc
1368
1373
 
1369
1374
  # Flags
1370
1375
  @mode = ModeHash.new
1376
+
1377
+ # creation time, only on some networks
1378
+ @creation_time = nil
1379
+
1380
+ # URL, only on some networks
1381
+ @url = nil
1371
1382
  end
1372
1383
 
1373
1384
  # Removes a user from the channel
@@ -277,14 +277,6 @@ class Bot
277
277
  Config.register Config::IntegerValue.new('server.reconnect_wait',
278
278
  :default => 5, :validate => Proc.new{|v| v >= 0},
279
279
  :desc => "Seconds to wait before attempting to reconnect, on disconnect")
280
- Config.register Config::FloatValue.new('server.sendq_delay',
281
- :default => 2.0, :validate => Proc.new{|v| v >= 0},
282
- :desc => "(flood prevention) the delay between sending messages to the server (in seconds)",
283
- :on_change => Proc.new {|bot, v| bot.socket.sendq_delay = v })
284
- Config.register Config::IntegerValue.new('server.sendq_burst',
285
- :default => 4, :validate => Proc.new{|v| v >= 0},
286
- :desc => "(flood prevention) max lines to burst to the server before throttling. Most ircd's allow bursts of up 5 lines",
287
- :on_change => Proc.new {|bot, v| bot.socket.sendq_burst = v })
288
280
  Config.register Config::IntegerValue.new('server.ping_timeout',
289
281
  :default => 30, :validate => Proc.new{|v| v >= 0},
290
282
  :desc => "reconnect if server doesn't respond to PING within this many seconds (set to 0 to disable)")
@@ -589,7 +581,7 @@ class Bot
589
581
  debug "server.list is now #{@config['server.list'].inspect}"
590
582
  end
591
583
 
592
- @socket = Irc::Socket.new(@config['server.list'], @config['server.bindhost'], @config['server.sendq_delay'], @config['server.sendq_burst'], :ssl => @config['server.ssl'])
584
+ @socket = Irc::Socket.new(@config['server.list'], @config['server.bindhost'], :ssl => @config['server.ssl'])
593
585
  @client = Client.new
594
586
 
595
587
  @plugins.scan
@@ -698,6 +690,12 @@ class Bot
698
690
  m.modes = data[:modes]
699
691
  @plugins.delegate "modechange", m
700
692
  }
693
+ @client[:whois] = proc {|data|
694
+ source = data[:source]
695
+ target = server.get_user(data[:whois][:nick])
696
+ m = WhoisMessage.new(self, server, source, target, data[:whois])
697
+ @plugins.delegate "whois", m
698
+ }
701
699
  @client[:join] = proc {|data|
702
700
  m = JoinMessage.new(self, server, data[:source], data[:channel], data[:message])
703
701
  sendq("MODE #{data[:channel]}", nil, 0) if m.address?
@@ -1209,6 +1207,11 @@ class Bot
1209
1207
  sendq "MODE #{channel} #{mode} #{target}", channel, 2
1210
1208
  end
1211
1209
 
1210
+ # asking whois
1211
+ def whois(nick, target=nil)
1212
+ sendq "WHOIS #{target} #{nick}", nil, 0
1213
+ end
1214
+
1212
1215
  # kicking a user
1213
1216
  def kick(channel, user, msg)
1214
1217
  sendq "KICK #{channel} #{user} :#{msg}", channel, 2
@@ -1,10 +1,18 @@
1
+ #-- vim:sw=2:et
2
+ #++
3
+ #
4
+ # :title: IRC Socket
5
+ #
6
+ # This module implements the IRC socket interface, including IRC message
7
+ # penalty computation and the message queue system
8
+
1
9
  require 'monitor'
2
10
 
3
11
  class ::String
4
12
  # Calculate the penalty which will be assigned to this message
5
13
  # by the IRCd
6
14
  def irc_send_penalty
7
- # According to eggrdop, the initial penalty is
15
+ # According to eggdrop, the initial penalty is
8
16
  penalty = 1 + self.size/100
9
17
  # on everything but UnderNET where it's
10
18
  # penalty = 2 + self.size/120
@@ -231,12 +239,6 @@ module Irc
231
239
  # accumulator for the throttle
232
240
  attr_reader :throttle_bytes
233
241
 
234
- # delay between lines sent
235
- attr_accessor :sendq_delay
236
-
237
- # max lines to burst
238
- attr_accessor :sendq_burst
239
-
240
242
  # an optional filter object. we call @filter.in(data) for
241
243
  # all incoming data and @filter.out(data) for all outgoing data
242
244
  attr_reader :filter
@@ -263,7 +265,7 @@ module Irc
263
265
  # server_list:: list of servers to connect to
264
266
  # host:: optional local host to bind to (ruby 1.7+ required)
265
267
  # create a new Irc::Socket
266
- def initialize(server_list, host, sendq_delay=2, sendq_burst=4, opts={})
268
+ def initialize(server_list, host, opts={})
267
269
  @server_list = server_list.dup
268
270
  @server_uri = nil
269
271
  @conn_count = 0
@@ -278,17 +280,6 @@ module Irc
278
280
  else
279
281
  @ssl = false
280
282
  end
281
-
282
- if sendq_delay
283
- @sendq_delay = sendq_delay.to_f
284
- else
285
- @sendq_delay = 2
286
- end
287
- if sendq_burst
288
- @sendq_burst = sendq_burst.to_i
289
- else
290
- @sendq_burst = 4
291
- end
292
283
  end
293
284
 
294
285
  def connected?
@@ -330,9 +321,8 @@ module Irc
330
321
  sock.connect
331
322
  end
332
323
  @sock = sock
333
- @last_send = Time.new - @sendq_delay
324
+ @last_send = Time.new
334
325
  @flood_send = Time.new
335
- @last_throttle = Time.new
336
326
  @burst = 0
337
327
  @sock.extend(MonitorMixin)
338
328
  @sendq = MessageQueue.new
@@ -405,7 +395,6 @@ module Irc
405
395
  error "error while shutting down: #{e.pretty_inspect}"
406
396
  end
407
397
  @sock = nil
408
- @burst = 0
409
398
  @sendq.clear
410
399
  end
411
400
 
@@ -413,30 +402,15 @@ module Irc
413
402
 
414
403
  def writer_loop
415
404
  loop do
416
- # we could wait for the message, then calculate the delay and sleep
417
- # if necessary. however, if high-priority message is enqueued while
418
- # we sleep, it won't be the first to go out when the sleep is over.
419
- # thus, we have to call Time.now() twice, once to calculate the delay
420
- # and once to adjust @burst / @flood_send.
421
405
  begin
422
406
  now = Time.now
423
- if @sendq_delay > 0
424
- burst_delay = 0
425
- if @burst > @sendq_burst
426
- burst_delay = @last_send + @sendq_delay - now
427
- end
428
-
429
- flood_delay = @flood_send - MAX_IRC_SEND_PENALTY - now
430
- delay = [burst_delay, flood_delay, 0].max
431
- if delay > 0
432
- debug "sleep(#{delay}) # (f: #{flood_delay}, b: #{burst_delay})"
433
- sleep(delay)
434
- end
407
+ flood_delay = @flood_send - MAX_IRC_SEND_PENALTY - now
408
+ delay = [flood_delay, 0].max
409
+ if delay > 0
410
+ debug "sleep(#{delay}) # (f: #{flood_delay})"
411
+ sleep(delay)
435
412
  end
436
413
  msg = @sendq.shift
437
- now = Time.now
438
- @flood_send = now if @flood_send < now
439
- @burst = 0 if @last_send + @sendq_delay < now
440
414
  debug "got #{msg.inspect} from queue, sending"
441
415
  emergency_puts(msg, true)
442
416
  rescue Exception => e
@@ -459,11 +433,12 @@ module Irc
459
433
  # the latter is racy and can cause double message output in
460
434
  # some circumstances
461
435
  actual = @filter.out(message) + "\n"
436
+ now = Time.new
462
437
  @sock.syswrite actual
463
- @last_send = Time.new
438
+ @last_send = now
439
+ @flood_send = now if @flood_send < now
464
440
  @flood_send += message.irc_send_penalty if penalty
465
441
  @lines_sent += 1
466
- @burst += 1
467
442
  end
468
443
  rescue Exception => e
469
444
  handle_socket_error(:SEND, e)
@@ -195,9 +195,7 @@ module Irc
195
195
  @plainmessage = BasicUserMessage.strip_formatting(@message)
196
196
  @message = BasicUserMessage.strip_initial_formatting(@message)
197
197
 
198
- if target && target == @bot.myself
199
- @address = true
200
- end
198
+ @address = true if source == @bot.myself
201
199
 
202
200
  end
203
201
 
@@ -323,9 +321,8 @@ module Irc
323
321
  @ctcp = false
324
322
  @action = false
325
323
 
326
- if target == @bot.myself
324
+ if @address = (target == @bot.myself)
327
325
  @private = true
328
- @address = true
329
326
  @channel = nil
330
327
  @replyto = source
331
328
  else
@@ -357,6 +354,8 @@ module Irc
357
354
  @action = @ctcp == 'ACTION'
358
355
  debug "Received CTCP command #{@ctcp} with options #{@message} (action? #{@action})"
359
356
  @logmessage = @message.dup
357
+ @plainmessage = BasicUserMessage.strip_formatting(@message)
358
+ @message = BasicUserMessage.strip_initial_formatting(@message)
360
359
  end
361
360
 
362
361
  # free splitting for plugins
@@ -541,7 +540,6 @@ module Irc
541
540
  attr_accessor :modes
542
541
  def initialize(bot, server, source, target, message="")
543
542
  super(bot, server, source, target, message)
544
- @address = (source == @bot.myself)
545
543
  @modes = []
546
544
  end
547
545
 
@@ -551,6 +549,20 @@ module Irc
551
549
  end
552
550
  end
553
551
 
552
+ # class to manage WHOIS replies
553
+ class WhoisMessage < BasicUserMessage
554
+ attr_reader :whois
555
+ def initialize(bot, server, source, target, whois)
556
+ super(bot, server, source, target, "")
557
+ @whois = whois
558
+ end
559
+
560
+ def inspect
561
+ fields = ' whois=' << whois.inspect
562
+ super(fields)
563
+ end
564
+ end
565
+
554
566
  # class to manage NAME replies
555
567
  class NamesMessage < BasicUserMessage
556
568
  attr_accessor :users
@@ -612,7 +624,6 @@ module Irc
612
624
  super(bot, server, source, channel, message)
613
625
  @channel = channel
614
626
  # in this case sourcenick is the nick that could be the bot
615
- @address = (source == @bot.myself)
616
627
  end
617
628
  end
618
629
 
@@ -144,6 +144,12 @@ module Irc
144
144
  # "<channel> <mode> <mode params>"
145
145
  RPL_CHANNELMODEIS=324
146
146
 
147
+ # "<channel> <unixtime>"
148
+ RPL_CREATIONTIME=329
149
+
150
+ # "<channel> <url>"
151
+ RPL_CHANNEL_URL=328
152
+
147
153
  # "<channel> :No topic is set"
148
154
  RPL_NOTOPIC=331
149
155
 
@@ -975,7 +981,6 @@ module Irc
975
981
  #
976
982
  # ==server events currently supported:
977
983
  #
978
- # TODO handle errors ERR_NOSUCHNICK, ERR_NOSUCHCHANNEL
979
984
  # TODO handle errors ERR_CHANOPRIVSNEEDED, ERR_CANNOTSENDTOCHAN
980
985
  #
981
986
  # welcome:: server welcome message on connect
@@ -1147,13 +1152,7 @@ module Irc
1147
1152
  # - "@" is used for secret channels, "*" for private
1148
1153
  # channels, and "=" for others (public channels).
1149
1154
  data[:channeltype] = argv[1]
1150
- data[:channel] = argv[2]
1151
-
1152
- chan = @server.get_channel(data[:channel])
1153
- unless chan
1154
- warning "Received names #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on"
1155
- return
1156
- end
1155
+ data[:channel] = chan = @server.channel(argv[2])
1157
1156
 
1158
1157
  users = []
1159
1158
  argv[3].scan(/\S+/).each { |u|
@@ -1177,7 +1176,7 @@ module Irc
1177
1176
  }
1178
1177
  @tmpusers += users
1179
1178
  when RPL_ENDOFNAMES
1180
- data[:channel] = argv[1]
1179
+ data[:channel] = @server.channel(argv[1])
1181
1180
  data[:users] = @tmpusers
1182
1181
  handle(:names, data)
1183
1182
  @tmpusers = Array.new
@@ -1238,12 +1237,17 @@ module Irc
1238
1237
  when RPL_DATASTR
1239
1238
  data[:text] = argv[1]
1240
1239
  handle(:datastr, data)
1240
+ when RPL_AWAY
1241
+ data[:nick] = user = @server.user(argv[1])
1242
+ data[:message] = argv[-1]
1243
+ user.away = data[:message]
1244
+ handle(:away, data)
1241
1245
  when RPL_WHOREPLY
1242
- data[:channel] = argv[1]
1246
+ data[:channel] = channel = @server.channel(argv[1])
1243
1247
  data[:user] = argv[2]
1244
1248
  data[:host] = argv[3]
1245
1249
  data[:userserver] = argv[4]
1246
- data[:nick] = argv[5]
1250
+ data[:nick] = user = @server.user(argv[5])
1247
1251
  if argv[6] =~ /^(H|G)(\*)?(.*)?$/
1248
1252
  data[:away] = ($1 == 'G')
1249
1253
  data[:ircop] = $2
@@ -1256,8 +1260,6 @@ module Irc
1256
1260
  end
1257
1261
  data[:hopcount], data[:real_name] = argv[7].split(" ", 2)
1258
1262
 
1259
- user = @server.get_user(data[:nick])
1260
-
1261
1263
  user.user = data[:user]
1262
1264
  user.host = data[:host]
1263
1265
  user.away = data[:away] # FIXME doesn't provide the actual message
@@ -1266,8 +1268,6 @@ module Irc
1266
1268
  # TODO hopcount
1267
1269
  user.real_name = data[:real_name]
1268
1270
 
1269
- channel = @server.get_channel(data[:channel])
1270
-
1271
1271
  channel.add_user(user, :silent=>true)
1272
1272
  data[:modes].map { |mode|
1273
1273
  channel.mode[mode].set(user)
@@ -1276,9 +1276,85 @@ module Irc
1276
1276
  handle(:who, data)
1277
1277
  when RPL_ENDOFWHO
1278
1278
  handle(:eowho, data)
1279
+ when RPL_WHOISUSER
1280
+ @whois ||= Hash.new
1281
+ @whois[:nick] = argv[1]
1282
+ @whois[:user] = argv[2]
1283
+ @whois[:host] = argv[3]
1284
+ @whois[:real_name] = argv[-1]
1285
+
1286
+ user = @server.user(@whois[:nick])
1287
+ user.user = @whois[:user]
1288
+ user.host = @whois[:host]
1289
+ user.real_name = @whois[:real_name]
1290
+ when RPL_WHOISSERVER
1291
+ @whois ||= Hash.new
1292
+ @whois[:nick] = argv[1]
1293
+ @whois[:server] = argv[2]
1294
+ @whois[:server_info] = argv[-1]
1295
+ # TODO update user info
1296
+ when RPL_WHOISOPERATOR
1297
+ @whois ||= Hash.new
1298
+ @whois[:nick] = argv[1]
1299
+ @whois[:operator] = argv[-1]
1300
+ # TODO update user info
1301
+ when RPL_WHOISIDLE
1302
+ @whois ||= Hash.new
1303
+ @whois[:nick] = argv[1]
1304
+ user = @server.user(@whois[:nick])
1305
+ @whois[:idle] = argv[2].to_i
1306
+ user.idle_since = Time.now - @whois[:idle]
1307
+ if argv[-1] == 'seconds idle, signon time'
1308
+ @whois[:signon] = Time.at(argv[3].to_i)
1309
+ user.signon = @whois[:signon]
1310
+ end
1311
+ when RPL_ENDOFWHOIS
1312
+ @whois ||= Hash.new
1313
+ @whois[:nick] = argv[1]
1314
+ data[:whois] = @whois.dup
1315
+ @whois.clear
1316
+ handle(:whois, data)
1317
+ when RPL_WHOISCHANNELS
1318
+ @whois ||= Hash.new
1319
+ @whois[:nick] = argv[1]
1320
+ @whois[:channels] = []
1321
+ user = @server.user(@whois[:nick])
1322
+ argv[-1].split.each do |prechan|
1323
+ pfx = prechan.scan(/[#{@server.supports[:prefix][:prefixes].join}]/)
1324
+ modes = pfx.map { |p| @server.mode_for_prefix p }
1325
+ chan = prechan[pfx.length..prechan.length]
1326
+
1327
+ channel = @server.channel(chan)
1328
+ channel.add_user(user, :silent => true)
1329
+ modes.map { |mode| channel.mode[mode].set(user) }
1330
+
1331
+ @whois[:channels] << [chan, modes]
1332
+ end
1279
1333
  when RPL_CHANNELMODEIS
1280
1334
  parse_mode(serverstring, argv[1..-1], data)
1281
1335
  handle(:mode, data)
1336
+ when RPL_CREATIONTIME
1337
+ data[:channel] = @server.channel(argv[1])
1338
+ data[:time] = Time.at(argv[2].to_i)
1339
+ data[:channel].creation_time=data[:time]
1340
+ handle(:creationtime, data)
1341
+ when RPL_CHANNEL_URL
1342
+ data[:channel] = @server.channel(argv[1])
1343
+ data[:url] = argv[2]
1344
+ data[:channel].url=data[:url].dup
1345
+ handle(:channel_url, data)
1346
+ when ERR_NOSUCHNICK
1347
+ data[:nick] = argv[1]
1348
+ if user = @server.get_user(data[:nick])
1349
+ @server.delete_user(user)
1350
+ end
1351
+ handle(:nosuchnick, data)
1352
+ when ERR_NOSUCHCHANNEL
1353
+ data[:channel] = argv[1]
1354
+ if channel = @server.get_channel(data[:channel])
1355
+ @server.delete_channel(channel)
1356
+ end
1357
+ handle(:nosuchchannel, data)
1282
1358
  else
1283
1359
  warning "Unknown message #{serverstring.inspect}"
1284
1360
  handle(:unknown, data)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.12
4
+ version: 0.9.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Gilbert
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-08-08 00:00:00 +02:00
12
+ date: 2008-09-02 00:00:00 +02:00
13
13
  default_executable: rbot
14
14
  dependencies: []
15
15
 
@@ -85,6 +85,7 @@ files:
85
85
  - data/rbot/plugins/nslookup.rb
86
86
  - data/rbot/plugins/imdb.rb
87
87
  - data/rbot/plugins/seen.rb
88
+ - data/rbot/plugins/geoip.rb
88
89
  - data/rbot/plugins/twitter.rb
89
90
  - data/rbot/plugins/spell.rb
90
91
  - data/rbot/plugins/ri.rb