grinch 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/lib/cinch.rb +7 -5
- data/lib/cinch/ban.rb +6 -2
- data/lib/cinch/bot.rb +21 -31
- data/lib/cinch/cached_list.rb +2 -0
- data/lib/cinch/callback.rb +2 -0
- data/lib/cinch/channel.rb +43 -44
- data/lib/cinch/channel_list.rb +2 -0
- data/lib/cinch/configuration.rb +8 -6
- data/lib/cinch/configuration/bot.rb +35 -33
- data/lib/cinch/configuration/dcc.rb +4 -2
- data/lib/cinch/configuration/plugins.rb +8 -6
- data/lib/cinch/configuration/sasl.rb +6 -4
- data/lib/cinch/configuration/ssl.rb +7 -5
- data/lib/cinch/configuration/timeouts.rb +4 -2
- data/lib/cinch/constants.rb +3 -1
- data/lib/cinch/dcc.rb +2 -0
- data/lib/cinch/dcc/dccable_object.rb +6 -8
- data/lib/cinch/dcc/incoming.rb +2 -0
- data/lib/cinch/dcc/incoming/send.rb +10 -8
- data/lib/cinch/dcc/outgoing.rb +2 -0
- data/lib/cinch/dcc/outgoing/send.rb +13 -14
- data/lib/cinch/exceptions.rb +2 -0
- data/lib/cinch/formatting.rb +32 -30
- data/lib/cinch/handler.rb +15 -13
- data/lib/cinch/handler_list.rb +13 -13
- data/lib/cinch/helpers.rb +16 -16
- data/lib/cinch/irc.rb +118 -142
- data/lib/cinch/isupport.rb +22 -20
- data/lib/cinch/log_filter.rb +3 -2
- data/lib/cinch/logger.rb +7 -2
- data/lib/cinch/logger/formatted_logger.rb +17 -13
- data/lib/cinch/logger/zcbot_logger.rb +4 -0
- data/lib/cinch/logger_list.rb +15 -14
- data/lib/cinch/mask.rb +11 -9
- data/lib/cinch/message.rb +6 -6
- data/lib/cinch/message_queue.rb +5 -4
- data/lib/cinch/mode_parser.rb +7 -5
- data/lib/cinch/network.rb +2 -0
- data/lib/cinch/open_ended_queue.rb +5 -5
- data/lib/cinch/pattern.rb +11 -8
- data/lib/cinch/plugin.rb +43 -49
- data/lib/cinch/plugin_list.rb +4 -4
- data/lib/cinch/rubyext/float.rb +3 -3
- data/lib/cinch/rubyext/module.rb +2 -0
- data/lib/cinch/rubyext/string.rb +8 -6
- data/lib/cinch/sasl.rb +2 -0
- data/lib/cinch/sasl/dh_blowfish.rb +5 -3
- data/lib/cinch/sasl/diffie_hellman.rb +6 -3
- data/lib/cinch/sasl/mechanism.rb +2 -0
- data/lib/cinch/sasl/plain.rb +2 -0
- data/lib/cinch/syncable.rb +10 -9
- data/lib/cinch/target.rb +15 -17
- data/lib/cinch/timer.rb +9 -7
- data/lib/cinch/user.rb +52 -48
- data/lib/cinch/user_list.rb +8 -11
- data/lib/cinch/utilities/deprecation.rb +5 -5
- data/lib/cinch/utilities/encoding.rb +9 -9
- data/lib/cinch/utilities/kernel.rb +4 -1
- data/lib/cinch/version.rb +3 -1
- metadata +4 -4
data/lib/cinch/network.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Like Ruby's Queue class, but allowing both pushing and unshifting
|
4
4
|
# objects.
|
@@ -9,17 +9,17 @@ class OpenEndedQueue < Queue
|
|
9
9
|
# @return [void]
|
10
10
|
def unshift(obj)
|
11
11
|
t = nil
|
12
|
-
@mutex.synchronize
|
12
|
+
@mutex.synchronize do
|
13
13
|
@que.unshift obj
|
14
14
|
begin
|
15
15
|
t = @waiting.shift
|
16
|
-
t
|
16
|
+
t&.wakeup
|
17
17
|
rescue ThreadError
|
18
18
|
retry
|
19
19
|
end
|
20
|
-
|
20
|
+
end
|
21
21
|
begin
|
22
|
-
t
|
22
|
+
t&.run
|
23
23
|
rescue ThreadError
|
24
24
|
end
|
25
25
|
end
|
data/lib/cinch/pattern.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Cinch
|
3
4
|
# @api private
|
4
5
|
# @since 1.1.0
|
@@ -9,25 +10,25 @@ module Cinch
|
|
9
10
|
def self.obj_to_r(obj, anchor = nil)
|
10
11
|
case obj
|
11
12
|
when Regexp, NilClass
|
12
|
-
|
13
|
+
obj
|
13
14
|
else
|
14
15
|
escaped = Regexp.escape(obj.to_s)
|
15
16
|
case anchor
|
16
17
|
when :start
|
17
|
-
|
18
|
+
Regexp.new("^" + escaped)
|
18
19
|
when :end
|
19
|
-
|
20
|
+
Regexp.new(escaped + "$")
|
20
21
|
when nil
|
21
|
-
|
22
|
+
Regexp.new(Regexp.escape(obj.to_s))
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
26
27
|
def self.resolve_proc(obj, msg = nil)
|
27
28
|
if obj.is_a?(Proc)
|
28
|
-
|
29
|
+
resolve_proc(obj.call(msg), msg)
|
29
30
|
else
|
30
|
-
|
31
|
+
obj
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
@@ -44,7 +45,9 @@ module Cinch
|
|
44
45
|
attr_reader :suffix
|
45
46
|
attr_reader :pattern
|
46
47
|
def initialize(prefix, pattern, suffix)
|
47
|
-
@prefix
|
48
|
+
@prefix = prefix
|
49
|
+
@pattern = pattern
|
50
|
+
@suffix = suffix
|
48
51
|
end
|
49
52
|
|
50
53
|
def to_r(msg = nil)
|
data/lib/cinch/plugin.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "cinch/helpers"
|
2
4
|
|
3
5
|
module Cinch
|
@@ -42,11 +44,11 @@ module Cinch
|
|
42
44
|
|
43
45
|
# @return [String]
|
44
46
|
def plugin_name=(new_name)
|
45
|
-
if new_name.nil? &&
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
@plugin_name = if new_name.nil? && name
|
48
|
+
name.split("::").last.downcase
|
49
|
+
else
|
50
|
+
new_name
|
51
|
+
end
|
50
52
|
end
|
51
53
|
|
52
54
|
# @return [Array<Matcher>] All matchers
|
@@ -124,7 +126,7 @@ module Cinch
|
|
124
126
|
@listeners = []
|
125
127
|
@timers = []
|
126
128
|
@help = nil
|
127
|
-
@hooks = Hash.new{|h, k| h[k] = []}
|
129
|
+
@hooks = Hash.new { |h, k| h[k] = [] }
|
128
130
|
@prefix = nil
|
129
131
|
@suffix = nil
|
130
132
|
@react_on = :message
|
@@ -161,13 +163,13 @@ module Cinch
|
|
161
163
|
when 1
|
162
164
|
# {:key => value, ...}
|
163
165
|
args.first.each do |key, value|
|
164
|
-
|
166
|
+
send("#{key}=", value)
|
165
167
|
end
|
166
168
|
when 2
|
167
169
|
# key, value
|
168
|
-
|
170
|
+
send("#{args.first}=", args.last)
|
169
171
|
else
|
170
|
-
raise ArgumentError # TODO proper error message
|
172
|
+
raise ArgumentError # TODO: proper error message
|
171
173
|
end
|
172
174
|
end
|
173
175
|
|
@@ -194,18 +196,16 @@ module Cinch
|
|
194
196
|
# @todo Document match/listener grouping
|
195
197
|
def match(pattern, options = {})
|
196
198
|
options = {
|
197
|
-
:
|
198
|
-
:
|
199
|
-
:
|
200
|
-
:
|
201
|
-
:
|
202
|
-
:
|
203
|
-
:
|
204
|
-
:
|
199
|
+
use_prefix: true,
|
200
|
+
use_suffix: true,
|
201
|
+
method: :execute,
|
202
|
+
group: nil,
|
203
|
+
prefix: nil,
|
204
|
+
suffix: nil,
|
205
|
+
react_on: nil,
|
206
|
+
strip_colors: false,
|
205
207
|
}.merge(options)
|
206
|
-
if options[:react_on]
|
207
|
-
options[:react_on] = options[:react_on].to_s.to_sym
|
208
|
-
end
|
208
|
+
options[:react_on] = options[:react_on].to_s.to_sym if options[:react_on]
|
209
209
|
matcher = Matcher.new(pattern, *options.values_at(:use_prefix,
|
210
210
|
:use_suffix,
|
211
211
|
:method,
|
@@ -229,12 +229,10 @@ module Cinch
|
|
229
229
|
# execute
|
230
230
|
# @return [Array<Listener>]
|
231
231
|
def listen_to(*types)
|
232
|
-
options = {:
|
233
|
-
if types.last.is_a?(Hash)
|
234
|
-
options.merge!(types.pop)
|
235
|
-
end
|
232
|
+
options = { method: :listen }
|
233
|
+
options.merge!(types.pop) if types.last.is_a?(Hash)
|
236
234
|
|
237
|
-
listeners = types.map {|type| Listener.new(type.to_s.to_sym, options[:method])}
|
235
|
+
listeners = types.map { |type| Listener.new(type.to_s.to_sym, options[:method]) }
|
238
236
|
@listeners.concat listeners
|
239
237
|
|
240
238
|
listeners
|
@@ -266,7 +264,7 @@ module Cinch
|
|
266
264
|
# @return [Timer]
|
267
265
|
# @since 1.1.0
|
268
266
|
def timer(interval, options = {})
|
269
|
-
options = {:
|
267
|
+
options = { method: :timer, threaded: true }.merge(options)
|
270
268
|
timer = Timer.new(interval, options, false)
|
271
269
|
@timers << timer
|
272
270
|
|
@@ -287,7 +285,7 @@ module Cinch
|
|
287
285
|
# @return [Hook]
|
288
286
|
# @since 1.1.0
|
289
287
|
def hook(type, options = {})
|
290
|
-
options = {:
|
288
|
+
options = { for: %i[match listen_to ctcp], method: :hook, group: nil }.merge(options)
|
291
289
|
hook = Hook.new(type, options[:for], options[:method], options[:group])
|
292
290
|
__hooks(type) << hook
|
293
291
|
|
@@ -297,36 +295,34 @@ module Cinch
|
|
297
295
|
# @return [Hash]
|
298
296
|
# @api private
|
299
297
|
def __hooks(type = nil, events = nil, group = nil)
|
300
|
-
if type.nil?
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
298
|
+
hooks = if type.nil?
|
299
|
+
@hooks
|
300
|
+
else
|
301
|
+
@hooks[type]
|
302
|
+
end
|
305
303
|
|
306
304
|
if events.nil?
|
307
305
|
return hooks
|
308
306
|
else
|
309
307
|
events = [*events]
|
310
|
-
if hooks.is_a?(Hash)
|
311
|
-
|
312
|
-
end
|
313
|
-
hooks = hooks.select { |hook| (events & hook.for).size > 0 }
|
308
|
+
hooks = hooks.map { |_k, v| v } if hooks.is_a?(Hash)
|
309
|
+
hooks = hooks.reject { |hook| (events & hook.for).empty? }
|
314
310
|
end
|
315
311
|
|
316
|
-
|
312
|
+
hooks.select { |hook| hook.group.nil? || hook.group == group }
|
317
313
|
end
|
318
314
|
|
319
315
|
# @return [Boolean] True if processing should continue
|
320
316
|
# @api private
|
321
317
|
def call_hooks(type, event, group, instance, args)
|
322
|
-
stop = __hooks(type, event, group).find
|
318
|
+
stop = __hooks(type, event, group).find do |hook|
|
323
319
|
# stop after the first hook that returns false
|
324
320
|
if hook.method.respond_to?(:call)
|
325
321
|
instance.instance_exec(*args, &hook.method) == false
|
326
322
|
else
|
327
323
|
instance.__send__(hook.method, *args) == false
|
328
324
|
end
|
329
|
-
|
325
|
+
end
|
330
326
|
|
331
327
|
!stop
|
332
328
|
end
|
@@ -336,9 +332,9 @@ module Cinch
|
|
336
332
|
# @since 2.0.0
|
337
333
|
# @api private
|
338
334
|
def check_for_missing_options(bot)
|
339
|
-
@required_options.
|
340
|
-
|
341
|
-
|
335
|
+
@required_options.reject do |option|
|
336
|
+
bot.config.plugins.options[self].key?(option)
|
337
|
+
end
|
342
338
|
end
|
343
339
|
end
|
344
340
|
|
@@ -379,13 +375,13 @@ module Cinch
|
|
379
375
|
private :__register_ctcps
|
380
376
|
|
381
377
|
def __register_timers
|
382
|
-
@timers = self.class.timers.map
|
378
|
+
@timers = self.class.timers.map do |timer_struct|
|
383
379
|
@bot.loggers.debug "[plugin] #{self.class.plugin_name}: Registering timer with interval `#{timer_struct.interval}` for method `#{timer_struct.options[:method]}`"
|
384
380
|
|
385
|
-
block =
|
381
|
+
block = method(timer_struct.options[:method])
|
386
382
|
options = timer_struct.options.merge(interval: timer_struct.interval)
|
387
383
|
Cinch::Timer.new(@bot, options, &block)
|
388
|
-
|
384
|
+
end
|
389
385
|
end
|
390
386
|
private :__register_timers
|
391
387
|
|
@@ -479,9 +475,7 @@ module Cinch
|
|
479
475
|
# @since 2.0.0
|
480
476
|
def unregister
|
481
477
|
@bot.loggers.debug "[plugin] #{self.class.plugin_name}: Unloading plugin"
|
482
|
-
@timers.each
|
483
|
-
timer.stop
|
484
|
-
end
|
478
|
+
@timers.each(&:stop)
|
485
479
|
|
486
480
|
handlers.each do |handler|
|
487
481
|
handler.stop
|
@@ -512,4 +506,4 @@ module Cinch
|
|
512
506
|
end
|
513
507
|
end
|
514
508
|
|
515
|
-
# TODO more details in "message dropped" debug output
|
509
|
+
# TODO: more details in "message dropped" debug output
|
data/lib/cinch/plugin_list.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Cinch
|
2
4
|
# @since 2.0.0
|
3
5
|
class PluginList < Array
|
4
6
|
def initialize(bot)
|
5
|
-
@bot
|
7
|
+
@bot = bot
|
6
8
|
super()
|
7
9
|
end
|
8
10
|
|
@@ -24,9 +26,7 @@ module Cinch
|
|
24
26
|
|
25
27
|
# @since 2.0.0
|
26
28
|
def unregister_plugins(plugins)
|
27
|
-
if plugins == self
|
28
|
-
plugins = self.dup
|
29
|
-
end
|
29
|
+
plugins = dup if plugins == self
|
30
30
|
plugins.each { |plugin| unregister_plugin(plugin) }
|
31
31
|
end
|
32
32
|
|
data/lib/cinch/rubyext/float.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Float::INFINITY = 1.0 / 0.0 unless Float.const_defined?(:INFINITY)
|
data/lib/cinch/rubyext/module.rb
CHANGED
data/lib/cinch/rubyext/string.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Extensions to Ruby's String class.
|
2
4
|
class String
|
3
5
|
# Like `String#downcase`, but respecting different IRC casemaps.
|
@@ -7,12 +9,12 @@ class String
|
|
7
9
|
def irc_downcase(mapping)
|
8
10
|
case mapping
|
9
11
|
when :rfc1459
|
10
|
-
|
12
|
+
tr("A-Z[]\\\\^", "a-z{}|~")
|
11
13
|
when :"strict-rfc1459"
|
12
|
-
|
14
|
+
tr("A-Z[]\\\\", "a-z{}|")
|
13
15
|
else
|
14
16
|
# when :ascii or unknown/nil
|
15
|
-
|
17
|
+
tr("A-Z", "a-z")
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
@@ -23,11 +25,11 @@ class String
|
|
23
25
|
def irc_upcase(mapping)
|
24
26
|
case mapping
|
25
27
|
when :ascii
|
26
|
-
|
28
|
+
tr("a-z", "A-Z")
|
27
29
|
when :rfc1459
|
28
|
-
|
30
|
+
tr("a-z{}|~", "A-Z[]\\\\^")
|
29
31
|
when :"strict-rfc1459"
|
30
|
-
|
32
|
+
tr("a-z{}|", "A-Z[]\\\\")
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
data/lib/cinch/sasl.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "openssl"
|
2
4
|
require "base64"
|
3
5
|
require "cinch/sasl/mechanism"
|
@@ -21,13 +23,13 @@ module Cinch
|
|
21
23
|
payload = payload.dup
|
22
24
|
|
23
25
|
3.times do
|
24
|
-
size = payload.
|
26
|
+
size = payload.unpack1("n")
|
25
27
|
payload.slice!(0, 2)
|
26
|
-
pgy << payload.
|
28
|
+
pgy << payload.unpack1("a#{size}")
|
27
29
|
payload.slice!(0, size)
|
28
30
|
end
|
29
31
|
|
30
|
-
pgy.map {|i| OpenSSL::BN.new(i, 2).to_i}
|
32
|
+
pgy.map { |i| OpenSSL::BN.new(i, 2).to_i }
|
31
33
|
end
|
32
34
|
|
33
35
|
# @param [String] user
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Cinch
|
2
4
|
module SASL
|
3
5
|
class DiffieHellman
|
@@ -24,13 +26,14 @@ module Cinch
|
|
24
26
|
end
|
25
27
|
|
26
28
|
private
|
29
|
+
|
27
30
|
# validate a public key
|
28
31
|
def valid?
|
29
|
-
@e
|
32
|
+
@e&.between?(2, @p - 2) && bits_set(@e) > 1
|
30
33
|
end
|
31
34
|
|
32
35
|
def bits_set(e)
|
33
|
-
("%b" % e).count(
|
36
|
+
("%b" % e).count("1")
|
34
37
|
end
|
35
38
|
|
36
39
|
def mod_exp(b, e, m)
|
@@ -40,7 +43,7 @@ module Cinch
|
|
40
43
|
e = e >> 1
|
41
44
|
b = (b * b) % m
|
42
45
|
end
|
43
|
-
|
46
|
+
result
|
44
47
|
end
|
45
48
|
end
|
46
49
|
end
|
data/lib/cinch/sasl/mechanism.rb
CHANGED
data/lib/cinch/sasl/plain.rb
CHANGED
data/lib/cinch/syncable.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Cinch
|
2
4
|
# Provide blocking access to user/channel information.
|
3
5
|
module Syncable
|
@@ -8,17 +10,18 @@ module Cinch
|
|
8
10
|
def wait_until_synced(attr)
|
9
11
|
attr = attr.to_sym
|
10
12
|
waited = 0
|
11
|
-
|
13
|
+
loop do
|
12
14
|
return if attribute_synced?(attr)
|
15
|
+
|
13
16
|
waited += 1
|
14
17
|
|
15
18
|
if waited % 100 == 0
|
16
|
-
bot.loggers.warn "A synced attribute ('%s' for %s) has not been available for %d seconds, still waiting" % [attr,
|
17
|
-
bot.loggers.warn caller.map {|s| " #{s}"}
|
19
|
+
bot.loggers.warn "A synced attribute ('%s' for %s) has not been available for %d seconds, still waiting" % [attr, inspect, waited / 10]
|
20
|
+
bot.loggers.warn caller.map { |s| " #{s}" }
|
18
21
|
|
19
22
|
if waited / 10 >= 30
|
20
23
|
bot.loggers.warn " Giving up..."
|
21
|
-
raise Exceptions::SyncedAttributeNotAvailable, "'%s' for %s" % [attr,
|
24
|
+
raise Exceptions::SyncedAttributeNotAvailable, "'%s' for %s" % [attr, inspect]
|
22
25
|
end
|
23
26
|
end
|
24
27
|
sleep 0.1
|
@@ -61,16 +64,14 @@ module Cinch
|
|
61
64
|
# @api private
|
62
65
|
def attr(attribute, data = false, unsync = false)
|
63
66
|
unless unsync
|
64
|
-
|
65
|
-
@when_requesting_synced_attribute.call(attribute)
|
66
|
-
end
|
67
|
+
@when_requesting_synced_attribute&.call(attribute)
|
67
68
|
wait_until_synced(attribute)
|
68
69
|
end
|
69
70
|
|
70
71
|
if data
|
71
|
-
|
72
|
+
@data[attribute]
|
72
73
|
else
|
73
|
-
|
74
|
+
instance_variable_get("@#{attribute}")
|
74
75
|
end
|
75
76
|
end
|
76
77
|
|