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.
Files changed (82) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +192 -0
  3. data/Rakefile +53 -43
  4. data/examples/basic/autovoice.rb +32 -0
  5. data/examples/basic/google.rb +35 -0
  6. data/examples/basic/hello.rb +15 -0
  7. data/examples/basic/join_part.rb +38 -0
  8. data/examples/basic/memo.rb +39 -0
  9. data/examples/basic/msg.rb +16 -0
  10. data/examples/basic/seen.rb +36 -0
  11. data/examples/basic/urban_dict.rb +35 -0
  12. data/examples/basic/url_shorten.rb +35 -0
  13. data/examples/plugins/autovoice.rb +40 -0
  14. data/examples/plugins/custom_prefix.rb +23 -0
  15. data/examples/plugins/google.rb +37 -0
  16. data/examples/plugins/hello.rb +22 -0
  17. data/examples/plugins/join_part.rb +42 -0
  18. data/examples/plugins/memo.rb +50 -0
  19. data/examples/plugins/msg.rb +22 -0
  20. data/examples/plugins/multiple_matches.rb +41 -0
  21. data/examples/plugins/seen.rb +45 -0
  22. data/examples/plugins/urban_dict.rb +30 -0
  23. data/examples/plugins/url_shorten.rb +32 -0
  24. data/lib/cinch.rb +7 -20
  25. data/lib/cinch/ban.rb +41 -0
  26. data/lib/cinch/bot.rb +479 -0
  27. data/lib/cinch/callback.rb +11 -0
  28. data/lib/cinch/channel.rb +419 -0
  29. data/lib/cinch/constants.rb +369 -0
  30. data/lib/cinch/exceptions.rb +25 -0
  31. data/lib/cinch/helpers.rb +21 -0
  32. data/lib/cinch/irc.rb +344 -38
  33. data/lib/cinch/isupport.rb +96 -0
  34. data/lib/cinch/logger/formatted_logger.rb +80 -0
  35. data/lib/cinch/logger/logger.rb +44 -0
  36. data/lib/cinch/logger/null_logger.rb +18 -0
  37. data/lib/cinch/mask.rb +46 -0
  38. data/lib/cinch/message.rb +183 -0
  39. data/lib/cinch/message_queue.rb +62 -0
  40. data/lib/cinch/plugin.rb +205 -0
  41. data/lib/cinch/rubyext/infinity.rb +1 -0
  42. data/lib/cinch/rubyext/module.rb +18 -0
  43. data/lib/cinch/rubyext/queue.rb +19 -0
  44. data/lib/cinch/rubyext/string.rb +24 -0
  45. data/lib/cinch/syncable.rb +55 -0
  46. data/lib/cinch/user.rb +325 -0
  47. data/spec/bot_spec.rb +5 -0
  48. data/spec/channel_spec.rb +5 -0
  49. data/spec/cinch_spec.rb +5 -0
  50. data/spec/irc_spec.rb +5 -0
  51. data/spec/message_spec.rb +5 -0
  52. data/spec/plugin_spec.rb +5 -0
  53. data/spec/{helper.rb → spec_helper.rb} +0 -0
  54. data/spec/user_spec.rb +5 -0
  55. metadata +69 -51
  56. data/README.rdoc +0 -195
  57. data/examples/autovoice.rb +0 -32
  58. data/examples/custom_patterns.rb +0 -19
  59. data/examples/custom_prefix.rb +0 -25
  60. data/examples/google.rb +0 -31
  61. data/examples/hello.rb +0 -13
  62. data/examples/join_part.rb +0 -26
  63. data/examples/memo.rb +0 -40
  64. data/examples/msg.rb +0 -14
  65. data/examples/named-param-types.rb +0 -19
  66. data/examples/seen.rb +0 -41
  67. data/examples/urban_dict.rb +0 -31
  68. data/examples/url_shorten.rb +0 -34
  69. data/lib/cinch/base.rb +0 -368
  70. data/lib/cinch/irc/message.rb +0 -135
  71. data/lib/cinch/irc/parser.rb +0 -141
  72. data/lib/cinch/irc/socket.rb +0 -329
  73. data/lib/cinch/names.rb +0 -54
  74. data/lib/cinch/rules.rb +0 -171
  75. data/spec/base_spec.rb +0 -94
  76. data/spec/irc/helper.rb +0 -8
  77. data/spec/irc/message_spec.rb +0 -61
  78. data/spec/irc/parser_spec.rb +0 -103
  79. data/spec/irc/socket_spec.rb +0 -90
  80. data/spec/names_spec.rb +0 -393
  81. data/spec/options_spec.rb +0 -45
  82. 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
@@ -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