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
@@ -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