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,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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module CampfireBot
2
+ VERSION = '0.0.1'
3
+ end
@@ -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
@@ -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
@@ -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
@@ -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