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/lib/event.rb
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
module CampfireBot
|
|
2
|
+
module Event
|
|
3
|
+
|
|
4
|
+
# This is an abstract base class for all event types, not to be used directly.
|
|
5
|
+
class EventHandler
|
|
6
|
+
|
|
7
|
+
attr_reader :kind, :matcher, :plugin, :method
|
|
8
|
+
|
|
9
|
+
def self.handles(event_type)
|
|
10
|
+
@kind = event_type
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(matcher, plugin_name, method_name)
|
|
14
|
+
@matcher = matcher
|
|
15
|
+
@plugin = plugin_name
|
|
16
|
+
@method = method_name
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def run(msg, force = false)
|
|
20
|
+
if force || match?(msg)
|
|
21
|
+
Plugin.registered_plugins[@plugin].send(@method, filter_message(msg))
|
|
22
|
+
else
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
|
|
29
|
+
def filter_message(msg)
|
|
30
|
+
msg
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class Command < EventHandler
|
|
35
|
+
handles :messages
|
|
36
|
+
|
|
37
|
+
def match?(msg)
|
|
38
|
+
(
|
|
39
|
+
msg[:message][0..0] == '!' ||
|
|
40
|
+
msg[:message] =~ Regexp.new("^#{bot.config['nickname']}(,|:)", Regexp::IGNORECASE)
|
|
41
|
+
) &&
|
|
42
|
+
msg[:message].gsub(/^\!/, '').gsub(Regexp.new("^#{bot.config['nickname']}(,|:)?\\s*", Regexp::IGNORECASE), '').split(' ')[0].to_s.downcase == @matcher.downcase
|
|
43
|
+
# FIXME - the above should be just done with one regexp to pull out the first non-! non-<bot name> word.
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
protected
|
|
47
|
+
|
|
48
|
+
def filter_message(msg)
|
|
49
|
+
msg[:message] = msg[:message].gsub(Regexp.new("^(!|#{bot.config['nickname']}[,:]\\s+)#{@matcher.downcase}\\s?", Regexp::IGNORECASE), '')
|
|
50
|
+
msg
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class Speaker < EventHandler
|
|
55
|
+
handles :messages
|
|
56
|
+
|
|
57
|
+
def match?(msg)
|
|
58
|
+
msg[:person] == @matcher
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class Message < EventHandler
|
|
63
|
+
handles :messages
|
|
64
|
+
|
|
65
|
+
def match?(msg)
|
|
66
|
+
msg[:message] =~ @matcher
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class Interval < EventHandler
|
|
71
|
+
handles :times
|
|
72
|
+
|
|
73
|
+
def initialize(*args)
|
|
74
|
+
@last_run = ::Time.now
|
|
75
|
+
super(*args)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def match?
|
|
79
|
+
@last_run < ::Time.now - @matcher
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def run(msg, force = false)
|
|
83
|
+
if match?
|
|
84
|
+
@last_run = ::Time.now
|
|
85
|
+
Plugin.registered_plugins[@plugin].send(@method, msg)
|
|
86
|
+
else
|
|
87
|
+
false
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class Time < EventHandler
|
|
93
|
+
handles :times
|
|
94
|
+
|
|
95
|
+
def initialize(*args)
|
|
96
|
+
@run = false
|
|
97
|
+
super(*args)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def match?
|
|
101
|
+
@matcher <= ::Time.now && !@run
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def run(force = false)
|
|
105
|
+
if match?
|
|
106
|
+
@run = true
|
|
107
|
+
Plugin.registered_plugins[@plugin].send(@method)
|
|
108
|
+
else
|
|
109
|
+
false
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
data/lib/message.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module CampfireBot
|
|
2
|
+
class Message < ActiveSupport::HashWithIndifferentAccess
|
|
3
|
+
def initialize(attributes)
|
|
4
|
+
self.merge!(attributes)
|
|
5
|
+
self[:message] = self['body'] if !!self['body']
|
|
6
|
+
self[:person] = self['user']['name'] if !!self['user']
|
|
7
|
+
self[:room] = attributes[:room]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def reply(str)
|
|
11
|
+
speak(str)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def speak(str)
|
|
15
|
+
self[:room].speak(str)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def paste(str)
|
|
19
|
+
self[:room].paste(str)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def upload(file_path)
|
|
23
|
+
self[:room].upload(file_path)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def play(str)
|
|
27
|
+
self[:room].play(str)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/plugin.rb
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module CampfireBot
|
|
2
|
+
class Plugin
|
|
3
|
+
@registered_plugins = {}
|
|
4
|
+
|
|
5
|
+
@registered_commands = []
|
|
6
|
+
@registered_messages = []
|
|
7
|
+
@registered_speakers = []
|
|
8
|
+
@registered_intervals = []
|
|
9
|
+
@registered_times = []
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
attr_reader :registered_plugins,
|
|
13
|
+
:registered_commands,
|
|
14
|
+
:registered_messages,
|
|
15
|
+
:registered_speakers,
|
|
16
|
+
:registered_intervals,
|
|
17
|
+
:registered_times
|
|
18
|
+
|
|
19
|
+
@@config_defaults = {}
|
|
20
|
+
|
|
21
|
+
# Registering plugins
|
|
22
|
+
|
|
23
|
+
def inherited(child)
|
|
24
|
+
Plugin.registered_plugins[child.to_s] = child
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Event handlers
|
|
28
|
+
|
|
29
|
+
def on_command(command, *methods)
|
|
30
|
+
methods.each do |method|
|
|
31
|
+
Plugin.registered_commands << Event::Command.new(command, self.to_s, method)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def on_message(regexp, *methods)
|
|
36
|
+
methods.each do |method|
|
|
37
|
+
Plugin.registered_messages << Event::Message.new(regexp, self.to_s, method)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def on_speaker(speaker, *methods)
|
|
42
|
+
methods.each do |method|
|
|
43
|
+
Plugin.registered_speakers << Event::Speaker.new(speaker, self.to_s, method)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def at_interval(interval, *methods)
|
|
48
|
+
methods.each do |method|
|
|
49
|
+
Plugin.registered_intervals << Event::Interval.new(interval, self.to_s, method)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def at_time(timestamp, *methods)
|
|
54
|
+
methods.each do |method|
|
|
55
|
+
Plugin.registered_times << Event::Time.new(timestamp, self.to_s, method)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Declare a plugin configuration parameter with its default value
|
|
60
|
+
def config_var(name, default)
|
|
61
|
+
attr_reader name
|
|
62
|
+
@@config_defaults ||= {}
|
|
63
|
+
@@config_defaults[self.name] ||= {}
|
|
64
|
+
@@config_defaults[self.name][name] = default
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
def initialize
|
|
68
|
+
# initialize attr_readers setup with config_var
|
|
69
|
+
config_prefix = self.class.to_s.underscore
|
|
70
|
+
(@@config_defaults[self.class.name] || {}).each_pair { |name, default|
|
|
71
|
+
instance_variable_set("@#{name.to_s}",
|
|
72
|
+
bot.config["#{config_prefix}_#{name.to_s}"] ||
|
|
73
|
+
default)
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
data/lib/version.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
class Accountability < CampfireBot::Plugin
|
|
2
|
+
on_message /^\[INDAY/i, :save_speaker
|
|
3
|
+
at_interval 5.minutes, :keep_them_honest
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
@accountable_people = []
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def save_speaker(msg)
|
|
10
|
+
if msg[:room].name == accountability_room.name
|
|
11
|
+
puts "WE'RE IN!"
|
|
12
|
+
@accountable_people << strip_tags(msg[:person]) unless @accountable_people.include?(strip_tags(msg[:person]))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def keep_them_honest(msg)
|
|
17
|
+
if accountability_time?
|
|
18
|
+
unaccountable_people.each do |person|
|
|
19
|
+
@room.speak("#{person}: We haven't seen your INDAY today. So what's the plan?")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@accountable_people.clear
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def unaccountable_people
|
|
29
|
+
accountability_room.users.map { |u| strip_tags(u) } - @accountable_people - [bot.config['nickname']]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def accountability_room
|
|
33
|
+
@room ||= bot.rooms[bot.config['accountability_room']] || bot.rooms.first
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def accountability_time?
|
|
37
|
+
# Once a day, sometime between 11:00 and 11:05.
|
|
38
|
+
now = Time.now
|
|
39
|
+
(Time.mktime(now.year, now.month, now.day, 11, 0)..Time.mktime(now.year, now.month, now.day, 11, 5)).include?(now)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def strip_tags(str)
|
|
43
|
+
str.gsub(/<\/?[^>]*>/, '').strip
|
|
44
|
+
end
|
|
45
|
+
end
|
data/plugins/austin.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'open-uri'
|
|
2
|
+
require 'hpricot'
|
|
3
|
+
|
|
4
|
+
class Austin < CampfireBot::Plugin
|
|
5
|
+
BASE_URL = 'http://www.imdb.com/character/ch0002425/quotes'
|
|
6
|
+
|
|
7
|
+
on_command 'austin', :austin
|
|
8
|
+
|
|
9
|
+
def austin(msg)
|
|
10
|
+
doc = Hpricot(open(BASE_URL))
|
|
11
|
+
chunks = []
|
|
12
|
+
doc.traverse_element("br") {|b| chunks << b if b.next_node != nil && b.next_node.elem? && b.next_node.to_s == '<br />' }
|
|
13
|
+
chunks.pop
|
|
14
|
+
random_chunk = rand(chunks.size - 1)
|
|
15
|
+
raw_quote = chunks[random_chunk].nodes_at(2..(chunks[random_chunk + 1].node_position - chunks[random_chunk].node_position - 1))
|
|
16
|
+
quote = raw_quote.to_s
|
|
17
|
+
quote.gsub!(/(\\n)/, "")
|
|
18
|
+
quote.gsub!(/(<br \/>)/, "\n")
|
|
19
|
+
quote.gsub!(/^\s/, "")
|
|
20
|
+
quote.gsub!(/ {2,}/, " ")
|
|
21
|
+
quote.gsub!(/<\/?[^>]*>/, "")
|
|
22
|
+
quote.split("\n")
|
|
23
|
+
|
|
24
|
+
quote.each {|l| msg.speak l}
|
|
25
|
+
|
|
26
|
+
rescue
|
|
27
|
+
msg.speak 'Austin Powers: Yeah, baby, yeah'
|
|
28
|
+
end
|
|
29
|
+
end
|
data/plugins/basecamp.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'hpricot'
|
|
2
|
+
|
|
3
|
+
class Basecamp < CampfireBot::Plugin
|
|
4
|
+
on_command 'writeboard', :writeboard
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
# TODO find a better temp file name
|
|
8
|
+
@cookie_jar = BOT_ROOT + '/tmp/basecamp-cookies.txt'
|
|
9
|
+
@writeboard = bot.config['basecamp_writeboard_url']
|
|
10
|
+
@domain = @writeboard.split(/\/+/)[1]
|
|
11
|
+
@username = bot.config['basecamp_username']
|
|
12
|
+
@password = bot.config['basecamp_password']
|
|
13
|
+
@ssl = !!bot.config['basecamp_use_ssl']
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def writeboard(msg)
|
|
17
|
+
msg.paste get_contents
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
# TODO escape stuff here. output = `#{Escape.shell_command(['figlet', '--', m[:message]])}`
|
|
23
|
+
def get_contents
|
|
24
|
+
# Prime the cookie jar: log in.
|
|
25
|
+
basecamp_login = `curl -c #{@cookie_jar} -b #{@cookie_jar} -d "user_name=#{@username}&password=#{@password}" -L http#{'s' if @ssl}://#{@domain}/login/authenticate`
|
|
26
|
+
|
|
27
|
+
# Fetch the contents of the writeboard redirect page
|
|
28
|
+
writeboard_redir = `curl -c #{@cookie_jar} -b #{@cookie_jar} -L #{@writeboard}`
|
|
29
|
+
|
|
30
|
+
# Simulate the javascripted login to the writeboard site
|
|
31
|
+
redir_form = Hpricot(writeboard_redir).search('form').first
|
|
32
|
+
writeboard_url = redir_form['action'].gsub(/\/login$/, '')
|
|
33
|
+
writeboard_author = bot.config['nickname']
|
|
34
|
+
writeboard_password = redir_form.search("input[@name='password']").first['value']
|
|
35
|
+
|
|
36
|
+
writeboard_login = `curl -c #{@cookie_jar} -b #{@cookie_jar} -d "author_name=#{writeboard_author}&password=#{writeboard_password}" -L #{redir_form['action']}`
|
|
37
|
+
|
|
38
|
+
# Now we can get the contents of the writeboard's page, which contains a link to the text export
|
|
39
|
+
writeboard_page = Hpricot(`curl -c #{@cookie_jar} -b #{@cookie_jar} -L #{writeboard_url}`)
|
|
40
|
+
|
|
41
|
+
export_link = 'http://123.writeboard.com' + writeboard_page.search("a[@href*='?format=txt']").first['href']
|
|
42
|
+
|
|
43
|
+
# Finally, grab the text export
|
|
44
|
+
writeboard_text = `curl -c #{@cookie_jar} -b #{@cookie_jar} #{export_link}`
|
|
45
|
+
|
|
46
|
+
return writeboard_text
|
|
47
|
+
end
|
|
48
|
+
end
|
data/plugins/beer.rb
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
class Beer < CampfireBot::Plugin
|
|
4
|
+
|
|
5
|
+
# Beer::COMMAND_REGEXP = "^(?:!|#{bot.config['nickname']},\\s+)"
|
|
6
|
+
|
|
7
|
+
# Beer::GIVE_REGEXP = Regexp.new("#{Beer::COMMAND_REGEXP}([a-z\\s\\.]{4,12})([0-9]+)*", Regexp::IGNORECASE)
|
|
8
|
+
# Beer::CREDIT_REGEXP = //
|
|
9
|
+
# Beer::BALANCE_REGEXP = //
|
|
10
|
+
|
|
11
|
+
# on_message @debit_matcher, :respond
|
|
12
|
+
# on_message Regexp.new("#{CREDIT_REGEXP.source}", Regexp::IGNORECASE), :credit_cmd
|
|
13
|
+
on_command 'give_beer', :give_beer
|
|
14
|
+
on_command 'demand_beer', :demand_beer
|
|
15
|
+
on_command 'redeem_beer', :redeem_beer
|
|
16
|
+
on_command 'balance', :balance_cmd
|
|
17
|
+
|
|
18
|
+
# balances: {'josh' => {'party1' => 1 }, 'party1': '}
|
|
19
|
+
|
|
20
|
+
class BadArgumentException < Exception
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def give_beer(msg)
|
|
24
|
+
give_or_demand_beer(msg, :give)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def demand_beer(msg)
|
|
28
|
+
give_or_demand_beer(msg, :demand)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def redeem_beer(msg)
|
|
32
|
+
give_or_demand_beer(msg, :redeem)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def balance_cmd(msg)
|
|
36
|
+
@balances = init()
|
|
37
|
+
begin
|
|
38
|
+
if msg[:message].empty?
|
|
39
|
+
return
|
|
40
|
+
# raise BadArgumentException.new, "Sorry, I don't know whom to #{trans_type_msg}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
speaker = msg[:person]
|
|
44
|
+
payee = msg[:message]
|
|
45
|
+
|
|
46
|
+
bal = balance(speaker, payee)
|
|
47
|
+
|
|
48
|
+
units = bal.abs == 1 ? "beer" : "beers"
|
|
49
|
+
|
|
50
|
+
if bal < 0
|
|
51
|
+
msg.speak("#{payee} owes you #{bal * -1} #{units}")
|
|
52
|
+
elsif bal > 0
|
|
53
|
+
msg.speak("You owe #{payee} #{bal} #{units}")
|
|
54
|
+
else
|
|
55
|
+
msg.speak("You and #{payee} are even")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
rescue BadArgumentException => exception
|
|
59
|
+
msg.speak(exception.message)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def give_or_demand_beer(msg, trans_type)
|
|
65
|
+
args = msg[:message].match(/(^[a-z\s\.\-]+?)(\s*\-*[0-9]*)$/i)
|
|
66
|
+
# puts "args = #{args.inspect}"
|
|
67
|
+
# puts "args[0] = #{args[0]}"
|
|
68
|
+
# puts "args[1] = #{args[1]}"
|
|
69
|
+
# puts "args[2] = #{args[2]}"
|
|
70
|
+
|
|
71
|
+
trans_type_msg = {:give => 'give beer to', :demand => 'demand beer from', :redeem => 'redeem beer from'}[trans_type]
|
|
72
|
+
|
|
73
|
+
begin
|
|
74
|
+
|
|
75
|
+
if args.nil?
|
|
76
|
+
raise BadArgumentException.new, "Sorry, I don't know whom to #{trans_type_msg}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
payee = args[1].strip
|
|
80
|
+
speaker = msg[:person]
|
|
81
|
+
|
|
82
|
+
if args[2] != "" and args[2].to_i == 0
|
|
83
|
+
raise BadArgumentException.new, "Sorry, I don't accept non-integer amounts"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
amt = args[2] != "" ? args[2].to_i : 1
|
|
87
|
+
|
|
88
|
+
if amt <= 0
|
|
89
|
+
raise BadArgumentException.new, "Sorry, I don't accept negative numbers as an argument"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
case trans_type
|
|
93
|
+
when :give
|
|
94
|
+
# no change - this is a debit
|
|
95
|
+
when :demand
|
|
96
|
+
amt = amt * -1 # this is a credit
|
|
97
|
+
when :redeem
|
|
98
|
+
# amt = amt * -1 # this is a credit
|
|
99
|
+
# no change - this is a debit
|
|
100
|
+
if bal = balance(speaker, payee) == 0
|
|
101
|
+
raise BadArgumentException.new, "#{payee} didn't owe you any beers to begin with."
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
beer_transaction(speaker, payee, amt)
|
|
106
|
+
|
|
107
|
+
bal = balance(speaker, payee)
|
|
108
|
+
|
|
109
|
+
# puts "post transaction balance = #{bal}"
|
|
110
|
+
if bal > 0
|
|
111
|
+
msg.speak("Okay, you now owe #{payee} #{bal} beers")
|
|
112
|
+
elsif bal < 0
|
|
113
|
+
msg.speak("Okay, #{payee} now owes you #{bal * -1} beers")
|
|
114
|
+
else
|
|
115
|
+
msg.speak("Okay, you and #{payee} are now even")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
rescue BadArgumentException => exception
|
|
119
|
+
msg.speak(exception.message)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def beer_transaction(user1, user2, amount)
|
|
126
|
+
#beer_transaction user1, user2, 1 : user1 owes user2 a beer
|
|
127
|
+
#beer_transaction user1, user2, -1 : user1 demands a beer from user2 (user1 owes user2 -1 beers)
|
|
128
|
+
@balances = init()
|
|
129
|
+
# puts 'beer_transaction'
|
|
130
|
+
# p "beer_transaction start: #{@balances.inspect}"
|
|
131
|
+
# p "beer_transaction args: #{user1}, #{user2}, #{amount}"
|
|
132
|
+
user1 = user1.downcase.strip
|
|
133
|
+
user2 = user2.downcase.strip
|
|
134
|
+
|
|
135
|
+
if !@balances.key?(user1)
|
|
136
|
+
@balances[user1] = {}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
if !@balances[user1].key?(user2)
|
|
140
|
+
@balances[user1][user2] = 0
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
@balances[user1][user2] += amount
|
|
144
|
+
|
|
145
|
+
# p "beer_transaction end: #{@balances.inspect}"
|
|
146
|
+
write
|
|
147
|
+
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def balance(user1, user2)
|
|
153
|
+
# verb => user1 owes user2 #{balance} beers
|
|
154
|
+
# bal(user1, user2) = 1 # user1 is owed a beer from user2
|
|
155
|
+
# bal(user1, user2) = -1 # user1 is owed -1 beers from user 2 (meaning user2 owes user1)
|
|
156
|
+
|
|
157
|
+
@balances = init()
|
|
158
|
+
|
|
159
|
+
# puts "hash is #{hash}, @balances[#{hash}] = #{@balances[hash]}"
|
|
160
|
+
user1 = user1.downcase.strip
|
|
161
|
+
user2 = user2.downcase.strip
|
|
162
|
+
|
|
163
|
+
if @balances.key?(user1) and @balances[user1].key?(user2)
|
|
164
|
+
bal1 = @balances[user1][user2]
|
|
165
|
+
else
|
|
166
|
+
bal1 = 0
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
if @balances.key?(user2) and @balances[user2].key?(user1)
|
|
170
|
+
bal2 = @balances[user2][user1]
|
|
171
|
+
else
|
|
172
|
+
bal2 = 0
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
bal = bal1 - bal2
|
|
176
|
+
|
|
177
|
+
if (@balances.key?(user1) ? !@balances[user1].key?(user2) : true) and
|
|
178
|
+
(@balances.key?(user2) ? !@balances[user2].key?(user1) : true)
|
|
179
|
+
raise BadArgumentException.new, "Sorry, you don't have any beer transactions with anyone named #{user2}"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
bal
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def get_hash(user_1, user_2)
|
|
186
|
+
user1 = user_1.downcase
|
|
187
|
+
user2 = user_2.downcase
|
|
188
|
+
|
|
189
|
+
[user1,user2].sort.to_s
|
|
190
|
+
# if user1 < user2
|
|
191
|
+
# "#{user1}#{user2}"
|
|
192
|
+
# else
|
|
193
|
+
# "#{user2}#{user1}"
|
|
194
|
+
# end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def init
|
|
198
|
+
# puts "entering init()"
|
|
199
|
+
YAML::load(File.read(File.join(BOT_ROOT, 'tmp', 'beer.yml')))
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def write
|
|
203
|
+
File.open(File.join(BOT_ROOT, 'tmp', 'beer.yml'), 'w') do |out|
|
|
204
|
+
YAML.dump(@balances, out)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def reload(msg)
|
|
210
|
+
@balances = init()
|
|
211
|
+
speak("ok, reloaded #{@balances.size} balances")
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
end
|