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 +1 -1
- data/bin/rbot +1 -1
- data/data/rbot/plugins/geoip.rb +161 -0
- data/data/rbot/plugins/iplookup.rb +4 -3
- data/data/rbot/plugins/nickrecover.rb +13 -5
- data/data/rbot/plugins/quakeauth.rb +3 -2
- data/lib/rbot/core/irclog.rb +33 -4
- data/lib/rbot/irc.rb +12 -1
- data/lib/rbot/ircbot.rb +12 -9
- data/lib/rbot/ircsocket.rb +19 -44
- data/lib/rbot/message.rb +18 -7
- data/lib/rbot/rfc2812.rb +91 -15
- metadata +3 -2
data/Rakefile
CHANGED
data/bin/rbot
CHANGED
@@ -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
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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')
|
data/lib/rbot/core/irclog.rb
CHANGED
@@ -272,10 +272,39 @@ class IrcLogModule < CoreBotModule
|
|
272
272
|
end
|
273
273
|
end
|
274
274
|
fp = logfilepath(where_str, now)
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
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}"
|
data/lib/rbot/irc.rb
CHANGED
@@ -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
|
data/lib/rbot/ircbot.rb
CHANGED
@@ -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'],
|
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
|
data/lib/rbot/ircsocket.rb
CHANGED
@@ -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
|
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,
|
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
|
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
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
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 =
|
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)
|
data/lib/rbot/message.rb
CHANGED
@@ -195,9 +195,7 @@ module Irc
|
|
195
195
|
@plainmessage = BasicUserMessage.strip_formatting(@message)
|
196
196
|
@message = BasicUserMessage.strip_initial_formatting(@message)
|
197
197
|
|
198
|
-
|
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
|
|
data/lib/rbot/rfc2812.rb
CHANGED
@@ -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.
|
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-
|
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
|