cinch 1.1.3 → 2.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/LICENSE +1 -0
  2. data/README.md +3 -3
  3. data/docs/bot_options.md +435 -0
  4. data/docs/changes.md +440 -0
  5. data/docs/common_mistakes.md +35 -0
  6. data/docs/common_tasks.md +47 -0
  7. data/docs/encodings.md +67 -0
  8. data/docs/events.md +272 -0
  9. data/docs/logging.md +5 -0
  10. data/docs/migrating.md +267 -0
  11. data/docs/readme.md +18 -0
  12. data/examples/plugins/custom_prefix.rb +1 -1
  13. data/examples/plugins/dice_roll.rb +38 -0
  14. data/examples/plugins/lambdas.rb +1 -1
  15. data/examples/plugins/memo.rb +16 -10
  16. data/examples/plugins/url_shorten.rb +1 -0
  17. data/lib/cinch.rb +5 -60
  18. data/lib/cinch/ban.rb +13 -7
  19. data/lib/cinch/bot.rb +228 -403
  20. data/lib/cinch/{cache_manager.rb → cached_list.rb} +5 -1
  21. data/lib/cinch/callback.rb +3 -0
  22. data/lib/cinch/channel.rb +119 -195
  23. data/lib/cinch/{channel_manager.rb → channel_list.rb} +6 -3
  24. data/lib/cinch/configuration.rb +73 -0
  25. data/lib/cinch/configuration/bot.rb +47 -0
  26. data/lib/cinch/configuration/dcc.rb +16 -0
  27. data/lib/cinch/configuration/plugins.rb +41 -0
  28. data/lib/cinch/configuration/sasl.rb +17 -0
  29. data/lib/cinch/configuration/ssl.rb +19 -0
  30. data/lib/cinch/configuration/storage.rb +37 -0
  31. data/lib/cinch/configuration/timeouts.rb +14 -0
  32. data/lib/cinch/constants.rb +531 -369
  33. data/lib/cinch/dcc.rb +12 -0
  34. data/lib/cinch/dcc/dccable_object.rb +37 -0
  35. data/lib/cinch/dcc/incoming.rb +1 -0
  36. data/lib/cinch/dcc/incoming/send.rb +131 -0
  37. data/lib/cinch/dcc/outgoing.rb +1 -0
  38. data/lib/cinch/dcc/outgoing/send.rb +115 -0
  39. data/lib/cinch/exceptions.rb +8 -1
  40. data/lib/cinch/formatting.rb +106 -0
  41. data/lib/cinch/handler.rb +104 -0
  42. data/lib/cinch/handler_list.rb +86 -0
  43. data/lib/cinch/helpers.rb +167 -10
  44. data/lib/cinch/irc.rb +525 -110
  45. data/lib/cinch/isupport.rb +11 -9
  46. data/lib/cinch/logger.rb +168 -0
  47. data/lib/cinch/logger/formatted_logger.rb +72 -55
  48. data/lib/cinch/logger/zcbot_logger.rb +9 -24
  49. data/lib/cinch/logger_list.rb +62 -0
  50. data/lib/cinch/mask.rb +19 -10
  51. data/lib/cinch/message.rb +94 -28
  52. data/lib/cinch/message_queue.rb +70 -28
  53. data/lib/cinch/mode_parser.rb +6 -1
  54. data/lib/cinch/network.rb +104 -0
  55. data/lib/cinch/{rubyext/queue.rb → open_ended_queue.rb} +8 -1
  56. data/lib/cinch/pattern.rb +24 -4
  57. data/lib/cinch/plugin.rb +352 -177
  58. data/lib/cinch/plugin_list.rb +35 -0
  59. data/lib/cinch/rubyext/float.rb +3 -0
  60. data/lib/cinch/rubyext/module.rb +7 -0
  61. data/lib/cinch/rubyext/string.rb +9 -0
  62. data/lib/cinch/sasl.rb +34 -0
  63. data/lib/cinch/sasl/dh_blowfish.rb +71 -0
  64. data/lib/cinch/sasl/diffie_hellman.rb +47 -0
  65. data/lib/cinch/sasl/mechanism.rb +6 -0
  66. data/lib/cinch/sasl/plain.rb +26 -0
  67. data/lib/cinch/storage.rb +62 -0
  68. data/lib/cinch/storage/null.rb +12 -0
  69. data/lib/cinch/storage/yaml.rb +96 -0
  70. data/lib/cinch/syncable.rb +13 -1
  71. data/lib/cinch/target.rb +144 -0
  72. data/lib/cinch/timer.rb +145 -0
  73. data/lib/cinch/user.rb +169 -225
  74. data/lib/cinch/{user_manager.rb → user_list.rb} +7 -2
  75. data/lib/cinch/utilities/deprecation.rb +12 -0
  76. data/lib/cinch/utilities/encoding.rb +54 -0
  77. data/lib/cinch/utilities/kernel.rb +13 -0
  78. data/lib/cinch/utilities/string.rb +13 -0
  79. data/lib/cinch/version.rb +4 -0
  80. metadata +88 -47
  81. data/lib/cinch/logger/logger.rb +0 -44
  82. data/lib/cinch/logger/null_logger.rb +0 -18
  83. data/lib/cinch/rubyext/infinity.rb +0 -1
@@ -1,5 +1,9 @@
1
1
  module Cinch
2
- class CacheManager
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
3
7
  include Enumerable
4
8
 
5
9
  def initialize(bot)
@@ -1,4 +1,7 @@
1
1
  module Cinch
2
+ # Class used for encapsulating handlers to prevent them from
3
+ # overwriting instance variables in {Bot}
4
+ #
2
5
  # @api private
3
6
  class Callback
4
7
  include Helpers
@@ -1,62 +1,24 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require "set"
3
+ require "cinch/target"
3
4
 
4
5
  module Cinch
5
- class Channel
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
6
14
  include Syncable
7
- @channels = {}
8
-
9
- class << self
10
- # Finds or creates a channel.
11
- #
12
- # @param [String] name name of a channel
13
- # @param [Bot] bot a bot
14
- # @return [Channel]
15
- # @see Bot#Channel
16
- # @deprecated See {Bot#channel_manager} and {ChannelManager#find_ensured} instead
17
- # @note This method does not work properly if running more than one bot
18
- # @note This method will be removed in Cinch 2.0.0
19
- def find_ensured(name, bot)
20
- $stderr.puts "Deprecation warning: Beginning with version 1.1.0, Channel.find_ensured should not be used anymore."
21
- puts caller
22
-
23
- downcased_name = name.irc_downcase(bot.irc.isupport["CASEMAPPING"])
24
- @channels[downcased_name] ||= bot.channel_manager.find_ensured(name)
25
- end
26
-
27
- # Finds a channel.
28
- #
29
- # @param [String] name name of a channel
30
- # @return [Channel, nil]
31
- # @deprecated See {Bot#channel_manager} and {ChannelManager#find} instead
32
- # @note This method does not work properly if running more than one bot
33
- # @note This method will be removed in Cinch 2.0.0
34
- def find(name)
35
- $stderr.puts "Deprecation warning: Beginning with version 1.1.0, Channel.find should not be used anymore."
36
- puts caller
37
-
38
- @channels[name]
39
- end
40
-
41
- # @return [Array<Channel>] Returns all channels
42
- # @deprecated See {Bot#channel_manager} and {CacheManager#each} instead
43
- # @note This method does not work properly if running more than one bot
44
- # @note This method will be removed in Cinch 2.0.0
45
- def all
46
- $stderr.puts "Deprecation warning: Beginning with version 1.1.0, User.all should not be used anymore."
47
- puts caller
48
-
49
- @channels.values
50
- end
51
- end
52
-
53
- # @return [Bot]
54
- attr_reader :bot
55
-
56
- # @return [String] the channel's name
57
- attr_reader :name
15
+ include Helpers
58
16
 
59
- # @return [Array<User>] all users in the channel
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
60
22
  attr_reader :users
61
23
  synced_attr_reader :users
62
24
 
@@ -68,6 +30,16 @@ module Cinch
68
30
  attr_reader :bans
69
31
  synced_attr_reader :bans
70
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
+ #
71
43
  # @return [Hash<String => Object>]
72
44
  attr_reader :modes
73
45
  synced_attr_reader :modes
@@ -76,6 +48,7 @@ module Cinch
76
48
  @name = name
77
49
  @users = Hash.new {|h,k| h[k] = []}
78
50
  @bans = []
51
+ @owners = []
79
52
 
80
53
  @modes = {}
81
54
  # TODO raise if not a channel
@@ -90,13 +63,21 @@ module Cinch
90
63
  unsync(attr)
91
64
  case attr
92
65
  when :users
93
- @bot.raw "NAMES #@name"
66
+ @bot.irc.send "NAMES #@name"
94
67
  when :topic
95
- @bot.raw "TOPIC #@name"
68
+ @bot.irc.send "TOPIC #@name"
96
69
  when :bans
97
- @bot.raw "MODE #@name +b"
70
+ @bot.irc.send "MODE #@name +b"
71
+ when :owners
72
+ if @bot.irc.network.owner_list_mode
73
+ @bot.irc.send "MODE #@name +#{@bot.irc.network.owner_list_mode}"
74
+ else
75
+ # the current IRCd does not support channel owners, so
76
+ # just mark the empty array as synced
77
+ mark_as_synced(:owners)
78
+ end
98
79
  when :modes
99
- @bot.raw "MODE #@name"
80
+ @bot.irc.send "MODE #@name"
100
81
  end
101
82
  end
102
83
  }
@@ -106,37 +87,60 @@ module Cinch
106
87
 
107
88
  # @param [User, String] user An {User}-object or a nickname
108
89
  # @return [Boolean] Check if a user is in the channel
90
+ # @since 1.1.0
91
+ # @version 1.1.2
109
92
  def has_user?(user)
110
- user = @bot.user_manager.find_ensured(user) unless user.is_a?(User)
111
- @users.has_key?(user)
93
+ @users.has_key?(User(user))
112
94
  end
113
95
 
114
-
115
96
  # @return [Boolean] true if `user` is opped in the channel
97
+ # @since 1.1.0
116
98
  def opped?(user)
117
- user = @bot.user_manager.find_ensured(user) unless user.is_a?(User)
118
- @users[user].include? "o"
99
+ @users[User(user)].include? "o"
119
100
  end
120
101
 
121
102
  # @return [Boolean] true if `user` is half-opped in the channel
103
+ # @since 1.1.0
122
104
  def half_opped?(user)
123
- user = @bot.user_manager.find_ensured(user) unless user.is_a?(User)
124
- @users[user].include? "h"
105
+ @users[User(user)].include? "h"
125
106
  end
126
107
 
127
108
  # @return [Boolean] true if `user` is voiced in the channel
109
+ # @since 1.1.0
128
110
  def voiced?(user)
129
- user = @bot.user_manager.find_ensured(user) unless user.is_a?(User)
130
- @users[user].include? "v"
111
+ @users[User(user)].include? "v"
112
+ end
113
+
114
+ # @endgroup
115
+
116
+ # @group User groups
117
+ # @return [Array<User>] All ops in the channel
118
+ # @since 2.0.0
119
+ def ops
120
+ @users.select {|user, modes| modes.include?("o")}.keys
131
121
  end
132
122
 
123
+ # @return [Array<User>] All half-ops in the channel
124
+ # @since 2.0.0
125
+ def half_ops
126
+ @users.select {|user, modes| modes.include?("h")}.keys
127
+ end
128
+
129
+ # @return [Array<User>] All voiced users in the channel
130
+ # @since 2.0.0
131
+ def voiced
132
+ @users.select {|user, modes| modes.include?("v")}.keys
133
+ end
134
+
135
+ # @return [Array<User>] All admins in the channel
136
+ # @since 2.0.0
137
+ def admins
138
+ @users.select {|user, modes| modes.include?("a")}.keys
139
+ end
133
140
  # @endgroup
134
141
 
135
142
  # @return [Number] The maximum number of allowed users in the
136
143
  # channel. 0 if unlimited.
137
- attr_accessor :limit
138
- undef_method "limit"
139
- undef_method "limit="
140
144
  def limit
141
145
  @modes["l"].to_i
142
146
  end
@@ -150,9 +154,6 @@ module Cinch
150
154
  end
151
155
 
152
156
  # @return [Boolean] true if the channel is secret (+s)
153
- attr_accessor :secret
154
- undef_method "secret"
155
- undef_method "secret="
156
157
  def secret
157
158
  @modes["s"]
158
159
  end
@@ -166,17 +167,13 @@ module Cinch
166
167
  end
167
168
  end
168
169
 
169
- # @return [Boolean] true if the channel is moderated (only users
170
- # with +o and +v are able to send messages)
171
- attr_accessor :moderated
172
- undef_method "moderated"
173
- undef_method "moderated="
170
+ # @return [Boolean] true if the channel is moderated
174
171
  def moderated
175
172
  @modes["m"]
176
173
  end
177
174
  alias_method :moderated?, :moderated
178
175
 
179
- def moderated=(val)
176
+ def moderated=(bool)
180
177
  if bool
181
178
  mode "+m"
182
179
  else
@@ -185,9 +182,6 @@ module Cinch
185
182
  end
186
183
 
187
184
  # @return [Boolean] true if the channel is invite only (+i)
188
- attr_accessor :invite_only
189
- undef_method "invite_only"
190
- undef_method "invite_only="
191
185
  def invite_only
192
186
  @modes["i"]
193
187
  end
@@ -202,9 +196,6 @@ module Cinch
202
196
  end
203
197
 
204
198
  # @return [String, nil] The channel's key (aka password)
205
- attr_accessor :key
206
- undef_method "key"
207
- undef_method "key="
208
199
  def key
209
200
  @modes["k"]
210
201
  end
@@ -219,61 +210,82 @@ module Cinch
219
210
 
220
211
  # @api private
221
212
  # @return [void]
222
- def sync_modes(all = true)
213
+ def sync_modes
223
214
  unsync :users
224
215
  unsync :bans
225
216
  unsync :modes
226
- @bot.raw "NAMES #@name" if all
227
- @bot.raw "MODE #@name +b" # bans
228
- @bot.raw "MODE #@name"
217
+ unsync :owners
218
+
219
+ if @bot.irc.isupport["WHOX"]
220
+ @bot.irc.send "WHO #@name %acfhnru"
221
+ else
222
+ @bot.irc.send "WHO #@name"
223
+ end
224
+ @bot.irc.send "MODE #@name +b" # bans
225
+ @bot.irc.send "MODE #@name"
226
+ if @bot.irc.network.owner_list_mode
227
+ @bot.irc.send "MODE #@name +#{@bot.irc.network.owner_list_mode}"
228
+ else
229
+ mark_as_synced :owners
230
+ end
229
231
  end
230
232
 
231
233
  # @group Channel Manipulation
232
234
 
233
235
  # Bans someone from the channel.
234
236
  #
235
- # @param [Ban, Mask, User, String] target the mask to ban
237
+ # @param [Mask, String, #mask] target the mask, or an object having a mask, to ban
236
238
  # @return [Mask] the mask used for banning
239
+ # @see #unban #unban for unbanning users
237
240
  def ban(target)
238
241
  mask = Mask.from(target)
239
242
 
240
- @bot.raw "MODE #@name +b #{mask}"
243
+ @bot.irc.send "MODE #@name +b #{mask}"
241
244
  mask
242
245
  end
243
246
 
244
247
  # Unbans someone from the channel.
245
248
  #
246
- # @param [Ban, Mask, User, String] target the mask to unban
249
+ # @param [Mask, String, #mask] target the mask to unban
247
250
  # @return [Mask] the mask used for unbanning
251
+ # @see #ban #ban for banning users
248
252
  def unban(target)
249
253
  mask = Mask.from(target)
250
254
 
251
- @bot.raw "MODE #@name -b #{mask}"
255
+ @bot.irc.send "MODE #@name -b #{mask}"
252
256
  mask
253
257
  end
254
258
 
259
+ # Ops a user.
260
+ #
255
261
  # @param [String, User] user the user to op
256
262
  # @return [void]
257
263
  def op(user)
258
- @bot.raw "MODE #@name +o #{user}"
264
+ @bot.irc.send "MODE #@name +o #{user}"
259
265
  end
260
266
 
267
+ # Deops a user.
268
+ #
261
269
  # @param [String, User] user the user to deop
262
270
  # @return [void]
263
271
  def deop(user)
264
- @bot.raw "MODE #@name -o #{user}"
272
+ @bot.irc.send "MODE #@name -o #{user}"
265
273
  end
266
274
 
275
+ # Voices a user.
276
+ #
267
277
  # @param [String, User] user the user to voice
268
278
  # @return [void]
269
279
  def voice(user)
270
- @bot.raw "MODE #@name +v #{user}"
280
+ @bot.irc.send "MODE #@name +v #{user}"
271
281
  end
272
282
 
283
+ # Devoices a user.
284
+ #
273
285
  # @param [String, User] user the user to devoice
274
286
  # @return [void]
275
287
  def devoice(user)
276
- @bot.raw "MODE #@name -v #{user}"
288
+ @bot.irc.send "MODE #@name -v #{user}"
277
289
  end
278
290
 
279
291
  # Invites a user to the channel.
@@ -281,19 +293,20 @@ module Cinch
281
293
  # @param [String, User] user the user to invite
282
294
  # @return [void]
283
295
  def invite(user)
284
- @bot.raw("INVITE #{user} #@name")
296
+ @bot.irc.send("INVITE #{user} #@name")
285
297
  end
286
298
 
287
299
  # Sets the topic.
288
300
  #
289
301
  # @param [String] new_topic the new topic
290
- # @raise [Exceptions::TopicTooLong]
302
+ # @raise [Exceptions::TopicTooLong] Raised if the bot is operating
303
+ # in {Bot#strict? strict mode} and when the new topic is too long.
291
304
  def topic=(new_topic)
292
305
  if new_topic.size > @bot.irc.isupport["TOPICLEN"] && @bot.strict?
293
306
  raise Exceptions::TopicTooLong, new_topic
294
307
  end
295
308
 
296
- @bot.raw "TOPIC #@name :#{new_topic}"
309
+ @bot.irc.send "TOPIC #@name :#{new_topic}"
297
310
  end
298
311
 
299
312
  # Kicks a user from the channel.
@@ -307,7 +320,7 @@ module Cinch
307
320
  raise Exceptions::KickReasonTooLong, reason
308
321
  end
309
322
 
310
- @bot.raw("KICK #@name #{user} :#{reason}")
323
+ @bot.irc.send("KICK #@name #{user} :#{reason}")
311
324
  end
312
325
 
313
326
  # Sets or unsets modes. Most of the time you won't need this but
@@ -318,7 +331,7 @@ module Cinch
318
331
  # @example
319
332
  # channel.mode "+n"
320
333
  def mode(s)
321
- @bot.raw "MODE #@name #{s}"
334
+ @bot.irc.send "MODE #@name #{s}"
322
335
  end
323
336
 
324
337
  # Causes the bot to part from the channel.
@@ -326,7 +339,7 @@ module Cinch
326
339
  # @param [String] message the part message.
327
340
  # @return [void]
328
341
  def part(message = nil)
329
- @bot.raw "PART #@name :#{message}"
342
+ @bot.irc.send "PART #@name :#{message}"
330
343
  end
331
344
 
332
345
  # Joins the channel
@@ -338,20 +351,21 @@ module Cinch
338
351
  if key.nil? and self.key != true
339
352
  key = self.key
340
353
  end
341
- @bot.raw "JOIN #{[@name, key].compact.join(" ")}"
354
+ @bot.irc.send "JOIN #{[@name, key].compact.join(" ")}"
342
355
  end
343
356
 
344
357
  # @endgroup
345
358
 
346
359
  # @api private
347
- # @return [void]
360
+ # @return [User] The added user
348
361
  def add_user(user, modes = [])
349
362
  @in_channel = true if user == @bot
350
363
  @users[user] = modes
364
+ user
351
365
  end
352
366
 
353
367
  # @api private
354
- # @return [void]
368
+ # @return [User, nil] The removed user
355
369
  def remove_user(user)
356
370
  @in_channel = false if user == @bot
357
371
  @users.delete(user)
@@ -365,96 +379,6 @@ module Cinch
365
379
  @users.clear
366
380
  end
367
381
 
368
- # @group Sending messages
369
-
370
- # Send a message to the channel.
371
- #
372
- # @param [String] message the message
373
- # @return [void]
374
- # @see #safe_send
375
- def send(message)
376
- @bot.msg(@name, message)
377
- end
378
- alias_method :privmsg, :send
379
- alias_method :msg, :send
380
-
381
- # Send a notice to the channel.
382
- #
383
- # @param [String] message the message
384
- # @return [void]
385
- def notice(message)
386
- @bot.notice(@name, message)
387
- end
388
-
389
- # Like {#safe_send} but for notices.
390
- #
391
- # @param (see #safe_send)
392
- # @return (see #safe_send)
393
- # @see #safe_send
394
- # @todo (see #safe_send)
395
- def safe_notice(message)
396
- @bot.safe_notice(@name, message)
397
- end
398
-
399
- # Send a message to the channel, but remove any non-printable
400
- # characters. The purpose of this method is to send text from
401
- # untrusted sources, like other users or feeds.
402
- #
403
- # Note: this will **break** any mIRC color codes embedded in the
404
- # string.
405
- #
406
- # @param (see #send)
407
- # @return (see #send)
408
- # @see #send
409
- # @todo Handle mIRC color codes more gracefully.
410
- def safe_send(message)
411
- @bot.safe_msg(@name, message)
412
- end
413
- alias_method :safe_privmsg, :safe_send
414
- alias_method :safe_msg, :safe_send
415
-
416
-
417
-
418
- # Send a CTCP to the channel.
419
- #
420
- # @param [String] message the ctcp message
421
- # @return [void]
422
- def ctcp(message)
423
- send "\001#{message}\001"
424
- end
425
-
426
- # Invoke an action (/me) in the channel.
427
- #
428
- # @param [String] message the message
429
- # @return [void]
430
- # @see #safe_action
431
- def action(message)
432
- @bot.action(@name, message)
433
- end
434
-
435
- # Invoke an action (/me) in the channel but remove any
436
- # non-printable characters. The purpose of this method is to send
437
- # text from untrusted sources, like other users or feeds.
438
- #
439
- # Note: this will **break** any mIRC color codes embedded in the
440
- # string.
441
- #
442
- # @param (see #action)
443
- # @return (see #action)
444
- # @see #action
445
- # @todo Handle mIRC color codes more gracefully.
446
- def safe_action(message)
447
- @bot.safe_action(@name, message)
448
- end
449
-
450
- # @endgroup
451
-
452
- # @return [Boolean]
453
- def ==(other)
454
- @name == other.to_s
455
- end
456
- alias_method :eql?, "=="
457
-
458
382
  # @return [Fixnum]
459
383
  def hash
460
384
  @name.hash