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,19 +0,0 @@
1
- require 'cinch'
2
-
3
- bot = Cinch.setup do
4
- server "irc.freenode.org"
5
- channels %w{ #cinch }
6
- end
7
-
8
- bot.add_custom_pattern(:friends, /injekt|lee|john|bob/)
9
- bot.add_custom_pattern(:hex, /[\dA-Fa-f]+/)
10
-
11
- bot.plugin("I like :person-friends", :prefix => false) do |m|
12
- m.reply "I like #{m.args[:person]} too!"
13
- end
14
-
15
- bot.plugin("checkhex :n-hex") do |m|
16
- m.answer "Yes, #{m.args[:n]} is indeed hex."
17
- end
18
-
19
- bot.run
@@ -1,25 +0,0 @@
1
- require 'cinch'
2
-
3
- bot = Cinch.setup do
4
- server "irc.freenode.org"
5
- channels %w( #cinch )
6
- end
7
-
8
- bot.plugin "default" do |m|
9
- m.reply "default prefix"
10
- end
11
-
12
- bot.plugin "custom", :prefix => '@' do |m|
13
- m.reply "custom prefix"
14
- end
15
-
16
- bot.plugin "botnick", :prefix => :botnick do |m|
17
- m.reply "botnick prefix"
18
- end
19
-
20
- bot.plugin "botnick2", :prefix => bot.nick do |m|
21
- m.reply "another botnick prefix"
22
- end
23
-
24
- bot.run
25
-
@@ -1,31 +0,0 @@
1
- require 'cinch'
2
- require 'open-uri'
3
- require 'nokogiri'
4
- require 'cgi'
5
-
6
- bot = Cinch.setup do
7
- server "irc.freenode.net"
8
- nick "MrCinch"
9
- channels %w/ #cinch /
10
- end
11
-
12
- # Extremely basic method, grabs the first result returned by Google
13
- # or "No results found" otherwise
14
- def google(query)
15
- url = "http://www.google.com/search?q=#{CGI.escape(query)}"
16
- res = Nokogiri::HTML(open(url)).at("h3.r")
17
-
18
- title = res.text
19
- link = res.at('a')[:href]
20
- desc = res.at("./following::div").children.first.text
21
- rescue
22
- "No results found"
23
- else
24
- CGI.unescape_html "#{title} - #{desc} (#{link})"
25
- end
26
-
27
- bot.plugin("google :query") do |m|
28
- m.reply google(m.args[:query])
29
- end
30
-
31
- bot.run
@@ -1,13 +0,0 @@
1
- require 'cinch'
2
-
3
- bot = Cinch.setup do
4
- server "irc.freenode.org"
5
- channels %w( #cinch )
6
- end
7
-
8
- bot.plugin "hello" do |m|
9
- m.reply "Hello, #{m.nick}!"
10
- end
11
-
12
- bot.run
13
-
@@ -1,26 +0,0 @@
1
- require 'cinch'
2
-
3
- bot = Cinch.setup do
4
- server "irc.freenode.org"
5
- nick "CinchBot"
6
- channels %w( #cinch )
7
- end
8
-
9
- # Who should be able to access these plugins
10
- admin = 'injekt'
11
-
12
- bot.plugin "join :channel", :nick => admin do |m|
13
- bot.join m.args[:channel]
14
- end
15
-
16
- bot.plugin "part :channel", :nick => admin do |m|
17
- bot.part m.args[:channel]
18
- end
19
-
20
- # Part current channel if none is given
21
- bot.plugin "part", :nick => admin do |m|
22
- bot.part m.channel
23
- end
24
-
25
- bot.run
26
-
@@ -1,40 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'cinch'
4
-
5
- bot = Cinch.setup do
6
- server "irc.freenode.org"
7
- channels %w( #cinch )
8
- end
9
-
10
- class Memo < Struct.new(:nick, :channel, :text, :time)
11
- def to_s
12
- "[#{time.asctime}] <#{channel}/#{nick}> #{text}"
13
- end
14
- end
15
-
16
- @memos = {}
17
-
18
- bot.on :privmsg do |m|
19
- if @memos.has_key?(m.nick)
20
- bot.privmsg m.nick, @memos[m.nick].to_s
21
- @memos.delete(m.nick)
22
- end
23
- end
24
-
25
- bot.plugin("memo :nick-string :memo") do |m|
26
- nick = m.args[:nick]
27
-
28
- if @memos.key?(nick)
29
- m.reply "There's already a memo #{nick}. You can only store one right now"
30
- elsif nick == m.nick
31
- m.reply "You can't leave memos for yourself.."
32
- elsif nick == bot.options.nick
33
- m.reply "You can't leave memos for me.."
34
- else
35
- @memos[nick] = Memo.new(m.nick, m.channel, m.args[:memo], Time.new)
36
- m.reply "Added memo for #{nick}"
37
- end
38
- end
39
-
40
- bot.run
@@ -1,14 +0,0 @@
1
- require 'cinch'
2
-
3
- bot = Cinch.setup do
4
- server "irc.freenode.org"
5
- nick "CinchBot"
6
- channels %w/ #cinch /
7
- end
8
-
9
- bot.plugin("msg :who :text") do |m|
10
- bot.privmsg m.args[:who], m.args[:text]
11
- end
12
-
13
- bot.run
14
-
@@ -1,19 +0,0 @@
1
- require 'cinch'
2
-
3
- bot = Cinch.setup do
4
- server "irc.freenode.org"
5
- channels %w( #cinch )
6
- end
7
-
8
- bot.plugin("say :n-digit :text") do |m|
9
- m.args[:n].to_i.times {
10
- m.reply m.args[:text]
11
- }
12
- end
13
-
14
- bot.plugin("say :text-word :rest") do |m|
15
- stuff = [m.args[:text], m.args[:rest]].join(' ')
16
- m.reply stuff
17
- end
18
-
19
- bot.run
@@ -1,41 +0,0 @@
1
- require 'cinch'
2
-
3
- users = {}
4
-
5
- class Seen < Struct.new(:who, :where, :what, :time)
6
- def to_s
7
- "[#{time.asctime}] #{who} was seen in #{where} saying #{what}"
8
- end
9
- end
10
-
11
- bot = Cinch.setup(
12
- :server => 'irc.freenode.org',
13
- :channels => ['#cinch'],
14
- :prefix => '!',
15
- :verbose => true,
16
- )
17
-
18
- # Only log a PRIVMSG
19
- bot.on :privmsg do |m|
20
- # Dont record a private message
21
- unless m.private?
22
- users[m.nick] = Seen.new(m.nick, m.channel, m.text, Time.new)
23
- end
24
- end
25
-
26
- bot.plugin("seen :nick") do |m|
27
- nick = m.args[:nick]
28
-
29
- if nick == bot.nick
30
- m.reply "That's me!"
31
- elsif nick == m.nick
32
- m.reply "That's you!"
33
- elsif users.key?(nick)
34
- m.reply users[nick].to_s
35
- else
36
- m.reply "I haven't seen #{nick}"
37
- end
38
- end
39
-
40
- bot.run
41
-
@@ -1,31 +0,0 @@
1
- require 'cinch'
2
- require 'open-uri'
3
- require 'nokogiri'
4
- require 'cgi'
5
-
6
- # This bot connects to urban dictionary and returns the first result
7
- # for a given query, replying with the result directly to the sender
8
-
9
- bot = Cinch.setup do
10
- server "irc.freenode.net"
11
- nick "MrCinch"
12
- channels %w/ #cinch /
13
- end
14
-
15
- # This method assumes everything will go ok, it's not the best method
16
- # of doing this *by far* and is simply a helper method to show how it
17
- # can be done.. it works!
18
- def urban_dict(query)
19
- url = "http://www.urbandictionary.com/define.php?term=#{CGI.escape(query)}"
20
- CGI.unescape_html Nokogiri::HTML(open(url)).at("div.definition").text.gsub(/\s+/, ' ') rescue nil
21
- end
22
-
23
- bot.plugin("urban :query") do |m|
24
- m.answer urban_dict(m.args[:query]) || "No results found"
25
- end
26
-
27
- bot.run
28
-
29
- # injekt> !urban cinch
30
- # MrCinch> injekt: describing an action that's extremely easy.
31
-
@@ -1,34 +0,0 @@
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
@@ -1,368 +0,0 @@
1
- module Cinch
2
-
3
- # == Description
4
- # The base for an IRC connection
5
- # TODO: More documentation
6
- #
7
- # == Example
8
- # bot = Cinch::Base.new :server => 'irc.freenode.org'
9
- #
10
- # bot.on :join do |m|
11
- # m.reply "Welcome to #{m.channel}, #{m.nick}!" unless m.nick == bot.nick
12
- # end
13
- #
14
- # bot.plugin "say :text" do |m|
15
- # m.reply m.args[:text]
16
- # end
17
- #
18
- # == Author
19
- # * Lee Jarvis - ljjarvis@gmail.com
20
- class Base
21
-
22
- # A Hash holding rules and attributes
23
- attr_reader :rules
24
-
25
- # A Hash holding listeners and reply Procs
26
- attr_reader :listeners
27
-
28
- # An OpenStruct holding all configuration options
29
- attr_reader :options
30
-
31
- # Our IRC::Socket instance
32
- attr_reader :irc
33
-
34
- # Default options hash
35
- DEFAULTS = {
36
- :port => 6667,
37
- :nick => "Cinch",
38
- :nick_suffix => '_',
39
- :username => 'cinch',
40
- :realname => "Cinch IRC Bot Building Framework",
41
- :prefix => '!',
42
- :usermode => 0,
43
- :password => nil,
44
- :ssl => false,
45
- }
46
-
47
- # Options can be passed via a hash, a block, or on the instance
48
- # independantly. Or of course via the command line
49
- #
50
- # == Example
51
- # # With a Hash
52
- # bot = Cinch::Base.new(:server => 'irc.freenode.org')
53
- #
54
- # # With a block
55
- # bot = Cinch::Base.new do
56
- # server "irc.freenode.org"
57
- # end
58
- #
59
- # # After the instance is created
60
- # bot = Cinch::Base.new
61
- # bot.options.server = "irc.freenode.org"
62
- #
63
- # # Nothing, but invoked with "ruby foo.rb -s irc.freenode.org"
64
- # bot = Cinch::Base.new
65
- def initialize(ops={}, &blk)
66
- options = DEFAULTS.merge(ops).merge(Options.new(&blk))
67
- @options = OpenStruct.new(options.merge(cli_ops))
68
-
69
- @rules = Rules.new
70
- @listeners = {}
71
- @listeners[:ctcp] = {}
72
-
73
- @irc = IRC::Socket.new(options[:server], options[:port], options[:ssl])
74
- @parser = IRC::Parser.new
75
-
76
- # Default listeners
77
- on(:ping) {|m| @irc.pong(m.text) }
78
-
79
- on(:ctcp, :version) {|m| m.ctcp_reply "Cinch IRC Bot Building Framework v#{Cinch::VERSION}"}
80
- end
81
-
82
- # Add a new plugin
83
- #
84
- # == Example
85
- # plugin('hello') do |m|
86
- # m.reply "Hello, #{m.nick}!"
87
- # end
88
- def plugin(rule, options={}, &blk)
89
- rule, keys = compile(rule)
90
-
91
- if @rules.has_rule?(rule)
92
- @rules.add_callback(rule, blk)
93
- @rules.merge_options(rule, options)
94
- else
95
- @rules.add_rule(rule, keys, options, blk)
96
- end
97
- end
98
- alias :rule :plugin
99
-
100
- # Add new listeners
101
- #
102
- # == Example
103
- # on(376) do |m|
104
- # m.join "#mychan"
105
- # end
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
- #
122
- # Note that when adding listeners for numberic IRC replies which
123
- # begin with a 0 (digit), make sure you define the command as a
124
- # String and not Integer. This is because 001.to_s == "1" so the
125
- # command will not work as expected.
126
- def on(*commands, &blk)
127
- if commands.first == :message
128
- rule, options = commands[1..2]
129
- options = {} unless options.is_a?(Hash)
130
- plugin(rule, options, &blk)
131
- elsif commands.first == :ctcp
132
- action = commands[1]
133
-
134
- if @listeners[:ctcp].key?(action)
135
- @listeners[:ctcp][action] << blk
136
- else
137
- @listeners[:ctcp][action] = [blk]
138
- end
139
- else
140
- commands.map {|x| x.to_s.downcase.to_sym }.each do |cmd|
141
- if @listeners.key?(cmd)
142
- @listeners[cmd] << blk
143
- else
144
- @listeners[cmd] = [blk]
145
- end
146
- end
147
- end
148
- end
149
-
150
- # This method builds a regular expression from your rule
151
- # and defines all named parameters, as well as dealing with
152
- # patterns.
153
- #
154
- # All patterns which exist in Cinch::IRC::Parser are supported
155
- #
156
- # == Examples
157
- # === Capturing digits
158
- # bot.plugin("say :text-digit")
159
- # * Does match !say 3
160
- # * Does not match !say 3 4
161
- # * Does not match !say foo
162
- #
163
- # === Both digit and word
164
- # bot.plugin("say :n-digit :text-word")
165
- # * Does match !say 3 foo
166
- # * Does not match !say 3 foo bar
167
- #
168
- # === Capturing until the end of the line
169
- # bot.plugin("say :text")
170
- # * Does match !say foo
171
- # * Does match !say foo bar
172
- #
173
- # === Or mix them all
174
- # bot.plugin("say :n-digit :who-word :text")
175
- #
176
- # Using "!say 3 injekt some text here" would provide
177
- # the following attributes
178
- # * m.args[:n] => 3
179
- # * m.args[:who] => injekt
180
- # * m.args[:text] => some text here
181
- def compile(rule)
182
- return [rule, []] if rule.is_a?(Regexp)
183
- keys = []
184
- special_chars = %w{. + ( )}
185
-
186
- pattern = rule.to_s.gsub(/((:[\w\-]+)|[\*#{special_chars.join}])/) do |match|
187
- case match
188
- when *special_chars
189
- Regexp.escape(match)
190
- else
191
- k = $2
192
- if k =~ /\-\w+$/
193
- key, type = k.split('-')
194
- keys << key[1..-1]
195
-
196
- if @parser.has_pattern?(type)
197
- "(#{@parser.pattern(type)})"
198
- else
199
- "([^\x00\r\n]+?)"
200
- end
201
- else
202
- keys << k[1..-1]
203
- "([^\x00\r\n]+?)"
204
- end
205
- end
206
- end
207
- ["^#{pattern}$", keys]
208
- end
209
-
210
- # Add a custom pattern for rule validation
211
- #
212
- # == Example
213
- # bot = Cinch.setup do
214
- # server 'irc.freenode.org'
215
- # port 6667
216
- # end
217
- #
218
- # bot.add_pattern(:number, /[0-9]/)
219
- #
220
- # bot.plugin("getnum :foo-number") do |m|
221
- # m.reply "Your number was: #{m.args[:foo]}"
222
- # end
223
- def add_pattern(name, pattern)
224
- @parser.add_pattern(name, pattern)
225
- end
226
- alias :add_custom_pattern :add_pattern
227
-
228
- # Run run run
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
-
240
- @irc.connect options.server, options.port
241
- @irc.pass options.password if options.password
242
- @irc.nick options.nick
243
- @irc.user options.username, options.usermode, '*', options.realname
244
-
245
- begin
246
- process(@irc.read) while @irc.connected?
247
- rescue Interrupt
248
- @irc.quit("Interrupted")
249
- puts "\nInterrupted. Shutting down .."
250
- exit
251
- end
252
- end
253
- alias :start :run
254
-
255
- private
256
-
257
- # Process the next line read from the server
258
- def process(line)
259
- return unless line
260
- message = @parser.parse(line)
261
- message.irc = @irc unless message.irc
262
- puts message if options.verbose
263
-
264
- # runs on any symbol
265
- @listeners[:any].each { |l| l.call(message) } if @listeners.key?(:any)
266
-
267
- if @listeners.key?(message.symbol)
268
- if message.symbol == :ctcp
269
- action = message.ctcp_action.downcase.to_sym
270
-
271
- if @listeners[:ctcp].include?(action)
272
- @listeners[:ctcp][action].each {|l| l.call(message) }
273
- end
274
- else
275
- @listeners[message.symbol].each {|l| l.call(message) }
276
- end
277
- end
278
-
279
- if [:privmsg].include?(message.symbol)
280
- rules.each do |rule|
281
- pattern = rule.to_s
282
- rule.keys.map! {|key| key.to_sym }
283
- prefix = nil
284
-
285
- if options.prefix
286
- if rule.options.key?(:prefix)
287
- if [:bot, :botnick, options.nick].include? rule.options[:prefix]
288
- prefix = options.nick + "[:,] "
289
- else
290
- prefix = rule.options[:prefix]
291
- end
292
- else
293
- if [:bot, :botnick, options.nick].include? options.prefix
294
- prefix = options.nick + "[:,] "
295
- else
296
- prefix = options.prefix
297
- end
298
- end
299
- end
300
-
301
- # insert prefix unless it already exists
302
- pattern.insert(1, prefix) unless pattern[1..prefix.size] == prefix
303
-
304
- if rule.options[:ignore_case]
305
- regex = Regexp.new(pattern, Regexp::IGNORECASE)
306
- else
307
- regex = Regexp.new(pattern)
308
- end
309
-
310
- if message.text && mdata = message.text.rstrip.match(regex)
311
- unless rule.keys.empty? || mdata.captures.empty?
312
- args = Hash[rule.keys.zip(mdata.captures)]
313
- message.args = args
314
- end
315
- # execute rule
316
- rule.execute(message)
317
- end
318
- end
319
- end
320
- end
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
-
347
- # Catch methods
348
- def method_missing(meth, *args, &blk) # :nodoc:
349
- if options.respond_to?(meth)
350
- options.send(meth)
351
- elsif @irc.respond_to?(meth)
352
- @irc.send(meth, *args)
353
- else
354
- super
355
- end
356
- end
357
-
358
- # Option management
359
- class Options < Hash # :nodoc:
360
- def initialize(&blk)
361
- instance_eval(&blk) if block_given?
362
- end
363
- def method_missing(meth, *args, &blk)
364
- self[meth] = args.first unless args.empty?
365
- end
366
- end
367
- end
368
- end