butler 1.8.3 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +293 -37
- data/README.txt +10 -0
- data/Rakefile +24 -13
- data/bin/botcontrol +6 -5
- data/data/butler/dialogs/create.rb +21 -6
- data/data/butler/dialogs/create_config.rb +5 -2
- data/data/butler/dialogs/en/create.yaml +6 -3
- data/data/butler/dialogs/en/create_config.yaml +1 -0
- data/data/butler/dialogs/en/quickcreate.yaml +1 -1
- data/data/butler/dialogs/en/sync.yaml +7 -0
- data/data/butler/dialogs/en/username.yaml +2 -0
- data/data/butler/dialogs/quickcreate.rb +6 -2
- data/data/butler/dialogs/sync.rb +83 -0
- data/data/butler/dialogs/username.rb +1 -0
- data/data/butler/plugins/core/ping.rb +22 -0
- data/data/butler/plugins/core/remote.rb +38 -0
- data/data/butler/plugins/dev/eval.rb +6 -4
- data/data/butler/plugins/dev/onhandlers.rb +93 -0
- data/data/butler/plugins/dev/rawlog.rb +109 -45
- data/data/butler/plugins/games/countdown.rb +23 -14
- data/data/butler/plugins/games/eightball.rb +21 -13
- data/data/butler/plugins/games/roll.rb +12 -12
- data/data/butler/plugins/irc/join.rb +2 -2
- data/data/butler/plugins/irc/notice.rb +10 -10
- data/data/butler/plugins/irc/part.rb +12 -12
- data/data/butler/plugins/irc/privmsg.rb +10 -10
- data/data/butler/plugins/irc/quit.rb +12 -12
- data/data/butler/plugins/operator/devoice.rb +1 -1
- data/data/butler/plugins/public/help.rb +10 -4
- data/data/butler/plugins/{util → service}/calculator.rb +0 -0
- data/data/butler/plugins/service/define.rb +16 -13
- data/data/butler/plugins/service/log.rb +85 -0
- data/data/butler/plugins/service/seen.rb +64 -0
- data/data/butler/plugins/service/svn.rb +6 -5
- data/data/butler/plugins/util/load.rb +3 -1
- data/data/butler/services/org.rubyforge.butler/calculator/1/service.rb +96 -0
- data/data/butler/services/org.rubyforge.butler/log/1/service.rb +148 -68
- data/lib/access/admin.rb +5 -0
- data/lib/blank.rb +32 -0
- data/lib/butler.rb +4 -4
- data/lib/butler/bot.rb +118 -33
- data/lib/butler/control.rb +5 -4
- data/lib/butler/debuglog.rb +12 -4
- data/lib/butler/dialog.rb +1 -1
- data/lib/butler/initialvalues.rb +1 -1
- data/lib/butler/irc/client.rb +31 -12
- data/lib/butler/irc/message.rb +32 -13
- data/lib/butler/irc/parser.rb +67 -30
- data/lib/butler/irc/parser/{commands.rb → command.rb} +0 -38
- data/lib/butler/irc/parser/generic.rb +9 -12
- data/lib/butler/irc/parser/rfc2812.rb +40 -2
- data/lib/butler/irc/socket.rb +66 -41
- data/lib/butler/irc/string.rb +1 -5
- data/lib/butler/plugin.rb +56 -23
- data/lib/butler/plugin/configproxy.rb +1 -0
- data/lib/butler/plugin/more.rb +2 -2
- data/lib/butler/plugins.rb +7 -1
- data/lib/butler/remote/connection.rb +113 -0
- data/lib/butler/remote/message.rb +157 -0
- data/lib/butler/remote/server.rb +85 -0
- data/lib/butler/remote/user.rb +46 -0
- data/lib/butler/service.rb +2 -1
- data/lib/butler/services.rb +2 -2
- data/lib/butler/version.rb +2 -2
- data/lib/configuration.rb +13 -16
- data/lib/ostructfixed.rb +0 -6
- data/lib/ruby/array/random.rb +2 -1
- data/lib/scriptfile.rb +63 -14
- data/lib/timingoutresource.rb +54 -0
- data/test/test_scriptfile.rb +51 -0
- metadata +63 -61
- data/data/butler/dialogs/en/sync_plugins.yaml +0 -3
- data/data/butler/dialogs/sync_plugins.rb +0 -30
- data/data/butler/services/org.rubyforge.butler/calculator/1/calculator.rb +0 -68
- data/lib/log/splitter.rb +0 -30
- data/test/test_access/privilege/banners.statistics.yaml +0 -3
- data/test/test_access/privilege/banners.yaml +0 -3
- data/test/test_access/privilege/news.create.yaml +0 -3
- data/test/test_access/privilege/news.delete.yaml +0 -3
- data/test/test_access/privilege/news.edit.yaml +0 -3
- data/test/test_access/privilege/news.read.yaml +0 -3
- data/test/test_access/privilege/news.yaml +0 -3
- data/test/test_access/privilege/paid_content.yaml +0 -3
- data/test/test_access/privilege/statistics.ftp.yaml +0 -3
- data/test/test_access/privilege/statistics.web.yaml +0 -3
- data/test/test_access/privilege/statistics.yaml +0 -3
- data/test/test_access/role/chiefeditor.yaml +0 -7
- data/test/test_access/role/editor.yaml +0 -9
- data/test/test_access/user/test.yaml +0 -12
data/lib/access/admin.rb
CHANGED
@@ -7,9 +7,14 @@
|
|
7
7
|
|
8
8
|
|
9
9
|
class Access
|
10
|
+
|
10
11
|
# Extend Admin users with this plugin, speeds up privileged?
|
11
12
|
# by statically returning true and not performing any lookup.
|
12
13
|
module Admin
|
14
|
+
# Admin is admin, of course
|
15
|
+
def admin?
|
16
|
+
true
|
17
|
+
end
|
13
18
|
|
14
19
|
# admins have all privileges
|
15
20
|
def privileged?(*args)
|
data/lib/blank.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
module Kernel
|
10
|
+
# returns a blank class with only __* methods and those mentioned in
|
11
|
+
# leave_methods argument
|
12
|
+
# be aware that e.g. all Kernel methods are gone too, such as raise
|
13
|
+
# you can still use it via e.g. Kernel.raise or use leave_methods
|
14
|
+
# v0.1.1
|
15
|
+
def Blank(*leave_methods)
|
16
|
+
@_blank_classes ||= {}
|
17
|
+
leave_methods.push("__send__", "__id__", "initialize")
|
18
|
+
leave_methods.flatten!
|
19
|
+
leave_methods.uniq!
|
20
|
+
leave_methods.sort!
|
21
|
+
unless @_blank_classes[leave_methods] then
|
22
|
+
slate = Class.new
|
23
|
+
(slate.instance_methods+slate.private_instance_methods+slate.protected_instance_methods).each { |m|
|
24
|
+
slate.send(:undef_method, m) unless leave_methods.include?(m)
|
25
|
+
}
|
26
|
+
@_blank_classes[leave_methods] = slate
|
27
|
+
end
|
28
|
+
@_blank_classes[leave_methods]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Blank = Blank() unless defined? Blank
|
data/lib/butler.rb
CHANGED
@@ -35,7 +35,7 @@ class Butler
|
|
35
35
|
# -:daemonize => whether this script should become a deamon or not [true]
|
36
36
|
# -...
|
37
37
|
def start(path, botname, opts={}, &block)
|
38
|
-
Thread.abort_on_exception = true
|
38
|
+
Thread.abort_on_exception = true if $DEBUG
|
39
39
|
path ||= @path
|
40
40
|
butler = nil
|
41
41
|
|
@@ -55,8 +55,8 @@ class Butler
|
|
55
55
|
File.write(pidfile, $$)
|
56
56
|
butler = Bot.new(path, botname, opts)
|
57
57
|
path = butler.path
|
58
|
-
$stderr = File.open(path.log+"/error.log", "
|
59
|
-
$stdout = File.open(path.log+"/out.log", "
|
58
|
+
$stderr = File.open(path.log+"/error.log", "a")
|
59
|
+
$stdout = File.open(path.log+"/out.log", "a")
|
60
60
|
trap("SIGHUP") { butler.quit }
|
61
61
|
butler.output_to_logfiles
|
62
62
|
butler.plugins.load_all
|
@@ -93,7 +93,7 @@ class Butler
|
|
93
93
|
exception(e)
|
94
94
|
ensure
|
95
95
|
File.delete(pidfile) if pidfile and File.exist?(pidfile)
|
96
|
-
butler.quit if butler
|
96
|
+
butler.quit if butler and butler.connected?
|
97
97
|
info("Terminated")
|
98
98
|
end
|
99
99
|
|
data/lib/butler/bot.rb
CHANGED
@@ -12,6 +12,10 @@ require 'butler/debuglog'
|
|
12
12
|
require 'configuration'
|
13
13
|
require 'log'
|
14
14
|
require 'butler/plugins'
|
15
|
+
require 'butler/remote/connection'
|
16
|
+
require 'butler/remote/message'
|
17
|
+
require 'butler/remote/server'
|
18
|
+
require 'butler/remote/user'
|
15
19
|
require 'butler/services'
|
16
20
|
require 'butler/session'
|
17
21
|
require 'ruby/string/arguments'
|
@@ -27,16 +31,32 @@ class Butler
|
|
27
31
|
class Bot < IRC::Client
|
28
32
|
include Log::Comfort
|
29
33
|
|
34
|
+
# The access framework for this bot. An instance of Access.
|
30
35
|
attr_reader :access
|
31
|
-
|
36
|
+
|
37
|
+
# This bot instances configuration. An instance of Configuration.
|
32
38
|
attr_reader :config
|
39
|
+
|
40
|
+
# The logging device, per default nil
|
33
41
|
attr_reader :logger
|
42
|
+
|
43
|
+
# An OpenStruct with all important paths of this bot instance.
|
34
44
|
attr_reader :path
|
45
|
+
|
46
|
+
# Services butler offers, instance of Butler::Plugins
|
35
47
|
attr_reader :plugins
|
48
|
+
|
49
|
+
# Remote connections server, instance of Butler::Remote::Server
|
50
|
+
attr_reader :remote
|
51
|
+
|
52
|
+
# Scheduler to hook up timed events, instance of Scheduler
|
53
|
+
# For plugins and services, please look up the class methods for
|
54
|
+
# Butler::Plugin and Butler::Service, they are to be prefered.
|
36
55
|
attr_reader :scheduler
|
56
|
+
|
57
|
+
# Services butler offers, instance of Butler::Services
|
37
58
|
attr_reader :services
|
38
59
|
|
39
|
-
# FIXME, raise if selftest fails
|
40
60
|
def initialize(path, name, opts={})
|
41
61
|
path ||= Butler.path
|
42
62
|
@irc = nil # early inspects
|
@@ -55,6 +75,7 @@ class Butler
|
|
55
75
|
$LOAD_PATH.unshift(@path.lib)
|
56
76
|
require 'butlerinit' if File.exist?(@path.lib+"/butlerinit.rb")
|
57
77
|
@logger = $stderr
|
78
|
+
@remote = Remote::Server.new(self)
|
58
79
|
@config = Configuration.new(@base+'/config')
|
59
80
|
@scheduler = Scheduler.new
|
60
81
|
@access = Access.new(
|
@@ -89,8 +110,10 @@ class Butler
|
|
89
110
|
|
90
111
|
subscribe(:PRIVMSG, -10, &method(:invoke_commands))
|
91
112
|
subscribe(:NOTICE, -10, &method(:invoke_commands))
|
92
|
-
|
93
|
-
|
113
|
+
end
|
114
|
+
|
115
|
+
def connected?
|
116
|
+
@irc.connected?
|
94
117
|
end
|
95
118
|
|
96
119
|
def invoke_commands(listener, message)
|
@@ -144,38 +167,51 @@ class Butler
|
|
144
167
|
|
145
168
|
# returns the rest of the sequence if it was an invocation, nil else
|
146
169
|
def invocation(sequence)
|
147
|
-
@myself && sequence[/^(?:!?#{@myself.nick}[:;,])\s+/i]
|
148
|
-
end
|
149
|
-
|
150
|
-
# runs a diagnose, will collect all wrongs and raise if it finds any
|
151
|
-
def selftest
|
152
|
-
|
170
|
+
(@myself && sequence[/^(?:!?#{@myself.nick}[:;,])\s+/i]) || sequence[/^#{@config['invocation']}/i]
|
153
171
|
end
|
154
172
|
|
155
173
|
def login
|
156
174
|
@access.default_user = @access["default_user"]
|
157
175
|
@access.default_user.login
|
158
|
-
p @access.default_user
|
159
176
|
|
160
177
|
nick = @config["connections/main/nick"]
|
161
178
|
pass = @config["connections/main/password"]
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
179
|
+
begin
|
180
|
+
super(
|
181
|
+
nick,
|
182
|
+
@config["connections/main/user"],
|
183
|
+
@config["connections/main/real"],
|
184
|
+
@config["connections/main/serverpass"]
|
185
|
+
)
|
186
|
+
rescue Errno::ECONNREFUSED
|
187
|
+
if @reconnect[1].zero? then
|
188
|
+
warn("Connection was refused")
|
189
|
+
return false
|
190
|
+
else
|
191
|
+
warn("Connection was refused, trying again in #{@reconnect.first} seconds")
|
192
|
+
@reconnect[1] -= 1
|
193
|
+
sleep(@reconnect[0])
|
194
|
+
retry
|
195
|
+
end
|
196
|
+
end
|
167
197
|
info("Logged in")
|
168
|
-
if pass &&
|
169
|
-
@irc.ghost(nick, pass)
|
170
|
-
|
171
|
-
|
198
|
+
if pass && !@parser.same_nick?(@myself.nick, nick) then
|
199
|
+
if wait_for(:NOTICE, 60, :prepare => proc { @irc.ghost(nick, pass) } ) { |m|
|
200
|
+
m.from && m.from.nick =~ /nickserv/i && m.text =~ /has been killed/
|
201
|
+
} then
|
202
|
+
@irc.nick(nick)
|
203
|
+
else
|
204
|
+
warn("Could not ghost the user already occupying my nick '#{nick}'")
|
205
|
+
end
|
172
206
|
end
|
173
207
|
@irc.identify(pass) if pass
|
174
208
|
join(*@config["connections/main/channels"])
|
175
209
|
plugins_dispatch(:on_login, "main")
|
210
|
+
true
|
176
211
|
end
|
177
212
|
|
178
213
|
def on_disconnect(reason)
|
214
|
+
info("Disconnected due to #{reason}")
|
179
215
|
return if reason == :quit
|
180
216
|
plugins_dispatch(:on_disconnect, reason)
|
181
217
|
unless @reconnect[1].zero? then
|
@@ -254,9 +290,10 @@ class Butler
|
|
254
290
|
alias butler_initialize initialize unless method_defined? :butler_initialize
|
255
291
|
def initialize(*args, &block)
|
256
292
|
butler_initialize(*args, &block)
|
257
|
-
@language
|
258
|
-
@invocation
|
259
|
-
@arguments
|
293
|
+
@language = nil
|
294
|
+
@invocation = nil
|
295
|
+
@arguments = nil
|
296
|
+
@formatted_arguments = nil
|
260
297
|
@post_arguments = nil
|
261
298
|
end
|
262
299
|
|
@@ -264,23 +301,71 @@ class Butler
|
|
264
301
|
# e.g. 'Hallo "this is token2" "and this \"token\" is token3"'
|
265
302
|
# would be parsed into: ["hallo", "this is token2", "and this \"token\" is token3"]
|
266
303
|
def arguments
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
304
|
+
# only messages with text can be tokenized to parameters
|
305
|
+
raise NoMethodError, "Message has no text, can't generate arguments" unless text
|
306
|
+
# cached
|
307
|
+
return @arguments if @arguments
|
308
|
+
# split the args into double-, single- and unquoted arguments, a lonely quote starts the last arg
|
309
|
+
args = text[@invocation.length..-1].mirc_stripped.scan(/"(?:\\.|[^\\"])*"|'(?:\\.|[^\\'])*'|(?:\\.|[^\\'"\s])+|["'].*/)
|
310
|
+
# sanitize the last argument in case it's not correctly quoted
|
311
|
+
args[-1] = args[-1]+args[-1][0,1] if args[-1][0,1] =~ /'|"/ and args[-1][0,1] != args[-1][-1,1]
|
312
|
+
# unescape data and remove quotes
|
313
|
+
@arguments = args.map { |arg|
|
314
|
+
case arg[0,1]
|
315
|
+
when "'": arg[1..-2].gsub(/\\(['\\])/, '\1')
|
316
|
+
when '"': arg[1..-2].gsub(/\\(["\\])/, '\1')
|
317
|
+
else arg.gsub(/\\(["'\\ ])/, '\1')
|
318
|
+
end
|
319
|
+
}
|
273
320
|
end
|
274
|
-
|
275
|
-
#
|
321
|
+
|
322
|
+
# like #arguments, but doesn't strip color information
|
323
|
+
def formatted_arguments
|
324
|
+
# only messages with text can be tokenized to parameters
|
325
|
+
raise NoMethodError, "Message has no text, can't generate arguments" unless text
|
326
|
+
# cached
|
327
|
+
return @arguments if @arguments
|
328
|
+
# split the args into double-, single- and unquoted arguments, a lonely quote starts the last arg
|
329
|
+
args = text[@invocation.length..-1].scan(/"(?:\\.|[^\\"])*"|'(?:\\.|[^\\'])*'|(?:\\.|[^\\'"\s])+|["'].*/)
|
330
|
+
# sanitize the last argument in case it's not correctly quoted
|
331
|
+
args[-1] = args[-1]+args[-1][0,1] if args[-1][0,1] =~ /'|"/ and args[-1][0,1] != args[-1][-1,1]
|
332
|
+
# unescape data and remove quotes
|
333
|
+
@formatted_arguments = args.map { |arg|
|
334
|
+
case arg[0,1]
|
335
|
+
when "'": arg[1..-2].gsub(/\\(['\\])/, '\1')
|
336
|
+
when '"': arg[1..-2].gsub(/\\(["\\])/, '\1')
|
337
|
+
else arg.gsub(/\\(["'\\ ])/, '\1')
|
338
|
+
end
|
339
|
+
}
|
340
|
+
end
|
341
|
+
|
276
342
|
# Returns the rest of message text after argument
|
277
343
|
# == Synopsis
|
278
344
|
# "foo bar baz".post_arguments[0] # => "foo bar baz"
|
279
345
|
# "foo bar baz".post_arguments[1] # => "bar baz"
|
280
346
|
# "foo bar baz".post_arguments[2] # => "baz"
|
281
347
|
def post_arguments
|
282
|
-
|
283
|
-
|
348
|
+
# only messages with text can be tokenized to parameters
|
349
|
+
raise NoMethodError, "Message has no text, can't generate arguments" unless text
|
350
|
+
# cached
|
351
|
+
return @post_arguments if @post_arguments
|
352
|
+
# split the args into double-, single- and unquoted arguments, a lonely quote starts the last arg
|
353
|
+
args = text[@invocation.length..-1].scan(/\s+|"(?:\\.|[^\\"])*"|'(?:\\.|[^\\'])*'|(?:\\.|[^\\'"\s])+|["'].*/)
|
354
|
+
# sanitize arguments
|
355
|
+
args.shift if args.first =~ /^\s+$/
|
356
|
+
args.pop if args.last =~ /^\s+$/
|
357
|
+
args[-1] = args[-1]+args[-1][0,1] if args[-1][0,1] =~ /'|"/ and args[-1][0,1] != args[-1][-1,1]
|
358
|
+
# unescape data and remove quotes
|
359
|
+
args.map! { |arg|
|
360
|
+
case arg[0,1]
|
361
|
+
when "'": arg.gsub(/\\(['\\])/, '\1')
|
362
|
+
when '"': arg.gsub(/\\(["\\])/, '\1')
|
363
|
+
else arg.gsub(/\\(["'\\ ])/, '\1')
|
364
|
+
end
|
365
|
+
}
|
366
|
+
@post_arguments = []
|
367
|
+
(0...args.length).step(2) { |i| @post_arguments << args[i..-1].join('') }
|
368
|
+
@post_arguments
|
284
369
|
end
|
285
370
|
end # IRC::Message
|
286
371
|
end # Butler
|
data/lib/butler/control.rb
CHANGED
@@ -13,8 +13,8 @@ require 'ruby/file/write'
|
|
13
13
|
class Butler
|
14
14
|
IsGem = true # REPLACE: IsGem = false
|
15
15
|
|
16
|
-
#
|
17
|
-
#
|
16
|
+
# This Class provides services to botcontrol
|
17
|
+
# For gems, this class uses the Gem module to get information on the where-
|
18
18
|
# abouts of data. If installed using rake from tarball, it is hard-coded.
|
19
19
|
class Control
|
20
20
|
attr_reader :dialog
|
@@ -38,7 +38,7 @@ class Butler
|
|
38
38
|
|
39
39
|
def user(name=nil)
|
40
40
|
# use Etc.getlogin?
|
41
|
-
name || ENV['SUDO_USER'] || ENV['USER'] || @dialog.discuss("username", false)[:
|
41
|
+
name || ENV['SUDO_USER'] || ENV['USER'] || @dialog.discuss("username", false)[:username]
|
42
42
|
end
|
43
43
|
|
44
44
|
def configured?(user=nil)
|
@@ -72,7 +72,8 @@ class Butler
|
|
72
72
|
def user_config(user=nil)
|
73
73
|
path = @config.users[user(user)]
|
74
74
|
user_config = OpenStruct.new(YAML.load_file(path))
|
75
|
-
user_config.plugin_repository
|
75
|
+
user_config.plugin_repository = @path.plugins
|
76
|
+
user_config.service_repository = @path.services
|
76
77
|
user_config
|
77
78
|
end
|
78
79
|
|
data/lib/butler/debuglog.rb
CHANGED
@@ -1,16 +1,24 @@
|
|
1
1
|
class Butler
|
2
|
+
|
3
|
+
# Extend an object with debug-log and it will print all read/write actions to @raw_log (defaulting
|
4
|
+
# to $stderr)
|
2
5
|
module DebugLog
|
6
|
+
def self.extended(obj) # :nodoc:
|
7
|
+
obj.raw_log = $stderr
|
8
|
+
end
|
9
|
+
|
10
|
+
# The object to call printf on to log read/writes.
|
3
11
|
attr_accessor :raw_log
|
4
12
|
|
5
|
-
def read
|
13
|
+
def read # :nodoc:
|
6
14
|
data = super
|
7
|
-
@raw_log.printf("%s
|
15
|
+
@raw_log.printf("<- %s %p\n", Time.now, data)
|
8
16
|
data
|
9
17
|
end
|
10
18
|
|
11
|
-
def write(data)
|
19
|
+
def write(data) # :nodoc:
|
12
20
|
rv = super
|
13
|
-
@raw_log.printf("%s
|
21
|
+
@raw_log.printf("-> %s %p\n", Time.now, data)
|
14
22
|
rv
|
15
23
|
end
|
16
24
|
end
|
data/lib/butler/dialog.rb
CHANGED
@@ -29,7 +29,7 @@ class Butler
|
|
29
29
|
|
30
30
|
def initialize(plugin)
|
31
31
|
@plugin = plugin
|
32
|
-
@plugin.butler.subscribe(:PRIVMSG,
|
32
|
+
@plugin.butler.subscribe(:PRIVMSG, 0, true) { |listener, message|
|
33
33
|
if message.private? then
|
34
34
|
postpone
|
35
35
|
dialog(listener, message)
|
data/lib/butler/initialvalues.rb
CHANGED
data/lib/butler/irc/client.rb
CHANGED
@@ -121,7 +121,18 @@ class Butler
|
|
121
121
|
|
122
122
|
subscribe(:PING, 100) { |listener, message| @irc.pong(message.pong) }
|
123
123
|
subscribe(:ISUPPORT) { |listener, message|
|
124
|
-
|
124
|
+
isupport = {}
|
125
|
+
@parser.isupport.to_hash.each { |key, value|
|
126
|
+
isupport[key.to_s] = value
|
127
|
+
}
|
128
|
+
message.support.each { |key,value|
|
129
|
+
isupport[key.downcase] = value
|
130
|
+
}
|
131
|
+
if message.support.has_key?("PREFIX") then
|
132
|
+
isupport["prefixes"] = message.support["PREFIX"].values.join('')
|
133
|
+
end
|
134
|
+
@parser.reset(isupport)
|
135
|
+
@irc.write_with_eol("CAPAB IDENTIFY-MSG") if @parser.isupport.capab and !@parser.msg_identify
|
125
136
|
}
|
126
137
|
subscribe(:RPL_IDENTIFY_MSG) { |listener, message|
|
127
138
|
@parser.msg_identify = true
|
@@ -158,10 +169,12 @@ class Butler
|
|
158
169
|
# (optionally passing a test given as block) is received,
|
159
170
|
# returns the message received that matches.
|
160
171
|
# returns nil if it times out before a match
|
161
|
-
def wait_for(symbol, timeout=nil, &test)
|
172
|
+
def wait_for(symbol, timeout=nil, opt={}, &test)
|
173
|
+
listener = nil
|
162
174
|
timeout(timeout) {
|
163
175
|
queue = Queue.new
|
164
176
|
listener = subscribe(symbol) { |l, m| queue.push(m) }
|
177
|
+
opt[:prepare].call if opt.has_key?(:prepare)
|
165
178
|
begin
|
166
179
|
message = queue.shift
|
167
180
|
end until block_given? ? yield(message) : true
|
@@ -190,7 +203,7 @@ class Butler
|
|
190
203
|
end
|
191
204
|
|
192
205
|
# login under nick, user, real
|
193
|
-
def login(nick, user, real)
|
206
|
+
def login(nick, user, real, serverpass=nil)
|
194
207
|
queue, nick_change = nil, nil
|
195
208
|
number = 0
|
196
209
|
timeout(@timeout[:login]) {
|
@@ -205,7 +218,7 @@ class Butler
|
|
205
218
|
@myself.nick = change
|
206
219
|
}
|
207
220
|
@thread_read = Thread.new(&method(:thread_read))
|
208
|
-
@irc.login(nick, user, real)
|
221
|
+
@irc.login(nick, user, real, serverpass)
|
209
222
|
queue.shift
|
210
223
|
}
|
211
224
|
true
|
@@ -287,18 +300,20 @@ class Butler
|
|
287
300
|
# normally this thread is alive as long as the client is connected to the server
|
288
301
|
def thread_read
|
289
302
|
while message = @irc.read; process(message); end
|
303
|
+
@irc.close
|
290
304
|
on_disconnect(:disconnect)
|
291
305
|
rescue Terminate => e # got the termination signal
|
292
306
|
info("Terminating read thread")
|
307
|
+
@irc.close
|
293
308
|
on_disconnect(:quit)
|
294
309
|
rescue Errno::EPIPE => error # irc server closed connection
|
295
310
|
exception(error)
|
311
|
+
@irc.close
|
296
312
|
on_disconnect(:disconnect)
|
297
313
|
rescue Exception => error
|
298
314
|
exception(error)
|
299
|
-
on_disconnect(:error)
|
300
|
-
ensure
|
301
315
|
@irc.close
|
316
|
+
on_disconnect(:error)
|
302
317
|
end
|
303
318
|
|
304
319
|
# Called on disconnects, see disconnect_callback attribute documentation
|
@@ -323,17 +338,21 @@ class Butler
|
|
323
338
|
|
324
339
|
# process a Butler::IRC::Message, normally fed from thread_read.
|
325
340
|
def process(message)
|
326
|
-
|
327
|
-
message.transcode!(@channel_charset[message.channel], @client_charset)
|
328
|
-
@listener.synchronized_each_for(message.symbol) { |listener|
|
329
|
-
listener.call(message)
|
330
|
-
}
|
341
|
+
dispatch(@parser.server_message(message))
|
331
342
|
rescue Terminate, Errno::EPIPE => error
|
332
343
|
raise error # on these errors we got to get out of the loop
|
333
344
|
rescue Exception => error
|
334
345
|
exception(error) # those errors are logged, reading goes on
|
335
346
|
end
|
336
347
|
|
348
|
+
# dispatch a message
|
349
|
+
def dispatch(message)
|
350
|
+
message.transcode!(@channel_charset[message.channel], @client_charset)
|
351
|
+
@listener.synchronized_each_for(message.symbol) { |listener|
|
352
|
+
listener.call(message)
|
353
|
+
}
|
354
|
+
end
|
355
|
+
|
337
356
|
def inspect # :nodoc:
|
338
357
|
"#<%s:0x%08x irc=%s>" % [
|
339
358
|
self.class,
|
@@ -343,4 +362,4 @@ class Butler
|
|
343
362
|
end
|
344
363
|
end # Client
|
345
364
|
end # IRC
|
346
|
-
end # Butler
|
365
|
+
end # Butler
|