butler 1.8.0 → 1.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +40 -0
- data/README +9 -9
- data/Rakefile +15 -71
- data/bin/botcontrol +151 -146
- data/data/butler/dialogs/botcontrol.rb +8 -3
- data/data/butler/dialogs/create.rb +18 -18
- data/data/butler/dialogs/create_config.rb +8 -0
- data/data/butler/dialogs/en/create_config.yaml +2 -0
- data/data/butler/dialogs/en/list.yaml +2 -1
- data/data/butler/dialogs/info.rb +2 -2
- data/data/butler/dialogs/list.rb +13 -8
- data/data/butler/plugins/games/countdown.rb +41 -0
- data/data/butler/plugins/games/roll.rb +59 -0
- data/lib/access.rb +6 -107
- data/lib/access/admin.rb +3 -0
- data/lib/access/role.rb +37 -2
- data/lib/access/savable.rb +5 -0
- data/lib/access/user.rb +21 -2
- data/lib/access/yamlbase.rb +4 -0
- data/lib/butler.rb +4 -4
- data/lib/butler/bot.rb +13 -13
- data/lib/butler/irc/client.rb +10 -2
- data/lib/butler/irc/parser.rb +7 -2
- data/lib/butler/irc/parser/commands.rb +24 -7
- data/lib/butler/irc/parser/generic.rb +27 -315
- data/lib/butler/irc/parser/rfc2812.rb +328 -0
- data/lib/butler/irc/socket.rb +1 -1
- data/lib/butler/irc/user.rb +13 -0
- data/lib/butler/plugin.rb +1 -1
- data/lib/butler/plugin/configproxy.rb +4 -4
- data/lib/butler/plugins.rb +1 -1
- data/lib/butler/version.rb +1 -1
- data/lib/configuration.rb +22 -71
- data/lib/dialogline.rb +12 -0
- data/lib/event.rb +5 -2
- data/lib/installer.rb +52 -24
- data/lib/iterator.rb +17 -7
- data/lib/log.rb +32 -5
- data/lib/log/comfort.rb +33 -16
- data/lib/log/entry.rb +25 -5
- data/lib/log/fakeio.rb +1 -0
- data/lib/log/splitter.rb +6 -2
- data/lib/ostructfixed.rb +5 -0
- data/lib/ruby/exception/detailed.rb +3 -3
- data/lib/scheduler.rb +19 -4
- data/lib/scriptfile.rb +9 -2
- data/lib/string.rb +176 -0
- data/lib/string/ascii.rb +31 -0
- data/lib/string/mbencoded.rb +79 -0
- data/lib/string/sbencoded.rb +77 -0
- data/lib/string/utf8.rb +157 -0
- data/lib/templater.rb +68 -10
- data/lib/w3validator.rb +86 -0
- data/test/irc/serverlistings/test_rpl_hiddenhost.txt +60 -0
- data/test/test_access.rb +101 -0
- data/test/test_configuration.rb +63 -0
- metadata +19 -2
@@ -0,0 +1,328 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2007 by Stefan Rusterholz.
|
3
|
+
# All rights reserved.
|
4
|
+
# See LICENSE.txt for permissions.
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
# A list with RFC2812 compatible IRC-Commands and it's parsing instructions
|
10
|
+
# Currently many of them are still in generic.rb
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
# --- Text based ----------------------------------------
|
15
|
+
add("error", :ERROR) # ERROR :<error-message>
|
16
|
+
add("invite", :INVITE, /^(\S*) :(.*)/, [:invited, :channel])
|
17
|
+
add("join", :JOIN, /^:(.*)/, [:channel]) { |message, parser|
|
18
|
+
if message.from && message.channel then
|
19
|
+
message.from.add_channel(message.channel, :join)
|
20
|
+
message.channel.add_user(message.from, :join)
|
21
|
+
end
|
22
|
+
}
|
23
|
+
add("kick", :KICK, /^(\S*) (\S*) :(.*)/, [:channel, :for, :text]) { |message, parser|
|
24
|
+
parser.leave_channel(message, :kick, :kicked)
|
25
|
+
}
|
26
|
+
add("kill", :KILL, /^(\S*) (\S*) (.*)/, [:channel, :for, :text]) { |message, parser|
|
27
|
+
if message.for then
|
28
|
+
message.for.kill
|
29
|
+
parser.channels.delete_user(message.for, :kill)
|
30
|
+
end
|
31
|
+
}
|
32
|
+
# FIXME, really ':?' or just plain ':'?
|
33
|
+
add("mode", :MODE, /^(\S*) :?(.*)/, [:for, :arguments]) { |message, parser|
|
34
|
+
modifiers = message[:arguments].split(" ")
|
35
|
+
modes = modifiers.shift.split("")
|
36
|
+
flags = {"o" => User::Flags::OP, "v" => User::Flags::VOICE, "u" => User::Flags::UOP}
|
37
|
+
message.create_member(:modes, [])
|
38
|
+
argumentIndex = 0
|
39
|
+
direction = "+"
|
40
|
+
|
41
|
+
modes.each { |mode|
|
42
|
+
# MODEs direction
|
43
|
+
if (["+", "-"].include?(mode)) then
|
44
|
+
direction = mode
|
45
|
+
# MODEs taking an argument
|
46
|
+
elsif (["k", "o", "v", "u"].include?(mode) || (mode == "l" && direction == "+")) then
|
47
|
+
if ["o", "v", "u"].include?(mode) then
|
48
|
+
if (direction == "+") then
|
49
|
+
parser.users[modifiers[argumentIndex]].add_flags(message.channel, flags[mode]) #adding flags to user
|
50
|
+
else
|
51
|
+
parser.users[modifiers[argumentIndex]].delete_flags(message.channel, flags[mode]) #removing flags from user
|
52
|
+
end
|
53
|
+
end
|
54
|
+
message.modes << [direction, mode, modifiers[argumentIndex]]
|
55
|
+
argumentIndex = argumentIndex+1
|
56
|
+
# MODEs without argument
|
57
|
+
else
|
58
|
+
message.modes << [direction, mode, nil]
|
59
|
+
end
|
60
|
+
}
|
61
|
+
}
|
62
|
+
add("nick", :NICK, /^:(.*)/, [:nick]) { |message, parser|
|
63
|
+
message.create_member(:old_nick, message.from.nick)
|
64
|
+
message.from.nick = message.nick if message.from
|
65
|
+
}
|
66
|
+
add("notice", :NOTICE, /(\S+) :(.*)/, [:for, :text]) { |message, parser|
|
67
|
+
if message.channel then
|
68
|
+
message.create_member(:realm, :public)
|
69
|
+
message.create_method(:private?) { false }
|
70
|
+
message.create_method(:public?) { true }
|
71
|
+
else
|
72
|
+
message.create_member(:realm, :private)
|
73
|
+
message.create_method(:private?) { true }
|
74
|
+
message.create_method(:public?) { false }
|
75
|
+
end
|
76
|
+
}
|
77
|
+
add("part", :PART, /^([^\x00\x07\x10\x0D\x20,:]+)(?: :(.*))?/, [:channel, :reason]) { |message, parser|
|
78
|
+
parser.leave_channel(message, :part, :parted)
|
79
|
+
}
|
80
|
+
add("ping", :PING, /:(.*)/, [:pong])
|
81
|
+
add("pong", :PONG)
|
82
|
+
add("privmsg", :PRIVMSG, /(\S+) :(.*)/, [:for, :text]) { |message, parser|
|
83
|
+
if message.channel then
|
84
|
+
message.create_member(:realm, :public)
|
85
|
+
message.create_method(:private?) { false }
|
86
|
+
message.create_method(:public?) { true }
|
87
|
+
else
|
88
|
+
message.create_member(:realm, :private)
|
89
|
+
message.create_method(:private?) { true }
|
90
|
+
message.create_method(:public?) { false }
|
91
|
+
end
|
92
|
+
}
|
93
|
+
add("quit", :QUIT, /(.*)/, [:text]) { |message, parser|
|
94
|
+
if message.from then
|
95
|
+
message.from.quit
|
96
|
+
parser.channels.delete_user(message.from, :quit)
|
97
|
+
parser.users.delete(message.from, :quit)
|
98
|
+
end
|
99
|
+
}
|
100
|
+
add("topic", :TOPIC, /(\S+) :(.*)/, [:channel, :text])
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
# --- 0** Codes ----------------------------------------
|
105
|
+
add("001", :RPL_WELCOME)
|
106
|
+
add("002", :RPL_YOURHOST)
|
107
|
+
add("003", :RPL_CREATED)
|
108
|
+
add("004", :RPL_MYINFO)
|
109
|
+
add("005", :RPL_ISUPPORT)
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
# --- 2** Codes ----------------------------------------
|
115
|
+
add("200", :RPL_TRACELINK)
|
116
|
+
add("201", :RPL_TRACECONNECTING)
|
117
|
+
add("202", :RPL_TRACEHANDSHAKE)
|
118
|
+
add("203", :RPL_TRACEUNKNOWN)
|
119
|
+
add("204", :RPL_TRACEOPERATOR)
|
120
|
+
add("205", :RPL_TRACEUSER)
|
121
|
+
add("206", :RPL_TRACESERVER)
|
122
|
+
add("207", :RPL_TRACESERVICE)
|
123
|
+
add("208", :RPL_TRACENEWTYPE)
|
124
|
+
add("209", :RPL_TRACECLASS)
|
125
|
+
add("210", :RPL_TRACERECONNECT)
|
126
|
+
add("211", :RPL_STATSLINKINFO)
|
127
|
+
add("212", :RPL_STATSCOMMANDS)
|
128
|
+
add("213", :RPL_STATSCLINE)
|
129
|
+
add("214", :RPL_STATSNLINE)
|
130
|
+
add("215", :RPL_STATSILINE)
|
131
|
+
add("216", :RPL_STATSKLINE)
|
132
|
+
add("217", :RPL_STATSQLINE)
|
133
|
+
add("218", :RPL_STATSYLINE)
|
134
|
+
add("219", :RPL_ENDOFSTATS)
|
135
|
+
|
136
|
+
add("221", :RPL_UMODEIS)
|
137
|
+
|
138
|
+
add("231", :RPL_SERVICEINFO)
|
139
|
+
add("232", :RPL_ENDOFSERVICES)
|
140
|
+
add("233", :RPL_SERVICE)
|
141
|
+
add("234", :RPL_SERVLIST)
|
142
|
+
add("235", :RPL_SERVLISTEND)
|
143
|
+
|
144
|
+
add("240", :RPL_STATSVLINE)
|
145
|
+
add("241", :RPL_STATSLLINE)
|
146
|
+
add("242", :RPL_STATSUPTIME)
|
147
|
+
add("243", :RPL_STATSOLINE)
|
148
|
+
add("244", :RPL_STATSHLINE)
|
149
|
+
add("245", :RPL_STATSSLINE) # RFC 2812 seems to be erroneous, it assigns 244 double
|
150
|
+
add("246", :RPL_STATSPING)
|
151
|
+
add("247", :RPL_STATSBLINE)
|
152
|
+
add("250", :RPL_STATSCONN)
|
153
|
+
add("251", :RPL_LUSERCLIENT)
|
154
|
+
add("252", :RPL_LUSEROP)
|
155
|
+
add("253", :RPL_LUSERUNKNOWN)
|
156
|
+
add("254", :RPL_LUSERCHANNELS)
|
157
|
+
add("255", :RPL_LUSERME)
|
158
|
+
add("256", :RPL_ADMINME)
|
159
|
+
add("257", :RPL_ADMINLOC1)
|
160
|
+
add("258", :RPL_ADMINLOC2)
|
161
|
+
add("259", :RPL_ADMINEMAIL)
|
162
|
+
|
163
|
+
add("261", :RPL_TRACELOG)
|
164
|
+
add("262", :RPL_TRACEEND)
|
165
|
+
|
166
|
+
add("263", :RPL_TRYAGAIN)
|
167
|
+
|
168
|
+
|
169
|
+
|
170
|
+
# --- 3** Codes ----------------------------------------
|
171
|
+
add("300", :RPL_NONE)
|
172
|
+
add("301", :RPL_AWAY)
|
173
|
+
add("302", :RPL_USERHOST)
|
174
|
+
add("303", :RPL_ISON)
|
175
|
+
add("305", :RPL_UNAWAY)
|
176
|
+
add("306", :RPL_NOWAWAY)
|
177
|
+
add("311", :RPL_WHOISUSER, /^(\S+) (\S+) (\S+) (\S+) \* :(.*)/, [:for, :nick, :user, :host, :real])
|
178
|
+
add("312", :RPL_WHOISSERVER)
|
179
|
+
add("313", :RPL_WHOISOPERATOR)
|
180
|
+
add("314", :RPL_WHOWASUSER)
|
181
|
+
add("315", :RPL_ENDOFWHO)
|
182
|
+
add("316", :RPL_WHOISCHANOP)
|
183
|
+
add("317", :RPL_WHOISIDLE, /^(\S+) (\S+) ([^:]+) :(.*)/, [:for, :nick, :values, :descriptions]) { |message, parser|
|
184
|
+
values = message[:values].split(" ");
|
185
|
+
descriptions = message[:descriptions].split(", ").map { |desc| desc.gsub(" ", "_").to_sym };
|
186
|
+
message.delete(:values)
|
187
|
+
message.delete(:descriptions)
|
188
|
+
0.upto([values.length, descriptions.length].min-1) { |index|
|
189
|
+
message[descriptions[index]] = values[index]
|
190
|
+
}
|
191
|
+
}
|
192
|
+
add("318", :RPL_ENDOFWHOIS)
|
193
|
+
add("319", :RPL_WHOISCHANNELS, /^(\S+) (\S+) :(.*)/, [:for, :nick, :channels]) { |message, parser|
|
194
|
+
# FIXME
|
195
|
+
message.alter_member(:channels, message.channels.split(" ").map { |channel| parser.channels.create(channel) })
|
196
|
+
}
|
197
|
+
add("321", :RPL_LISTSTART)
|
198
|
+
add("322", :RPL_LIST, /^(\S+) (\S+) (\d+) :(.*)/, [:for, :channelname, :usercount, :topic])
|
199
|
+
add("323", :RPL_LISTEND)
|
200
|
+
add("324", :RPL_CHANNELMODEIS)
|
201
|
+
add("325", :RPL_UNIQOPIS)
|
202
|
+
add("331", :RPL_NOTOPIC)
|
203
|
+
# :irc.server.net 332 YourNickname #channel :Topic
|
204
|
+
add("332", :RPL_TOPIC, /^(\S+) (\S+) :(.*)/, [:for, :channel, :topic]) { |message, parser|
|
205
|
+
message.channel.topic.text = message[:topic]
|
206
|
+
}
|
207
|
+
add("341", :RPL_INVITING)
|
208
|
+
add("342", :RPL_SUMMONING)
|
209
|
+
add("346", :RPL_INVITELIST)
|
210
|
+
add("347", :RPL_ENDOFINVITELIST)
|
211
|
+
add("348", :RPL_EXCEPTLIST)
|
212
|
+
add("349", :RPL_ENDOFEXCEPTLIST)
|
213
|
+
add("351", :RPL_VERSION)
|
214
|
+
# :irc.server.net 352 YourNickname <channel> <user> <host> <server> <nick> ( "H" / "G" > ["*"] [ ( "@" / "+" ) ] :<hopcount> <real name>
|
215
|
+
add("352", :RPL_WHOREPLY,
|
216
|
+
/(\S+) (\S+) (\S+) (\S+) (\S+) (\S+) ([HG])[*%]{0,2}([@+-]{0,3}) :(\d+) (.*)/,
|
217
|
+
[:for, :channel, :user, :host, :server, :nick, :status, :flags, :hopcount, :real]) { |message, parser|
|
218
|
+
#"for", "channel", "user", "host", "server", "nick", "status", "flags", "hopcount", "real"
|
219
|
+
user = parser.users.create(message[:nick])
|
220
|
+
user.update(message[:user], message[:host], message[:real])
|
221
|
+
user.away = message[:status] == "G"
|
222
|
+
user.add_channel(message.channel, :joined)
|
223
|
+
message.channel.add_user(user, :joined)
|
224
|
+
user.add_flags(message.channel, message[:flags].to_flags)
|
225
|
+
}
|
226
|
+
add("353", :RPL_NAMEREPLY, /(\S+) [=@] (\S+) :(.*)/, [:for, :channel, :users]) { |message, parser|
|
227
|
+
users = message[:users]
|
228
|
+
message.alter_member(:users, [])
|
229
|
+
users.split(/ /).each { |nick|
|
230
|
+
user = parser.users.create(nick)
|
231
|
+
message[:users] << user
|
232
|
+
user.add_channel(message.channel, :joined)
|
233
|
+
message.channel.add_user(user, :joined)
|
234
|
+
}
|
235
|
+
}
|
236
|
+
add("361", :RPL_KILLDONE)
|
237
|
+
add("362", :RPL_CLOSING)
|
238
|
+
add("363", :RPL_CLOSEEND)
|
239
|
+
add("364", :RPL_LINKS)
|
240
|
+
add("365", :RPL_ENDOFLINKS)
|
241
|
+
add("366", :RPL_ENDOFNAMES)
|
242
|
+
add("367", :RPL_BANLIST,
|
243
|
+
/(\S+) (\S+) (\S+) (\S+) (\d+)/,
|
244
|
+
[:for, :channel, :banmask, :banned_by, :bantime]
|
245
|
+
) { |message, parser| #367 nickname #channel nick!user@host nickname 1140125288
|
246
|
+
message[:bantime] = Time.at(message[:bantime].to_i)
|
247
|
+
message[:banmask] = Hostmask.new(message[:banmask])
|
248
|
+
}
|
249
|
+
add("369", :RPL_ENDOFWHOWAS)
|
250
|
+
|
251
|
+
add("368", :RPL_ENDOFBANLIST)
|
252
|
+
add("371", :RPL_INFO)
|
253
|
+
add("372", :RPL_MOTD)
|
254
|
+
add("373", :RPL_INFOSTART)
|
255
|
+
add("374", :RPL_ENDOFINFO)
|
256
|
+
add("375", :RPL_MOTDSTART)
|
257
|
+
add("376", :RPL_ENDOFMOTD)
|
258
|
+
add("381", :RPL_YOUREOPER)
|
259
|
+
add("382", :RPL_REHASHING)
|
260
|
+
add("383", :RPL_YOURESERVICE)
|
261
|
+
add("384", :RPL_MYPORTIS)
|
262
|
+
add("391", :RPL_TIME)
|
263
|
+
add("392", :RPL_USERSSTART)
|
264
|
+
add("393", :RPL_USERS)
|
265
|
+
add("394", :RPL_ENDOFUSERS)
|
266
|
+
add("395", :RPL_NOUSERS)
|
267
|
+
|
268
|
+
|
269
|
+
|
270
|
+
# --- 4** Codes ----------------------------------------
|
271
|
+
add("401", :ERR_NOSUCHNICK)
|
272
|
+
add("402", :ERR_NOSUCHSERVER)
|
273
|
+
add("403", :ERR_NOSUCHCHANNEL)
|
274
|
+
add("404", :ERR_CANNOTSENDTOCHAN)
|
275
|
+
add("405", :ERR_TOOMANYCHANNELS)
|
276
|
+
add("406", :ERR_WASNOSUCHNICK)
|
277
|
+
add("407", :ERR_TOOMANYTARGETS)
|
278
|
+
add("408", :ERR_NOSUCHSERVICE)
|
279
|
+
add("409", :ERR_NOORIGIN)
|
280
|
+
add("411", :ERR_NORECIPIENT)
|
281
|
+
add("412", :ERR_NOTEXTTOSEND)
|
282
|
+
add("413", :ERR_NOTOPLEVEL)
|
283
|
+
add("414", :ERR_WILDTOPLEVEL)
|
284
|
+
add("415", :ERR_BADMASK)
|
285
|
+
add("421", :ERR_UNKNOWNCOMMAND)
|
286
|
+
add("422", :ERR_NOMOTD)
|
287
|
+
add("423", :ERR_NOADMININFO)
|
288
|
+
add("424", :ERR_FILEERROR)
|
289
|
+
add("431", :ERR_NONICKNAMEGIVEN)
|
290
|
+
add("432", :ERR_ERRONEUSNICKNAME)
|
291
|
+
add("433", :ERR_NICKNAMEINUSE)
|
292
|
+
add("436", :ERR_NICKCOLLISION)
|
293
|
+
add("437", :ERR_UNAVAILRESOURCE)
|
294
|
+
add("441", :ERR_USERNOTINCHANNEL)
|
295
|
+
add("442", :ERR_NOTONCHANNEL)
|
296
|
+
add("443", :ERR_USERONCHANNEL)
|
297
|
+
add("444", :ERR_NOLOGIN)
|
298
|
+
add("445", :ERR_SUMMONDISABLED)
|
299
|
+
add("446", :ERR_USERSDISABLED)
|
300
|
+
add("451", :ERR_NOTREGISTERED)
|
301
|
+
add("461", :ERR_NEEDMOREPARAMS)
|
302
|
+
add("462", :ERR_ALREADYREGISTRED)
|
303
|
+
add("463", :ERR_NOPERMFORHOST)
|
304
|
+
add("464", :ERR_PASSWDMISMATCH)
|
305
|
+
add("465", :ERR_YOUREBANNEDCREEP)
|
306
|
+
add("466", :ERR_YOUWILLBEBANNED)
|
307
|
+
add("467", :ERR_KEYSET)
|
308
|
+
add("471", :ERR_CHANNELISFULL)
|
309
|
+
add("472", :ERR_UNKNOWNMODE)
|
310
|
+
add("473", :ERR_INVITEONLYCHAN)
|
311
|
+
add("474", :ERR_BANNEDFROMCHAN)
|
312
|
+
add("475", :ERR_BADCHANNELKEY)
|
313
|
+
add("476", :ERR_BADCHANMASK)
|
314
|
+
add("477", :ERR_NOCHANMODES)
|
315
|
+
add("478", :ERR_BANLISTFULL)
|
316
|
+
add("481", :ERR_NOPRIVILEGES)
|
317
|
+
add("482", :ERR_CHANOPRIVSNEEDED)
|
318
|
+
add("483", :ERR_CANTKILLSERVER)
|
319
|
+
add("484", :ERR_RESTRICTED)
|
320
|
+
add("485", :ERR_UNIQOPPRIVSNEEDED)
|
321
|
+
add("491", :ERR_NOOPERHOST)
|
322
|
+
add("492", :ERR_NOSERVICEHOST)
|
323
|
+
|
324
|
+
|
325
|
+
|
326
|
+
# --- 5** Codes ----------------------------------------
|
327
|
+
add("501", :ERR_UMODEUNKNOWNFLAG)
|
328
|
+
add("502", :ERR_USERSDONTMATCH)
|
data/lib/butler/irc/socket.rb
CHANGED
@@ -87,7 +87,7 @@ class Butler
|
|
87
87
|
#
|
88
88
|
def initialize(server, options={})
|
89
89
|
options = OptionsDefault.merge(options)
|
90
|
-
@
|
90
|
+
@logger = options.delete(:log)
|
91
91
|
@server = server # options.delete(:server)
|
92
92
|
@port = options.delete(:port)
|
93
93
|
@eol = options.delete(:eol).dup.freeze
|
data/lib/butler/irc/user.rb
CHANGED
@@ -87,6 +87,19 @@ class Butler
|
|
87
87
|
@real = real.freeze if real
|
88
88
|
self
|
89
89
|
end
|
90
|
+
|
91
|
+
# :nodoc:
|
92
|
+
# DO NOT USE THIS METHOD
|
93
|
+
# This method is intended to be used by IRC::Parser or IRC::Client
|
94
|
+
# in case the server alters parts about 'myself'
|
95
|
+
# examples: some ircd's change the 'user' part (prefix it), some
|
96
|
+
# ircd's allow hiding the host, ...
|
97
|
+
def force_update(user=nil, host=nil, real=nil)
|
98
|
+
@user = user.freeze if user
|
99
|
+
@host = host.freeze if host
|
100
|
+
@real = real.freeze if real
|
101
|
+
self
|
102
|
+
end
|
90
103
|
|
91
104
|
# set users nickname (should only be used by Butler::IRC::Parser)
|
92
105
|
def nick=(nick) #:nodoc:
|
data/lib/butler/plugin.rb
CHANGED
@@ -41,7 +41,7 @@ class Butler
|
|
41
41
|
@name = File.basename(base).freeze
|
42
42
|
@commands = []
|
43
43
|
@listener = []
|
44
|
-
@config = ConfigProxy.new(@butler.config, "plugin
|
44
|
+
@config = ConfigProxy.new(@butler.config, "plugin/#{name}")
|
45
45
|
|
46
46
|
if File.directory?(path) then
|
47
47
|
raise "Not supported yet" # FIXME
|
@@ -15,20 +15,20 @@ class Butler
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def []=(key, value)
|
18
|
-
@config[key.empty? ? @base : "#{@base}
|
18
|
+
@config[key.empty? ? @base : "#{@base}/#{key}"] = value
|
19
19
|
end
|
20
20
|
|
21
21
|
def [](key, *args)
|
22
|
-
@config[(key.empty? ? @base : "#{@base}
|
22
|
+
@config[(key.empty? ? @base : "#{@base}/#{key}"), *args]
|
23
23
|
end
|
24
24
|
|
25
25
|
def exist?(key)
|
26
|
-
@config.exist?(key.empty? ? @base : "#{@base}
|
26
|
+
@config.exist?(key.empty? ? @base : "#{@base}/#{key}")
|
27
27
|
end
|
28
28
|
alias has_key? exist?
|
29
29
|
|
30
30
|
def delete(key)
|
31
|
-
@config.delete(key.empty? ? @base : "#{@base}
|
31
|
+
@config.delete(key.empty? ? @base : "#{@base}/#{key}")
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
data/lib/butler/plugins.rb
CHANGED
@@ -114,7 +114,7 @@ class Butler
|
|
114
114
|
constant = "%s_%08X" % [name, rand(0xffffffff)]
|
115
115
|
end while Butler::Plugins.const_defined?(constant)
|
116
116
|
plugin = Butler::Plugins.const_set(constant, Class.new(Plugin))
|
117
|
-
plugin.
|
117
|
+
plugin.logger = @butler.logger
|
118
118
|
begin
|
119
119
|
plugin.load_plugin(@butler, base, path)
|
120
120
|
plugin.class_eval(File.read(codefile), codefile)
|
data/lib/butler/version.rb
CHANGED
data/lib/configuration.rb
CHANGED
@@ -13,10 +13,24 @@ require 'yaml'
|
|
13
13
|
|
14
14
|
class String
|
15
15
|
def config_key # FIXME %2e instead of . is just ugly...
|
16
|
-
gsub(
|
16
|
+
gsub(/\//, '%2f')
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
# == Indexing
|
21
|
+
# Author: Stefan Rusterholz
|
22
|
+
# Contact: apeiros@gmx.net>
|
23
|
+
# Version: 1.0.0
|
24
|
+
# Date: 2007-10-12
|
25
|
+
#
|
26
|
+
# == About
|
27
|
+
# Map configuration keys/data to files
|
28
|
+
#
|
29
|
+
# == Synopsis
|
30
|
+
# config = Configuration.new("my/config")
|
31
|
+
# config["some/key"] = "Some value"
|
32
|
+
# config["some/key"] = config["some/key"].upcase # can't use upcase!
|
33
|
+
#
|
20
34
|
class Configuration
|
21
35
|
module VERSION
|
22
36
|
MAJOR = 0
|
@@ -28,7 +42,7 @@ class Configuration
|
|
28
42
|
attr_reader :key
|
29
43
|
def initialize(key)
|
30
44
|
@key = key
|
31
|
-
super("Keys must not be empty
|
45
|
+
super("Keys must not be empty")
|
32
46
|
end
|
33
47
|
end
|
34
48
|
|
@@ -105,25 +119,26 @@ class Configuration
|
|
105
119
|
def setup(dirs)
|
106
120
|
FileUtils.mkdir_p(dirs.map { |dir| "#@base/#{dir}" })
|
107
121
|
end
|
108
|
-
|
122
|
+
|
109
123
|
private
|
110
124
|
def key_exist?(file, key)
|
111
125
|
File.exist?(file) && (!key || load(file).has_key?(key))
|
112
126
|
end
|
113
127
|
|
114
128
|
def split(key)
|
115
|
-
keys = Array === key ? key.map { |k| String(k) } : key.split(
|
129
|
+
keys = Array === key ? key.map { |k| String(k) } : key.split(/\//)
|
116
130
|
file = @base.dup
|
117
|
-
raise InvalidKey.new(key) if
|
131
|
+
raise InvalidKey.new(key) if keys.empty?
|
118
132
|
file << "/#{encode_filename(keys.shift)}" while (File.directory?(file) && keys.first)
|
119
133
|
file << ".yaml"
|
120
134
|
return [file, keys.empty? ? nil : keys.join(".")]
|
121
135
|
end
|
122
136
|
|
123
137
|
def encode_filename(name)
|
124
|
-
|
138
|
+
# FIXME: {}()\[\]~ what about those?
|
139
|
+
name.gsub(/[\x00-\x1f%\x2f\x7f]/) { |m| "%%%02x"%m[0] }.sub(/^\x2e/, '%2e')
|
125
140
|
end
|
126
|
-
|
141
|
+
|
127
142
|
def decode_filename(name)
|
128
143
|
name.gsub(/%[a-f\d]{2}/) { |m| m[1,2].to_i(16).chr }
|
129
144
|
end
|
@@ -157,67 +172,3 @@ class Configuration
|
|
157
172
|
end
|
158
173
|
end
|
159
174
|
end
|
160
|
-
|
161
|
-
if $0 == __FILE__ then
|
162
|
-
require 'test/unit'
|
163
|
-
class TestConfiguration < Test::Unit::TestCase
|
164
|
-
def setup
|
165
|
-
@conf = Configuration.new("./test")
|
166
|
-
end
|
167
|
-
|
168
|
-
def test_setup
|
169
|
-
assert(@conf)
|
170
|
-
assert(@conf.base)
|
171
|
-
assert(File.directory?(@conf.base))
|
172
|
-
end
|
173
|
-
|
174
|
-
def test_accessor
|
175
|
-
assert(@conf["file1"] = "bar")
|
176
|
-
assert_equal(@conf[[:file1]], "bar")
|
177
|
-
assert_equal(@conf[["file1"]], "bar")
|
178
|
-
assert_equal(@conf["file1"], "bar")
|
179
|
-
assert(File.exist?("#{@conf.base}/file1.yaml"))
|
180
|
-
|
181
|
-
assert(@conf["file2"] = "foo")
|
182
|
-
assert_equal(@conf[:file2], "foo")
|
183
|
-
assert_equal(@conf["file2"], "foo")
|
184
|
-
assert(File.exist?("#{@conf.base}/file2.yaml"))
|
185
|
-
|
186
|
-
assert(@conf[[:file3, :sub]] = "baz")
|
187
|
-
assert_equal(@conf[[:file3, :sub]], "baz")
|
188
|
-
assert_equal(@conf["file3", "sub"], "baz")
|
189
|
-
assert(File.exist?("#{@conf.base}/file3.yaml"))
|
190
|
-
assert(!File.exist?("#{@conf.base}/file3"))
|
191
|
-
|
192
|
-
assert(@conf.exist?(:file3))
|
193
|
-
assert(!@conf.exist?(:file))
|
194
|
-
#assert_raise { @conf
|
195
|
-
end
|
196
|
-
|
197
|
-
def test_nesting
|
198
|
-
end
|
199
|
-
|
200
|
-
def xtest_common_usecase
|
201
|
-
@conf.setup(%w(
|
202
|
-
main
|
203
|
-
plugins
|
204
|
-
plugins/demo
|
205
|
-
))
|
206
|
-
@conf.merge(
|
207
|
-
"main.language" => "de",
|
208
|
-
"main.channels" => %w(foo bar baz),
|
209
|
-
"plugins.demo.value" => 42
|
210
|
-
)
|
211
|
-
@conf["plugins.demo.other"] = 24
|
212
|
-
@conf["main.language"].replace("en")
|
213
|
-
@conf.update
|
214
|
-
file, key = @conf.send(:split, "main.language")
|
215
|
-
File.open(file, "w") { |fh| fh.write({"language" => "en"}.to_yaml) }
|
216
|
-
@conf.rehash
|
217
|
-
end
|
218
|
-
|
219
|
-
def teardown
|
220
|
-
FileUtils.rm_r(@conf.base)
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|