grinch 1.0.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.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -0
  3. data/LICENSE +22 -0
  4. data/README.md +180 -0
  5. data/docs/bot_options.md +454 -0
  6. data/docs/changes.md +541 -0
  7. data/docs/common_mistakes.md +60 -0
  8. data/docs/common_tasks.md +57 -0
  9. data/docs/encodings.md +69 -0
  10. data/docs/events.md +273 -0
  11. data/docs/getting_started.md +184 -0
  12. data/docs/logging.md +90 -0
  13. data/docs/migrating.md +267 -0
  14. data/docs/plugins.md +4 -0
  15. data/docs/readme.md +20 -0
  16. data/examples/basic/autovoice.rb +32 -0
  17. data/examples/basic/google.rb +35 -0
  18. data/examples/basic/hello.rb +15 -0
  19. data/examples/basic/join_part.rb +34 -0
  20. data/examples/basic/memo.rb +39 -0
  21. data/examples/basic/msg.rb +16 -0
  22. data/examples/basic/seen.rb +36 -0
  23. data/examples/basic/urban_dict.rb +35 -0
  24. data/examples/basic/url_shorten.rb +35 -0
  25. data/examples/plugins/autovoice.rb +37 -0
  26. data/examples/plugins/custom_prefix.rb +23 -0
  27. data/examples/plugins/dice_roll.rb +38 -0
  28. data/examples/plugins/google.rb +36 -0
  29. data/examples/plugins/hello.rb +22 -0
  30. data/examples/plugins/hooks.rb +36 -0
  31. data/examples/plugins/join_part.rb +42 -0
  32. data/examples/plugins/lambdas.rb +35 -0
  33. data/examples/plugins/last_nick.rb +24 -0
  34. data/examples/plugins/msg.rb +22 -0
  35. data/examples/plugins/multiple_matches.rb +32 -0
  36. data/examples/plugins/own_events.rb +37 -0
  37. data/examples/plugins/seen.rb +45 -0
  38. data/examples/plugins/timer.rb +22 -0
  39. data/examples/plugins/url_shorten.rb +33 -0
  40. data/lib/cinch.rb +5 -0
  41. data/lib/cinch/ban.rb +50 -0
  42. data/lib/cinch/bot.rb +479 -0
  43. data/lib/cinch/cached_list.rb +19 -0
  44. data/lib/cinch/callback.rb +20 -0
  45. data/lib/cinch/channel.rb +463 -0
  46. data/lib/cinch/channel_list.rb +29 -0
  47. data/lib/cinch/configuration.rb +73 -0
  48. data/lib/cinch/configuration/bot.rb +48 -0
  49. data/lib/cinch/configuration/dcc.rb +16 -0
  50. data/lib/cinch/configuration/plugins.rb +41 -0
  51. data/lib/cinch/configuration/sasl.rb +19 -0
  52. data/lib/cinch/configuration/ssl.rb +19 -0
  53. data/lib/cinch/configuration/timeouts.rb +14 -0
  54. data/lib/cinch/constants.rb +533 -0
  55. data/lib/cinch/dcc.rb +12 -0
  56. data/lib/cinch/dcc/dccable_object.rb +37 -0
  57. data/lib/cinch/dcc/incoming.rb +1 -0
  58. data/lib/cinch/dcc/incoming/send.rb +147 -0
  59. data/lib/cinch/dcc/outgoing.rb +1 -0
  60. data/lib/cinch/dcc/outgoing/send.rb +122 -0
  61. data/lib/cinch/exceptions.rb +46 -0
  62. data/lib/cinch/formatting.rb +125 -0
  63. data/lib/cinch/handler.rb +118 -0
  64. data/lib/cinch/handler_list.rb +90 -0
  65. data/lib/cinch/helpers.rb +231 -0
  66. data/lib/cinch/irc.rb +924 -0
  67. data/lib/cinch/isupport.rb +98 -0
  68. data/lib/cinch/log_filter.rb +21 -0
  69. data/lib/cinch/logger.rb +168 -0
  70. data/lib/cinch/logger/formatted_logger.rb +97 -0
  71. data/lib/cinch/logger/zcbot_logger.rb +22 -0
  72. data/lib/cinch/logger_list.rb +85 -0
  73. data/lib/cinch/mask.rb +69 -0
  74. data/lib/cinch/message.rb +392 -0
  75. data/lib/cinch/message_queue.rb +107 -0
  76. data/lib/cinch/mode_parser.rb +76 -0
  77. data/lib/cinch/network.rb +104 -0
  78. data/lib/cinch/open_ended_queue.rb +26 -0
  79. data/lib/cinch/pattern.rb +65 -0
  80. data/lib/cinch/plugin.rb +515 -0
  81. data/lib/cinch/plugin_list.rb +38 -0
  82. data/lib/cinch/rubyext/float.rb +3 -0
  83. data/lib/cinch/rubyext/module.rb +26 -0
  84. data/lib/cinch/rubyext/string.rb +33 -0
  85. data/lib/cinch/sasl.rb +34 -0
  86. data/lib/cinch/sasl/dh_blowfish.rb +71 -0
  87. data/lib/cinch/sasl/diffie_hellman.rb +47 -0
  88. data/lib/cinch/sasl/mechanism.rb +6 -0
  89. data/lib/cinch/sasl/plain.rb +26 -0
  90. data/lib/cinch/syncable.rb +83 -0
  91. data/lib/cinch/target.rb +199 -0
  92. data/lib/cinch/timer.rb +145 -0
  93. data/lib/cinch/user.rb +488 -0
  94. data/lib/cinch/user_list.rb +87 -0
  95. data/lib/cinch/utilities/deprecation.rb +16 -0
  96. data/lib/cinch/utilities/encoding.rb +37 -0
  97. data/lib/cinch/utilities/kernel.rb +13 -0
  98. data/lib/cinch/version.rb +4 -0
  99. metadata +140 -0
@@ -0,0 +1,19 @@
1
+ module Cinch
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 Cinch
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
@@ -0,0 +1,463 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "set"
3
+ require "cinch/target"
4
+
5
+ module Cinch
6
+ # @attr limit
7
+ # @attr secret
8
+ # @attr moderated
9
+ # @attr invite_only
10
+ # @attr key
11
+ #
12
+ # @version 2.0.0
13
+ class Channel < Target
14
+ include Syncable
15
+ include Helpers
16
+
17
+ # Users are represented by a Hash, mapping individual users to an
18
+ # array of modes (e.g. "o" for opped).
19
+ #
20
+ # @return [Hash{User => Array<String}>] all users in the channel
21
+ # @version 1.1.0
22
+ attr_reader :users
23
+ synced_attr_reader :users
24
+
25
+ # @return [String] the channel's topic
26
+ attr_accessor :topic
27
+ synced_attr_reader :topic
28
+
29
+ # @return [Array<Ban>] all active bans
30
+ attr_reader :bans
31
+ synced_attr_reader :bans
32
+
33
+ # @return [Array<User>] all channel owners
34
+ # @note Only some networks implement this
35
+ attr_reader :owners
36
+ synced_attr_reader :owners
37
+
38
+ # This attribute describes all modes set in the channel. They're
39
+ # represented as a Hash, mapping the mode (e.g. "i", "k", …) to
40
+ # either a value in the case of modes that take an option (e.g.
41
+ # "k" for the channel key) or true.
42
+ #
43
+ # @return [Hash{String => Object}]
44
+ attr_reader :modes
45
+ synced_attr_reader :modes
46
+
47
+ # @note Generally, you shouldn't initialize new instances of this
48
+ # class. Use {ChannelList#find_ensured} instead.
49
+ def initialize(name, bot)
50
+ @bot = bot
51
+ @name = name
52
+ @users = Hash.new {|h, k| h[k] = []}
53
+ @bans = []
54
+ @owners = []
55
+
56
+ @modes = {}
57
+ # TODO raise if not a channel
58
+
59
+ @topic = nil
60
+
61
+ @in_channel = false
62
+
63
+ @synced_attributes = Set.new
64
+ @when_requesting_synced_attribute = lambda {|attr|
65
+ if @in_channel && attr == :topic && !attribute_synced?(:topic)
66
+ # Even if we are in the channel, if there's no topic set,
67
+ # the attribute won't be synchronised yet. Explicitly
68
+ # request the topic.
69
+ @bot.irc.send "TOPIC #@name"
70
+ next
71
+ end
72
+
73
+ unless @in_channel
74
+ unsync(attr)
75
+ case attr
76
+ when :users
77
+ @bot.irc.send "NAMES #@name"
78
+ when :topic
79
+ @bot.irc.send "TOPIC #@name"
80
+ when :bans
81
+ @bot.irc.send "MODE #@name +b"
82
+ when :owners
83
+ if @bot.irc.network.owner_list_mode
84
+ @bot.irc.send "MODE #@name +#{@bot.irc.network.owner_list_mode}"
85
+ else
86
+ # the current IRCd does not support channel owners, so
87
+ # just mark the empty array as synced
88
+ mark_as_synced(:owners)
89
+ end
90
+ when :modes
91
+ @bot.irc.send "MODE #@name"
92
+ end
93
+ end
94
+ }
95
+ end
96
+
97
+ # @group Checks
98
+
99
+ # @param [User, String] user An {User}-object or a nickname
100
+ # @return [Boolean] Check if a user is in the channel
101
+ # @since 1.1.0
102
+ # @version 1.1.2
103
+ def has_user?(user)
104
+ @users.has_key?(User(user))
105
+ end
106
+
107
+ # @return [Boolean] true if `user` is opped in the channel
108
+ # @since 1.1.0
109
+ def opped?(user)
110
+ @users[User(user)].include? "o"
111
+ end
112
+
113
+ # @return [Boolean] true if `user` is half-opped in the channel
114
+ # @since 1.1.0
115
+ def half_opped?(user)
116
+ @users[User(user)].include? "h"
117
+ end
118
+
119
+ # @return [Boolean] true if `user` is voiced in the channel
120
+ # @since 1.1.0
121
+ def voiced?(user)
122
+ @users[User(user)].include? "v"
123
+ end
124
+
125
+ # @endgroup
126
+
127
+ # @group User groups
128
+ # @return [Array<User>] All ops in the channel
129
+ # @since 2.0.0
130
+ def ops
131
+ @users.select {|user, modes| modes.include?("o")}.keys
132
+ end
133
+
134
+ # @return [Array<User>] All half-ops in the channel
135
+ # @since 2.0.0
136
+ def half_ops
137
+ @users.select {|user, modes| modes.include?("h")}.keys
138
+ end
139
+
140
+ # @return [Array<User>] All voiced users in the channel
141
+ # @since 2.0.0
142
+ def voiced
143
+ @users.select {|user, modes| modes.include?("v")}.keys
144
+ end
145
+
146
+ # @return [Array<User>] All admins in the channel
147
+ # @since 2.0.0
148
+ def admins
149
+ @users.select {|user, modes| modes.include?("a")}.keys
150
+ end
151
+ # @endgroup
152
+
153
+ # @return [Integer] The maximum number of allowed users in the
154
+ # channel. 0 if unlimited.
155
+ def limit
156
+ @modes["l"].to_i
157
+ end
158
+
159
+ def limit=(val)
160
+ if val == -1 or val.nil?
161
+ mode "-l"
162
+ else
163
+ mode "+l #{val}"
164
+ end
165
+ end
166
+
167
+ # @return [Boolean] true if the channel is secret (+s)
168
+ def secret
169
+ @modes["s"]
170
+ end
171
+ alias_method :secret?, :secret
172
+
173
+ def secret=(bool)
174
+ if bool
175
+ mode "+s"
176
+ else
177
+ mode "-s"
178
+ end
179
+ end
180
+
181
+ # @return [Boolean] true if the channel is moderated
182
+ def moderated
183
+ @modes["m"]
184
+ end
185
+ alias_method :moderated?, :moderated
186
+
187
+ def moderated=(bool)
188
+ if bool
189
+ mode "+m"
190
+ else
191
+ mode "-m"
192
+ end
193
+ end
194
+
195
+ # @return [Boolean] true if the channel is invite only (+i)
196
+ def invite_only
197
+ @modes["i"]
198
+ end
199
+ alias_method :invite_only?, :invite_only
200
+
201
+ def invite_only=(bool)
202
+ if bool
203
+ mode "+i"
204
+ else
205
+ mode "-i"
206
+ end
207
+ end
208
+
209
+ # @return [String, nil] The channel's key (aka password)
210
+ def key
211
+ @modes["k"]
212
+ end
213
+
214
+ def key=(new_key)
215
+ if new_key.nil?
216
+ mode "-k #{key}"
217
+ else
218
+ mode "+k #{new_key}"
219
+ end
220
+ end
221
+
222
+ # @api private
223
+ # @return [void]
224
+ def sync_modes
225
+ unsync :users
226
+ unsync :bans
227
+ unsync :modes
228
+ unsync :owners
229
+
230
+ if @bot.irc.isupport["WHOX"]
231
+ @bot.irc.send "WHO #@name %acfhnru"
232
+ else
233
+ @bot.irc.send "WHO #@name"
234
+ end
235
+ @bot.irc.send "MODE #@name +b" # bans
236
+ @bot.irc.send "MODE #@name"
237
+ if @bot.irc.network.owner_list_mode
238
+ @bot.irc.send "MODE #@name +#{@bot.irc.network.owner_list_mode}"
239
+ else
240
+ mark_as_synced :owners
241
+ end
242
+ end
243
+
244
+ # @group Channel Manipulation
245
+
246
+ # Bans someone from the channel.
247
+ #
248
+ # @param [Mask, String, #mask] target the mask, or an object having a mask, to ban
249
+ # @return [Mask] the mask used for banning
250
+ # @see #unban #unban for unbanning users
251
+ def ban(target)
252
+ mask = Mask.from(target)
253
+
254
+ @bot.irc.send "MODE #@name +b #{mask}"
255
+ mask
256
+ end
257
+
258
+ # Unbans someone from the channel.
259
+ #
260
+ # @param [Mask, String, #mask] target the mask to unban
261
+ # @return [Mask] the mask used for unbanning
262
+ # @see #ban #ban for banning users
263
+ def unban(target)
264
+ mask = Mask.from(target)
265
+
266
+ @bot.irc.send "MODE #@name -b #{mask}"
267
+ mask
268
+ end
269
+
270
+ # Ops a user.
271
+ #
272
+ # @param [String, User] user the user to op
273
+ # @return [void]
274
+ def op(user)
275
+ @bot.irc.send "MODE #@name +o #{user}"
276
+ end
277
+
278
+ # Deops a user.
279
+ #
280
+ # @param [String, User] user the user to deop
281
+ # @return [void]
282
+ def deop(user)
283
+ @bot.irc.send "MODE #@name -o #{user}"
284
+ end
285
+
286
+ # Voices a user.
287
+ #
288
+ # @param [String, User] user the user to voice
289
+ # @return [void]
290
+ def voice(user)
291
+ @bot.irc.send "MODE #@name +v #{user}"
292
+ end
293
+
294
+ # Devoices a user.
295
+ #
296
+ # @param [String, User] user the user to devoice
297
+ # @return [void]
298
+ def devoice(user)
299
+ @bot.irc.send "MODE #@name -v #{user}"
300
+ end
301
+
302
+ # Invites a user to the channel.
303
+ #
304
+ # @param [String, User] user the user to invite
305
+ # @return [void]
306
+ def invite(user)
307
+ @bot.irc.send("INVITE #{user} #@name")
308
+ end
309
+
310
+ undef_method(:topic=)
311
+ # Sets the topic.
312
+ #
313
+ # @param [String] new_topic the new topic
314
+ # @raise [Exceptions::TopicTooLong] Raised if the bot is operating
315
+ # in {Bot#strict? strict mode} and when the new topic is too long.
316
+ def topic=(new_topic)
317
+ if new_topic.size > @bot.irc.isupport["TOPICLEN"] && @bot.strict?
318
+ raise Exceptions::TopicTooLong, new_topic
319
+ end
320
+
321
+ @bot.irc.send "TOPIC #@name :#{new_topic}"
322
+ end
323
+
324
+ # Kicks a user from the channel.
325
+ #
326
+ # @param [String, User] user the user to kick
327
+ # @param [String] reason a reason for the kick
328
+ # @raise [Exceptions::KickReasonTooLong]
329
+ # @return [void]
330
+ def kick(user, reason = nil)
331
+ if reason.to_s.size > @bot.irc.isupport["KICKLEN"] && @bot.strict?
332
+ raise Exceptions::KickReasonTooLong, reason
333
+ end
334
+
335
+ @bot.irc.send("KICK #@name #{user} :#{reason}")
336
+ end
337
+
338
+ # Removes a user from the channel.
339
+ #
340
+ # This uses the REMOVE command, which is a non-standardized
341
+ # extension. Unlike a kick, it makes a user part. This prevents
342
+ # auto-rejoin scripts from firing and might also be perceived as
343
+ # less aggressive by some. Not all IRC networks support this
344
+ # command.
345
+ #
346
+ # @param [User] user the user to remove
347
+ # @param [String] reason a reason for the removal
348
+ # @return [void]
349
+ def remove(user, reason = nil)
350
+ @bot.irc.send("REMOVE #@name #{user} :#{reason}")
351
+ end
352
+
353
+ # Sets or unsets modes. Most of the time you won't need this but
354
+ # use setter methods like {Channel#invite_only=}.
355
+ #
356
+ # @param [String] s a mode string
357
+ # @return [void]
358
+ # @example
359
+ # channel.mode "+n"
360
+ def mode(s)
361
+ @bot.irc.send "MODE #@name #{s}"
362
+ end
363
+
364
+ # Causes the bot to part from the channel.
365
+ #
366
+ # @param [String] message the part message.
367
+ # @return [void]
368
+ def part(message = nil)
369
+ @bot.irc.send "PART #@name :#{message}"
370
+ end
371
+
372
+ # Joins the channel
373
+ #
374
+ # @param [String] key the channel key, if any. If none is
375
+ # specified but @key is set, @key will be used
376
+ # @return [void]
377
+ def join(key = nil)
378
+ if key.nil? and self.key != true
379
+ key = self.key
380
+ end
381
+ @bot.irc.send "JOIN #{[@name, key].compact.join(" ")}"
382
+ end
383
+
384
+ # @endgroup
385
+
386
+ # @api private
387
+ # @return [User] The added user
388
+ def add_user(user, modes = [])
389
+ @in_channel = true if user == @bot
390
+ @users[user] = modes
391
+ user
392
+ end
393
+
394
+ # @api private
395
+ # @return [User, nil] The removed user
396
+ def remove_user(user)
397
+ @in_channel = false if user == @bot
398
+ @users.delete(user)
399
+ end
400
+
401
+ # Removes all users
402
+ #
403
+ # @api private
404
+ # @return [void]
405
+ def clear_users
406
+ @users.clear
407
+ end
408
+
409
+ # @note The aliases `msg` and `privmsg` are deprecated and will be
410
+ # removed in a future version.
411
+ def send(text, notice = false)
412
+ # TODO deprecate 'notice' argument
413
+ text = text.to_s
414
+ if @modes["c"]
415
+ # Remove all formatting and colors if the channel doesn't
416
+ # allow colors.
417
+ text = Cinch::Formatting.unformat(text)
418
+ end
419
+ super(text, notice)
420
+ end
421
+ alias_method :msg, :send # deprecated
422
+ alias_method :privmsg, :send # deprecated
423
+ undef_method(:msg) # yardoc hack
424
+ undef_method(:privmsg) # yardoc hack
425
+
426
+ # @deprecated
427
+ def msg(*args)
428
+ Cinch::Utilities::Deprecation.print_deprecation("2.2.0", "Channel#msg", "Channel#send")
429
+ send(*args)
430
+ end
431
+
432
+ # @deprecated
433
+ def privmsg(*args)
434
+ Cinch::Utilities::Deprecation.print_deprecation("2.2.0", "Channel#privmsg", "Channel#send")
435
+ send(*args)
436
+ end
437
+
438
+ # @return [Fixnum]
439
+ def hash
440
+ @name.hash
441
+ end
442
+
443
+ # @return [String]
444
+ # @note The alias `to_str` is deprecated and will be removed in a
445
+ # future version. Channel objects should not be treated like
446
+ # strings.
447
+ def to_s
448
+ @name
449
+ end
450
+ alias_method :to_str, :to_s # deprecated
451
+ undef_method(:to_str) # yardoc hack
452
+
453
+ def to_str
454
+ Cinch::Utilities::Deprecation.print_deprecation("2.2.0", "Channel#to_str", "Channel#to_s")
455
+ to_s
456
+ end
457
+
458
+ # @return [String]
459
+ def inspect
460
+ "#<Channel name=#{@name.inspect}>"
461
+ end
462
+ end
463
+ end