rbot 0.9.12 → 0.9.13

Sign up to get free protection for your applications and to get access to all the features.
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