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.
- data/.autotest +11 -0
- data/.gitignore +6 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +57 -0
- data/README.textile +52 -0
- data/TODO +72 -0
- data/bin/bot +13 -0
- data/campfire-bot.gemspec +27 -0
- data/cfbot-stop.sh +8 -0
- data/config.example.yml +31 -0
- data/lib/bot.rb +194 -0
- data/lib/event.rb +114 -0
- data/lib/message.rb +30 -0
- data/lib/plugin.rb +77 -0
- data/lib/version.rb +3 -0
- data/plugins/accountability.rb +45 -0
- data/plugins/austin.rb +29 -0
- data/plugins/basecamp.rb +48 -0
- data/plugins/beer.rb +214 -0
- data/plugins/beijing_tally.rb +30 -0
- data/plugins/boop.rb +127 -0
- data/plugins/bruce.rb +15 -0
- data/plugins/bugzilla.rb +198 -0
- data/plugins/calvin.rb +43 -0
- data/plugins/chuck.rb +23 -0
- data/plugins/dilbert.rb +51 -0
- data/plugins/excuse.rb +478 -0
- data/plugins/fail.rb +16 -0
- data/plugins/figlet.rb +10 -0
- data/plugins/fun.rb +95 -0
- data/plugins/garfield.rb +43 -0
- data/plugins/generic_search.rb +66 -0
- data/plugins/help.rb +13 -0
- data/plugins/infobot.rb +58 -0
- data/plugins/insult.rb +87 -0
- data/plugins/jira.rb +197 -0
- data/plugins/lolcats.rb +17 -0
- data/plugins/our_quotes.rb +195 -0
- data/plugins/quote.rb +31 -0
- data/plugins/schneier.rb +28 -0
- data/plugins/seen.rb +88 -0
- data/plugins/signal_filter.rb +9 -0
- data/plugins/svn.rb +167 -0
- data/plugins/twitter_echo.rb +54 -0
- data/plugins/unfuddle.rb +69 -0
- data/plugins/weather.rb +25 -0
- data/plugins/xkcd.rb +43 -0
- data/spec/beer_spec.rb +224 -0
- data/spec/bugzilla_spec.rb +90 -0
- data/spec/command_spec.rb +96 -0
- data/spec/jira_spec.rb +264 -0
- data/spec/our_quotes_spec.rb +186 -0
- data/spec/plugin_spec.rb +43 -0
- data/spec/spec.opts +1 -0
- data/vendor/escape/ChangeLog +30 -0
- data/vendor/escape/Makefile +5 -0
- data/vendor/escape/README +81 -0
- data/vendor/escape/VERSION +1 -0
- data/vendor/escape/escape.rb +302 -0
- data/vendor/escape/install.rb +109 -0
- data/vendor/escape/misc/README.erb +85 -0
- data/vendor/escape/rdoc/classes/Escape.html +427 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000022.html +19 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000023.html +32 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000024.html +24 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000025.html +19 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000026.html +48 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000027.html +19 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000028.html +19 -0
- data/vendor/escape/rdoc/classes/Escape/HTMLAttrValue.html +113 -0
- data/vendor/escape/rdoc/classes/Escape/HTMLEscaped.html +113 -0
- data/vendor/escape/rdoc/classes/Escape/PercentEncoded.html +113 -0
- data/vendor/escape/rdoc/classes/Escape/ShellEscaped.html +113 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.html +242 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000029.html +18 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000030.html +18 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000031.html +18 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000032.html +18 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000033.html +18 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000035.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapeHTML.html +182 -0
- data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000008.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000009.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000010.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000011.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.html +182 -0
- data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000012.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000013.html +19 -0
- data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000014.html +20 -0
- data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000015.html +22 -0
- data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.html +167 -0
- data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.src/M000016.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.src/M000017.html +20 -0
- data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.src/M000018.html +20 -0
- data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.html +167 -0
- data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.src/M000019.html +20 -0
- data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.src/M000020.html +24 -0
- data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.src/M000021.html +22 -0
- data/vendor/escape/rdoc/files/escape_rb.html +136 -0
- data/vendor/escape/rdoc/files/install_rb.html +250 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000001.html +23 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000002.html +31 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000003.html +27 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000004.html +27 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000005.html +21 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000006.html +23 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000007.html +21 -0
- data/vendor/escape/rdoc/files/test/test-escape_rb.html +109 -0
- data/vendor/escape/rdoc/fr_class_index.html +36 -0
- data/vendor/escape/rdoc/fr_file_index.html +29 -0
- data/vendor/escape/rdoc/fr_method_index.html +61 -0
- data/vendor/escape/rdoc/index.html +24 -0
- data/vendor/escape/rdoc/rdoc-style.css +208 -0
- data/vendor/escape/test/test-escape.rb +90 -0
- metadata +259 -0
data/.autotest
ADDED
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -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!
|
data/README.textile
ADDED
|
@@ -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
|
data/cfbot-stop.sh
ADDED
data/config.example.yml
ADDED
|
@@ -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/
|
data/lib/bot.rb
ADDED
|
@@ -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
|