cinch 0.3.1 → 0.3.2

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.
@@ -41,14 +41,19 @@ Your typical <em>Hello, World</em> application would go something like this:
41
41
  It doesn't take much to work out what's happening here, but I'll explain it anyway.
42
42
 
43
43
  First we run the <em>Cinch::setup</em> block which is required in every application. Cinch is boxed
44
- with a load of default values, so the <b>only</b> option required in this block is the <em>server</em>
45
- option.
44
+ with a load of default values, so the <b>only</b> option required in this block is the server.
46
45
 
47
46
  We then define a plugin using the <em>plugin</em> method and pass it a rule (a String in this
48
47
  case). Every plugin must be mapped to a rule. When the rule matches an IRC message its block is
49
48
  invoked, the contents of which contains your plugin interface. The variable passed to the block is
50
- an instance of IRC::Message. This provides us with a couple of helper methods which can be used to
51
- reply to a message. See Cinch::IRC::Message#reply and Cinch::IRC::Message#answer
49
+ an instance of Cinch::IRC::Message.
50
+
51
+ Cinch::IRC::Message also supplies us with some helper methods which aid in replying to users and
52
+ channels.
53
+
54
+ === See Also
55
+ * Cinch::IRC::Message#reply
56
+ * Cinch::IRC::Message#answer
52
57
 
53
58
  This example would provide the following response on IRC:
54
59
 
@@ -67,10 +72,10 @@ options within your script.
67
72
  ruby hello.rb -s irc.freenode.org -n Coolbot
68
73
  ruby hello.rb -C foo,bar
69
74
 
70
- Doing a <b>ruby hello.rb -h</b> provides all possible command line options. When using the <em>-C</em>
71
- or <em>--channels</em> option, the channel prefix is option, and if none is given the channel
72
- will be prefixed with a hash (#)
73
- character
75
+ Doing a <b>ruby hello.rb -h</b> provides all possible command line options.
76
+
77
+ When using the <em>-C</em> or <em>--channels</em> option, the channel prefix is
78
+ optional, and if none is given the channel will be prefixed with a hash (#) character.
74
79
 
75
80
  == Plugins
76
81
  Plugins are invoked using the command prefix character (which by default is set to <b>!</b>). You can
@@ -150,7 +155,7 @@ This would provide the following output on IRC
150
155
  Cinch also supports custom named parameter patterns. That's right, you can define you own
151
156
  pattern. Just like this:
152
157
 
153
- bot.add_custom_pattern(:friends, "injekt|lee|john|bob")
158
+ bot.add_custom_pattern(:friends, /injekt|lee|john|bob/)
154
159
 
155
160
  bot.plugin("I like :friend-friends", :prefix => false) do |m|
156
161
  m.reply "I like #{m.args[:friend]} too!"
@@ -176,7 +181,6 @@ channel or by email
176
181
 
177
182
  == Notes
178
183
  * RDoc API documentation is available {here}[http://rdoc.injekt.net/cinch]
179
- * Wiki is available {here}[https://github.com/injekt/cinch/wikis]
180
184
  * Issue and feature tracking is available {here}[https://github.com/injekt/cinch/issues]
181
185
  * Contribution in the form of bugfixes or feature requests is welcome and encouraged
182
186
 
@@ -1,12 +1,12 @@
1
- require '/home/injekt/code/cinch/lib/cinch'
1
+ require 'cinch'
2
2
 
3
3
  bot = Cinch.setup do
4
4
  server "irc.freenode.org"
5
5
  channels %w{ #cinch }
6
6
  end
7
7
 
8
- bot.add_custom_pattern(:friends, "injekt|lee|john|bob")
9
- bot.add_custom_pattern(:hex, "[\\dA-Fa-f]+")
8
+ bot.add_custom_pattern(:friends, /injekt|lee|john|bob/)
9
+ bot.add_custom_pattern(:hex, /[\dA-Fa-f]+/)
10
10
 
11
11
  bot.plugin("I like :person-friends", :prefix => false) do |m|
12
12
  m.reply "I like #{m.args[:person]} too!"
@@ -0,0 +1,34 @@
1
+ require 'open-uri'
2
+ require 'cinch'
3
+
4
+ # Automatically shorten URL's found in messages
5
+ # Using the tinyURL API
6
+
7
+ bot = Cinch.configure do
8
+ server "irc.freenode.org"
9
+ verbose true
10
+ channels %w/#cinch/
11
+ end
12
+
13
+ def shorten(url)
14
+ url = open("http://tinyurl.com/api-create.php?url=#{URI.escape(url)}").read
15
+ url == "Error" ? nil : url
16
+ rescue OpenURI::HTTPError
17
+ nil
18
+ end
19
+
20
+ bot.on :privmsg do |m|
21
+ unless m.private?
22
+ urls = URI.extract(m.text, "http")
23
+
24
+ unless urls.empty?
25
+ short_urls = urls.map {|url| shorten(url) }.compact
26
+
27
+ unless short_urls.empty?
28
+ m.reply short_urls.join(", ")
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ bot.start
@@ -9,7 +9,7 @@ require 'cinch/rules'
9
9
  require 'cinch/base'
10
10
 
11
11
  module Cinch
12
- VERSION = '0.3.1'
12
+ VERSION = '0.3.2'
13
13
 
14
14
  class << self
15
15
 
@@ -28,9 +28,6 @@ module Cinch
28
28
  # An OpenStruct holding all configuration options
29
29
  attr_reader :options
30
30
 
31
- # Hash of custom rule patterns
32
- attr_reader :custom_patterns
33
-
34
31
  # Our IRC::Socket instance
35
32
  attr_reader :irc
36
33
 
@@ -73,58 +70,15 @@ module Cinch
73
70
  @listeners = {}
74
71
  @listeners[:ctcp] = {}
75
72
 
76
- @custom_patterns = {
77
- 'digit' => "(\\d+?)",
78
- 'word' => "([a-zA-Z_]+?)",
79
- 'string' => "(\\w+?)",
80
- 'upper' => "([A-Z]+?)",
81
- 'lower' => "([a-z]+?)",
82
- }
83
-
84
73
  @irc = IRC::Socket.new(options[:server], options[:port], options[:ssl])
85
74
  @parser = IRC::Parser.new
86
75
 
87
76
  # Default listeners
88
77
  on(:ping) {|m| @irc.pong(m.text) }
89
78
 
90
- on(433) do |m|
91
- @options.nick += @options.nick_suffix
92
- @irc.nick @options.nick
93
- end
94
-
95
- if @options.respond_to?(:channels)
96
- on("004") { @options.channels.each {|c| @irc.join(c) } }
97
- end
98
-
99
79
  on(:ctcp, :version) {|m| m.ctcp_reply "Cinch IRC Bot Building Framework v#{Cinch::VERSION}"}
100
80
  end
101
81
 
102
- # Parse command line options
103
- def cli_ops
104
- options = {}
105
- if ARGV.any?
106
- begin
107
- OptionParser.new do |op|
108
- op.on("-s server") {|v| options[:server] = v }
109
- op.on("-p port") {|v| options[:port] = v.to_i }
110
- op.on("-n nick") {|v| options[:nick] = v }
111
- op.on("-c command_prefix") {|v| options[:prefix] = v }
112
- op.on("-v", "--verbose", "Enable verbose mode") {|v| options[:verbose] = true }
113
- op.on("--ssl") {|v| options[:ssl] = true }
114
- op.on("-C", "--channels x,y,z", Array, "Autojoin channels") {|v|
115
- options[:channels] = v.map {|c| %w(# + &).include?(c[0].chr) ? c : c.insert(0, '#') }
116
- }
117
- end.parse(ARGV)
118
- rescue OptionParser::MissingArgument => err
119
- warn "Missing values for options: #{err.args.join(', ')}\nFalling back to default"
120
- rescue OptionParser::InvalidOption => err
121
- warn err.message
122
- exit
123
- end
124
- end
125
- options
126
- end
127
-
128
82
  # Add a new plugin
129
83
  #
130
84
  # == Example
@@ -150,6 +104,21 @@ module Cinch
150
104
  # m.join "#mychan"
151
105
  # end
152
106
  #
107
+ # This method also provides an alternative way to define a rule.
108
+ # The following are equivalent
109
+ # plugin("foo bar") do |m|
110
+ # m.reply "baz!"
111
+ # end
112
+ #
113
+ # on(:message, "foo bar") do |m|
114
+ # m.reply "baz!"
115
+ # end
116
+ #
117
+ # This also gives us the opportunity to reply to CTCP messages
118
+ # on(:ctcp, :time) do |m|
119
+ # m.ctcp_reply Time.now.asctime
120
+ # end
121
+ #
153
122
  # Note that when adding listeners for numberic IRC replies which
154
123
  # begin with a 0 (digit), make sure you define the command as a
155
124
  # String and not Integer. This is because 001.to_s == "1" so the
@@ -182,20 +151,9 @@ module Cinch
182
151
  # and defines all named parameters, as well as dealing with
183
152
  # patterns.
184
153
  #
185
- # So far 3 patterns are supported:
186
- #
187
- # * word - matches [a-zA-Z_]+
188
- # * string - matches \w+
189
- # * digit - matches \d+
190
- # * lower - matches [a-z]+
191
- # * upper - matches [A-Z]+
154
+ # All patterns which exist in Cinch::IRC::Parser are supported
192
155
  #
193
156
  # == Examples
194
- # === Capturing individual words
195
- # bot.plugin("say :text-word")
196
- # * Does match !say foo
197
- # * Does not match !say foo bar baz
198
- #
199
157
  # === Capturing digits
200
158
  # bot.plugin("say :text-digit")
201
159
  # * Does match !say 3
@@ -217,9 +175,9 @@ module Cinch
217
175
  #
218
176
  # Using "!say 3 injekt some text here" would provide
219
177
  # the following attributes
220
- # m.args[:n] => 3
221
- # m.args[:who] => injekt
222
- # m.args[:text] => some text here
178
+ # * m.args[:n] => 3
179
+ # * m.args[:who] => injekt
180
+ # * m.args[:text] => some text here
223
181
  def compile(rule)
224
182
  return [rule, []] if rule.is_a?(Regexp)
225
183
  keys = []
@@ -235,8 +193,8 @@ module Cinch
235
193
  key, type = k.split('-')
236
194
  keys << key[1..-1]
237
195
 
238
- if @custom_patterns.include?(type)
239
- @custom_patterns[type]
196
+ if @parser.has_pattern?(type)
197
+ "(#{@parser.pattern(type)})"
240
198
  else
241
199
  "([^\x00\r\n]+?)"
242
200
  end
@@ -249,7 +207,7 @@ module Cinch
249
207
  ["^#{pattern}$", keys]
250
208
  end
251
209
 
252
- # Add a custom 'type', for rule validation
210
+ # Add a custom pattern for rule validation
253
211
  #
254
212
  # == Example
255
213
  # bot = Cinch.setup do
@@ -257,18 +215,28 @@ module Cinch
257
215
  # port 6667
258
216
  # end
259
217
  #
260
- # bot.add_custom_pattern(:number, "[0-9]")
218
+ # bot.add_pattern(:number, /[0-9]/)
261
219
  #
262
220
  # bot.plugin("getnum :foo-number") do |m|
263
221
  # m.reply "Your number was: #{m.args[:foo]}"
264
222
  # end
265
- def add_custom_pattern(name, pattern)
266
- @custom_patterns[name.to_s] = "(#{pattern.to_s})"
223
+ def add_pattern(name, pattern)
224
+ @parser.add_pattern(name, pattern)
267
225
  end
268
- alias :add_pattern :add_custom_pattern
226
+ alias :add_custom_pattern :add_pattern
269
227
 
270
228
  # Run run run
271
229
  def run
230
+ # Configure some runtime listeners
231
+ on(433) do |m|
232
+ @options.nick += @options.nick_suffix
233
+ @irc.nick @options.nick
234
+ end
235
+
236
+ if @options.respond_to?(:channels)
237
+ on("004") { @options.channels.each {|c| @irc.join(c) } }
238
+ end
239
+
272
240
  @irc.connect options.server, options.port
273
241
  @irc.pass options.password if options.password
274
242
  @irc.nick options.nick
@@ -278,17 +246,19 @@ module Cinch
278
246
  process(@irc.read) while @irc.connected?
279
247
  rescue Interrupt
280
248
  @irc.quit("Interrupted")
281
- puts "\nInterrupted. Shutting down.."
249
+ puts "\nInterrupted. Shutting down .."
282
250
  exit
283
251
  end
284
252
  end
285
253
  alias :start :run
286
254
 
255
+ private
256
+
287
257
  # Process the next line read from the server
288
258
  def process(line)
289
259
  return unless line
290
260
  message = @parser.parse(line)
291
- message.irc = @irc
261
+ message.irc = @irc unless message.irc
292
262
  puts message if options.verbose
293
263
 
294
264
  # runs on any symbol
@@ -307,13 +277,10 @@ module Cinch
307
277
  end
308
278
 
309
279
  if [:privmsg].include?(message.symbol)
310
-
311
- # At the moment we must traverse all possible rules, which
312
- # could get clunky with a lot of rules. This is because each
313
- # rule can be unique in what prefix it uses, in future some kind
314
- # of loose checking should be put in place
315
280
  rules.each do |rule|
316
281
  pattern = rule.to_s
282
+ rule.keys.map! {|key| key.to_sym }
283
+ prefix = nil
317
284
 
318
285
  if options.prefix
319
286
  if rule.options.key?(:prefix)
@@ -329,17 +296,20 @@ module Cinch
329
296
  prefix = options.prefix
330
297
  end
331
298
  end
332
- else
333
- prefix = nil
334
299
  end
300
+
301
+ # insert prefix unless it already exists
302
+ pattern.insert(1, prefix) unless pattern[1..prefix.size] == prefix
335
303
 
336
- if prefix && pattern[1..prefix.size] != prefix
337
- pattern.insert(1, prefix)
304
+ if rule.options[:ignore_case]
305
+ regex = Regexp.new(pattern, Regexp::IGNORECASE)
306
+ else
307
+ regex = Regexp.new(pattern)
338
308
  end
339
309
 
340
- if message.text && mdata = message.text.rstrip.match(Regexp.new(pattern))
310
+ if message.text && mdata = message.text.rstrip.match(regex)
341
311
  unless rule.keys.empty? || mdata.captures.empty?
342
- args = Hash[rule.keys.map {|k| k.to_sym}.zip(mdata.captures)]
312
+ args = Hash[rule.keys.zip(mdata.captures)]
343
313
  message.args = args
344
314
  end
345
315
  # execute rule
@@ -349,6 +319,31 @@ module Cinch
349
319
  end
350
320
  end
351
321
 
322
+ # Parse command line options
323
+ def cli_ops
324
+ options = {}
325
+ if ARGV.any?
326
+ begin
327
+ OptionParser.new do |op|
328
+ op.on("-s server") {|v| options[:server] = v }
329
+ op.on("-p port") {|v| options[:port] = v.to_i }
330
+ op.on("-n nick") {|v| options[:nick] = v }
331
+ op.on("-c command_prefix") {|v| options[:prefix] = v }
332
+ op.on("-v", "--verbose", "Enable verbose mode") {|v| options[:verbose] = true }
333
+ op.on("--ssl") {|v| options[:ssl] = true }
334
+ op.on("-C", "--channels x,y,z", Array, "Autojoin channels") {|v|
335
+ options[:channels] = v.map {|c| %w(# + &).include?(c[0].chr) ? c : c.insert(0, '#') }
336
+ }
337
+ end.parse(ARGV)
338
+ rescue OptionParser::MissingArgument => err
339
+ warn "Missing values for options: #{err.args.join(', ')}\nFalling back to default"
340
+ rescue OptionParser::InvalidOption => err
341
+ warn err.message
342
+ end
343
+ end
344
+ options
345
+ end
346
+
352
347
  # Catch methods
353
348
  def method_missing(meth, *args, &blk) # :nodoc:
354
349
  if options.respond_to?(meth)
@@ -7,9 +7,8 @@ module Cinch
7
7
  #
8
8
  # == Example
9
9
  # require 'cinch/irc/parser'
10
- # include Cinch::IRC::Parser
11
10
  #
12
- # message = parse(":foo!bar@myhost.com PRIVMSG #mychan :ding dong!")
11
+ # message = Cinch::IRC::Parser.parse(":foo!bar@myhost.com PRIVMSG #mychan :ding dong!")
13
12
  #
14
13
  # message.class #=> Cinch::IRC::Message
15
14
  # message.command #=> PRIVMSG
@@ -31,24 +30,30 @@ module Cinch
31
30
 
32
31
  # Add a new pattern
33
32
  def add_pattern(key, pattern)
34
- raise ArgumentError, "Pattern is not a regular expression" unless pattern.is_a?(Regexp)
35
33
  @patterns[key.to_sym] = pattern
36
34
  end
37
35
 
38
36
  # Remove a pattern
39
37
  def remove_pattern(key)
40
- key = key.to_sym
41
- @patterns.delete(key) if @patterns.key?(key)
38
+ @patterns.delete(key.to_sym)
42
39
  end
43
40
 
44
41
  # Helper for our patterns Hash
45
42
  def pattern(key)
46
- @patterns[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)
47
49
  end
48
50
 
49
51
  # Set up some default patterns used directly by this class
50
52
  def setup_patterns
51
53
  add_pattern :letter, /[a-zA-Z]/
54
+ add_pattern :word, /\w+/
55
+ add_pattern :upper, /[A-Z]+/
56
+ add_pattern :lower, /[a-z]+/
52
57
  add_pattern :hex, /[\dA-Fa-f]/
53
58
 
54
59
  add_pattern :ip4addr, /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
@@ -33,12 +33,6 @@ describe "Cinch::Base" do
33
33
  @base.listeners.should include :ctcp
34
34
  @base.listeners[:ctcp].should include :version
35
35
  end
36
-
37
- it "should add default custom_patterns" do
38
- [:digit, :word, :string, :upper, :lower].each do |l|
39
- @base.custom_patterns.should include l.to_s
40
- end
41
- end
42
36
  end
43
37
 
44
38
  describe "#plugin" do
@@ -101,31 +95,6 @@ describe "Cinch::Base" do
101
95
  keys.should be_empty
102
96
  end
103
97
 
104
- it "should convert a digit pattern" do
105
- rule, keys = @base.compile(":foo-digit")
106
- rule.should == "^(\\d+?)$"
107
- end
108
-
109
- it "should convert a string pattern" do
110
- rule, keys = @base.compile(":foo-string")
111
- rule.should == "^(\\w+?)$"
112
- end
113
-
114
- it "should convert a word pattern" do
115
- rule, keys = @base.compile(":foo-word")
116
- rule.should == "^([a-zA-Z_]+?)$"
117
- end
118
-
119
- it "should convert a lowercase pattern" do
120
- rule, keys = @base.compile(":foo-lower")
121
- rule.should == "^([a-z]+?)$"
122
- end
123
-
124
- it "should convert an uppercase pattern" do
125
- rule, keys = @base.compile(":foo-upper")
126
- rule.should == "^([A-Z]+?)$"
127
- end
128
-
129
98
  it "should convert a custom pattern" do
130
99
  @base.add_custom_pattern(:people, "foo|bar|baz")
131
100
  rule, keys = @base.compile(":foo-people")
@@ -38,10 +38,6 @@ describe "IRC::Parser" do
38
38
  @parser.add_pattern(:custom, /foo/)
39
39
  @parser.patterns.key?(:custom)
40
40
  end
41
-
42
- it "should raise ArgumentError if pattern is not Regexp" do
43
- lambda { @parser.add_pattern(:custom, 'foo') }.should raise_error(ArgumentError)
44
- end
45
41
  end
46
42
 
47
43
  describe "#remove_pattern" do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cinch
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 1
10
- version: 0.3.1
9
+ - 2
10
+ version: 0.3.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Lee 'injekt' Jarvis
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-05-26 00:00:00 +01:00
18
+ date: 2010-07-08 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -45,33 +45,34 @@ extra_rdoc_files:
45
45
  files:
46
46
  - README.rdoc
47
47
  - Rakefile
48
- - spec/base_spec.rb
49
- - spec/spec.opts
50
- - spec/irc/socket_spec.rb
51
- - spec/irc/helper.rb
52
48
  - spec/irc/message_spec.rb
53
49
  - spec/irc/parser_spec.rb
54
- - spec/helper.rb
50
+ - spec/irc/socket_spec.rb
51
+ - spec/irc/helper.rb
55
52
  - spec/rules_spec.rb
53
+ - spec/spec.opts
56
54
  - spec/options_spec.rb
55
+ - spec/base_spec.rb
56
+ - spec/helper.rb
57
57
  - lib/cinch.rb
58
- - lib/cinch/base.rb
59
- - lib/cinch/irc/message.rb
60
58
  - lib/cinch/irc/socket.rb
59
+ - lib/cinch/irc/message.rb
61
60
  - lib/cinch/irc/parser.rb
62
- - lib/cinch/rules.rb
63
61
  - lib/cinch/irc.rb
64
- - examples/google.rb
65
- - examples/custom_prefix.rb
62
+ - lib/cinch/base.rb
63
+ - lib/cinch/rules.rb
66
64
  - examples/join_part.rb
65
+ - examples/named-param-types.rb
66
+ - examples/url_shorten.rb
67
+ - examples/custom_prefix.rb
68
+ - examples/urban_dict.rb
67
69
  - examples/msg.rb
70
+ - examples/hello.rb
71
+ - examples/seen.rb
68
72
  - examples/memo.rb
69
73
  - examples/custom_patterns.rb
70
- - examples/urban_dict.rb
71
- - examples/seen.rb
72
- - examples/hello.rb
74
+ - examples/google.rb
73
75
  - examples/autovoice.rb
74
- - examples/named-param-types.rb
75
76
  has_rdoc: true
76
77
  homepage: http://rdoc.injekt.net/cinch
77
78
  licenses: []