cinch 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []