campfire-bot 0.0.1

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