wesabot 1.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 (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