cinch 0.3.5 → 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.
- data/LICENSE +20 -0
- data/README.md +192 -0
- data/Rakefile +53 -43
- data/examples/basic/autovoice.rb +32 -0
- data/examples/basic/google.rb +35 -0
- data/examples/basic/hello.rb +15 -0
- data/examples/basic/join_part.rb +38 -0
- data/examples/basic/memo.rb +39 -0
- data/examples/basic/msg.rb +16 -0
- data/examples/basic/seen.rb +36 -0
- data/examples/basic/urban_dict.rb +35 -0
- data/examples/basic/url_shorten.rb +35 -0
- data/examples/plugins/autovoice.rb +40 -0
- data/examples/plugins/custom_prefix.rb +23 -0
- data/examples/plugins/google.rb +37 -0
- data/examples/plugins/hello.rb +22 -0
- data/examples/plugins/join_part.rb +42 -0
- data/examples/plugins/memo.rb +50 -0
- data/examples/plugins/msg.rb +22 -0
- data/examples/plugins/multiple_matches.rb +41 -0
- data/examples/plugins/seen.rb +45 -0
- data/examples/plugins/urban_dict.rb +30 -0
- data/examples/plugins/url_shorten.rb +32 -0
- data/lib/cinch.rb +7 -20
- data/lib/cinch/ban.rb +41 -0
- data/lib/cinch/bot.rb +479 -0
- data/lib/cinch/callback.rb +11 -0
- data/lib/cinch/channel.rb +419 -0
- data/lib/cinch/constants.rb +369 -0
- data/lib/cinch/exceptions.rb +25 -0
- data/lib/cinch/helpers.rb +21 -0
- data/lib/cinch/irc.rb +344 -38
- data/lib/cinch/isupport.rb +96 -0
- data/lib/cinch/logger/formatted_logger.rb +80 -0
- data/lib/cinch/logger/logger.rb +44 -0
- data/lib/cinch/logger/null_logger.rb +18 -0
- data/lib/cinch/mask.rb +46 -0
- data/lib/cinch/message.rb +183 -0
- data/lib/cinch/message_queue.rb +62 -0
- data/lib/cinch/plugin.rb +205 -0
- data/lib/cinch/rubyext/infinity.rb +1 -0
- data/lib/cinch/rubyext/module.rb +18 -0
- data/lib/cinch/rubyext/queue.rb +19 -0
- data/lib/cinch/rubyext/string.rb +24 -0
- data/lib/cinch/syncable.rb +55 -0
- data/lib/cinch/user.rb +325 -0
- data/spec/bot_spec.rb +5 -0
- data/spec/channel_spec.rb +5 -0
- data/spec/cinch_spec.rb +5 -0
- data/spec/irc_spec.rb +5 -0
- data/spec/message_spec.rb +5 -0
- data/spec/plugin_spec.rb +5 -0
- data/spec/{helper.rb → spec_helper.rb} +0 -0
- data/spec/user_spec.rb +5 -0
- metadata +69 -51
- data/README.rdoc +0 -195
- data/examples/autovoice.rb +0 -32
- data/examples/custom_patterns.rb +0 -19
- data/examples/custom_prefix.rb +0 -25
- data/examples/google.rb +0 -31
- data/examples/hello.rb +0 -13
- data/examples/join_part.rb +0 -26
- data/examples/memo.rb +0 -40
- data/examples/msg.rb +0 -14
- data/examples/named-param-types.rb +0 -19
- data/examples/seen.rb +0 -41
- data/examples/urban_dict.rb +0 -31
- data/examples/url_shorten.rb +0 -34
- data/lib/cinch/base.rb +0 -368
- data/lib/cinch/irc/message.rb +0 -135
- data/lib/cinch/irc/parser.rb +0 -141
- data/lib/cinch/irc/socket.rb +0 -329
- data/lib/cinch/names.rb +0 -54
- data/lib/cinch/rules.rb +0 -171
- data/spec/base_spec.rb +0 -94
- data/spec/irc/helper.rb +0 -8
- data/spec/irc/message_spec.rb +0 -61
- data/spec/irc/parser_spec.rb +0 -103
- data/spec/irc/socket_spec.rb +0 -90
- data/spec/names_spec.rb +0 -393
- data/spec/options_spec.rb +0 -45
- data/spec/rules_spec.rb +0 -109
@@ -0,0 +1 @@
|
|
1
|
+
Infinity = 1.0/0.0
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Module
|
2
|
+
# @api private
|
3
|
+
def synced_attr_reader(attribute)
|
4
|
+
define_method(attribute) do
|
5
|
+
attr(attribute)
|
6
|
+
end
|
7
|
+
|
8
|
+
define_method("#{attribute}_unsynced") do
|
9
|
+
attr(attribute, false, true)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
def synced_attr_accessor(attr)
|
15
|
+
synced_attr_reader(attr)
|
16
|
+
attr_accessor(attr)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "thread"
|
2
|
+
class Queue
|
3
|
+
def unshift(obj)
|
4
|
+
t = nil
|
5
|
+
@mutex.synchronize{
|
6
|
+
@que.unshift obj
|
7
|
+
begin
|
8
|
+
t = @waiting.shift
|
9
|
+
t.wakeup if t
|
10
|
+
rescue ThreadError
|
11
|
+
retry
|
12
|
+
end
|
13
|
+
}
|
14
|
+
begin
|
15
|
+
t.run if t
|
16
|
+
rescue ThreadError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class String
|
2
|
+
def irc_downcase(mapping)
|
3
|
+
case mapping
|
4
|
+
when :rfc1459
|
5
|
+
self.tr("A-Z[]\\^", "a-z{}|~")
|
6
|
+
when :"strict-rfc1459"
|
7
|
+
self.tr("A-Z[]\\", "a-z{}|")
|
8
|
+
else
|
9
|
+
# when :ascii or unknown/nil
|
10
|
+
self.tr("A-Z", "a-z")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def irc_upcase(mapping)
|
15
|
+
case mapping
|
16
|
+
when :ascii
|
17
|
+
self.tr("a-z", "A-Z")
|
18
|
+
when :rfc1459
|
19
|
+
self.tr("a-z{}|~", "A-Z[]\\^")
|
20
|
+
when :strict-rfc1459
|
21
|
+
self.tr("a-z{}|", "A-Z[]\\")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Cinch
|
2
|
+
module Syncable
|
3
|
+
# Blocks until the object is synced.
|
4
|
+
#
|
5
|
+
# @return [void]
|
6
|
+
def wait_until_synced(attr)
|
7
|
+
attr = attr.to_sym
|
8
|
+
while true
|
9
|
+
return if @synced_attributes.include?(attr)
|
10
|
+
sleep 0.1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
# @return [void]
|
16
|
+
def sync(attribute, value, data = false)
|
17
|
+
if data
|
18
|
+
@data[attribute] = value
|
19
|
+
else
|
20
|
+
instance_variable_set("@#{attribute}", value)
|
21
|
+
end
|
22
|
+
@synced_attributes << attribute
|
23
|
+
end
|
24
|
+
|
25
|
+
def synced?(attribute)
|
26
|
+
@synced_attributes.include?(attribute)
|
27
|
+
end
|
28
|
+
|
29
|
+
def unsync(attribute)
|
30
|
+
@synced_attributes.delete(attribute)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
def attr(attribute, data = false, unsync = false)
|
35
|
+
unless unsync
|
36
|
+
if @when_requesting_synced_attribute
|
37
|
+
@when_requesting_synced_attribute.call(attribute)
|
38
|
+
end
|
39
|
+
wait_until_synced(attribute)
|
40
|
+
end
|
41
|
+
|
42
|
+
if data
|
43
|
+
return @data[attribute]
|
44
|
+
else
|
45
|
+
return instance_variable_get("@#{attribute}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
# @return [void]
|
51
|
+
def mark_as_synced(attribute)
|
52
|
+
@synced_attributes << attribute
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/cinch/user.rb
ADDED
@@ -0,0 +1,325 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Cinch
|
3
|
+
class User
|
4
|
+
include Syncable
|
5
|
+
|
6
|
+
@users = {}
|
7
|
+
class << self
|
8
|
+
|
9
|
+
# @overload find_ensured(nick, bot)
|
10
|
+
# Finds or creates a user based on his nick.
|
11
|
+
#
|
12
|
+
# @param [String] nick The user's nickname
|
13
|
+
# @param [Bot] bot An instance of Bot
|
14
|
+
# @overload find_ensured(user, nick, host, bot)
|
15
|
+
# Finds or creates a user based on his nick but already
|
16
|
+
# setting user and host.
|
17
|
+
#
|
18
|
+
# @param [String] user The username
|
19
|
+
# @param [String] nick The nickname
|
20
|
+
# @param [String] host The user's hostname
|
21
|
+
# @param [Bot] bot An instance of bot
|
22
|
+
#
|
23
|
+
# @return [User]
|
24
|
+
def find_ensured(*args)
|
25
|
+
# FIXME CASEMAPPING
|
26
|
+
case args.size
|
27
|
+
when 2
|
28
|
+
nick = args.first
|
29
|
+
bargs = [args.first]
|
30
|
+
bot = args.last
|
31
|
+
when 4
|
32
|
+
nick = args[1]
|
33
|
+
bot = args.pop
|
34
|
+
bargs = args
|
35
|
+
else
|
36
|
+
raise ArgumentError
|
37
|
+
end
|
38
|
+
downcased_nick = nick.irc_downcase(bot.irc.isupport["CASEMAPPING"])
|
39
|
+
@users[downcased_nick] ||= new(*bargs, bot)
|
40
|
+
@users[downcased_nick]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Finds a user.
|
44
|
+
#
|
45
|
+
# @param [String] nick nick of a user
|
46
|
+
# @return [User, nil]
|
47
|
+
def find(nick)
|
48
|
+
@users[nick]
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Array<User>] Returns all users
|
52
|
+
def all
|
53
|
+
@users.values
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# @return [String]
|
59
|
+
attr_reader :nick
|
60
|
+
# @return [Bot]
|
61
|
+
attr_reader :bot
|
62
|
+
# @return [Boolean]
|
63
|
+
attr_reader :synced
|
64
|
+
# @return [Boolean]
|
65
|
+
attr_reader :in_whois
|
66
|
+
|
67
|
+
# @return [String]
|
68
|
+
attr_reader :user
|
69
|
+
undef_method "user"
|
70
|
+
|
71
|
+
# @return [String]
|
72
|
+
attr_reader :host
|
73
|
+
undef_method "host"
|
74
|
+
|
75
|
+
# @return [String]
|
76
|
+
attr_reader :realname
|
77
|
+
undef_method "realname"
|
78
|
+
|
79
|
+
# @return [String]
|
80
|
+
attr_reader :authname
|
81
|
+
undef_method "authname"
|
82
|
+
|
83
|
+
# @return [Number] How long this user has been idle, in seconds.
|
84
|
+
# This is a snapshot of the last WHOIS.
|
85
|
+
attr_reader :idle
|
86
|
+
undef_method "idle"
|
87
|
+
|
88
|
+
# @return [Time]
|
89
|
+
attr_reader :signed_on_at
|
90
|
+
undef_method "signed_on_at"
|
91
|
+
|
92
|
+
# @return [Boolean] True if the instance references an user who
|
93
|
+
# cannot be found on the server.
|
94
|
+
attr_reader :unknown
|
95
|
+
alias_method :unknown?, :unknown
|
96
|
+
undef_method "unknown?"
|
97
|
+
undef_method "unknown"
|
98
|
+
def unknown
|
99
|
+
self.unknown?
|
100
|
+
end
|
101
|
+
|
102
|
+
# @return [Array<Channel>] All channels the user is in.
|
103
|
+
attr_reader :channels
|
104
|
+
undef_method "channels"
|
105
|
+
|
106
|
+
# @return [Boolean] True if the user is using a secure connection, i.e. SSL.
|
107
|
+
attr_reader :secure
|
108
|
+
alias_method :secure?, :secure
|
109
|
+
undef_method "secure?"
|
110
|
+
undef_method "secure"
|
111
|
+
def secure
|
112
|
+
self.secure?
|
113
|
+
end
|
114
|
+
|
115
|
+
# By default, you can use methods like User#user, User#host and
|
116
|
+
# alike – If you however fear that another thread might change
|
117
|
+
# data while you're using it and if this means a critical issue to
|
118
|
+
# your code, you can store the result of this method and work with
|
119
|
+
# that instead.
|
120
|
+
#
|
121
|
+
# @example
|
122
|
+
# on :channel do
|
123
|
+
# data = user.data
|
124
|
+
# do_something_with(data.user)
|
125
|
+
# do_something_with(data.host)
|
126
|
+
# end
|
127
|
+
# @return [Hash]
|
128
|
+
attr_reader :data
|
129
|
+
def initialize(*args)
|
130
|
+
@data = {
|
131
|
+
:user => nil,
|
132
|
+
:host => nil,
|
133
|
+
:realname => nil,
|
134
|
+
:authname => nil,
|
135
|
+
:idle => 0,
|
136
|
+
:signed_on_at => nil,
|
137
|
+
:unknown? => false,
|
138
|
+
:channels => [],
|
139
|
+
:secure? => false,
|
140
|
+
}
|
141
|
+
case args.size
|
142
|
+
when 2
|
143
|
+
@nick, @bot = args
|
144
|
+
when 4
|
145
|
+
@data[:user], @nick, @data[:host], @bot = args
|
146
|
+
else
|
147
|
+
raise ArgumentError
|
148
|
+
end
|
149
|
+
|
150
|
+
@synced_attributes = Set.new
|
151
|
+
|
152
|
+
@when_requesting_synced_attribute = lambda {|attr|
|
153
|
+
unless @synced
|
154
|
+
unsync attr
|
155
|
+
whois
|
156
|
+
end
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
# Checks if the user is identified. Currently officially supports
|
161
|
+
# Quakenet and Freenode.
|
162
|
+
#
|
163
|
+
# @return [Boolean] true if the user is identified
|
164
|
+
def authed?
|
165
|
+
@data[:authname]
|
166
|
+
end
|
167
|
+
|
168
|
+
# @see Syncable#attr
|
169
|
+
def attr(attribute, data = true, unsync = false)
|
170
|
+
super
|
171
|
+
end
|
172
|
+
|
173
|
+
# Queries the IRC server for information on the user. This will
|
174
|
+
# set the User's state to not synced. After all information are
|
175
|
+
# received, the object will be set back to synced.
|
176
|
+
#
|
177
|
+
# @return [void]
|
178
|
+
def whois
|
179
|
+
return if @in_whois
|
180
|
+
@synced = false
|
181
|
+
@data.keys.each do |attr|
|
182
|
+
unsync attr
|
183
|
+
end
|
184
|
+
|
185
|
+
@in_whois = true
|
186
|
+
@bot.raw "WHOIS #@nick #@nick"
|
187
|
+
end
|
188
|
+
alias_method :refresh, :whois
|
189
|
+
|
190
|
+
# Send a message to the user.
|
191
|
+
#
|
192
|
+
# @param [String] message the message
|
193
|
+
# @return [void]
|
194
|
+
def send(message)
|
195
|
+
@bot.msg(@nick, message)
|
196
|
+
end
|
197
|
+
alias_method :privmsg, :send
|
198
|
+
alias_method :msg, :send
|
199
|
+
|
200
|
+
# Send a message to the user, but remove any non-printable
|
201
|
+
# characters. The purpose of this method is to send text from
|
202
|
+
# untrusted sources, like other users or feeds.
|
203
|
+
#
|
204
|
+
# Note: this will **break** any mIRC color codes embedded in the
|
205
|
+
# string.
|
206
|
+
#
|
207
|
+
# @param (see #send)
|
208
|
+
# @return (see #send)
|
209
|
+
# @see #send
|
210
|
+
# @see Bot#safe_msg
|
211
|
+
# @todo Handle mIRC color codes more gracefully.
|
212
|
+
def safe_send(message)
|
213
|
+
@bot.safe_msg(@nick, message)
|
214
|
+
end
|
215
|
+
alias_method :safe_privmsg, :safe_send
|
216
|
+
alias_method :safe_msg, :safe_send
|
217
|
+
|
218
|
+
# Send a CTCP to the user.
|
219
|
+
#
|
220
|
+
# @param [String] message the ctcp message
|
221
|
+
# @return [void]
|
222
|
+
def ctcp(message)
|
223
|
+
send "\001#{message}\001"
|
224
|
+
end
|
225
|
+
|
226
|
+
# Send an action (/me) to the user.
|
227
|
+
#
|
228
|
+
# @param [String] message the message
|
229
|
+
# @return [void]
|
230
|
+
# @see #safe_action
|
231
|
+
def action(message)
|
232
|
+
@bot.action(@name, message)
|
233
|
+
end
|
234
|
+
|
235
|
+
# Send an action (/me) to the user but remove any non-printable
|
236
|
+
# characters. The purpose of this method is to send text from
|
237
|
+
# untrusted sources, like other users or feeds.
|
238
|
+
#
|
239
|
+
# Note: this will **break** any mIRC color codes embedded in the
|
240
|
+
# string.
|
241
|
+
#
|
242
|
+
# @param (see #action)
|
243
|
+
# @return (see #action)
|
244
|
+
# @see #action
|
245
|
+
# @see Bot#safe_action
|
246
|
+
# @todo Handle mIRC color codes more gracefully.
|
247
|
+
def safe_action(message)
|
248
|
+
@bot.safe_action(@name, message)
|
249
|
+
end
|
250
|
+
|
251
|
+
# @return [String]
|
252
|
+
def to_s
|
253
|
+
@nick
|
254
|
+
end
|
255
|
+
|
256
|
+
# @return [String]
|
257
|
+
def inspect
|
258
|
+
"#<User nick=#{@nick.inspect}>"
|
259
|
+
end
|
260
|
+
|
261
|
+
# Generates a mask for the user.
|
262
|
+
#
|
263
|
+
# @param [String] s a pattern for generating the mask.
|
264
|
+
#
|
265
|
+
# - %n = nickname
|
266
|
+
# - %u = username
|
267
|
+
# - %h = host
|
268
|
+
# - %r = realname
|
269
|
+
# - %a = authname
|
270
|
+
#
|
271
|
+
# @return [Mask]
|
272
|
+
def mask(s = "%n!%u@%h")
|
273
|
+
s = s.gsub(/%(.)/) {
|
274
|
+
case $1
|
275
|
+
when "n"
|
276
|
+
@nick
|
277
|
+
when "u"
|
278
|
+
self.user
|
279
|
+
when "h"
|
280
|
+
self.host
|
281
|
+
when "r"
|
282
|
+
self.realname
|
283
|
+
when "a"
|
284
|
+
self.authname
|
285
|
+
end
|
286
|
+
}
|
287
|
+
|
288
|
+
Mask.new(s)
|
289
|
+
end
|
290
|
+
|
291
|
+
# Provides synced access to user attributes.
|
292
|
+
def method_missing(m, *args)
|
293
|
+
if m.to_s =~ /^(.+)_unsynced$/
|
294
|
+
m = $1.to_sym
|
295
|
+
unsync = true
|
296
|
+
end
|
297
|
+
|
298
|
+
if @data.has_key?(m)
|
299
|
+
attr(m, true, unsync = false)
|
300
|
+
else
|
301
|
+
super
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# @return [Boolean]
|
306
|
+
def ==(other)
|
307
|
+
return case other
|
308
|
+
when self.class
|
309
|
+
@nick == other.nick
|
310
|
+
when String
|
311
|
+
self.to_s == other
|
312
|
+
when Bot
|
313
|
+
self.nick == other.config.nick
|
314
|
+
else
|
315
|
+
false
|
316
|
+
end
|
317
|
+
end
|
318
|
+
alias_method :eql?, "=="
|
319
|
+
|
320
|
+
# @return [Fixnum]
|
321
|
+
def hash
|
322
|
+
@nick.hash
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|