cinch 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +25 -44
- data/examples/basic/autovoice.rb +1 -1
- data/examples/basic/join_part.rb +0 -4
- data/examples/plugins/autovoice.rb +2 -5
- data/examples/plugins/google.rb +1 -2
- data/examples/plugins/hooks.rb +36 -0
- data/examples/plugins/lambdas.rb +35 -0
- data/examples/plugins/last_nick.rb +24 -0
- data/examples/plugins/multiple_matches.rb +1 -10
- data/examples/plugins/own_events.rb +37 -0
- data/examples/plugins/timer.rb +22 -0
- data/examples/plugins/url_shorten.rb +1 -1
- data/lib/cinch.rb +50 -1
- data/lib/cinch/ban.rb +5 -2
- data/lib/cinch/bot.rb +360 -193
- data/lib/cinch/cache_manager.rb +15 -0
- data/lib/cinch/callback.rb +6 -0
- data/lib/cinch/channel.rb +150 -96
- data/lib/cinch/channel_manager.rb +26 -0
- data/lib/cinch/constants.rb +6 -4
- data/lib/cinch/exceptions.rb +9 -0
- data/lib/cinch/irc.rb +197 -82
- data/lib/cinch/logger/formatted_logger.rb +8 -8
- data/lib/cinch/logger/zcbot_logger.rb +37 -0
- data/lib/cinch/mask.rb +17 -3
- data/lib/cinch/message.rb +14 -7
- data/lib/cinch/message_queue.rb +8 -4
- data/lib/cinch/mode_parser.rb +56 -0
- data/lib/cinch/pattern.rb +45 -0
- data/lib/cinch/plugin.rb +129 -34
- data/lib/cinch/rubyext/string.rb +4 -4
- data/lib/cinch/syncable.rb +8 -0
- data/lib/cinch/user.rb +68 -13
- data/lib/cinch/user_manager.rb +60 -0
- metadata +17 -35
- data/Rakefile +0 -66
- data/lib/cinch/PLANNED +0 -4
- data/spec/bot_spec.rb +0 -5
- data/spec/channel_spec.rb +0 -5
- data/spec/cinch_spec.rb +0 -5
- data/spec/irc_spec.rb +0 -5
- data/spec/message_spec.rb +0 -5
- data/spec/plugin_spec.rb +0 -5
- data/spec/spec.opts +0 -2
- data/spec/spec_helper.rb +0 -8
- data/spec/user_spec.rb +0 -5
data/lib/cinch/callback.rb
CHANGED
data/lib/cinch/channel.rb
CHANGED
@@ -13,22 +13,39 @@ module Cinch
|
|
13
13
|
# @param [Bot] bot a bot
|
14
14
|
# @return [Channel]
|
15
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
|
16
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
|
+
|
17
23
|
downcased_name = name.irc_downcase(bot.irc.isupport["CASEMAPPING"])
|
18
|
-
@channels[downcased_name] ||=
|
19
|
-
@channels[downcased_name]
|
24
|
+
@channels[downcased_name] ||= bot.channel_manager.find_ensured(name)
|
20
25
|
end
|
21
26
|
|
22
27
|
# Finds a channel.
|
23
28
|
#
|
24
29
|
# @param [String] name name of a channel
|
25
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
|
26
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
|
+
|
27
38
|
@channels[name]
|
28
39
|
end
|
29
40
|
|
30
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
|
31
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
|
+
|
32
49
|
@channels.values
|
33
50
|
end
|
34
51
|
end
|
@@ -36,28 +53,28 @@ module Cinch
|
|
36
53
|
# @return [Bot]
|
37
54
|
attr_reader :bot
|
38
55
|
|
39
|
-
# @return [String]
|
56
|
+
# @return [String] the channel's name
|
40
57
|
attr_reader :name
|
41
58
|
|
42
|
-
# @return [Array<User>]
|
59
|
+
# @return [Array<User>] all users in the channel
|
43
60
|
attr_reader :users
|
44
61
|
synced_attr_reader :users
|
45
62
|
|
46
|
-
# @return [String]
|
63
|
+
# @return [String] the channel's topic
|
47
64
|
attr_accessor :topic
|
48
65
|
synced_attr_reader :topic
|
49
66
|
|
50
|
-
# @return [Array<Ban>]
|
67
|
+
# @return [Array<Ban>] all active bans
|
51
68
|
attr_reader :bans
|
52
69
|
synced_attr_reader :bans
|
53
70
|
|
54
|
-
# @return [
|
71
|
+
# @return [Hash<String => Object>]
|
55
72
|
attr_reader :modes
|
56
73
|
synced_attr_reader :modes
|
57
74
|
def initialize(name, bot)
|
58
75
|
@bot = bot
|
59
76
|
@name = name
|
60
|
-
@users = {}
|
77
|
+
@users = Hash.new {|h,k| h[k] = []}
|
61
78
|
@bans = []
|
62
79
|
|
63
80
|
@modes = {}
|
@@ -85,8 +102,40 @@ module Cinch
|
|
85
102
|
}
|
86
103
|
end
|
87
104
|
|
105
|
+
# @group Checks
|
106
|
+
|
107
|
+
# @param [User, String] user An {User}-object or a nickname
|
108
|
+
# @return [Boolean] Check if a user is in the channel
|
109
|
+
def has_user?(user)
|
110
|
+
@users.has_key?(User(user))
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
# @return [Boolean] true if `user` is opped in the channel
|
115
|
+
def opped?(user)
|
116
|
+
user = @bot.user_manager.find_ensured(user) unless user.is_a?(User)
|
117
|
+
@users[user].include? "o"
|
118
|
+
end
|
119
|
+
|
120
|
+
# @return [Boolean] true if `user` is half-opped in the channel
|
121
|
+
def half_opped?(user)
|
122
|
+
user = @bot.user_manager.find_ensured(user) unless user.is_a?(User)
|
123
|
+
@users[user].include? "h"
|
124
|
+
end
|
125
|
+
|
126
|
+
# @return [Boolean] true if `user` is voiced in the channel
|
127
|
+
def voiced?(user)
|
128
|
+
user = @bot.user_manager.find_ensured(user) unless user.is_a?(User)
|
129
|
+
@users[user].include? "v"
|
130
|
+
end
|
131
|
+
|
132
|
+
# @endgroup
|
133
|
+
|
134
|
+
# @return [Number] The maximum number of allowed users in the
|
135
|
+
# channel. 0 if unlimited.
|
88
136
|
attr_accessor :limit
|
89
|
-
|
137
|
+
undef_method "limit"
|
138
|
+
undef_method "limit="
|
90
139
|
def limit
|
91
140
|
@modes["l"].to_i
|
92
141
|
end
|
@@ -151,7 +200,7 @@ module Cinch
|
|
151
200
|
end
|
152
201
|
end
|
153
202
|
|
154
|
-
# @return [String, nil]
|
203
|
+
# @return [String, nil] The channel's key (aka password)
|
155
204
|
attr_accessor :key
|
156
205
|
undef_method "key"
|
157
206
|
undef_method "key="
|
@@ -167,17 +216,6 @@ module Cinch
|
|
167
216
|
end
|
168
217
|
end
|
169
218
|
|
170
|
-
# Sets or unsets modes. Most of the time you won't need this but
|
171
|
-
# use setter methods like {Channel#invite_only=}.
|
172
|
-
#
|
173
|
-
# @param [String] s a mode string
|
174
|
-
# @return [void]
|
175
|
-
# @example
|
176
|
-
# channel.mode "+n"
|
177
|
-
def mode(s)
|
178
|
-
@bot.raw "MODE #@name #{s}"
|
179
|
-
end
|
180
|
-
|
181
219
|
# @api private
|
182
220
|
# @return [void]
|
183
221
|
def sync_modes(all = true)
|
@@ -189,17 +227,7 @@ module Cinch
|
|
189
227
|
@bot.raw "MODE #@name"
|
190
228
|
end
|
191
229
|
|
192
|
-
# @
|
193
|
-
def opped?(user)
|
194
|
-
user = User.find_ensured(user, @bot) unless user.is_a?(User)
|
195
|
-
@users[user] == "@"
|
196
|
-
end
|
197
|
-
|
198
|
-
# @return [Boolean] true if `user` is voiced in the channel
|
199
|
-
def voiced?(user)
|
200
|
-
user = User.find_ensured(user, @bot) unless user.is_a?(User)
|
201
|
-
@users[user] == "+"
|
202
|
-
end
|
230
|
+
# @group Channel Manipulation
|
203
231
|
|
204
232
|
# Bans someone from the channel.
|
205
233
|
#
|
@@ -247,11 +275,78 @@ module Cinch
|
|
247
275
|
@bot.raw "MODE #@name -v #{user}"
|
248
276
|
end
|
249
277
|
|
278
|
+
# Invites a user to the channel.
|
279
|
+
#
|
280
|
+
# @param [String, User] user the user to invite
|
281
|
+
# @return [void]
|
282
|
+
def invite(user)
|
283
|
+
@bot.raw("INVITE #{user} #@name")
|
284
|
+
end
|
285
|
+
|
286
|
+
# Sets the topic.
|
287
|
+
#
|
288
|
+
# @param [String] new_topic the new topic
|
289
|
+
# @raise [Exceptions::TopicTooLong]
|
290
|
+
def topic=(new_topic)
|
291
|
+
if new_topic.size > @bot.irc.isupport["TOPICLEN"] && @bot.strict?
|
292
|
+
raise Exceptions::TopicTooLong, new_topic
|
293
|
+
end
|
294
|
+
|
295
|
+
@bot.raw "TOPIC #@name :#{new_topic}"
|
296
|
+
end
|
297
|
+
|
298
|
+
# Kicks a user from the channel.
|
299
|
+
#
|
300
|
+
# @param [String, User] user the user to kick
|
301
|
+
# @param [String] a reason for the kick
|
302
|
+
# @raise [Exceptions::KickReasonTooLong]
|
303
|
+
# @return [void]
|
304
|
+
def kick(user, reason = nil)
|
305
|
+
if reason.to_s.size > @bot.irc.isupport["KICKLEN"] && @bot.strict?
|
306
|
+
raise Exceptions::KickReasonTooLong, reason
|
307
|
+
end
|
308
|
+
|
309
|
+
@bot.raw("KICK #@name #{user} :#{reason}")
|
310
|
+
end
|
311
|
+
|
312
|
+
# Sets or unsets modes. Most of the time you won't need this but
|
313
|
+
# use setter methods like {Channel#invite_only=}.
|
314
|
+
#
|
315
|
+
# @param [String] s a mode string
|
316
|
+
# @return [void]
|
317
|
+
# @example
|
318
|
+
# channel.mode "+n"
|
319
|
+
def mode(s)
|
320
|
+
@bot.raw "MODE #@name #{s}"
|
321
|
+
end
|
322
|
+
|
323
|
+
# Causes the bot to part from the channel.
|
324
|
+
#
|
325
|
+
# @param [String] message the part message.
|
326
|
+
# @return [void]
|
327
|
+
def part(message = nil)
|
328
|
+
@bot.raw "PART #@name :#{message}"
|
329
|
+
end
|
330
|
+
|
331
|
+
# Joins the channel
|
332
|
+
#
|
333
|
+
# @param [String] key the channel key, if any. If none is
|
334
|
+
# specified but @key is set, @key will be used
|
335
|
+
# @return [void]
|
336
|
+
def join(key = nil)
|
337
|
+
if key.nil? and self.key != true
|
338
|
+
key = self.key
|
339
|
+
end
|
340
|
+
@bot.raw "JOIN #{[@name, key].compact.join(" ")}"
|
341
|
+
end
|
342
|
+
|
343
|
+
# @endgroup
|
344
|
+
|
250
345
|
# @api private
|
251
346
|
# @return [void]
|
252
|
-
def add_user(user,
|
347
|
+
def add_user(user, modes = [])
|
253
348
|
@in_channel = true if user == @bot
|
254
|
-
@users[user] =
|
349
|
+
@users[user] = modes
|
255
350
|
end
|
256
351
|
|
257
352
|
# @api private
|
@@ -269,6 +364,8 @@ module Cinch
|
|
269
364
|
@users.clear
|
270
365
|
end
|
271
366
|
|
367
|
+
# @group Sending messages
|
368
|
+
|
272
369
|
# Send a message to the channel.
|
273
370
|
#
|
274
371
|
# @param [String] message the message
|
@@ -280,6 +377,24 @@ module Cinch
|
|
280
377
|
alias_method :privmsg, :send
|
281
378
|
alias_method :msg, :send
|
282
379
|
|
380
|
+
# Send a notice to the channel.
|
381
|
+
#
|
382
|
+
# @param [String] message the message
|
383
|
+
# @return [void]
|
384
|
+
def notice(message)
|
385
|
+
@bot.notice(@name, message)
|
386
|
+
end
|
387
|
+
|
388
|
+
# Like {#safe_send} but for notices.
|
389
|
+
#
|
390
|
+
# @param (see #safe_send)
|
391
|
+
# @return (see #safe_send)
|
392
|
+
# @see #safe_send
|
393
|
+
# @todo (see #safe_send)
|
394
|
+
def safe_notice(message)
|
395
|
+
@bot.safe_notice(@name, message)
|
396
|
+
end
|
397
|
+
|
283
398
|
# Send a message to the channel, but remove any non-printable
|
284
399
|
# characters. The purpose of this method is to send text from
|
285
400
|
# untrusted sources, like other users or feeds.
|
@@ -331,68 +446,7 @@ module Cinch
|
|
331
446
|
@bot.safe_action(@name, message)
|
332
447
|
end
|
333
448
|
|
334
|
-
|
335
|
-
# Invites a user to the channel.
|
336
|
-
#
|
337
|
-
# @param [String, User] user the user to invite
|
338
|
-
# @return [void]
|
339
|
-
def invite(user)
|
340
|
-
@bot.raw("INVITE #{user} #@name")
|
341
|
-
end
|
342
|
-
|
343
|
-
# Sets the topic.
|
344
|
-
#
|
345
|
-
# @param [String] new_topic the new topic
|
346
|
-
# @raise [Exceptions::TopicTooLong]
|
347
|
-
def topic=(new_topic)
|
348
|
-
if new_topic.size > @bot.irc.isupport["TOPICLEN"] && @bot.strict?
|
349
|
-
raise Exceptions::TopicTooLong, new_topic
|
350
|
-
end
|
351
|
-
|
352
|
-
@bot.raw "TOPIC #@name :#{new_topic}"
|
353
|
-
end
|
354
|
-
|
355
|
-
# Kicks a user from the channel.
|
356
|
-
#
|
357
|
-
# @param [String, User] user the user to kick
|
358
|
-
# @param [String] a reason for the kick
|
359
|
-
# @raise [Exceptions::KickReasonTooLong]
|
360
|
-
# @return [void]
|
361
|
-
def kick(user, reason = nil)
|
362
|
-
if reason.to_s.size > @bot.irc.isupport["KICKLEN"] && @bot.strict?
|
363
|
-
raise Exceptions::KickReasonTooLong, reason
|
364
|
-
end
|
365
|
-
|
366
|
-
@bot.raw("KICK #@name #{user} :#{reason}")
|
367
|
-
end
|
368
|
-
|
369
|
-
# Invites a user to the channel.
|
370
|
-
#
|
371
|
-
# @param [String, User] user the user to invite
|
372
|
-
# @return [void]
|
373
|
-
def invite(user)
|
374
|
-
@bot.raw "INVITE #{user} #@name"
|
375
|
-
end
|
376
|
-
|
377
|
-
# Causes the bot to part from the channel.
|
378
|
-
#
|
379
|
-
# @param [String] message the part message.
|
380
|
-
# @return [void]
|
381
|
-
def part(message = nil)
|
382
|
-
@bot.raw "PART #@name :#{message}"
|
383
|
-
end
|
384
|
-
|
385
|
-
# Joins the channel
|
386
|
-
#
|
387
|
-
# @param [String] key the channel key, if any. If none is
|
388
|
-
# specified but @key is set, @key will be used
|
389
|
-
# @return [void]
|
390
|
-
def join(key = nil)
|
391
|
-
if key.nil? and self.key != true
|
392
|
-
key = self.key
|
393
|
-
end
|
394
|
-
@bot.raw "JOIN #{[@name, key].compact.join(" ")}"
|
395
|
-
end
|
449
|
+
# @endgroup
|
396
450
|
|
397
451
|
# @return [Boolean]
|
398
452
|
def ==(other)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "cinch/cache_manager"
|
2
|
+
|
3
|
+
module Cinch
|
4
|
+
class ChannelManager < CacheManager
|
5
|
+
# Finds or creates a channel.
|
6
|
+
#
|
7
|
+
# @param [String] name name of a channel
|
8
|
+
# @return [Channel]
|
9
|
+
# @see Bot#Channel
|
10
|
+
def find_ensured(name)
|
11
|
+
downcased_name = name.irc_downcase(@bot.irc.isupport["CASEMAPPING"])
|
12
|
+
@mutex.synchronize do
|
13
|
+
@cache[downcased_name] ||= Channel.new(name, @bot)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Finds a channel.
|
18
|
+
#
|
19
|
+
# @param [String] name name of a channel
|
20
|
+
# @return [Channel, nil]
|
21
|
+
def find(name)
|
22
|
+
downcased_name = name.irc_downcase(@bot.irc.isupport["CASEMAPPING"])
|
23
|
+
@cache[downcased_name]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/cinch/constants.rb
CHANGED
@@ -38,7 +38,7 @@ module Cinch
|
|
38
38
|
# 412 - 414 are returned by PRIVMSG to indicate that the message
|
39
39
|
# wasn't delivered for some reason. ERR_NOTOPLEVEL and
|
40
40
|
# ERR_WILDTOPLEVEL are errors that are returned when an invalid use of
|
41
|
-
# "PRIVMSG
|
41
|
+
# "PRIVMSG $<server>" or "PRIVMSG #<host>" is attempted.
|
42
42
|
ERR_WILDTOPLEVEL = 414
|
43
43
|
|
44
44
|
# Returned to a registered client to indicate that the command sent is
|
@@ -229,8 +229,10 @@ module Cinch
|
|
229
229
|
# Reply by the server showing its version details. The <version>
|
230
230
|
# is the version of the software being used (including any patchlevel
|
231
231
|
# revisions) and the <debuglevel> is used to indicate if the
|
232
|
-
# server is running in "debug mode"
|
233
|
-
#
|
232
|
+
# server is running in "debug mode".
|
233
|
+
#
|
234
|
+
#The "comments" field may contain any comments about the version or
|
235
|
+
# further version details.
|
234
236
|
RPL_VERSION = 351
|
235
237
|
RPL_WHOREPLY = 352
|
236
238
|
|
@@ -238,7 +240,7 @@ module Cinch
|
|
238
240
|
# message. The RPL_WHOREPLY is only sent if there is an appropriate
|
239
241
|
# match to the WHO query. If there is a list of parameters supplied
|
240
242
|
# with a WHO message, a RPL_ENDOFWHO must be sent after processing
|
241
|
-
# each list item with
|
243
|
+
# each list item with <name> being the item.
|
242
244
|
RPL_ENDOFWHO = 315
|
243
245
|
RPL_NAMREPLY = 353
|
244
246
|
RPL_NAMEREPLY = RPL_NAMREPLY
|
data/lib/cinch/exceptions.rb
CHANGED
@@ -21,5 +21,14 @@ module Cinch
|
|
21
21
|
|
22
22
|
class UnsupportedFeature < Generic
|
23
23
|
end
|
24
|
+
|
25
|
+
class UnsupportedMode < Generic
|
26
|
+
def initialize(mode)
|
27
|
+
super "Cinch does not support the mode #{mode} yet."
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class InvalidModeString < Generic
|
32
|
+
end
|
24
33
|
end
|
25
34
|
end
|