cinch 0.3.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,135 +0,0 @@
1
- module Cinch
2
- module IRC
3
-
4
- # == Description
5
- # IRC::Message provies a nicely encapsulated IRC message object. Used directly by
6
- # IRC::Parser#parse and sent to any plugin or listener defined.
7
- #
8
- # TODO: Add more documentation
9
- #
10
- # == See Also
11
- # * Cinch::IRC::Parser#parse
12
- #
13
- # == Author
14
- # * Lee Jarvis - ljjarvis@gmail.com
15
- class Message
16
-
17
- # Message prefix
18
- attr_reader :prefix
19
-
20
- # Message command (PRIVMSG, JOIN, KICK, etc)
21
- attr_reader :command
22
-
23
- # Message params
24
- attr_reader :params
25
-
26
- # Message symbol (lowercase command, ie. :privmsg, :join, :kick)
27
- attr_accessor :symbol
28
-
29
- # The raw string passed to ::new
30
- attr_reader :raw
31
-
32
- # Hash with message attributes
33
- attr_reader :data
34
-
35
- # Message text
36
- attr_accessor :text
37
-
38
- # Arguments parsed from a rule
39
- attr_accessor :args
40
-
41
- # The IRC::Socket object (or nil)
42
- attr_accessor :irc
43
-
44
- # Invoked directly from IRC::Parser#parse_servermessage
45
- def initialize(raw, prefix, command, params)
46
- @raw = raw
47
- @prefix = prefix
48
- @command = command
49
- @params = params
50
- @text = params.last unless params.empty?
51
-
52
- @symbol = command.downcase.to_sym
53
- @data = {}
54
- @args = {}
55
- @irc = nil
56
- end
57
-
58
- # Access attribute
59
- def [](var)
60
- @data[var.to_sym]
61
- end
62
-
63
- # Add a new attribute (stored in @data)
64
- def add(var, val)
65
- @data[var.to_sym] = val
66
- end
67
- alias []= add
68
-
69
- # Remove an attribute
70
- def delete(var)
71
- var = var.to_sym
72
- return unless @data.key?(var)
73
- @data.delete(var)
74
- end
75
-
76
- # Alter an attribute
77
- def alter(var, val)
78
- if @data.key?(var)
79
- @data[var] = val
80
- end
81
- end
82
-
83
- # Check if our message was sent privately
84
- def private?
85
- !@data[:channel]
86
- end
87
-
88
- # Check if our message was sent publicly (in a channel)
89
- def public?
90
- !private?
91
- end
92
-
93
- # Reply to a channel or user, probably the most commonly used helper
94
- # method
95
- def reply(text)
96
- recipient = data[:channel] || data[:nick]
97
- @irc.privmsg(recipient, text)
98
- end
99
-
100
- # Same as reply but prefixes the users nickname
101
- def answer(text)
102
- return unless data[:channel]
103
- @irc.privmsg(data[:channel], "#{data[:nick]}: #{text}")
104
- end
105
-
106
- # Send a CTCP reply
107
- def ctcp_reply(text)
108
- recipient = data[:channel] || data[:nick]
109
- @irc.notice(recipient, "\001#{@data[:ctcp_action]} #{text}\001")
110
- end
111
-
112
- # The deadly /me action
113
- def action(text)
114
- reply("\001ACTION #{text}\001")
115
- end
116
-
117
- # The raw IRC message
118
- def to_s
119
- raw
120
- end
121
-
122
- # Catch methods and check if they exist as keys in
123
- # the attribute hash
124
- def method_missing(meth, *args, &blk) # :nodoc:
125
- if @data.key?(meth)
126
- @data[meth]
127
- else
128
- nil
129
- end
130
- end
131
-
132
- end
133
- end
134
- end
135
-
@@ -1,141 +0,0 @@
1
- module Cinch
2
- module IRC
3
-
4
- # == Description
5
- # Parse incoming IRC lines and extract data, returning a nicely
6
- # encapsulated Cinch::IRC::Message
7
- #
8
- # == Example
9
- # require 'cinch/irc/parser'
10
- #
11
- # message = Cinch::IRC::Parser.parse(":foo!bar@myhost.com PRIVMSG #mychan :ding dong!")
12
- #
13
- # message.class #=> Cinch::IRC::Message
14
- # message.command #=> PRIVMSG
15
- # message.nick #=> foo
16
- # message.channel #=> #mychan
17
- # message.text #=> ding dong!
18
- #
19
- # == Author
20
- # * Lee Jarvis - ljjarvis@gmail.com
21
- class Parser
22
-
23
- # A hash holding all of our patterns
24
- attr_reader :patterns
25
-
26
- def initialize
27
- @patterns = {}
28
- setup_patterns
29
- end
30
-
31
- # Add a new pattern
32
- def add_pattern(key, pattern)
33
- @patterns[key.to_sym] = pattern
34
- end
35
-
36
- # Remove a pattern
37
- def remove_pattern(key)
38
- @patterns.delete(key.to_sym)
39
- end
40
-
41
- # Helper for our patterns Hash
42
- def pattern(key)
43
- @patterns[key.to_sym]
44
- end
45
-
46
- # Check if a pattern exists
47
- def has_pattern?(key)
48
- @patterns.key?(key.to_sym)
49
- end
50
-
51
- # Set up some default patterns used directly by this class
52
- def setup_patterns
53
- add_pattern :letter, /[a-zA-Z]/
54
- add_pattern :word, /\w+/
55
- add_pattern :upper, /[A-Z]+/
56
- add_pattern :lower, /[a-z]+/
57
- add_pattern :hex, /[\dA-Fa-f]/
58
-
59
- add_pattern :ip4addr, /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
60
- add_pattern :ip6addr, /[\dA-Fa-f](?::[\dA-Fa-f]){7}|0:0:0:0:0:(?:0|[Ff]{4}):#{pattern(:ip4addr)}/
61
- add_pattern :hostaddr, /#{pattern(:ip4addr)}|#{pattern(:ip6addr)}/
62
- add_pattern :shortname, /[A-Za-z0-9][A-Za-z0-9\-\/]*/
63
- add_pattern :hostname, /#{pattern(:shortname)}(?:\.#{pattern(:shortname)})*/
64
- add_pattern :host, /#{pattern(:hostname)}|#{pattern(:hostaddr)}/
65
-
66
- add_pattern :user, /[^\x00\x10\x0D\x20@]+/
67
- add_pattern :nick, /[A-Za-z\[\]\\`_^{|}][A-Za-z\d\[\]\\`_^{|}-]{0,19}/
68
-
69
- add_pattern :ctcp, /\001(\S+)(?:\s([^\x00\r\n\001]+))?\001$/
70
-
71
- add_pattern :userhost, /(#{pattern(:nick)})(?:(?:!(#{pattern(:user)}))?@(#{pattern(:host)}))?/
72
-
73
- add_pattern :channel, /(?:[#+&]|![A-Z\d]{5})[^\x00\x07\x10\x0D\x20,:]/
74
-
75
- # Server message parsing patterns
76
- add_pattern :prefix, /(?:(\S+)\x20)?/
77
- add_pattern :command, /([A-Za-z]+|\d{3})/
78
- add_pattern :middle, /[^\x00\x20\r\n:][^\x00\x20\r\n]*/
79
- add_pattern :trailing, /[^\x00\r\n]*/
80
- add_pattern :params, /(?:((?:#{pattern(:middle)}){0,14}(?::?#{pattern(:trailing)})?))/
81
- add_pattern :message, /\A#{pattern(:prefix)}#{pattern(:command)}#{pattern(:params)}\Z/
82
-
83
- add_pattern :params_scan, /(?!:)([^\x00\x20\r\n:]+)|:([^\x00\r\n]*)/
84
- end
85
- private :setup_patterns
86
-
87
- # Parse the incoming raw IRC string and return
88
- # a nicely formatted IRC::Message
89
- def parse(raw)
90
- raise ArgumentError, raw unless raw && matches = raw.match(pattern(:message))
91
-
92
- prefix, command, parameters = matches.captures
93
-
94
- params = []
95
- parameters.scan(pattern(:params_scan)) {|a, c| params << (a || c) }
96
-
97
- m = IRC::Message.new(raw, prefix, command, params)
98
-
99
- if prefix && userhost = parse_userhost(prefix)
100
- nick, user, host = userhost
101
- m.add(:nick, nick)
102
- m.add(:user, user)
103
- m.add(:host, host)
104
-
105
- unless m.params.empty?
106
- m.add(:recipient, m.params.first)
107
- m.add(:channel, m.recipient) if valid_channel?(m.recipient)
108
- end
109
- end
110
-
111
- # Parse CTCP response
112
- if m.symbol == :privmsg && data = m.text.match(pattern(:ctcp))
113
- m.symbol = :ctcp
114
- m.text = data[2]
115
- m.add(:ctcp_action, data[1])
116
- end
117
-
118
- m # Return our IRC::Message
119
- end
120
-
121
- # Parse the prefix returned from the server
122
- # and return an Array of [nick, user, host] or
123
- # nil if no match is found
124
- def parse_userhost(prefix)
125
- if matches = prefix.match(pattern(:userhost))
126
- matches.captures
127
- else
128
- nil
129
- end
130
- end
131
- alias :extract_userhost :parse_userhost
132
-
133
- # Check if a string is a valid channel
134
- def valid_channel?(str)
135
- !str.match(pattern(:channel)).nil?
136
- end
137
-
138
- end
139
- end
140
- end
141
-
@@ -1,329 +0,0 @@
1
- module Cinch
2
- module IRC
3
-
4
- # == Description
5
- # This class has been directly taken from the irc-socket library. Original documentation
6
- # for this class can be found {here}[http://rdoc.injekt.net/irc-socket].
7
- #
8
- # IRCSocket is an IRC wrapper around a TCPSocket. It implements all of the major
9
- # commands laid out in {RFC 2812}[http://irchelp.org/irchelp/rfc/rfc2812.txt].
10
- # All these commands are available as instance methods of an IRCSocket Object.
11
- #
12
- # == Example
13
- # irc = IRCSocket.new('irc.freenode.org')
14
- # irc.connect
15
- #
16
- # if irc.connected?
17
- # irc.nick "HulkHogan"
18
- # irc.user "Hulk", 0, "*", "I am Hulk Hogan"
19
- #
20
- # while line = irc.read
21
- #
22
- # # Join a channel after MOTD
23
- # if line.split[1] == '376'
24
- # irc.join "#mychannel"
25
- # end
26
- #
27
- # puts "Received: #{line}"
28
- # end
29
- # end
30
- #
31
- # === Block Form
32
- # IRCSocket.new('irc.freenode.org') do |irc|
33
- # irc.nick "SpongeBob"
34
- # irc.user "Spongey", 0, "*", "Square Pants"
35
- #
36
- # puts irc.read
37
- # end
38
- #
39
- # == Author
40
- # * Lee Jarvis - ljjarvis@gmail.com
41
- class Socket
42
-
43
- # The server our socket is connected to
44
- attr_reader :server
45
-
46
- # The port our socket is connected on
47
- attr_reader :port
48
-
49
- # The TCPSocket instance
50
- attr_reader :socket
51
-
52
- # How long to wait before aborting a connection
53
- attr_accessor :timeout
54
-
55
- # Maximum attempts to retry failed connection
56
- attr_accessor :attempts
57
-
58
- # Creates a new IRCSocket and automatically connects
59
- #
60
- # === Example
61
- # irc = IRCSocket.open('irc.freenode.org')
62
- #
63
- # while data = irc.read
64
- # puts data
65
- # end
66
- def self.open(server, port=6667, ssl=false)
67
- irc = new(server, port)
68
- irc.connect
69
- irc
70
- end
71
-
72
- # Create a new IRCSocket to connect to +server+ on +port+. Defaults to port 6667.
73
- # If an optional code block is given, it will be passed an instance of the IRCSocket.
74
- # NOTE: Using the block form does not mean the socket will send the applicable QUIT
75
- # command to leave the IRC server. You must send this yourself.
76
- def initialize(server, port=6667, ssl=false)
77
- @server = server
78
- @port = port
79
- @ssl = ssl
80
-
81
- @socket = nil
82
- @connected = false
83
- @timeout = 5
84
- @attempts = 5
85
-
86
- if block_given?
87
- connect
88
- yield self
89
- end
90
- end
91
-
92
- # Check if our socket is alive and connected to an IRC server
93
- def connected?
94
- @connected
95
- end
96
- alias connected connected?
97
-
98
- # Connect to an IRC server, returns true on a successful connection, or
99
- # raises otherwise
100
- def connect(server=nil, port=nil, ssl=false)
101
- @server = server if server
102
- @port = port if port
103
- @ssl = ssl if ssl
104
-
105
- Timeout.timeout(@timeout) do
106
- socket = TCPSocket.new(@server, @port)
107
-
108
- if @ssl
109
- require 'openssl'
110
-
111
- ssl = OpenSSL::SSL::SSLContext.new
112
- ssl.verify_mode = OpenSSL::SSL::VERIFY_NONE
113
- @socket = OpenSSL::SSL::SSLSocket.new(socket, ssl)
114
- @socket.sync = true
115
- @socket.connect
116
- else
117
- @socket = socket
118
- end
119
- end
120
- rescue Timeout::Error
121
- if @attempts == 0
122
- puts "Maximum attempts reached. Aborting .."
123
- exit 1
124
- else
125
- puts "Connection timed out. Retrying #{@attempts} more time#{'s' unless @attempts == 1} .."
126
- @attempts -= 1
127
- connect(server, port)
128
- end
129
- rescue Interrupt
130
- puts "\nAborting connection .."
131
- exit
132
- rescue Exception
133
- raise
134
- else
135
- @connected = true
136
- end
137
-
138
- # Read the next line in from the server. If no arguments are passed
139
- # the line will have the CRLF chomp'ed. Returns nil if no data could be read
140
- def read(chompstr="\r\n")
141
- if data = @socket.gets("\r\n")
142
- data.chomp!(chompstr) if chompstr
143
- data
144
- else
145
- nil
146
- end
147
- rescue IOError
148
- nil
149
- end
150
-
151
- # Write to our Socket and append CRLF
152
- def write(data)
153
- @socket.write(data + "\r\n")
154
- rescue IOError
155
- raise
156
- end
157
-
158
- # Sugar for #write
159
- def raw(*args) # :nodoc:
160
- args.last.insert(0, ':') unless args.last.nil?
161
- write args.join(' ').strip
162
- end
163
-
164
- # More sugar
165
- def write_optional(command, *optional) # :nodoc:
166
- command = "#{command} #{optional.join(' ')}" if optional
167
- write(command.strip)
168
- end
169
- private :raw, :write_optional
170
-
171
- # Send PASS command
172
- def pass(password)
173
- write("PASS #{password}")
174
- end
175
-
176
- # Send NICK command
177
- def nick(nickname)
178
- write("NICK #{nickname}")
179
- end
180
-
181
- # Send USER command
182
- def user(user, mode, unused, realname)
183
- write("USER #{user} #{mode} #{unused} :#{realname}")
184
- end
185
-
186
- # Send OPER command
187
- def oper(name, password)
188
- write("OPER #{name} #{password}")
189
- end
190
-
191
- # Send the MODE command.
192
- # Should probably implement a better way of doing this
193
- def mode(channel, *modes)
194
- write("MODE #{channel} #{modes.join(' ')}")
195
- end
196
-
197
- # Send QUIT command
198
- def quit(message=nil)
199
- raw("QUIT", message)
200
- end
201
-
202
- # Send JOIN command - Join a channel with given password
203
- def join(channel, password=nil)
204
- write_optional("JOIN #{channel}", password)
205
- end
206
-
207
- # Send PART command
208
- def part(channel, message=nil)
209
- raw("PART", channel, message)
210
- end
211
-
212
- # Send TOPIC command
213
- def topic(channel, topic=nil)
214
- raw("TOPIC", channel, topic)
215
- end
216
-
217
- # Send NAMES command
218
- def names(*channels)
219
- write("NAMES #{channels.join(',') unless channels.empty?}")
220
- end
221
-
222
- # Send LIST command
223
- def list(*channels)
224
- write("LIST #{channels.join(',') unless channels.empty?}")
225
- end
226
-
227
- # Send INVITE command
228
- def invite(nickname, channel)
229
- write("INVITE #{nickname} #{channel}")
230
- end
231
-
232
- # Send KICK command
233
- def kick(channel, user, comment=nil)
234
- raw("KICK", channel, user, comment)
235
- end
236
-
237
- # Send PRIVMSG command
238
- def privmsg(target, message)
239
- write("PRIVMSG #{target} :#{message}")
240
- end
241
-
242
- # Send NOTICE command
243
- def notice(target, message)
244
- write("NOTICE #{target} :#{message}")
245
- end
246
-
247
- # Send MOTD command
248
- def motd(target=nil)
249
- write_optional("MOTD", target)
250
- end
251
-
252
- # Send VERSION command
253
- def version(target=nil)
254
- write_optional("VERSION", target)
255
- end
256
-
257
- # Send STATS command
258
- def stats(*params)
259
- write_optional("STATS", params)
260
- end
261
-
262
- # Send TIME command
263
- def time(target=nil)
264
- write_optional("TIME", target)
265
- end
266
-
267
- # Send INFO command
268
- def info(target=nil)
269
- write_optional("INFO", target)
270
- end
271
-
272
- # Send SQUERY command
273
- def squery(target, message)
274
- write("SQUERY #{target} :#{message}")
275
- end
276
-
277
- # Send WHO command
278
- def who(*params)
279
- write_optional("WHO", params)
280
- end
281
-
282
- # Send WHOIS command
283
- def whois(*params)
284
- write_optional("WHOIS", params)
285
- end
286
-
287
- # Send WHOWAS command
288
- def whowas(*params)
289
- write_optional("WHOWAS", params)
290
- end
291
-
292
- # Send KILL command
293
- def kill(user, message)
294
- write("KILL #{user} :#{message}")
295
- end
296
-
297
- # Send PING command
298
- def ping(server)
299
- write("PING #{server}")
300
- end
301
-
302
- # Send PONG command
303
- def pong(server)
304
- write("PONG #{server}")
305
- end
306
-
307
- # Send AWAY command
308
- def away(message=nil)
309
- raw("AWAY", message)
310
- end
311
-
312
- # Send USERS command
313
- def users(target=nil)
314
- write_optional("USERS", target)
315
- end
316
-
317
- # Send USERHOST command
318
- def userhost(*users)
319
- write("USERHOST #{users.join(' ')}")
320
- end
321
-
322
- # Close our socket instance
323
- def close
324
- @socket.close if connected?
325
- end
326
- end
327
- end
328
- end
329
-