cakewalk 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +1 -0
- data/LICENSE +22 -0
- data/README.md +169 -0
- data/docs/bot_options.md +454 -0
- data/docs/changes.md +541 -0
- data/docs/common_mistakes.md +60 -0
- data/docs/common_tasks.md +57 -0
- data/docs/encodings.md +69 -0
- data/docs/events.md +273 -0
- data/docs/getting_started.md +184 -0
- data/docs/logging.md +90 -0
- data/docs/migrating.md +267 -0
- data/docs/plugins.md +4 -0
- data/docs/readme.md +20 -0
- data/examples/basic/autovoice.rb +32 -0
- data/examples/basic/google.rb +35 -0
- data/examples/basic/hello.rb +15 -0
- data/examples/basic/join_part.rb +34 -0
- data/examples/basic/memo.rb +39 -0
- data/examples/basic/msg.rb +16 -0
- data/examples/basic/seen.rb +36 -0
- data/examples/basic/urban_dict.rb +35 -0
- data/examples/basic/url_shorten.rb +35 -0
- data/examples/plugins/autovoice.rb +37 -0
- data/examples/plugins/custom_prefix.rb +23 -0
- data/examples/plugins/dice_roll.rb +38 -0
- data/examples/plugins/google.rb +36 -0
- data/examples/plugins/hello.rb +22 -0
- data/examples/plugins/hooks.rb +36 -0
- data/examples/plugins/join_part.rb +42 -0
- data/examples/plugins/lambdas.rb +35 -0
- data/examples/plugins/last_nick.rb +24 -0
- data/examples/plugins/msg.rb +22 -0
- data/examples/plugins/multiple_matches.rb +32 -0
- data/examples/plugins/own_events.rb +37 -0
- data/examples/plugins/seen.rb +45 -0
- data/examples/plugins/timer.rb +22 -0
- data/examples/plugins/url_shorten.rb +33 -0
- data/lib/cakewalk/ban.rb +50 -0
- data/lib/cakewalk/bot.rb +479 -0
- data/lib/cakewalk/cached_list.rb +19 -0
- data/lib/cakewalk/callback.rb +20 -0
- data/lib/cakewalk/channel.rb +463 -0
- data/lib/cakewalk/channel_list.rb +29 -0
- data/lib/cakewalk/configuration/bot.rb +48 -0
- data/lib/cakewalk/configuration/dcc.rb +16 -0
- data/lib/cakewalk/configuration/plugins.rb +41 -0
- data/lib/cakewalk/configuration/sasl.rb +19 -0
- data/lib/cakewalk/configuration/ssl.rb +19 -0
- data/lib/cakewalk/configuration/timeouts.rb +14 -0
- data/lib/cakewalk/configuration.rb +73 -0
- data/lib/cakewalk/constants.rb +533 -0
- data/lib/cakewalk/dcc/dccable_object.rb +37 -0
- data/lib/cakewalk/dcc/incoming/send.rb +147 -0
- data/lib/cakewalk/dcc/incoming.rb +1 -0
- data/lib/cakewalk/dcc/outgoing/send.rb +122 -0
- data/lib/cakewalk/dcc/outgoing.rb +1 -0
- data/lib/cakewalk/dcc.rb +12 -0
- data/lib/cakewalk/exceptions.rb +46 -0
- data/lib/cakewalk/formatting.rb +125 -0
- data/lib/cakewalk/handler.rb +118 -0
- data/lib/cakewalk/handler_list.rb +90 -0
- data/lib/cakewalk/helpers.rb +231 -0
- data/lib/cakewalk/irc.rb +913 -0
- data/lib/cakewalk/isupport.rb +98 -0
- data/lib/cakewalk/log_filter.rb +21 -0
- data/lib/cakewalk/logger/formatted_logger.rb +97 -0
- data/lib/cakewalk/logger/zcbot_logger.rb +22 -0
- data/lib/cakewalk/logger.rb +168 -0
- data/lib/cakewalk/logger_list.rb +85 -0
- data/lib/cakewalk/mask.rb +69 -0
- data/lib/cakewalk/message.rb +391 -0
- data/lib/cakewalk/message_queue.rb +107 -0
- data/lib/cakewalk/mode_parser.rb +76 -0
- data/lib/cakewalk/network.rb +89 -0
- data/lib/cakewalk/open_ended_queue.rb +26 -0
- data/lib/cakewalk/pattern.rb +65 -0
- data/lib/cakewalk/plugin.rb +515 -0
- data/lib/cakewalk/plugin_list.rb +38 -0
- data/lib/cakewalk/rubyext/float.rb +3 -0
- data/lib/cakewalk/rubyext/module.rb +26 -0
- data/lib/cakewalk/rubyext/string.rb +33 -0
- data/lib/cakewalk/sasl/dh_blowfish.rb +71 -0
- data/lib/cakewalk/sasl/diffie_hellman.rb +47 -0
- data/lib/cakewalk/sasl/mechanism.rb +6 -0
- data/lib/cakewalk/sasl/plain.rb +26 -0
- data/lib/cakewalk/sasl.rb +34 -0
- data/lib/cakewalk/syncable.rb +83 -0
- data/lib/cakewalk/target.rb +199 -0
- data/lib/cakewalk/timer.rb +145 -0
- data/lib/cakewalk/user.rb +488 -0
- data/lib/cakewalk/user_list.rb +87 -0
- data/lib/cakewalk/utilities/deprecation.rb +16 -0
- data/lib/cakewalk/utilities/encoding.rb +37 -0
- data/lib/cakewalk/utilities/kernel.rb +13 -0
- data/lib/cakewalk/version.rb +4 -0
- data/lib/cakewalk.rb +5 -0
- metadata +140 -0
data/lib/cakewalk/bot.rb
ADDED
@@ -0,0 +1,479 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'socket'
|
3
|
+
require "thread"
|
4
|
+
require "ostruct"
|
5
|
+
require "cakewalk/rubyext/module"
|
6
|
+
require "cakewalk/rubyext/string"
|
7
|
+
require "cakewalk/rubyext/float"
|
8
|
+
|
9
|
+
require "cakewalk/exceptions"
|
10
|
+
|
11
|
+
require "cakewalk/handler"
|
12
|
+
require "cakewalk/helpers"
|
13
|
+
|
14
|
+
require "cakewalk/logger_list"
|
15
|
+
require "cakewalk/logger"
|
16
|
+
|
17
|
+
require "cakewalk/logger/formatted_logger"
|
18
|
+
require "cakewalk/syncable"
|
19
|
+
require "cakewalk/message"
|
20
|
+
require "cakewalk/message_queue"
|
21
|
+
require "cakewalk/irc"
|
22
|
+
require "cakewalk/target"
|
23
|
+
require "cakewalk/channel"
|
24
|
+
require "cakewalk/user"
|
25
|
+
require "cakewalk/constants"
|
26
|
+
require "cakewalk/callback"
|
27
|
+
require "cakewalk/ban"
|
28
|
+
require "cakewalk/mask"
|
29
|
+
require "cakewalk/isupport"
|
30
|
+
require "cakewalk/plugin"
|
31
|
+
require "cakewalk/pattern"
|
32
|
+
require "cakewalk/mode_parser"
|
33
|
+
require "cakewalk/dcc"
|
34
|
+
require "cakewalk/sasl"
|
35
|
+
|
36
|
+
require "cakewalk/handler_list"
|
37
|
+
require "cakewalk/cached_list"
|
38
|
+
require "cakewalk/channel_list"
|
39
|
+
require "cakewalk/user_list"
|
40
|
+
require "cakewalk/plugin_list"
|
41
|
+
|
42
|
+
require "cakewalk/timer"
|
43
|
+
require "cakewalk/formatting"
|
44
|
+
|
45
|
+
require "cakewalk/configuration"
|
46
|
+
require "cakewalk/configuration/bot"
|
47
|
+
require "cakewalk/configuration/plugins"
|
48
|
+
require "cakewalk/configuration/ssl"
|
49
|
+
require "cakewalk/configuration/timeouts"
|
50
|
+
require "cakewalk/configuration/dcc"
|
51
|
+
require "cakewalk/configuration/sasl"
|
52
|
+
|
53
|
+
module Cakewalk
|
54
|
+
# @attr nick
|
55
|
+
# @version 2.0.0
|
56
|
+
class Bot < User
|
57
|
+
include Helpers
|
58
|
+
|
59
|
+
|
60
|
+
# @return [Configuration::Bot]
|
61
|
+
# @version 2.0.0
|
62
|
+
attr_reader :config
|
63
|
+
|
64
|
+
# The underlying IRC connection
|
65
|
+
#
|
66
|
+
# @return [IRC]
|
67
|
+
attr_reader :irc
|
68
|
+
|
69
|
+
# The logger list containing all loggers
|
70
|
+
#
|
71
|
+
# @return [LoggerList]
|
72
|
+
# @since 2.0.0
|
73
|
+
attr_accessor :loggers
|
74
|
+
|
75
|
+
# @return [Array<Channel>] All channels the bot currently is in
|
76
|
+
attr_reader :channels
|
77
|
+
|
78
|
+
# @return [PluginList] The {PluginList} giving access to
|
79
|
+
# (un)loading plugins
|
80
|
+
# @version 2.0.0
|
81
|
+
attr_reader :plugins
|
82
|
+
|
83
|
+
# @return [Boolean] whether the bot is in the process of disconnecting
|
84
|
+
attr_reader :quitting
|
85
|
+
|
86
|
+
# @return [UserList] All {User users} the bot knows about.
|
87
|
+
# @see UserList
|
88
|
+
# @since 1.1.0
|
89
|
+
attr_reader :user_list
|
90
|
+
|
91
|
+
# @return [ChannelList] All {Channel channels} the bot knows about.
|
92
|
+
# @see ChannelList
|
93
|
+
# @since 1.1.0
|
94
|
+
attr_reader :channel_list
|
95
|
+
|
96
|
+
# @return [Boolean]
|
97
|
+
# @api private
|
98
|
+
attr_accessor :last_connection_was_successful
|
99
|
+
|
100
|
+
# @return [Callback]
|
101
|
+
# @api private
|
102
|
+
attr_reader :callback
|
103
|
+
|
104
|
+
# The {HandlerList}, providing access to all registered plugins
|
105
|
+
# and plugin manipulation as well as {HandlerList#dispatch calling handlers}.
|
106
|
+
#
|
107
|
+
# @return [HandlerList]
|
108
|
+
# @see HandlerList
|
109
|
+
# @since 2.0.0
|
110
|
+
attr_reader :handlers
|
111
|
+
|
112
|
+
# The bot's modes.
|
113
|
+
#
|
114
|
+
# @return [Array<String>]
|
115
|
+
# @since 2.0.0
|
116
|
+
attr_reader :modes
|
117
|
+
|
118
|
+
# @group Helper methods
|
119
|
+
|
120
|
+
# Define helper methods in the context of the bot.
|
121
|
+
#
|
122
|
+
# @yield Expects a block containing method definitions
|
123
|
+
# @return [void]
|
124
|
+
def helpers(&b)
|
125
|
+
@callback.instance_eval(&b)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Since Cakewalk uses threads, all handlers can be run
|
129
|
+
# simultaneously, even the same handler multiple times. This also
|
130
|
+
# means, that your code has to be thread-safe. Most of the time,
|
131
|
+
# this is not a problem, but if you are accessing stored data, you
|
132
|
+
# will most likely have to synchronize access to it. Instead of
|
133
|
+
# managing all mutexes yourself, Cakewalk provides a synchronize
|
134
|
+
# method, which takes a name and block.
|
135
|
+
#
|
136
|
+
# Synchronize blocks with the same name share the same mutex,
|
137
|
+
# which means that only one of them will be executed at a time.
|
138
|
+
#
|
139
|
+
# @param [String, Symbol] name a name for the synchronize block.
|
140
|
+
# @return [void]
|
141
|
+
# @yield
|
142
|
+
#
|
143
|
+
# @example
|
144
|
+
# configure do |c|
|
145
|
+
# …
|
146
|
+
# @i = 0
|
147
|
+
# end
|
148
|
+
#
|
149
|
+
# on :channel, /^start counting!/ do
|
150
|
+
# synchronize(:my_counter) do
|
151
|
+
# 10.times do
|
152
|
+
# val = @i
|
153
|
+
# # at this point, another thread might've incremented :i already.
|
154
|
+
# # this thread wouldn't know about it, though.
|
155
|
+
# @i = val + 1
|
156
|
+
# end
|
157
|
+
# end
|
158
|
+
# end
|
159
|
+
def synchronize(name, &block)
|
160
|
+
# Must run the default block +/ fetch in a thread safe way in order to
|
161
|
+
# ensure we always get the same mutex for a given name.
|
162
|
+
semaphore = @semaphores_mutex.synchronize { @semaphores[name] }
|
163
|
+
semaphore.synchronize(&block)
|
164
|
+
end
|
165
|
+
|
166
|
+
# @endgroup
|
167
|
+
|
168
|
+
# @group Events & Plugins
|
169
|
+
|
170
|
+
# Registers a handler.
|
171
|
+
#
|
172
|
+
# @param [String, Symbol, Integer] event the event to match. For a
|
173
|
+
# list of available events, check the {file:docs/events.md Events
|
174
|
+
# documentation}.
|
175
|
+
#
|
176
|
+
# @param [Regexp, Pattern, String] regexp every message of the
|
177
|
+
# right event will be checked against this argument and the event
|
178
|
+
# will only be called if it matches
|
179
|
+
#
|
180
|
+
# @param [Array<Object>] args Arguments that should be passed to
|
181
|
+
# the block, additionally to capture groups of the regexp.
|
182
|
+
#
|
183
|
+
# @yieldparam [Array<String>] args each capture group of the regex will
|
184
|
+
# be one argument to the block.
|
185
|
+
#
|
186
|
+
# @return [Handler] The handlers that have been registered
|
187
|
+
def on(event, regexp = //, *args, &block)
|
188
|
+
event = event.to_s.to_sym
|
189
|
+
|
190
|
+
pattern = case regexp
|
191
|
+
when Pattern
|
192
|
+
regexp
|
193
|
+
when Regexp
|
194
|
+
Pattern.new(nil, regexp, nil)
|
195
|
+
else
|
196
|
+
if event == :ctcp
|
197
|
+
Pattern.generate(:ctcp, regexp)
|
198
|
+
else
|
199
|
+
Pattern.new(/^/, /#{Regexp.escape(regexp.to_s)}/, /$/)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
handler = Handler.new(self, event, pattern, {args: args, execute_in_callback: true}, &block)
|
204
|
+
@handlers.register(handler)
|
205
|
+
|
206
|
+
return handler
|
207
|
+
end
|
208
|
+
|
209
|
+
# @endgroup
|
210
|
+
# @group Bot Control
|
211
|
+
|
212
|
+
# This method is used to set a bot's options. It indeed does
|
213
|
+
# nothing else but yielding {Bot#config}, but it makes for a nice DSL.
|
214
|
+
#
|
215
|
+
# @yieldparam [Struct] config the bot's config
|
216
|
+
# @return [void]
|
217
|
+
def configure
|
218
|
+
yield @config
|
219
|
+
end
|
220
|
+
|
221
|
+
# Disconnects from the server.
|
222
|
+
#
|
223
|
+
# @param [String] message The quit message to send while quitting
|
224
|
+
# @return [void]
|
225
|
+
def quit(message = nil)
|
226
|
+
@quitting = true
|
227
|
+
command = message ? "QUIT :#{message}" : "QUIT"
|
228
|
+
|
229
|
+
@irc.send command
|
230
|
+
end
|
231
|
+
|
232
|
+
# Connects the bot to a server.
|
233
|
+
#
|
234
|
+
# @param [Boolean] plugins Automatically register plugins from
|
235
|
+
# `@config.plugins.plugins`?
|
236
|
+
# @return [void]
|
237
|
+
def start(plugins = true)
|
238
|
+
@reconnects = 0
|
239
|
+
@plugins.register_plugins(@config.plugins.plugins) if plugins
|
240
|
+
|
241
|
+
begin
|
242
|
+
@user_list.each do |user|
|
243
|
+
user.in_whois = false
|
244
|
+
user.unsync_all
|
245
|
+
end # reset state of all users
|
246
|
+
|
247
|
+
@channel_list.each do |channel|
|
248
|
+
channel.unsync_all
|
249
|
+
end # reset state of all channels
|
250
|
+
|
251
|
+
@channels = [] # reset list of channels the bot is in
|
252
|
+
|
253
|
+
@join_handler.unregister if @join_handler
|
254
|
+
@join_timer.stop if @join_timer
|
255
|
+
|
256
|
+
join_lambda = lambda { @config.channels.each { |channel| Channel(channel).join }}
|
257
|
+
|
258
|
+
if @config.delay_joins.is_a?(Symbol)
|
259
|
+
@join_handler = join_handler = on(@config.delay_joins) {
|
260
|
+
join_handler.unregister
|
261
|
+
join_lambda.call
|
262
|
+
}
|
263
|
+
else
|
264
|
+
@join_timer = Timer.new(self, interval: @config.delay_joins, shots: 1) {
|
265
|
+
join_lambda.call
|
266
|
+
}
|
267
|
+
end
|
268
|
+
|
269
|
+
@modes = []
|
270
|
+
|
271
|
+
@loggers.info "Connecting to #{@config.server}:#{@config.port}"
|
272
|
+
@irc = IRC.new(self)
|
273
|
+
@irc.start
|
274
|
+
|
275
|
+
if @config.reconnect && !@quitting
|
276
|
+
# double the delay for each unsuccesful reconnection attempt
|
277
|
+
if @last_connection_was_successful
|
278
|
+
@reconnects = 0
|
279
|
+
@last_connection_was_successful = false
|
280
|
+
else
|
281
|
+
@reconnects += 1
|
282
|
+
end
|
283
|
+
|
284
|
+
# Throttle reconnect attempts
|
285
|
+
wait = 2**@reconnects
|
286
|
+
wait = @config.max_reconnect_delay if wait > @config.max_reconnect_delay
|
287
|
+
@loggers.info "Waiting #{wait} seconds before reconnecting"
|
288
|
+
start_time = Time.now
|
289
|
+
while !@quitting && (Time.now - start_time) < wait
|
290
|
+
sleep 1
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end while @config.reconnect and not @quitting
|
294
|
+
end
|
295
|
+
|
296
|
+
# @endgroup
|
297
|
+
# @group Channel Control
|
298
|
+
|
299
|
+
# Join a channel.
|
300
|
+
#
|
301
|
+
# @param [String, Channel] channel either the name of a channel or a {Channel} object
|
302
|
+
# @param [String] key optionally the key of the channel
|
303
|
+
# @return [Channel] The joined channel
|
304
|
+
# @see Channel#join
|
305
|
+
def join(channel, key = nil)
|
306
|
+
channel = Channel(channel)
|
307
|
+
channel.join(key)
|
308
|
+
|
309
|
+
channel
|
310
|
+
end
|
311
|
+
|
312
|
+
# Part a channel.
|
313
|
+
#
|
314
|
+
# @param [String, Channel] channel either the name of a channel or a {Channel} object
|
315
|
+
# @param [String] reason an optional reason/part message
|
316
|
+
# @return [Channel] The channel that was left
|
317
|
+
# @see Channel#part
|
318
|
+
def part(channel, reason = nil)
|
319
|
+
channel = Channel(channel)
|
320
|
+
channel.part(reason)
|
321
|
+
|
322
|
+
channel
|
323
|
+
end
|
324
|
+
|
325
|
+
# @endgroup
|
326
|
+
|
327
|
+
# @return [Boolean] True if the bot reports ISUPPORT violations as
|
328
|
+
# exceptions.
|
329
|
+
def strict?
|
330
|
+
@config.strictness == :strict
|
331
|
+
end
|
332
|
+
|
333
|
+
# @yield
|
334
|
+
def initialize(&b)
|
335
|
+
@config = Configuration::Bot.new
|
336
|
+
|
337
|
+
@loggers = LoggerList.new
|
338
|
+
@loggers << Logger::FormattedLogger.new($stderr, level: @config.default_logger_level)
|
339
|
+
@handlers = HandlerList.new
|
340
|
+
@semaphores_mutex = Mutex.new
|
341
|
+
@semaphores = Hash.new { |h, k| h[k] = Mutex.new }
|
342
|
+
@callback = Callback.new(self)
|
343
|
+
@channels = []
|
344
|
+
@quitting = false
|
345
|
+
@modes = []
|
346
|
+
|
347
|
+
@user_list = UserList.new(self)
|
348
|
+
@channel_list = ChannelList.new(self)
|
349
|
+
@plugins = PluginList.new(self)
|
350
|
+
|
351
|
+
@join_handler = nil
|
352
|
+
@join_timer = nil
|
353
|
+
|
354
|
+
super(nil, self)
|
355
|
+
instance_eval(&b) if block_given?
|
356
|
+
end
|
357
|
+
|
358
|
+
# @since 2.0.0
|
359
|
+
# @return [self]
|
360
|
+
# @api private
|
361
|
+
def bot
|
362
|
+
# This method is needed for the Helpers interface
|
363
|
+
self
|
364
|
+
end
|
365
|
+
|
366
|
+
# Sets a mode on the bot.
|
367
|
+
#
|
368
|
+
# @param [String] mode
|
369
|
+
# @return [void]
|
370
|
+
# @since 2.0.0
|
371
|
+
# @see Bot#modes
|
372
|
+
# @see Bot#unset_mode
|
373
|
+
def set_mode(mode)
|
374
|
+
@modes << mode unless @modes.include?(mode)
|
375
|
+
@irc.send "MODE #{nick} +#{mode}"
|
376
|
+
end
|
377
|
+
|
378
|
+
# Unsets a mode on the bot.
|
379
|
+
#
|
380
|
+
# @param [String] mode
|
381
|
+
# @return [void]
|
382
|
+
# @since 2.0.0
|
383
|
+
def unset_mode(mode)
|
384
|
+
@modes.delete(mode)
|
385
|
+
@irc.send "MODE #{nick} -#{mode}"
|
386
|
+
end
|
387
|
+
|
388
|
+
# @since 2.0.0
|
389
|
+
def modes=(modes)
|
390
|
+
(@modes - modes).each do |mode|
|
391
|
+
unset_mode(mode)
|
392
|
+
end
|
393
|
+
|
394
|
+
(modes - @modes).each do |mode|
|
395
|
+
set_mode(mode)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
# Used for updating the bot's nick from within the IRC parser.
|
400
|
+
#
|
401
|
+
# @param [String] nick
|
402
|
+
# @api private
|
403
|
+
# @return [String]
|
404
|
+
def set_nick(nick)
|
405
|
+
@name = nick
|
406
|
+
end
|
407
|
+
|
408
|
+
# The bot's nickname.
|
409
|
+
# @overload nick=(new_nick)
|
410
|
+
# @raise [Exceptions::NickTooLong] Raised if the bot is
|
411
|
+
# operating in {#strict? strict mode} and the new nickname is
|
412
|
+
# too long
|
413
|
+
# @return [String]
|
414
|
+
# @overload nick
|
415
|
+
# @return [String]
|
416
|
+
# @return [String]
|
417
|
+
def nick
|
418
|
+
@name
|
419
|
+
end
|
420
|
+
|
421
|
+
def nick=(new_nick)
|
422
|
+
if new_nick.size > @irc.isupport["NICKLEN"] && strict?
|
423
|
+
raise Exceptions::NickTooLong, new_nick
|
424
|
+
end
|
425
|
+
@config.nick = new_nick
|
426
|
+
@irc.send "NICK #{new_nick}"
|
427
|
+
end
|
428
|
+
|
429
|
+
# Gain oper privileges.
|
430
|
+
#
|
431
|
+
# @param [String] password
|
432
|
+
# @param [String] user The username to use. Defaults to the bot's
|
433
|
+
# nickname
|
434
|
+
# @since 2.1.0
|
435
|
+
# @return [void]
|
436
|
+
def oper(password, user = nil)
|
437
|
+
user ||= self.nick
|
438
|
+
@irc.send "OPER #{user} #{password}"
|
439
|
+
end
|
440
|
+
|
441
|
+
# Try to create a free nick, first by cycling through all
|
442
|
+
# available alternatives and then by appending underscores.
|
443
|
+
#
|
444
|
+
# @param [String] base The base nick to start trying from
|
445
|
+
# @api private
|
446
|
+
# @return [String]
|
447
|
+
# @since 2.0.0
|
448
|
+
def generate_next_nick!(base = nil)
|
449
|
+
nicks = @config.nicks || []
|
450
|
+
|
451
|
+
if base
|
452
|
+
# if `base` is not in our list of nicks to try, assume that it's
|
453
|
+
# custom and just append an underscore
|
454
|
+
if !nicks.include?(base)
|
455
|
+
new_nick = base + "_"
|
456
|
+
else
|
457
|
+
# if we have a base, try the next nick or append an
|
458
|
+
# underscore if no more nicks are left
|
459
|
+
new_index = nicks.index(base) + 1
|
460
|
+
if nicks[new_index]
|
461
|
+
new_nick = nicks[new_index]
|
462
|
+
else
|
463
|
+
new_nick = base + "_"
|
464
|
+
end
|
465
|
+
end
|
466
|
+
else
|
467
|
+
# if we have no base, try the first possible nick
|
468
|
+
new_nick = @config.nicks ? @config.nicks.first : @config.nick
|
469
|
+
end
|
470
|
+
|
471
|
+
@config.nick = new_nick
|
472
|
+
end
|
473
|
+
|
474
|
+
# @return [String]
|
475
|
+
def inspect
|
476
|
+
"#<Bot nick=#{@name.inspect}>"
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Cakewalk
|
2
|
+
# @api private
|
3
|
+
# @since 2.0.0
|
4
|
+
# @version 1.1.0
|
5
|
+
# @note In prior versions, this class was called CacheManager
|
6
|
+
class CachedList
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
def initialize(bot)
|
10
|
+
@bot = bot
|
11
|
+
@cache = {}
|
12
|
+
@mutex = Mutex.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def each(&block)
|
16
|
+
@cache.each_value(&block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Cakewalk
|
2
|
+
# Class used for encapsulating handlers to prevent them from
|
3
|
+
# overwriting instance variables in {Bot}
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
class Callback
|
7
|
+
include Helpers
|
8
|
+
|
9
|
+
# @return [Bot]
|
10
|
+
attr_reader :bot
|
11
|
+
def initialize(bot)
|
12
|
+
@bot = bot
|
13
|
+
end
|
14
|
+
|
15
|
+
# (see Bot#synchronize)
|
16
|
+
def synchronize(name, &block)
|
17
|
+
@bot.synchronize(name, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|