campfire-bot 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/.autotest +11 -0
  2. data/.gitignore +6 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +57 -0
  5. data/README.textile +52 -0
  6. data/TODO +72 -0
  7. data/bin/bot +13 -0
  8. data/campfire-bot.gemspec +27 -0
  9. data/cfbot-stop.sh +8 -0
  10. data/config.example.yml +31 -0
  11. data/lib/bot.rb +194 -0
  12. data/lib/event.rb +114 -0
  13. data/lib/message.rb +30 -0
  14. data/lib/plugin.rb +77 -0
  15. data/lib/version.rb +3 -0
  16. data/plugins/accountability.rb +45 -0
  17. data/plugins/austin.rb +29 -0
  18. data/plugins/basecamp.rb +48 -0
  19. data/plugins/beer.rb +214 -0
  20. data/plugins/beijing_tally.rb +30 -0
  21. data/plugins/boop.rb +127 -0
  22. data/plugins/bruce.rb +15 -0
  23. data/plugins/bugzilla.rb +198 -0
  24. data/plugins/calvin.rb +43 -0
  25. data/plugins/chuck.rb +23 -0
  26. data/plugins/dilbert.rb +51 -0
  27. data/plugins/excuse.rb +478 -0
  28. data/plugins/fail.rb +16 -0
  29. data/plugins/figlet.rb +10 -0
  30. data/plugins/fun.rb +95 -0
  31. data/plugins/garfield.rb +43 -0
  32. data/plugins/generic_search.rb +66 -0
  33. data/plugins/help.rb +13 -0
  34. data/plugins/infobot.rb +58 -0
  35. data/plugins/insult.rb +87 -0
  36. data/plugins/jira.rb +197 -0
  37. data/plugins/lolcats.rb +17 -0
  38. data/plugins/our_quotes.rb +195 -0
  39. data/plugins/quote.rb +31 -0
  40. data/plugins/schneier.rb +28 -0
  41. data/plugins/seen.rb +88 -0
  42. data/plugins/signal_filter.rb +9 -0
  43. data/plugins/svn.rb +167 -0
  44. data/plugins/twitter_echo.rb +54 -0
  45. data/plugins/unfuddle.rb +69 -0
  46. data/plugins/weather.rb +25 -0
  47. data/plugins/xkcd.rb +43 -0
  48. data/spec/beer_spec.rb +224 -0
  49. data/spec/bugzilla_spec.rb +90 -0
  50. data/spec/command_spec.rb +96 -0
  51. data/spec/jira_spec.rb +264 -0
  52. data/spec/our_quotes_spec.rb +186 -0
  53. data/spec/plugin_spec.rb +43 -0
  54. data/spec/spec.opts +1 -0
  55. data/vendor/escape/ChangeLog +30 -0
  56. data/vendor/escape/Makefile +5 -0
  57. data/vendor/escape/README +81 -0
  58. data/vendor/escape/VERSION +1 -0
  59. data/vendor/escape/escape.rb +302 -0
  60. data/vendor/escape/install.rb +109 -0
  61. data/vendor/escape/misc/README.erb +85 -0
  62. data/vendor/escape/rdoc/classes/Escape.html +427 -0
  63. data/vendor/escape/rdoc/classes/Escape.src/M000022.html +19 -0
  64. data/vendor/escape/rdoc/classes/Escape.src/M000023.html +32 -0
  65. data/vendor/escape/rdoc/classes/Escape.src/M000024.html +24 -0
  66. data/vendor/escape/rdoc/classes/Escape.src/M000025.html +19 -0
  67. data/vendor/escape/rdoc/classes/Escape.src/M000026.html +48 -0
  68. data/vendor/escape/rdoc/classes/Escape.src/M000027.html +19 -0
  69. data/vendor/escape/rdoc/classes/Escape.src/M000028.html +19 -0
  70. data/vendor/escape/rdoc/classes/Escape/HTMLAttrValue.html +113 -0
  71. data/vendor/escape/rdoc/classes/Escape/HTMLEscaped.html +113 -0
  72. data/vendor/escape/rdoc/classes/Escape/PercentEncoded.html +113 -0
  73. data/vendor/escape/rdoc/classes/Escape/ShellEscaped.html +113 -0
  74. data/vendor/escape/rdoc/classes/Escape/StringWrapper.html +242 -0
  75. data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000029.html +18 -0
  76. data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000030.html +18 -0
  77. data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000031.html +18 -0
  78. data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000032.html +18 -0
  79. data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000033.html +18 -0
  80. data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000035.html +18 -0
  81. data/vendor/escape/rdoc/classes/TestEscapeHTML.html +182 -0
  82. data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000008.html +18 -0
  83. data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000009.html +18 -0
  84. data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000010.html +18 -0
  85. data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000011.html +18 -0
  86. data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.html +182 -0
  87. data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000012.html +18 -0
  88. data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000013.html +19 -0
  89. data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000014.html +20 -0
  90. data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000015.html +22 -0
  91. data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.html +167 -0
  92. data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.src/M000016.html +18 -0
  93. data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.src/M000017.html +20 -0
  94. data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.src/M000018.html +20 -0
  95. data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.html +167 -0
  96. data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.src/M000019.html +20 -0
  97. data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.src/M000020.html +24 -0
  98. data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.src/M000021.html +22 -0
  99. data/vendor/escape/rdoc/files/escape_rb.html +136 -0
  100. data/vendor/escape/rdoc/files/install_rb.html +250 -0
  101. data/vendor/escape/rdoc/files/install_rb.src/M000001.html +23 -0
  102. data/vendor/escape/rdoc/files/install_rb.src/M000002.html +31 -0
  103. data/vendor/escape/rdoc/files/install_rb.src/M000003.html +27 -0
  104. data/vendor/escape/rdoc/files/install_rb.src/M000004.html +27 -0
  105. data/vendor/escape/rdoc/files/install_rb.src/M000005.html +21 -0
  106. data/vendor/escape/rdoc/files/install_rb.src/M000006.html +23 -0
  107. data/vendor/escape/rdoc/files/install_rb.src/M000007.html +21 -0
  108. data/vendor/escape/rdoc/files/test/test-escape_rb.html +109 -0
  109. data/vendor/escape/rdoc/fr_class_index.html +36 -0
  110. data/vendor/escape/rdoc/fr_file_index.html +29 -0
  111. data/vendor/escape/rdoc/fr_method_index.html +61 -0
  112. data/vendor/escape/rdoc/index.html +24 -0
  113. data/vendor/escape/rdoc/rdoc-style.css +208 -0
  114. data/vendor/escape/test/test-escape.rb +90 -0
  115. metadata +259 -0
@@ -0,0 +1,11 @@
1
+ require 'autotest/redgreen'
2
+ Autotest.add_hook :initialize do |at|
3
+ # Ignore files in tmp/
4
+ at.add_exception %r%^\./tmp%
5
+
6
+ at.add_mapping(/plugins\/(.+).rb/) do |f, _|
7
+ at.files_matching(/.*spec.rb$/)
8
+ end
9
+
10
+ end
11
+
@@ -0,0 +1,6 @@
1
+ *config.yml
2
+ tmp/*
3
+ var/*
4
+ .bundle
5
+ .rvmrc
6
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :gemcutter
2
+
3
+ gemspec
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ campfire-bot (0.0.1)
5
+ activesupport (~> 3.0.3)
6
+ eventmachine (~> 0.12.10)
7
+ hpricot (~> 0.8.3)
8
+ i18n (~> 0.5.0)
9
+ logging (~> 1.4.3)
10
+ mime-types (~> 1.16)
11
+ tinder (>= 1.4.3)
12
+ twitter-stream (~> 0.1)
13
+
14
+ GEM
15
+ remote: http://rubygems.org/
16
+ specs:
17
+ activesupport (3.0.10)
18
+ addressable (2.2.6)
19
+ eventmachine (0.12.10)
20
+ faraday (0.7.4)
21
+ addressable (~> 2.2.6)
22
+ multipart-post (~> 1.1.0)
23
+ rack (< 2, >= 1.1.0)
24
+ faraday_middleware (0.7.0)
25
+ faraday (~> 0.7.3)
26
+ hashie (1.1.0)
27
+ hpricot (0.8.4)
28
+ http_parser.rb (0.5.3)
29
+ i18n (0.5.0)
30
+ little-plugger (1.1.2)
31
+ logging (1.4.3)
32
+ little-plugger (>= 1.1.2)
33
+ mime-types (1.16)
34
+ multi_json (1.0.3)
35
+ multipart-post (1.1.3)
36
+ rack (1.3.4)
37
+ simple_oauth (0.1.5)
38
+ tinder (1.7.0)
39
+ activesupport (< 4, >= 2.3)
40
+ eventmachine (~> 0.12)
41
+ faraday (< 0.8, >= 0.6)
42
+ faraday_middleware (< 0.8, >= 0.6)
43
+ hashie (~> 1.0)
44
+ mime-types (~> 1.16)
45
+ multi_json (~> 1.0)
46
+ multipart-post (~> 1.1)
47
+ twitter-stream (~> 0.1)
48
+ twitter-stream (0.1.14)
49
+ eventmachine (>= 0.12.8)
50
+ http_parser.rb (~> 0.5.1)
51
+ simple_oauth (~> 0.1.4)
52
+
53
+ PLATFORMS
54
+ ruby
55
+
56
+ DEPENDENCIES
57
+ campfire-bot!
@@ -0,0 +1,52 @@
1
+ h1. Campfire Bot
2
+
3
+ This is a bot for 37 Signals' Campfire chat service.
4
+
5
+ It has plugin support so you can make your bot do whatever you want.
6
+
7
+ h2. Installation
8
+
9
+ h3. Download
10
+
11
+ Download the source from "github":http://github.com/joshwand/campfire-bot
12
+
13
+ h3. Gem Dependencies
14
+
15
+ gem install bundler
16
+ bundle install
17
+
18
+ h3. Configuration
19
+
20
+ Create a @config.yml@ in the root of the source directory. Use @config.example.yml@ as an example.
21
+
22
+ h2. Usage
23
+
24
+ To run the bot, run @script/bot@ with the environment name as the argument:
25
+
26
+ script/bot development
27
+
28
+
29
+ h2. Known issues
30
+
31
+ h3. [BUG] Bus Error
32
+
33
+ This is really an issue with EventMachine. If you are using this on a machine that has MacPorts, you'll need to make sure that you build both ruby and eventmachine linked against the openssl in /opt/local, not /usr/local.
34
+
35
+ h3. warning: peer certificate won't be verified in this SSL session
36
+
37
+ A bug in net/http. Noisy, but harmless.
38
+
39
+ h2. Original Author
40
+
41
+ Tim Riley - "github":http://github.com/timriley | "www":http://openmonkey.com/ | "email":mailto:tim@openmonkey.com
42
+
43
+ h2. Maintainer
44
+
45
+ * Josh Wand - "github":http://github.com/joshwand
46
+
47
+ h3. Contributors
48
+
49
+ * Marcel M. Cary - "github":http://github.com/mcary
50
+ * Hugh Evans - "github":http://github.com/artpop
51
+ * Sean O'Dowd - "github":http://github.com/seanodowd
52
+ * Andrew Erickson - "github":http://github.com/aerickson
data/TODO ADDED
@@ -0,0 +1,72 @@
1
+ - add chronic integration for interval-based events: at_time("every day at 9am", :method_name)
2
+
3
+ - per-plugin config files (inside a config-specific dir) - best not to pollute a single YAML file.
4
+
5
+ - get it to work with the newest version of mechanize.
6
+
7
+ /opt/local/lib/ruby/gems/1.8/gems/timriley-tinder-1.1.9/lib/tinder/mechanize_ext.rb:6: undefined method `set_headers' for class `WWW::Mechanize' (NameError)
8
+ from /opt/local/lib/ruby/vendor_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
9
+ from /opt/local/lib/ruby/vendor_ruby/1.8/rubygems/custom_require.rb:27:in `require'
10
+ from /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.2/lib/active_support/dependencies.rb:510:in `require'
11
+ from /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.2/lib/active_support/dependencies.rb:355:in `new_constants_in'
12
+ from /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.2/lib/active_support/dependencies.rb:510:in `require'
13
+ from /opt/local/lib/ruby/gems/1.8/gems/timriley-tinder-1.1.9/lib/tinder.rb:10
14
+ from /opt/local/lib/ruby/gems/1.8/gems/timriley-tinder-1.1.9/lib/tinder.rb:10:in `each'
15
+ from /opt/local/lib/ruby/gems/1.8/gems/timriley-tinder-1.1.9/lib/tinder.rb:10
16
+ from /opt/local/lib/ruby/vendor_ruby/1.8/rubygems/custom_require.rb:32:in `gem_original_require'
17
+ from /opt/local/lib/ruby/vendor_ruby/1.8/rubygems/custom_require.rb:32:in `require'
18
+ from /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.2/lib/active_support/dependencies.rb:510:in `require'
19
+ from /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.2/lib/active_support/dependencies.rb:355:in `new_constants_in'
20
+ from /opt/local/lib/ruby/gems/1.8/gems/activesupport-2.1.2/lib/active_support/dependencies.rb:510:in `require'
21
+ from ./script/../lib/bot.rb:13
22
+ from ./script/bot:7:in `require'
23
+ from ./script/bot:7
24
+
25
+ - need flood control for bot plugins
26
+
27
+ - plugins need callbacks, like on_join, bot_loaded, etc. Especially one that is run whenever control+c is caught (for plugins to clean up)
28
+
29
+ - gem-ify the bot and have it provide an executable to generate an application directory
30
+ like rails
31
+
32
+ - move the plugins into their own repository and provide a way for them to be installed
33
+ into the app dir above (like script/plugin install)
34
+
35
+ - use a message handler chain in the same way that activesupport uses callback chains.
36
+
37
+ - provide a way for plugins to buffer themselves, so they don't run too many times
38
+
39
+ - buffer the "speak" calls so that they can be uniq'ed before printed to the channel?
40
+
41
+ - plugins should be able to register respond_to blocks with a notion of priority.
42
+ eg. a low-priority command can say it doesn't need to be processed if something else matches.
43
+ conversely, a high-priority command can cause to bot to stop processing the message once it has matched.
44
+
45
+ - create a plugin that pulls in unfuddle ticket reports, eg. !unfuddle project_name active_tickets
46
+
47
+ - modify the austin powers plugin to be a generic imdb quote puller. eg. !imdb austin_powers
48
+
49
+ - unit tests!
50
+
51
+ - help plugin-- extend plugin class to provide help for each registered command
52
+
53
+ - room.log method, can be called from plugins - store logs in ./log/*.log
54
+
55
+ DONE
56
+
57
+ - multi-room support
58
+
59
+ - need to catch timeout errors and do something sensible, like wait and retry the connection
60
+
61
+ http://www.google.com/search?q=ruby%20rescue%20Timeout::Error
62
+ http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/136555
63
+ http://jerith.livejournal.com/40063.html
64
+ http://rails.lighthouseapp.com/projects/8994/tickets/6-patch-activeresource-connection-should-rescue-from-timeout-error
65
+
66
+ - alternative addressing syntax (!command)
67
+
68
+ - need to catch no-network errors and return a sensible error message:
69
+
70
+ /opt/local/lib/ruby/1.8/net/http.rb:560:in `initialize': Network is unreachable - connect(2) (Errno::ENETUNREACH)
71
+
72
+ - plugin: svn commit notification (multiple repos)
data/bin/bot ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Run this script with the environment as the only argument. eg. ./script/bot development
4
+ BOT_ENVIRONMENT = ARGV.first
5
+ BOT_ROOT = File.join(File.dirname(__FILE__), '..')
6
+
7
+ require 'rubygems'
8
+ require 'bundler/setup'
9
+ require 'bot'
10
+
11
+ bot = CampfireBot::Bot.instance
12
+ bot.connect
13
+ bot.run
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "campfire-bot"
7
+ s.version = CampfireBot::VERSION
8
+ s.authors = ["Josh Wand", "Chad Boyd"]
9
+ s.email = ["", "hoverlover@gmail.com"]
10
+ s.homepage = "https://github.com/joshwand/campfire-bot"
11
+ s.summary = %q{This is a bot for 37 Signals’ Campfire chat service.}
12
+ s.description = s.summary
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib", "plugins"]
18
+
19
+ s.add_dependency 'twitter-stream', '~> 0.1'
20
+ s.add_dependency 'tinder', '>= 1.4.3'
21
+ s.add_dependency 'hpricot', '~> 0.8.3'
22
+ s.add_dependency 'mime-types', '~> 1.16'
23
+ s.add_dependency 'activesupport', '~> 3.0.3'
24
+ s.add_dependency 'logging', '~> 1.4.3'
25
+ s.add_dependency 'eventmachine', '~> 0.12.10'
26
+ s.add_dependency 'i18n', '~> 0.5.0'
27
+ end
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+
3
+ # run as cfbot-stop.sh BOT_ENV_NAME
4
+ for i in $(ps ajx | awk "/[b]ot ${1}/ {print \$2;}")
5
+ do
6
+ echo "killing pid $i"
7
+ kill -9 $i
8
+ done
@@ -0,0 +1,31 @@
1
+ development:
2
+ site: campfire_subdomain_name
3
+ use_ssl: true
4
+ log_dir: var
5
+ rooms:
6
+ - Main room
7
+ - <%= "Other room" %> # ERB interpolation
8
+ api_key: your_api_key
9
+ nickname: Bot
10
+ fullname: Bot Name
11
+ password: not_actually_needed
12
+ twitter_feed: 'http://search.twitter.com/search.atom?q=from%3Atimriley+OR+from%3Ahughevans+OR+from%3Aschlick+OR+from%3Aseanodowd'
13
+ twitter_hide_replies: false
14
+ enable_plugins:
15
+ - xkcd
16
+ - basecamp
17
+ production:
18
+ site: campfire_subdomain_name
19
+ use_ssl: true
20
+ rooms:
21
+ - Company Chat
22
+ username: foo@bar.com
23
+ nickname: Bot Name
24
+ password: password
25
+ twitter_feed: 'http://search.twitter.com/search.atom?q=from%3Atimriley+OR+from%3Ahughevans+OR+from%3Aschlick+OR+from%3Aseanodowd'
26
+ twitter_hide_replies: false
27
+ enable_plugins:
28
+ - xkcd
29
+ - basecamp
30
+ svn_urls:
31
+ - http://your.svn/url/
@@ -0,0 +1,194 @@
1
+ # External Libs
2
+ require 'rubygems'
3
+ require 'active_support'
4
+ require 'active_support/hash_with_indifferent_access'
5
+ require 'active_support/core_ext'
6
+ require 'yaml'
7
+ require 'eventmachine'
8
+ require 'logging'
9
+ require 'fileutils'
10
+ require 'erb'
11
+ require 'tinder'
12
+
13
+ module CampfireBot
14
+ autoload :Plugin, 'plugin'
15
+ autoload :Event, 'event'
16
+ autoload :Message, 'message'
17
+
18
+ class Bot
19
+ # this is necessary so the room and campfire objects can be accessed by plugins.
20
+ include Singleton
21
+
22
+ # FIXME - these will be invalid if disconnected. handle this.
23
+ attr_reader :campfire, :rooms, :config, :log
24
+
25
+ def initialize
26
+ if BOT_ENVIRONMENT.nil?
27
+ puts "you must specify a BOT_ENVIRONMENT"
28
+ exit 1
29
+ end
30
+ @timeouts = 0
31
+ @config = YAML::load(ERB.new(File.read("#{BOT_ROOT}/config.yml")).result)[BOT_ENVIRONMENT]
32
+ @rooms = {}
33
+ @root_logger = Logging.logger["CampfireBot"]
34
+ @log = Logging.logger[self]
35
+
36
+ log_dir = @config['log_dir']
37
+ Dir.mkdir(log_dir) unless Dir.exists? log_dir
38
+
39
+ # TODO much of this should be configurable per environment
40
+ @root_logger.add_appenders Logging.appenders.rolling_file("#{log_dir}/#{BOT_ENVIRONMENT}.log",
41
+ :layout => Logging.layouts.pattern(:pattern => "%d | %-6l | %-12c | %m\n"),
42
+ :age => 'daily',
43
+ :keep => 7)
44
+ @root_logger.level = @config['log_level'] rescue :info
45
+ end
46
+
47
+ def connect
48
+ load_plugins unless !@config['enable_plugins']
49
+ begin
50
+ join_rooms
51
+ rescue Errno::ENETUNREACH, SocketError => e
52
+ @log.fatal "We had trouble connecting to the network: #{e.class}: #{e.message}"
53
+ abort "We had trouble connecting to the network: #{e.class}: #{e.message}"
54
+ rescue Exception => e
55
+ @log.fatal "Unhandled exception while joining rooms: #{e.class}: #{e.message} \n #{$!.backtrace.join("\n")}"
56
+ abort "Unhandled exception while joining rooms: #{e.class}: #{e.message} \n #{$!.backtrace.join("\n")}"
57
+ end
58
+ end
59
+
60
+ def run(interval = 5)
61
+ catch(:stop_listening) do
62
+ trap('INT') { throw :stop_listening }
63
+ trap('TERM') { throw :stop_listening }
64
+
65
+ # since room#listen blocks, stick it in its own thread
66
+ @rooms.each_pair do |room_name, room|
67
+ Thread.new do
68
+ begin
69
+ room.listen(:timeout => 8) do |raw_msg|
70
+ handle_message(CampfireBot::Message.new(raw_msg.merge({:room => room})))
71
+ end
72
+ rescue Exception => e
73
+ trace = e.backtrace.join("\n")
74
+ abort "something went wrong! #{e.message}\n #{trace}"
75
+ end
76
+ end
77
+ end
78
+
79
+ loop do
80
+ begin
81
+ @rooms.each_pair do |room_name, room|
82
+
83
+ # I assume if we reach here, all the network-related activity has occured successfully
84
+ # and that we're outside of the retry-cycle
85
+ @timeouts = 0
86
+
87
+ # Here's how I want it to look
88
+ # @room.listen.each { |m| EventHandler.handle_message(m) }
89
+ # EventHanlder.handle_time(optional_arg = Time.now)
90
+
91
+ # Run time-oriented events
92
+ Plugin.registered_intervals.each do |handler|
93
+ begin
94
+ handler.run(CampfireBot::Message.new(:room => room))
95
+ rescue
96
+ @log.error "error running #{handler.inspect}: #{$!.class}: #{$!.message} \n #{$!.backtrace.join("\n")}"
97
+ end
98
+ end
99
+
100
+ Plugin.registered_times.each_with_index do |handler, index|
101
+ begin
102
+ Plugin.registered_times.delete_at(index) if handler.run
103
+ rescue
104
+ @log.error "error running #{handler.inspect}: #{$!.class}: #{$!.message}, \n #{$!.backtrace.join("\n")}"
105
+ end
106
+ end
107
+
108
+ end
109
+ STDOUT.flush
110
+ sleep interval
111
+ rescue Timeout::Error => e
112
+ if @timeouts < 5
113
+ sleep(5 * @timeouts)
114
+ @timeouts += 1
115
+ retry
116
+ else
117
+ raise e.message
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ # Leave the room so users won't be under the false impression that the bot is still running.
124
+ @rooms.each_value.map(&:leave)
125
+ end
126
+
127
+ private
128
+
129
+ def join_rooms
130
+ join_rooms_as_user
131
+ @log.info "Joined all rooms."
132
+ end
133
+
134
+ def join_rooms_as_user
135
+ @campfire = Tinder::Campfire.new(@config['site'], :token => @config['api_key'])
136
+
137
+ @config['rooms'].each do |room_name|
138
+ @rooms[room_name] = @campfire.find_room_by_name(room_name)
139
+ raise Exception.new("couldn't find a room named #{room_name}!") if @rooms[room_name].nil?
140
+ res = @rooms[room_name].join
141
+ end
142
+ end
143
+
144
+ def load_plugins
145
+ @config['enable_plugins'].each do |plugin_name|
146
+ load "#{plugin_name}.rb"
147
+ end
148
+
149
+ # And instantiate them
150
+ Plugin.registered_plugins.each_pair do |name, klass|
151
+ @log.info "loading plugin: #{name}"
152
+ STDOUT.flush
153
+ Plugin.registered_plugins[name] = klass.new
154
+ end
155
+ end
156
+
157
+ def handle_message(message)
158
+ # puts message.inspect
159
+
160
+ case message[:type]
161
+ when "KickMessage"
162
+ if message[:user][:id] == @campfire.me[:id]
163
+ @log.info "got kicked... rejoining after 10 seconds"
164
+ sleep 10
165
+ join_rooms_as_user
166
+ @log.info "rejoined room."
167
+ return
168
+ end
169
+ when "TimestampMessage", "AdvertisementMessage"
170
+ return
171
+ when "TextMessage", "PasteMessage"
172
+ # only process non-bot messages
173
+ unless message[:user][:id] == @campfire.me[:id]
174
+ @log.info "#{message[:person]} | #{message[:message]}"
175
+ %w(commands speakers messages).each do |type|
176
+ Plugin.send("registered_#{type}").each do |handler|
177
+ begin
178
+ handler.run(message)
179
+ rescue Exception => e
180
+ @log.error "error running #{handler.inspect}: #{$!.class}: #{$!.message} \n #{$!.backtrace.join("\n")}"
181
+ end
182
+ end
183
+ end
184
+ end
185
+ else
186
+ @log.debug "got message of type #{message['type']} -- discarding"
187
+ end
188
+ end
189
+ end
190
+ end
191
+
192
+ def bot
193
+ CampfireBot::Bot.instance
194
+ end