bitbckt-botbckt 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :patch: 0
3
3
  :major: 0
4
- :minor: 3
4
+ :minor: 4
data/bin/botbckt ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'pathname'
5
+ local_lib = Pathname.new(__FILE__).expand_path.dirname.join('..', 'lib', 'botbckt.rb')
6
+ if local_lib.file?
7
+ require local_lib
8
+ else
9
+ require 'botbckt'
10
+ end
11
+ rescue LoadError
12
+ require 'rubygems'
13
+ require 'botbckt'
14
+ end
15
+
16
+ Botbckt::Cmd.run(ARGV)
data/lib/botbckt/bot.rb CHANGED
@@ -3,12 +3,14 @@ module Botbckt #:nodoc:
3
3
  # Create a new IRC bot. See Bot.start to get started.
4
4
  #
5
5
  class Bot
6
+ include Singleton
7
+ include ActiveSupport::BufferedLogger::Severity
6
8
 
7
9
  AFFIRMATIVE = ["'Sea, mhuise.", "In Ordnung", "Ik begrijp", "Alles klar", "Ok.", "Roger.", "You don't have to tell me twice.", "Ack. Ack.", "C'est bon!"]
8
10
  NEGATIVE = ["Titim gan éirí ort.", "Gabh mo leithscéal?", "No entiendo", "excusez-moi", "Excuse me?", "Huh?", "I don't understand.", "Pardon?", "It's greek to me."]
9
-
10
- cattr_accessor :commands
11
- @@commands = { }
11
+
12
+ attr_accessor :logger
13
+ attr_accessor :store
12
14
 
13
15
  # ==== Parameters
14
16
  # options<Hash{Symbol => String,Integer}>
@@ -21,22 +23,90 @@ module Botbckt #:nodoc:
21
23
  # :channels<Array[String]>:: An array of channels to join. Channel names should *not* include the '#' prefix. Required.
22
24
  # :log<String>:: The name of a log file. Defaults to 'botbckt.log'.
23
25
  # :log_level<Integer>:: The minimum severity level to log. Defaults to 1 (INFO).
26
+ # :pid<String>:: The name of a file to drop the PID. Defaults to 'botbckt.pid'.
27
+ # :daemonize<Boolean>:: Fork and background the process. Defaults to true.
28
+ # :backend_host<String>:: The hostname of a Redis store. Optional.
29
+ # :backend_port<Integer>:: The port used by the Redis store. Optional.
24
30
  #
25
31
  def self.start(options)
26
- EventMachine::run do
27
- Botbckt::IRC.connect(options)
32
+
33
+ self.instance.logger = ActiveSupport::BufferedLogger.new options.delete(:log) || 'botbckt.log',
34
+ options.delete(:log_level) || INFO
35
+
36
+ host, port = options.delete(:backend_host), options.delete(:backend_port)
37
+ daemonize = options.delete(:daemonize)
38
+ pid = options.delete(:pid) || 'botbckt.pid'
39
+
40
+ if daemonize || daemonize.nil?
41
+ EventMachine::fork_reactor do
42
+ Botbckt::IRC.connect(options)
43
+
44
+ if host && port
45
+ self.instance.store = Store.new(host, port)
46
+ end
47
+
48
+ if pid
49
+ File.open(pid, 'w'){ |f| f.write("#{Process.pid}") }
50
+ at_exit { File.delete(pid) if File.exist?(pid) }
51
+ end
52
+
53
+ end
54
+ else
55
+ EventMachine::run do
56
+ Botbckt::IRC.connect(options)
57
+ end
28
58
  end
29
59
  end
30
60
 
61
+ # Registers a new command.
62
+ #
63
+ # ==== Parameters
64
+ # command<~to_sym>:: Trigger to register. Required.
65
+ # callable<~call>:: Callback or class with #call to execute. Required.
66
+ #
67
+ def register(command, callable)
68
+ @commands ||= { }
69
+ @commands[command.to_sym] = callable
70
+ end
71
+
72
+ # Returns currently registered commands.
73
+ #
74
+ def commands
75
+ @commands
76
+ end
77
+
78
+ #--
79
+ # TODO: Forwardable?
80
+ #++
81
+ def set(key, value, &block)
82
+ self.store && self.store.set(key, value, &block)
83
+ end
84
+
85
+ #--
86
+ # TODO: Forwardable?
87
+ #++
88
+ def get(key, &block)
89
+ self.store && self.store.get(key, &block)
90
+ end
91
+
92
+ #--
93
+ # TODO: Forwardable?
94
+ #++
95
+ def increment!(key, &block)
96
+ self.store && self.store.increment!(key, &block)
97
+ end
98
+
31
99
  # ==== Parameters
32
100
  # command<Symbol>:: The name of a registered command to run. Required.
101
+ # sender<String>:: The sender (incl. hostmask) of the trigger. Required.
102
+ # channel<String>:: The channel on which the command was triggered. Required.
33
103
  # *args:: Arguments to be passed to the command. Optional.
34
104
  #
35
105
  #--
36
106
  # TODO: Before/after callbacks?
37
107
  #++
38
- def self.run(command, sender, channel, *args)
39
- callable = self.commands[command.to_sym]
108
+ def run(command, sender, channel, *args)
109
+ callable = commands[command.to_sym]
40
110
 
41
111
  if callable.is_a?(Class)
42
112
  # Callables are Singletons; we use #create! as a convention to give
@@ -44,7 +114,10 @@ module Botbckt #:nodoc:
44
114
  callable = callable.create!(sender, channel, *args)
45
115
  end
46
116
 
47
- callable.respond_to?(:call) ? callable.call(sender, channel, *args) : say(befuddled, channel)
117
+ callable.respond_to?(:call) ? callable.call(sender, channel, *args) : say(Bot.befuddled, channel)
118
+ # TODO: Log me.
119
+ rescue StandardError => e
120
+ say Bot.befuddled, channel
48
121
  end
49
122
 
50
123
  # Returns a random "affirmative" message. Use to acknowledge user input.
@@ -70,10 +143,14 @@ module Botbckt #:nodoc:
70
143
  # msg<String>:: A message to send to the channel
71
144
  # channel<String>:: The channel to send the message. Required.
72
145
  #
73
- def self.say(msg, channel)
146
+ def say(msg, channel)
74
147
  Botbckt::IRC.connection.say msg, channel
75
148
  end
149
+
150
+ def log(msg, level = INFO) #:nodoc:
151
+ self.logger.add(level, msg)
152
+ end
76
153
 
77
154
  end
78
155
 
79
- end
156
+ end
@@ -0,0 +1,86 @@
1
+ module Botbckt #:nodoc:
2
+
3
+ class Cmd
4
+
5
+ SEVERITY = %w{0 1 2 3 4 5}
6
+ SEVERITY_ALIASES = { "DEBUG" => 0, "INFO" => 1, "WARN" => 2, "ERROR" => 3, "FATAL" => 4, "UNKNOWN" => 5}
7
+
8
+ def self.run(args)
9
+ Botbckt::Bot.start parse(args)
10
+ end
11
+
12
+ def self.parse(args)
13
+ options = {}
14
+ options[:user] = 'botbckt'
15
+ options[:port] = 6667
16
+ options[:channels] = []
17
+ options[:backend_port] = 6379
18
+
19
+ opts = OptionParser.new do |opts|
20
+ opts.banner = 'Usage: botbckt [options]'
21
+
22
+ opts.separator ''
23
+ opts.separator 'Options:'
24
+
25
+ opts.on('-u', '--user [USER]', 'Username to identify as. (default: botbckt)') do |user|
26
+ options[:user] = user
27
+ end
28
+
29
+ opts.on('-p', '--password [PASSWORD]', 'Password to authenticate with.') do |pass|
30
+ options[:password] = pass
31
+ end
32
+
33
+ opts.on('-s', '--server SERVER', 'Address of the IRC server.') do |server|
34
+ options[:server] = server
35
+ end
36
+
37
+ opts.on('-P', '--port [PORT]', Integer, 'Port of the IRC server. (default: 6667)') do |port|
38
+ options[:port] = port
39
+ end
40
+
41
+ opts.on('-c', '--channels [x,y,z]', Array,
42
+ 'Comma-separated list of channels to JOIN. Do not include the # prefix.') do |channels|
43
+ options[:channels] = channels
44
+ end
45
+
46
+ opts.on('-l', '--logfile [FILE]', 'Log file. (default botbckt.log)') do |log|
47
+ options[:log] = log
48
+ end
49
+
50
+ severities = SEVERITY_ALIASES.keys.join(', ')
51
+ opts.on('-V', '--verbosity [LEVEL]', SEVERITY, SEVERITY_ALIASES, Integer,
52
+ "Minimum severity level to log.Accepted values are: #{severities}. (default: INFO)") do |level|
53
+ options[:log_level] = level
54
+ end
55
+
56
+ opts.on('-b', '--backend-host [SERVER]', 'Address of a Redis server.') do |backend|
57
+ options[:backend_host] = backend
58
+ end
59
+
60
+ opts.on('-B', '--backend-port [PORT]', Integer, 'Port of a Redis server. (default: 6379)') do |port|
61
+ options[:backend_port] = port
62
+ end
63
+
64
+ opts.on('-D', '--[no-]daemonize', 'Fork and run in the background. (default: true)') do |daemon|
65
+ options[:daemonize] = daemon
66
+ end
67
+
68
+ opts.on('-i', '--pid FILE', 'File to store PID (default: botbckt.pid)') do |file|
69
+ options[:pid] = file
70
+ end
71
+
72
+ opts.on_tail('-h', '--help', 'Show this message.') do |help|
73
+ puts opts
74
+ exit
75
+ end
76
+ end
77
+
78
+ opts.parse!(args)
79
+ options
80
+ rescue OptionParser::ParseError
81
+ puts opts
82
+ exit
83
+ end
84
+
85
+ end
86
+ end
@@ -1,11 +1,10 @@
1
- require 'singleton'
2
-
3
1
  module Botbckt #:nodoc:
4
2
 
5
- # This acts as a kind of abstract class for Botbckt commands. Extend your
6
- # command class with this module to define new bot commands.
3
+ # This acts as a kind of abstract class for Botbckt commands. Subclass
4
+ # this class to define new bot commands.
7
5
  #
8
- # Command subclasses must (re-)define initialize and call.
6
+ # Command subclasses must (re-)define call. If any setup is needed, override
7
+ # create! and return self.instance.
9
8
  #
10
9
  class Command
11
10
  include Utilities
@@ -39,9 +38,10 @@ module Botbckt #:nodoc:
39
38
  #
40
39
  # ==== Parameters
41
40
  # command<Symbol>:: In-channel trigger for the command. Required.
41
+ # &block:: An optional block to execute, in lieu of call.
42
42
  #
43
43
  def self.trigger(command, &block)
44
- Botbckt::Bot.commands[command.to_sym] = block_given? ? block : self
44
+ Botbckt::Bot.instance.register(command, block_given? ? block : self)
45
45
  end
46
46
 
47
47
  # ==== Parameters
@@ -49,7 +49,7 @@ module Botbckt #:nodoc:
49
49
  # channel<String>:: The channel to send the message. Required.
50
50
  #
51
51
  def self.say(msg, channel)
52
- Botbckt::Bot.say(msg, channel) if msg
52
+ Botbckt::Bot.instance.say(msg, channel) if msg
53
53
  end
54
54
 
55
55
  # Proxy for Command.say
@@ -58,6 +58,18 @@ module Botbckt #:nodoc:
58
58
  self.class.say(msg, channel)
59
59
  end
60
60
 
61
+ def set(key, value, &block)
62
+ Botbckt::Bot.instance.set(key, value, &block)
63
+ end
64
+
65
+ def get(key, &block)
66
+ Botbckt::Bot.instance.get(key, &block)
67
+ end
68
+
69
+ def increment!(key, &block)
70
+ Botbckt::Bot.instance.increment!(key, &block)
71
+ end
72
+
61
73
  end
62
74
 
63
75
  end
@@ -0,0 +1,114 @@
1
+ module Botbckt #:nodoc:
2
+
3
+ # Sends text repeatedly to Google Translate via the JSON API
4
+ # to garble the output. Translation starts and ends with
5
+ # MAIN_LANGUAGE unless a language option is passed in. If
6
+ # an option is present it is used as the starting language.
7
+ #
8
+ #
9
+ # < user> ~gooble We the People of the United States, in Order to form a more perfect Union
10
+ # < botbckt> Popular in the United States to create the completed
11
+ #
12
+ # With language option:
13
+ #
14
+ # < user> ~gooble --german Guten tag
15
+ # < botbckt> Good day
16
+ #
17
+ # or the short version
18
+ #
19
+ # < user> ~gooble -de Guten tag
20
+ # < botbckt> Good day
21
+
22
+ class Gooble < Command
23
+
24
+ MAIN_LANGUAGE = "en"
25
+ TRANSLATE_ATTEMPTS = 4
26
+ # Yeah this is wordy but it's self documenting.
27
+ LANGUAGES =
28
+ {
29
+ "Albanian" => "sq",
30
+ "Arabic" => "ar",
31
+ "Bulgarian" => "bg",
32
+ "Catalan" => "ca",
33
+ "Chinese" => "zh-CN",
34
+ "Croatian" => "hr",
35
+ "Czech" => "cs",
36
+ "Danish" => "da",
37
+ "Dutch" => "nl",
38
+ "English" => "en",
39
+ "Estonian" => "et",
40
+ "Filipino" => "tl",
41
+ "Finnish" => "fi",
42
+ "French" => "fr",
43
+ "Galician" => "gl",
44
+ "German" => "de",
45
+ "Greek" => "el",
46
+ "Hebrew" => "iw",
47
+ "Hindi" => "hi",
48
+ "Hungarian" => "hu",
49
+ "Indonesian" => "id",
50
+ "Italian" => "it",
51
+ "Japanese" => "ja",
52
+ "Korean" => "ko",
53
+ "Latvian" => "lv",
54
+ "Lithuanian" => "lt",
55
+ "Maltese" => "mt",
56
+ "Norwegian" => "no",
57
+ "Polish" => "pl",
58
+ "Portuguese" => "pt",
59
+ "Romanian" => "ro",
60
+ "Russian" => "ru",
61
+ "Serbian" => "sr",
62
+ "Slovak" => "sk",
63
+ "Slovenian" => "sl",
64
+ "Spanish" => "es",
65
+ "Swedish" => "sv",
66
+ "Thai" => "th",
67
+ "Turkish" => "tr",
68
+ "Ukrainian" => "uk",
69
+ "Vietnamese" => "vi"
70
+ }
71
+
72
+ trigger :gooble do |sender, channel, gooble_string|
73
+ # start with english unless asked for something else
74
+ gooble_string =~ /(-\w{2}|--\w+)?(.*)/i
75
+ option, text = $1, $2
76
+ clean_option = option.gsub(/-/,'') if option
77
+
78
+ start_language = case option
79
+ when /^--/ then
80
+ LANGUAGES[clean_option.capitalize!] || MAIN_LANGUAGE
81
+ when /^-/ then
82
+ LANGUAGES.value?(clean_option) ? clean_option : MAIN_LANGUAGE
83
+ else
84
+ MAIN_LANGUAGE
85
+ end
86
+
87
+ # languages to use for the goobling
88
+ languages = [start_language]
89
+ languages << LANGUAGES.values.sort_by{ rand }.slice(0...TRANSLATE_ATTEMPTS)
90
+ languages << MAIN_LANGUAGE # always end with main
91
+
92
+ result = gooble(text, languages.flatten)
93
+ say result, channel
94
+ end
95
+
96
+ private
97
+
98
+ # args
99
+ # text<String>: text to translate
100
+ # langs<Array>: languages to translate from/to
101
+ def self.gooble(text, languages) #:nodoc:
102
+ if languages.length >= 2
103
+ pair = "#{languages[0]}|#{languages[1]}"
104
+ json = open("http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=#{CGI.escape(text)}&langpair=#{CGI.escape(pair)}")
105
+ response = JSON.parse(json.read) # could check for failed response with response['responseStatus'] != 200
106
+ languages.shift
107
+ gooble(response['responseData']['translatedText'], languages)
108
+ else
109
+ text
110
+ end
111
+ end
112
+
113
+ end
114
+ end
@@ -7,6 +7,10 @@ module Botbckt #:nodoc:
7
7
  # ... five minutes
8
8
  # < botbckt> user: message
9
9
  #
10
+ # < user> ~remind at 10:30 with message
11
+ # ... at 10:30
12
+ # < botbckt> user: message
13
+ #
10
14
  class Remind < Command
11
15
 
12
16
  SCALES = %w{ minute minutes second seconds hour hours }
@@ -14,20 +18,52 @@ module Botbckt #:nodoc:
14
18
  trigger :remind
15
19
 
16
20
  def call(user, channel, reminder_string)
21
+
22
+ case reminder_string
23
+ when /^in/i
24
+ msg, time = *relative_reminder(reminder_string)
25
+ when /^at/i
26
+ msg, time = *absolute_reminder(reminder_string)
27
+ else
28
+ say Botbckt::Bot.befuddled, channel
29
+ return
30
+ end
31
+
32
+ if msg && time
33
+ remind(freenode_split(user).first, channel, msg, time)
34
+ else
35
+ say Botbckt::Bot.befuddled, channel
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def relative_reminder(str) #:nodoc:
17
42
  # Somewhat faster than #match...
18
- reminder_string =~ /in (\d+) (\w+) with (.*)/i
43
+ str =~ /in (\d+) (\w+) with (.*)/i
19
44
  num, scale, msg = $1, $2, $3
20
45
 
21
46
  if SCALES.include?(scale)
22
47
  time = num.to_i.send(scale.to_sym).seconds
23
-
24
- remind(freenode_split(user).first, channel, msg, time)
48
+ [ msg, time ]
25
49
  else
26
- say Botbckt::Bot.befuddled, channel
50
+ [ ]
27
51
  end
28
52
  end
29
53
 
30
- private
54
+ def absolute_reminder(str) #:nodoc:
55
+ # Somewhat faster than #match...
56
+ str =~ /at (.*) with (.*)/i
57
+ time, msg = $1, $2
58
+
59
+ time = Time.parse(time) - Time.now
60
+
61
+ [ msg, time ]
62
+ # TODO: Log me.
63
+ rescue ArgumentError => e
64
+ # raised by Time.parse; do nothing, for now...
65
+ [ ]
66
+ end
31
67
 
32
68
  def remind(user, channel, msg, seconds) #:nodoc:
33
69
  EventMachine::Timer.new(seconds) do
@@ -13,22 +13,14 @@ module Botbckt #:nodoc:
13
13
  class StarJar < Command
14
14
 
15
15
  trigger :star
16
-
17
- attr_accessor :jar
18
-
19
- def self.create!(*args) #:nodoc:
20
- self.instance.jar ||= { }
21
- self.instance
22
- end
23
16
 
24
17
  # Adds a star to the jar for the user
25
18
  #
26
19
  # ==== Parameters
27
20
  # user<String>:: The user receiving a star. Required.
28
21
  #
29
- def push(user)
30
- @jar[user] ||= 0
31
- @jar[user] += 1
22
+ def push(user, &block)
23
+ increment! "starjar-#{user}", &block
32
24
  end
33
25
 
34
26
  # Removes a star from the jar for the user
@@ -36,21 +28,23 @@ module Botbckt #:nodoc:
36
28
  # ==== Parameters
37
29
  # user<String>:: The user being docked a star. Required.
38
30
  #
39
- def pop(user)
40
- @jar[user] ||= 0
31
+ def pop(user, &block)
32
+ get "starjar-#{user}" do |stars|
41
33
 
42
- if @jar[user] == 0
43
- return 0
44
- else
45
- @jar[user] -= 1
34
+ if stars
35
+ set "starjar-#{user}", stars - 1, &block
36
+ else
37
+ set "starjar-#{user}", 0, &block
38
+ end
46
39
  end
47
40
  end
48
41
 
49
42
  def call(giver, channel, receiver)
50
43
  receiver.split(' ').each do |rcv|
51
44
  if rcv != freenode_split(giver).first
52
- total = push(rcv)
53
- say "#{rcv}: Gold star for you! (#{total})", channel
45
+ push(rcv) do |total|
46
+ say "#{rcv}: Gold star for you! (#{total})", channel
47
+ end
54
48
  else
55
49
  say "#{rcv}: No star for you!", channel
56
50
  end
@@ -6,7 +6,6 @@ module Botbckt #:nodoc:
6
6
  # < botbckt> Today's Forecast: 90F/65F (Sunny)
7
7
  #
8
8
  # < user> ~conditions Los Angeles, CA
9
- # < botbckt> Conditions -
10
9
  # < botbckt> Today: Areas of low clouds in the morning then mostly sunny. Highs in the upper 60s to mid 70s. West winds 10 to 20 mph in the afternoon.
11
10
  # < botbckt> Tonight: Mostly clear. Lows in the mid to upper 50s. West winds 10 to 20 mph in the evening.
12
11
  #
@@ -37,7 +36,7 @@ module Botbckt #:nodoc:
37
36
  daytime = (xml/'forecastday[1]')
38
37
  evening = (xml/'forecastday[2]')
39
38
 
40
- "Conditions -\nToday: #{(daytime/'fcttext').inner_html}\nTonight: #{(evening/'fcttext').inner_html}"
39
+ "Today: #{(daytime/'fcttext').inner_html}\nTonight: #{(evening/'fcttext').inner_html}"
41
40
  end
42
41
 
43
42
  def self.forecast(query) #:nodoc:
data/lib/botbckt/irc.rb CHANGED
@@ -4,7 +4,6 @@ module Botbckt #:nodoc:
4
4
  #
5
5
  class IRC < EventMachine::Connection
6
6
  include EventMachine::Protocols::LineText2
7
- include ActiveSupport::BufferedLogger::Severity
8
7
 
9
8
  attr_accessor :config
10
9
  cattr_accessor :connection
@@ -20,8 +19,6 @@ module Botbckt #:nodoc:
20
19
  # :server<String>:: The FQDN of the IRC server. Required.
21
20
  # :port<~to_i>:: The port number of the IRC server. Required.
22
21
  # :channels<Array[String]>:: An array of channels to join. Channel names should *not* include the '#' prefix. Required.
23
- # :log<String>:: The name of a log file. Defaults to 'botbckt.log'.
24
- # :log_level<Integer>:: The minimum severity level to log. Defaults to 1 (INFO).
25
22
  #
26
23
  def self.connect(options)
27
24
  self.connection = EM.connect(options[:server], options[:port].to_i, self, options)
@@ -34,9 +31,6 @@ module Botbckt #:nodoc:
34
31
  #++
35
32
  def initialize(options) #:nodoc:
36
33
  self.config = OpenStruct.new(options)
37
-
38
- @logger = ActiveSupport::BufferedLogger.new self.config.log || 'botbckt.log',
39
- self.config.log_level || INFO
40
34
  end
41
35
 
42
36
  # ==== Parameters
@@ -66,7 +60,7 @@ module Botbckt #:nodoc:
66
60
  args << $5.squish if $5
67
61
 
68
62
  # run args: command, sender, channel, optional args
69
- Botbckt::Bot.run($4, *args)
63
+ Botbckt::Bot.instance.run($4, *args)
70
64
  else
71
65
  log line
72
66
  end
@@ -81,8 +75,8 @@ module Botbckt #:nodoc:
81
75
 
82
76
  private
83
77
 
84
- def log(msg, level = INFO) #:nodoc:
85
- @logger.add(level, msg)
78
+ def log(msg, level = Botbckt::Bot::INFO) #:nodoc:
79
+ Botbckt::Bot.instance.log msg, level
86
80
  end
87
81
 
88
82
  def command(*cmd) #:nodoc:
@@ -0,0 +1,33 @@
1
+ require 'em-redis'
2
+
3
+ module Botbckt #:nodoc:
4
+
5
+ # Implements a basic key/value store API for cross-session state storage.
6
+ #
7
+ # Currently, this class is Redis-backed, but any key/value store could be
8
+ # supported, in theory.
9
+ #
10
+ class Store
11
+
12
+ attr_accessor :backend
13
+
14
+ def initialize(host, port)
15
+ self.backend = EventMachine::Protocols::Redis.connect(host, port)
16
+ end
17
+
18
+ def set(key, value, &block)
19
+ backend.set(key, value, &block)
20
+ end
21
+
22
+ def get(key, &block)
23
+ backend.get(key, &block)
24
+ end
25
+
26
+ def increment!(key, &block)
27
+ backend.incr(key, &block)
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
data/lib/botbckt.rb CHANGED
@@ -1,3 +1,3 @@
1
- %w{ rubygems eventmachine activesupport ostruct json open-uri cgi hpricot }.each { |lib| require lib }
2
- %w{ irc bot utilities command }.each { |lib| require File.dirname(__FILE__) + "/botbckt/#{ lib }" }
1
+ %w{ rubygems eventmachine activesupport ostruct json open-uri cgi hpricot singleton optparse }.each { |lib| require lib }
2
+ %w{ irc store bot utilities command cmd }.each { |lib| require File.dirname(__FILE__) + "/botbckt/#{ lib }" }
3
3
  Dir[File.dirname(__FILE__) + '/botbckt/commands/*'].each { |lib| require lib }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitbckt-botbckt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Mitchell
@@ -9,14 +9,14 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-04 00:00:00 -07:00
13
- default_executable:
12
+ date: 2009-06-23 00:00:00 -07:00
13
+ default_executable: botbckt
14
14
  dependencies: []
15
15
 
16
16
  description: Boredom strikes on Sunday mornings.
17
17
  email: brandon@systemisdown.net
18
- executables: []
19
-
18
+ executables:
19
+ - botbckt
20
20
  extensions: []
21
21
 
22
22
  extra_rdoc_files:
@@ -24,9 +24,12 @@ extra_rdoc_files:
24
24
  files:
25
25
  - Rakefile
26
26
  - VERSION.yml
27
+ - bin/botbckt
27
28
  - lib/botbckt.rb
28
29
  - lib/botbckt/bot.rb
30
+ - lib/botbckt/cmd.rb
29
31
  - lib/botbckt/command.rb
32
+ - lib/botbckt/commands/gooble.rb
30
33
  - lib/botbckt/commands/google.rb
31
34
  - lib/botbckt/commands/meme.rb
32
35
  - lib/botbckt/commands/ping.rb
@@ -36,6 +39,7 @@ files:
36
39
  - lib/botbckt/commands/ticker.rb
37
40
  - lib/botbckt/commands/weather.rb
38
41
  - lib/botbckt/irc.rb
42
+ - lib/botbckt/store.rb
39
43
  - lib/botbckt/utilities.rb
40
44
  - README
41
45
  has_rdoc: true