wesabot 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE +20 -0
  4. data/README.md +182 -0
  5. data/Rakefile +13 -0
  6. data/bin/wesabot +111 -0
  7. data/config/wesabot.yml.sample +4 -0
  8. data/lib/campfire/bot.rb +53 -0
  9. data/lib/campfire/configuration.rb +65 -0
  10. data/lib/campfire/message.rb +102 -0
  11. data/lib/campfire/polling_bot/plugin.rb +162 -0
  12. data/lib/campfire/polling_bot/plugins/airbrake/.gitignore +1 -0
  13. data/lib/campfire/polling_bot/plugins/airbrake/airbrake/api.rb +48 -0
  14. data/lib/campfire/polling_bot/plugins/airbrake/airbrake/error.rb +51 -0
  15. data/lib/campfire/polling_bot/plugins/airbrake/airbrake_plugin.rb +63 -0
  16. data/lib/campfire/polling_bot/plugins/airbrake/airbrake_plugin.yml.sample +3 -0
  17. data/lib/campfire/polling_bot/plugins/bookmark/bookmark.rb +15 -0
  18. data/lib/campfire/polling_bot/plugins/bookmark/bookmark_plugin.rb +89 -0
  19. data/lib/campfire/polling_bot/plugins/debug/debug_plugin.rb +23 -0
  20. data/lib/campfire/polling_bot/plugins/deploy/deploy_plugin.rb +155 -0
  21. data/lib/campfire/polling_bot/plugins/deploy/deploy_plugin.yml.sample +9 -0
  22. data/lib/campfire/polling_bot/plugins/deploy/spec/deploy_plugin_spec.rb +153 -0
  23. data/lib/campfire/polling_bot/plugins/greeting/greeting_plugin.rb +146 -0
  24. data/lib/campfire/polling_bot/plugins/greeting/greeting_setting.rb +19 -0
  25. data/lib/campfire/polling_bot/plugins/greeting/spec/greeting_plugin_spec.rb +51 -0
  26. data/lib/campfire/polling_bot/plugins/greeting/spec/greeting_setting_spec.rb +61 -0
  27. data/lib/campfire/polling_bot/plugins/help/help_plugin.rb +53 -0
  28. data/lib/campfire/polling_bot/plugins/history/history_plugin.rb +34 -0
  29. data/lib/campfire/polling_bot/plugins/image_search/displayed_image.rb +8 -0
  30. data/lib/campfire/polling_bot/plugins/image_search/image_search_plugin.rb +106 -0
  31. data/lib/campfire/polling_bot/plugins/interjector/interjector_plugin.rb +58 -0
  32. data/lib/campfire/polling_bot/plugins/kibitz/kibitz_plugin.rb +90 -0
  33. data/lib/campfire/polling_bot/plugins/kibitz/spec/kibitz_plugin_spec.rb +56 -0
  34. data/lib/campfire/polling_bot/plugins/plugin.db +0 -0
  35. data/lib/campfire/polling_bot/plugins/reload/reload_plugin.rb +24 -0
  36. data/lib/campfire/polling_bot/plugins/remind_me/remind_me_plugin.rb +149 -0
  37. data/lib/campfire/polling_bot/plugins/remind_me/reminder.rb +8 -0
  38. data/lib/campfire/polling_bot/plugins/shared/message.rb +37 -0
  39. data/lib/campfire/polling_bot/plugins/shared/user.rb +32 -0
  40. data/lib/campfire/polling_bot/plugins/sms/sms_plugin.rb +88 -0
  41. data/lib/campfire/polling_bot/plugins/sms/sms_setting.rb +7 -0
  42. data/lib/campfire/polling_bot/plugins/time/time_plugin.rb +17 -0
  43. data/lib/campfire/polling_bot/plugins/tweet/tweet.rb +52 -0
  44. data/lib/campfire/polling_bot/plugins/tweet/tweet_plugin.rb +117 -0
  45. data/lib/campfire/polling_bot/plugins/tweet/tweet_plugin.yml.sample +4 -0
  46. data/lib/campfire/polling_bot/plugins/twitter_search/twitter_search_plugin.rb +58 -0
  47. data/lib/campfire/polling_bot.rb +125 -0
  48. data/lib/campfire/sample_plugin.rb +56 -0
  49. data/lib/campfire/version.rb +3 -0
  50. data/lib/wesabot.rb +3 -0
  51. data/spec/.gitignore +1 -0
  52. data/spec/polling_bot_spec.rb +23 -0
  53. data/spec/spec_helper.rb +190 -0
  54. data/wesabot.gemspec +55 -0
  55. metadata +336 -0
@@ -0,0 +1,153 @@
1
+ require 'spec_helper'
2
+
3
+ describe DeployPlugin do
4
+ def use_example_config
5
+ @plugin.config = {
6
+ 'projects' => {
7
+ 'my_super_site' => {
8
+ 'deployed_revision_url' => 'http://www.example.com/REVISION'
9
+ },
10
+
11
+ 'other' => {
12
+ 'deployed_revision_url' => 'http://www.other.com/REVISION'
13
+ }}}
14
+ end
15
+
16
+ def use_no_config
17
+ @plugin.config = nil
18
+ end
19
+
20
+ def use_blank_config
21
+ @plugin.config = {'project' => {}}
22
+ end
23
+
24
+ before do
25
+ @plugin = DeployPlugin.new
26
+ end
27
+
28
+ context "with no configuration" do
29
+ before do
30
+ use_no_config
31
+ end
32
+
33
+ it "has no default project" do
34
+ @plugin.default_project.should be_nil
35
+ end
36
+
37
+ it "has an empty hash of projects" do
38
+ @plugin.projects.should == {}
39
+ end
40
+
41
+ it "omits the short form of the 'on deck' command from the help" do
42
+ @plugin.help.should have(2).items
43
+ end
44
+ end
45
+
46
+ describe "the 'on deck' command" do
47
+ context "with no projects" do
48
+ before do
49
+ use_blank_config
50
+ end
51
+
52
+ it "tells the sender that it doesn't know about any projects" do
53
+ asking("wes, what's on deck?").
54
+ should make_wes_say( %r{I don't know about any projects} )
55
+ end
56
+ end
57
+
58
+ context "with projects" do
59
+ before do
60
+ use_example_config
61
+ @plugin.stub!(:deployed_revision).and_return('abcde')
62
+ end
63
+
64
+ it "tells the sender when the project asked about does not exist" do
65
+ asking("wes, what's on deck for foobar?").
66
+ should make_wes_say( %r{I don't know anything about foobar} ).
67
+ and_paste("my_super_site\nother")
68
+ end
69
+
70
+ context "with no default project" do
71
+ before do
72
+ allow_message_expectations_on_nil
73
+ @project.stub!(:default_project).and_return(nil)
74
+ end
75
+
76
+ context "and more than one registered project" do
77
+ it "lists available projects when the project is omitted" do
78
+ asking("wes, what's on deck?").
79
+ should make_wes_say( %r{I don't have a default project} ).
80
+ and_paste("my_super_site\nother")
81
+ end
82
+ end
83
+
84
+ context "and only one registered project" do
85
+ before do
86
+ @plugin.projects.delete(@plugin.projects.keys.first) while @plugin.projects.size > 1
87
+ end
88
+
89
+ it "uses the only listed project as the default project" do
90
+ @plugin.default_project.should == @plugin.projects.keys.first
91
+ end
92
+ end
93
+ end
94
+
95
+ context "with a default project" do
96
+ before do
97
+ @plugin.stub!(:default_project).and_return('my_super_site')
98
+ @plugin.stub!(:project_shortlog).
99
+ and_return("John Tester (1):\n Fix homepage links.\n")
100
+ end
101
+
102
+ it "uses the default project when the project is omitted" do
103
+ asking("wes, what's on deck?").
104
+ should make_wes_say( %r{Here's what's on deck for my_super_site} ).
105
+ and_paste(anything)
106
+ end
107
+ end
108
+
109
+ context "when there is nothing on deck" do
110
+ before do
111
+ @plugin.stub!(:project_shortlog).
112
+ and_return('')
113
+ end
114
+
115
+ it "responds that nothing is on deck for the project" do
116
+ asking("wes, what's on deck for my_super_site?").
117
+ should make_wes_say("There's nothing on deck for my_super_site right now.")
118
+ end
119
+ end
120
+
121
+ context "when there is something on deck" do
122
+ before do
123
+ @plugin.stub!(:project_shortlog).
124
+ and_return("John Tester (1):\n Fix homepage links.\n\n")
125
+ end
126
+
127
+ it "responds with the shortlog for the project" do
128
+ asking("wes, what's on deck for my_super_site?").
129
+ should make_wes_say("Here's what's on deck for my_super_site:").
130
+ and_paste(<<-EOS)
131
+ $ git shortlog abcde..HEAD
132
+
133
+ John Tester (1):
134
+ Fix homepage links.
135
+
136
+ EOS
137
+ end
138
+ end
139
+
140
+ context "when getting the shortlog fails" do
141
+ before do
142
+ @plugin.stub!(:project_shortlog).
143
+ and_raise("testing")
144
+ end
145
+
146
+ it "reports the error to the user" do
147
+ asking("wes, what's on deck for my_super_site?").
148
+ should make_wes_say("Sorry John, I couldn't get what's on deck for my_super_site.")
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,146 @@
1
+ # Plugin to greet people when they enter, provide a catch-up url, and notify them of any "future" messages
2
+ # requires the HistoryPlugin
3
+ class GreetingPlugin < Campfire::PollingBot::Plugin
4
+ accepts :text_message, :addressed_to_me => true
5
+ accepts :enter_message
6
+
7
+ def process(message)
8
+ user = message.user
9
+ wants_greeting = wants_greeting?(user)
10
+ if message.kind_of?(Campfire::EnterMessage)
11
+ link = catch_up_link(message.person_full_name)
12
+ futures = future_messages(message.person_full_name, message.person)
13
+ if wants_greeting && link
14
+ bot.say("Hey #{message.person.downcase}. Catch up: #{link}")
15
+ end
16
+ futures.each{|m| bot.say(m) }
17
+ elsif message.kind_of?(Campfire::TextMessage)
18
+ case message.command
19
+ when /(disable|turn off) greetings/i
20
+ wants_greeting(user, false)
21
+ bot.say("OK, I've disabled greetings for you, #{message.person}")
22
+ return HALT
23
+ when /(enable|turn on) greetings/i
24
+ wants_greeting(user, true)
25
+ bot.say("OK, I've enabled greetings for you, #{message.person}")
26
+ return HALT
27
+ when /toggle greetings/i
28
+ old_setting = wants_greeting?(user)
29
+ wants_greeting(user, !old_setting)
30
+ bot.say("OK, I've #{old_setting ? 'disabled' : 'enabled'} greetings for you, #{message.person}")
31
+ return HALT
32
+ when /catch me up|ketchup/i
33
+ if link = catch_up_link(message.person_full_name, true)
34
+ bot.say("Here you go, #{message.person}: #{link}")
35
+ else
36
+ bot.say("Hmm...couldn't find when you last logged out, #{message.person}")
37
+ end
38
+ return HALT
39
+ end
40
+ end
41
+ end
42
+ # return array of available commands and descriptions
43
+ def help
44
+ [['(disable|turn off) greetings', "don't say hi when you log in (you grump)"],
45
+ ['(enable|turn on) greetings', "say hi when you log in"],
46
+ ['toggle greetings', "disable greetings if enabled, enable if disabled. You know--toggle."],
47
+ ['catch me up|ketchup', "gives you a link to the point in the transcript where you last logged out"]
48
+ ]
49
+ end
50
+
51
+ private
52
+
53
+ # return the last message we saw from the user
54
+ def last_message(person_full_name)
55
+ # look for a leave message
56
+ last_left = Message.last_left(person_full_name)
57
+ # look for any message more than a couple minutes ago (this allows us
58
+ # to say hi in CF and then ask for a catch-up link without getting a link
59
+ # to the message we just sent)
60
+ last_message = Message.last_message(person_full_name, Time.now - 120)
61
+
62
+ if last_left && last_left.message_type == 'Kick'
63
+ # if person timed out, look for their last entry before the timeout
64
+ last_seen = last_message
65
+ else
66
+ # if the person said things after their last leave message, they are
67
+ # probably still in the room, so we use their last message instead
68
+ last_seen = [last_left, last_message].compact.sort_by{|m| m.timestamp }.last
69
+ end
70
+
71
+ return last_seen
72
+ end
73
+
74
+ # get link to when the user last left the room so they can catch up
75
+ # only return a link if the user has been gone long enough, unless forced
76
+ def catch_up_link(person_full_name, force=false)
77
+ message = last_message(person_full_name)
78
+ # only give a link if the last message is likely no longer on the first page
79
+ if message && (force || !message.visible?)
80
+ return message_link(message.message_id)
81
+ else
82
+ return nil
83
+ end
84
+ end
85
+
86
+ # Tell the person who's just entered about what people were asking them to
87
+ # read about while they were gone.
88
+ def future_messages(person_full_name, person)
89
+ future_messages = []
90
+ verbs = ["invoked", "called to", "cried out for", "made a sacrifice to", "let slip",
91
+ "doorbell ditched", "whispered sweetly to", "walked over broken glass to get to",
92
+ "prayed to the god of", "ran headlong at", "checked in a timebomb for",
93
+ "interpolated some strings TWO TIMES for", "wished upon a", "was like, oh my god",
94
+ "went all", "tested the concept of"]
95
+
96
+ # future Brian/future Brian Donovan
97
+ names = [person_full_name, person]
98
+
99
+ # future BD
100
+ name_words = person_full_name.split(/\s+/)
101
+ names << (name_words.first[0,1]+name_words.last[0,1]) if name_words.size > 1
102
+
103
+ future_person = Regexp.new("(?:future |@)(#{names.join('|')})\\b", Regexp::IGNORECASE)
104
+ future_everybody = Regexp.new("(?:future |@)everybody", Regexp::IGNORECASE)
105
+
106
+ if message = last_message(person_full_name)
107
+ candidates = Message.all(
108
+ :message_id.gt => message.message_id,
109
+ :person.not => ['Fogbugz','Subversion','Capistrano',bot.name],
110
+ :message_type => 'Text')
111
+ candidates.each do |row|
112
+ if row.body.match(future_person)
113
+ verbed = verbs[rand(verbs.size)]
114
+ future_messages << "#{row.person} #{verbed} future #{person} at: #{message_link(row.message_id)}"
115
+ elsif row.body.match(future_everybody)
116
+ verbed = verbs[rand(verbs.size)]
117
+ future_messages << "#{row.person} #{verbed} future everybody: \"#{row.body}\""
118
+ end
119
+ end
120
+ end
121
+ return future_messages
122
+ end
123
+
124
+ def wants_greeting?(user)
125
+ @wants_greeting_cache ||= User.all.inject({}) do |memo, u|
126
+ memo.merge(u => u.wants_greeting?)
127
+ end
128
+
129
+ if @wants_greeting_cache[user].nil?
130
+ @wants_greeting_cache[user] = user.wants_greeting = true
131
+ end
132
+
133
+ return @wants_greeting_cache[user]
134
+ end
135
+
136
+ def wants_greeting(user, wants_greeting)
137
+ user.wants_greeting = wants_greeting
138
+ @wants_greeting_cache[user] = wants_greeting
139
+ end
140
+
141
+ def message_link(id)
142
+ "#{bot.base_uri}/room/#{bot.room.id}/transcript/message/#{id}#message_#{id}"
143
+ end
144
+
145
+ end
146
+
@@ -0,0 +1,19 @@
1
+ # used by GreetingPlugin
2
+ class GreetingSetting
3
+ include DataMapper::Resource
4
+ property :id, Serial
5
+ property :person, String, :index => true
6
+ property :wants_greeting, Boolean
7
+
8
+ belongs_to :user, :required => false
9
+
10
+ def self.for_user(user)
11
+ result = first(:user => user) || first(:person => user.name, :user => nil)
12
+
13
+ if result && result.user.nil?
14
+ result.update(:user => user)
15
+ end
16
+
17
+ return result
18
+ end
19
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe GreetingPlugin do
4
+ before do
5
+ GreetingSetting.all.destroy
6
+ User.all.destroy
7
+ @plugin = described_class.new
8
+ @plugin.stub!(:catch_up_link).and_return("http://example.com")
9
+ end
10
+
11
+ it 'greets users as they enter the room by default' do
12
+ entering.should make_wes_say(/^Hey John/i)
13
+ end
14
+
15
+ it 'allows disabling greetings per user' do
16
+ say('wes, disable greetings')
17
+ leave
18
+ entering.should_not make_wes_say_anything
19
+ end
20
+
21
+ it 'confirms disabling greetings per user' do
22
+ saying('wes, disable greetings').
23
+ should make_wes_say("OK, I've disabled greetings for you, John")
24
+ end
25
+
26
+ it 'allows re-enabling greetings per user' do
27
+ say('wes, disable greetings')
28
+ say('wes, enable greetings')
29
+ leave
30
+ entering.should make_wes_say(/^Hey John/i)
31
+ end
32
+
33
+ it 'confirms re-enabling greetings per user' do
34
+ say('wes, disable greetings')
35
+ saying('wes, enable greetings').
36
+ should make_wes_say("OK, I've enabled greetings for you, John")
37
+ end
38
+
39
+ it 'allows toggling greetings per user' do
40
+ say('wes, enable greetings')
41
+ say('wes, toggle greetings')
42
+ leave
43
+ entering.should_not make_wes_say_anything
44
+ end
45
+
46
+ it 'confirms toggling greetings per user' do
47
+ say('wes, enable greetings')
48
+ saying('wes, toggle greetings').
49
+ should make_wes_say("OK, I've disabled greetings for you, John")
50
+ end
51
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe GreetingSetting do
4
+ before do
5
+ @bot = FakeBot.new
6
+ User.all.destroy
7
+ GreetingSetting.all.destroy
8
+ end
9
+
10
+ describe ".for_user" do
11
+ context "when the user has no setting" do
12
+ before do
13
+ @user = User.create(:name => "Marc")
14
+ end
15
+
16
+ it "returns nil" do
17
+ described_class.for_user(@user).should be_nil
18
+ end
19
+ end
20
+
21
+ context "when the user has a setting by name only" do
22
+ before do
23
+ @user = User.create(:name => "Bob")
24
+ @gs = described_class.create(:person => "Bob")
25
+ end
26
+
27
+ it "returns the greeting setting" do
28
+ described_class.for_user(@user).should == @gs
29
+ end
30
+
31
+ it "associates the greeting setting with the user" do
32
+ lambda { described_class.for_user(@user) }.
33
+ should change { @gs.reload.user }.
34
+ from(nil).to(@user)
35
+ end
36
+ end
37
+
38
+ context "when the user has the same name as another with an associated setting" do
39
+ before do
40
+ @user = User.create(:name => "Sam")
41
+ @other = User.create(:name => "Sam")
42
+ @gs = described_class.create(:person => "Sam", :user => @other)
43
+ end
44
+
45
+ it "returns nil" do
46
+ described_class.for_user(@user).should be_nil
47
+ end
48
+ end
49
+
50
+ context "when the user has an associated greeting setting" do
51
+ before do
52
+ @user = User.create(:name => "Coda")
53
+ @gs = described_class.create(:person => "Coda", :user => @user)
54
+ end
55
+
56
+ it "returns the associated greeting setting" do
57
+ described_class.for_user(@user).should == @gs
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,53 @@
1
+ # Plugin to display available commands for each plugin
2
+ class HelpPlugin < Campfire::PollingBot::Plugin
3
+ accepts :text_message, :addressed_to_me => true
4
+ priority 0
5
+
6
+ def process(message)
7
+ person = message.person
8
+ case message.command
9
+ when /help(?:\s(.*))?/i
10
+ if $1
11
+ name = $1
12
+ plugin = plugin_helps.keys.find do |k|
13
+ k =~ /(#{name}|#{name.gsub(/s$/i, '')})(plugin)?/i
14
+ end
15
+ matched_helps = { plugin => plugin_helps[plugin] } if plugin
16
+ end
17
+ matched_helps ||= plugin_helps
18
+
19
+ bot.paste msg_for(matched_helps)
20
+ return HALT
21
+ end
22
+ end
23
+
24
+ # return array of available commands and descriptions
25
+ def help
26
+ [['help', "this message"]]
27
+ end
28
+
29
+ protected
30
+
31
+ def plugin_helps
32
+ @plugin_helps ||= begin
33
+ help = bot.plugins.map do |plugin|
34
+ begin
35
+ [plugin.to_s, plugin.help] if plugin.respond_to?(:help)
36
+ rescue Exception => e
37
+ bot.log_error(e)
38
+ end
39
+ end.compact
40
+ Hash[help]
41
+ end
42
+ end
43
+
44
+ def msg_for(help)
45
+ help.keys.sort.map do |plugin|
46
+ cmds = help[plugin].map do |command, description|
47
+ " - #{command}\n #{description}\n"
48
+ end.join
49
+ "#{plugin}:\n" + cmds
50
+ end.join("\n")
51
+ end
52
+
53
+ end
@@ -0,0 +1,34 @@
1
+ # records every message the bot receives
2
+ class HistoryPlugin < Campfire::PollingBot::Plugin
3
+ accepts :all
4
+ priority 100
5
+
6
+ def process(message)
7
+ @room_locked ||= false
8
+ if message.kind_of?(Campfire::LockMessage)
9
+ @room_locked = true
10
+ return
11
+ elsif message.kind_of?(Campfire::UnlockMessage)
12
+ @room_locked = false
13
+ return
14
+ end
15
+
16
+ save_message(message) unless @room_locked
17
+ end
18
+
19
+ private
20
+
21
+ def save_message(message)
22
+ params = {
23
+ :room => bot.room.id,
24
+ :message_id => message.message_id,
25
+ :message_type => message.type,
26
+ :person => message.respond_to?(:person_full_name) ? message.person_full_name : nil,
27
+ :user_id => message.user && message.user.id,
28
+ :link => message.respond_to?(:link) ? message.link : nil,
29
+ :body => message.respond_to?(:body) ? message.body : nil,
30
+ :timestamp => message.timestamp.to_i
31
+ }
32
+ Message.create(params)
33
+ end
34
+ end
@@ -0,0 +1,8 @@
1
+ # used by ImageSearchPlugin
2
+ require 'dm-core'
3
+
4
+ class DisplayedImage
5
+ include DataMapper::Resource
6
+ property :id, Serial
7
+ property :uri, String, :index => true
8
+ end
@@ -0,0 +1,106 @@
1
+ require 'httparty'
2
+ require 'google-search'
3
+
4
+ # plugin to send a tweet to a Twitter account
5
+ class ImageSearchPlugin < Campfire::PollingBot::Plugin
6
+ priority 1
7
+ accepts :text_message, :addressed_to_me => true
8
+
9
+ def process(message)
10
+ searched = false
11
+
12
+ case message.command
13
+ when /(google|flickr)\sfor\sa\s(?:photo|image|picture)\s+of:?\s+(?:a:?\s+)?\s*("?)(.*?)\1$/i
14
+ subject = $3
15
+ photo = next_photo(subject, $1)
16
+ searched = true
17
+ when /(?:photo|image|picture)\s+of:?\s+(?:a:?\s+)?\s*("?)(.*?)\1$/i
18
+ subject = $2
19
+ photo = next_photo(subject)
20
+ searched = true
21
+ end
22
+
23
+ if searched
24
+ if photo
25
+ DisplayedImage.create(:uri => photo)
26
+ bot.say(photo)
27
+ else
28
+ bot.say("Couldn't find anything for \"#{subject}\"")
29
+ end
30
+ return HALT
31
+ end
32
+ end
33
+
34
+ # return array of available commands and descriptions
35
+ def help
36
+ [
37
+ ['(photo|image|picture) of <subject>', "find a new picture of <subject>"],
38
+ ['search (google|flickr) for a (photo|image|picture) of <subject>', "search the stated service for a new picture of <subject>"]
39
+ ]
40
+ end
41
+
42
+ private
43
+
44
+ def next_photo(subject, sources = %w(google flickr))
45
+ next_photo = nil
46
+
47
+ if sources == ["google"] || (sources.include?("google") && bot.config.google_api_key)
48
+ # google search lazy-fetches pages of results, so we only search as far as we have to
49
+ if image = query_google(subject).find{|i| !DisplayedImage.first(:uri => i.uri) }
50
+ next_photo ||= image.uri
51
+ end
52
+ end
53
+
54
+ if sources.include?("flickr")
55
+ next_photo ||= query_flickr(subject).find{|u| !DisplayedImage.first(:uri => u) }
56
+ end
57
+
58
+ return next_photo
59
+ end
60
+
61
+ def query_google(subject)
62
+ unless bot.config.google_api_key
63
+ bot.say("I don't have a Google Search API key. " +
64
+ "Please add it to my config with the key 'google_api_key'.")
65
+ return []
66
+ end
67
+
68
+ logger.debug("Searching Google Images for #{subject}")
69
+ results = Google::Search::Image.new(
70
+ :query => subject,
71
+ :api_key => bot.config.google_api_key,
72
+ :image_type => :photo,
73
+ :safety_level => :off
74
+ )
75
+
76
+ logger.debug("Got response #{results.inspect}")
77
+ return results || []
78
+ end
79
+
80
+ def query_flickr(subject)
81
+ options = {:query => {:q => "select * from flickr.photos.search where text=\"#{subject}\""} }
82
+ logger.debug("YQL query #{options[:query][:q]}")
83
+
84
+ if proxy = (config && config['proxy']) || ENV['HTTP_PROXY']
85
+ proxy_uri = URI.parse(proxy)
86
+ options.update(:http_proxyaddr => proxy_uri.host, :http_proxyport => proxy_uri.port)
87
+ end
88
+
89
+ response = HTTParty.get("http://query.yahooapis.com/v1/public/yql", options)
90
+ logger.debug("Got response #{response.parsed_response.inspect}")
91
+ case response.code
92
+ when 200
93
+ return [] if response["query"]["yahoo:count"] == "0" || response["query"]["results"].nil?
94
+ photos = response["query"]["results"]["photo"]
95
+ photos = [photos] if photos.is_a?(Hash)
96
+ return photos.map {|p| "http://farm%s.static.flickr.com/%s/%s_%s.jpg?v=0" % [p['farm'], p['server'], p['id'], p['secret']]}
97
+ when 403
98
+ bot.say("Sorry, we seem to have hit our query limit for the day.")
99
+ return []
100
+ else
101
+ bot.say("Hmm...didn't work. Got this response:")
102
+ bot.paste(response.body)
103
+ return nil
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,58 @@
1
+ # Plugin to make Wes actively comment (and maybe super annoying)
2
+ class InterjectorPlugin < Campfire::PollingBot::Plugin
3
+ accepts :text_message
4
+ priority -1
5
+
6
+ def process(message)
7
+ person = message.person
8
+ case message.command || message.body
9
+ when /jessica alba/i
10
+ bot.say_random [
11
+ "Jessica Alba is my dream girl",
12
+ "no question, the hottest girl ever",
13
+ "yeah... er, what was I saying?",
14
+ "she really is incredibly hot, you know",
15
+ ]
16
+ return HALT
17
+ when /^(goodnight|night)(,?\s(all|every(body|one)))$/i
18
+ bot.say "goodnight, #{person}"
19
+ return HALT
20
+ when /^(facepalm|EFACEPALM|:fp|:facepalm:|\*facepalm\*|m\()$/i
21
+ bot.say_random [
22
+ # picard facepalm
23
+ "https://img.skitch.com/20110224-875h7w1654tgdhgrxm9bikhkwq.jpg",
24
+ # polar bear facepalm
25
+ "https://img.skitch.com/20110224-bsd2ds251eit8d3t1y2mkjtfx8.jpg"
26
+ ]
27
+ when /^(double facepalm|EDOUBLEFACEPALM|:fpfp|:doublefacepalm:|m\( m\()$/i
28
+ # picard + riker facepalm
29
+ bot.say "https://img.skitch.com/20110224-ncacgpudhfr2s4te6nswenxaqt.jpg"
30
+ when /^i see your problem/i
31
+ # pony mechanic
32
+ bot.say "https://img.skitch.com/20110224-8fmfwdmg6kkrcpijhamhqu7tm6.jpg"
33
+ when /^(wfm|womm|works on my machine)$/i
34
+ # works on my machine
35
+ bot.say "https://img.skitch.com/20110224-jrcf6e4gc936a2mxc3mueah2in.png"
36
+ when /^(stacktrace or gtfo|stacktrace or it didn't happen|stacktrace!)$/i
37
+ # stacktrace or gtfo
38
+ bot.say "https://img.skitch.com/20110224-pqtmiici9wp9nygqi4nw8gs6hg.png"
39
+ when /^this is sparta\!*?$/i
40
+ # this is sparta
41
+ bot.say "https://img.skitch.com/20110225-k9xpadr2hk37pe5ed4crcqria1.png"
42
+ when /^i have no idea what i'm doing$/i
43
+ # I have no idea what I'm doing
44
+ bot.say "https://img.skitch.com/20110304-1tcmatkhapiq6t51wqqq9igat5.jpg"
45
+ when /^party[?!.]*$/i
46
+ # party party party party party cat
47
+ bot.say "https://img.skitch.com/20110309-qtd33sy8k5yrdaeqa9e119bwd1.jpg"
48
+ when /^(bomb|system error)$/i
49
+ # sorry, a system error has occurred
50
+ bot.say "https://img.skitch.com/20110312-8g31a37spacdjgatr82g3g98j1.jpg"
51
+ when /^stop hitting yourself$/i
52
+ # and the angel said to him, Stop hitting yourself!
53
+ bot.say "https://img.skitch.com/20110316-q7h49p69pjhhyy8756rha2a1jf.jpg"
54
+ end
55
+ end
56
+
57
+
58
+ end