grinch 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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