wesabot 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +182 -0
- data/Rakefile +13 -0
- data/bin/wesabot +111 -0
- data/config/wesabot.yml.sample +4 -0
- data/lib/campfire/bot.rb +53 -0
- data/lib/campfire/configuration.rb +65 -0
- data/lib/campfire/message.rb +102 -0
- data/lib/campfire/polling_bot/plugin.rb +162 -0
- data/lib/campfire/polling_bot/plugins/airbrake/.gitignore +1 -0
- data/lib/campfire/polling_bot/plugins/airbrake/airbrake/api.rb +48 -0
- data/lib/campfire/polling_bot/plugins/airbrake/airbrake/error.rb +51 -0
- data/lib/campfire/polling_bot/plugins/airbrake/airbrake_plugin.rb +63 -0
- data/lib/campfire/polling_bot/plugins/airbrake/airbrake_plugin.yml.sample +3 -0
- data/lib/campfire/polling_bot/plugins/bookmark/bookmark.rb +15 -0
- data/lib/campfire/polling_bot/plugins/bookmark/bookmark_plugin.rb +89 -0
- data/lib/campfire/polling_bot/plugins/debug/debug_plugin.rb +23 -0
- data/lib/campfire/polling_bot/plugins/deploy/deploy_plugin.rb +155 -0
- data/lib/campfire/polling_bot/plugins/deploy/deploy_plugin.yml.sample +9 -0
- data/lib/campfire/polling_bot/plugins/deploy/spec/deploy_plugin_spec.rb +153 -0
- data/lib/campfire/polling_bot/plugins/greeting/greeting_plugin.rb +146 -0
- data/lib/campfire/polling_bot/plugins/greeting/greeting_setting.rb +19 -0
- data/lib/campfire/polling_bot/plugins/greeting/spec/greeting_plugin_spec.rb +51 -0
- data/lib/campfire/polling_bot/plugins/greeting/spec/greeting_setting_spec.rb +61 -0
- data/lib/campfire/polling_bot/plugins/help/help_plugin.rb +53 -0
- data/lib/campfire/polling_bot/plugins/history/history_plugin.rb +34 -0
- data/lib/campfire/polling_bot/plugins/image_search/displayed_image.rb +8 -0
- data/lib/campfire/polling_bot/plugins/image_search/image_search_plugin.rb +106 -0
- data/lib/campfire/polling_bot/plugins/interjector/interjector_plugin.rb +58 -0
- data/lib/campfire/polling_bot/plugins/kibitz/kibitz_plugin.rb +90 -0
- data/lib/campfire/polling_bot/plugins/kibitz/spec/kibitz_plugin_spec.rb +56 -0
- data/lib/campfire/polling_bot/plugins/plugin.db +0 -0
- data/lib/campfire/polling_bot/plugins/reload/reload_plugin.rb +24 -0
- data/lib/campfire/polling_bot/plugins/remind_me/remind_me_plugin.rb +149 -0
- data/lib/campfire/polling_bot/plugins/remind_me/reminder.rb +8 -0
- data/lib/campfire/polling_bot/plugins/shared/message.rb +37 -0
- data/lib/campfire/polling_bot/plugins/shared/user.rb +32 -0
- data/lib/campfire/polling_bot/plugins/sms/sms_plugin.rb +88 -0
- data/lib/campfire/polling_bot/plugins/sms/sms_setting.rb +7 -0
- data/lib/campfire/polling_bot/plugins/time/time_plugin.rb +17 -0
- data/lib/campfire/polling_bot/plugins/tweet/tweet.rb +52 -0
- data/lib/campfire/polling_bot/plugins/tweet/tweet_plugin.rb +117 -0
- data/lib/campfire/polling_bot/plugins/tweet/tweet_plugin.yml.sample +4 -0
- data/lib/campfire/polling_bot/plugins/twitter_search/twitter_search_plugin.rb +58 -0
- data/lib/campfire/polling_bot.rb +125 -0
- data/lib/campfire/sample_plugin.rb +56 -0
- data/lib/campfire/version.rb +3 -0
- data/lib/wesabot.rb +3 -0
- data/spec/.gitignore +1 -0
- data/spec/polling_bot_spec.rb +23 -0
- data/spec/spec_helper.rb +190 -0
- data/wesabot.gemspec +55 -0
- metadata +336 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
# Plugin to make Wes chatty (or annoying)
|
2
|
+
class KibitzPlugin < Campfire::PollingBot::Plugin
|
3
|
+
accepts :text_message, :addressed_to_me => true
|
4
|
+
priority -1
|
5
|
+
|
6
|
+
def process(message)
|
7
|
+
person = message.person
|
8
|
+
case message.command
|
9
|
+
when /^\s*$/
|
10
|
+
# question questioners, exclaim at exclaimers, dot dotters
|
11
|
+
message.body.match(/(\?|\!|\.)$/)
|
12
|
+
bot.say(person + $1.to_s)
|
13
|
+
when /say\s+(.*)/
|
14
|
+
bot.say($1)
|
15
|
+
when /^(hey|hi|hello|sup|howdy)/i
|
16
|
+
bot.say("#{$1} #{person}")
|
17
|
+
when /(^later|(?:good\s*)?bye)/i
|
18
|
+
bot.say("#{$1} #{person}")
|
19
|
+
when /you rock|awesome|cool|nice/i
|
20
|
+
sayings = ["Thanks, #{person}, you're pretty cool yourself.",
|
21
|
+
"I try.",
|
22
|
+
"Aw, shucks. Thanks, #{person}."]
|
23
|
+
bot.say_random(sayings)
|
24
|
+
when /(^|you|still)\s*there/i, /\byt\b/i
|
25
|
+
bot.say_random(%w{Yup y})
|
26
|
+
when /(wake up|you awake)/i
|
27
|
+
bot.say("Yo.")
|
28
|
+
when /zod/i
|
29
|
+
bot.say_random [
|
30
|
+
"Zod's a wanker.",
|
31
|
+
"I'd tell you about Zod, but you wouldn't listen. No one ever does.",
|
32
|
+
"somebody send Zod back to the Phantom Zone",
|
33
|
+
"Zod and I were friends, once. It all ended one awful night in El Paso over a bottle of shitty scotch and a ten-dollar whore. Christ, those were the days."
|
34
|
+
]
|
35
|
+
when /thanks|thank you/i
|
36
|
+
bot.say_random ["No problem.", "np", "any time", "that's what I'm here for", "You're welcome."]
|
37
|
+
when /^(good\s?night|(?:g')?night)$/i
|
38
|
+
bot.say_random [
|
39
|
+
"see you later, #{person}",
|
40
|
+
"later, #{person}",
|
41
|
+
"night",
|
42
|
+
"goodnight",
|
43
|
+
"bye",
|
44
|
+
"have a good night"
|
45
|
+
]
|
46
|
+
when /^(see you(?: later)?)$/i
|
47
|
+
bot.say_random [
|
48
|
+
"see you later, #{person}",
|
49
|
+
"later, #{person}",
|
50
|
+
"bye",
|
51
|
+
"later",
|
52
|
+
"see ya",
|
53
|
+
]
|
54
|
+
when /^(?:get|grab|fetch|bring)(?: (.*?))?(?: (?:a|some))? coffee$/i
|
55
|
+
person = $1
|
56
|
+
if person =~ /(me|us)/
|
57
|
+
person, do_they = "you", "do you"
|
58
|
+
else
|
59
|
+
do_they = "does #{person}"
|
60
|
+
end
|
61
|
+
|
62
|
+
bot.say_random [
|
63
|
+
"would #{person} like cream or sugar?",
|
64
|
+
"how #{do_they} take it?",
|
65
|
+
"coming right up",
|
66
|
+
"It is by caffeine alone I set my mind in motion",
|
67
|
+
"It is by the beans of Java that thoughts acquire speed",
|
68
|
+
"The hands acquire shakes, the shakes become a warning",
|
69
|
+
"It is by caffeine alone I set my mind in motion"
|
70
|
+
]
|
71
|
+
else
|
72
|
+
bot.say_random [
|
73
|
+
"I have no idea what you're talking about, #{person}.",
|
74
|
+
"eh?",
|
75
|
+
"oh, interesting",
|
76
|
+
"say more, #{person}",
|
77
|
+
"#{person}, you do realize that you're talking to a bot with a very limited vocabulary, don't you?",
|
78
|
+
"Whatever, #{person}.",
|
79
|
+
"#{bot.other_person(person) || 'Marc'}, tell #{person} to leave me alone.",
|
80
|
+
"Not now, #{person}.",
|
81
|
+
"brb crying",
|
82
|
+
"what do you think, #{person}?",
|
83
|
+
"That's really something.",
|
84
|
+
"but what can I do? I'm just a lowly bot",
|
85
|
+
"I'll get some electrons on that right away",
|
86
|
+
"How do you feel when someone says '#{message.command}' to you, #{person}?"
|
87
|
+
]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe KibitzPlugin do
|
4
|
+
before do
|
5
|
+
@plugin = KibitzPlugin.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "when not addressing wes" do
|
9
|
+
it "does not say anything" do
|
10
|
+
saying("should we deploy?").
|
11
|
+
should_not make_wes_say_anything
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "when addressing wes" do
|
16
|
+
it "responds with the name of the sender when the message has no command" do
|
17
|
+
saying("wes?").
|
18
|
+
should make_wes_say("John?")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "says whatever the sender tells wes to when using the 'say' command" do
|
22
|
+
saying("wes, say Hi There").
|
23
|
+
should make_wes_say("Hi There")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "responds with the same greeting the sender uses" do
|
27
|
+
saying("hey wes").
|
28
|
+
should make_wes_say("hey John")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "responds with the same goodbye the sender uses" do
|
32
|
+
saying("bye, wes").
|
33
|
+
should make_wes_say("bye John")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "responds politely to praise" do
|
37
|
+
saying("yay! you rock, wes").
|
38
|
+
should make_wes_say("Thanks, John, you're pretty cool yourself.")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "says yes when the sender asks if wes is there" do
|
42
|
+
asking("wes, yt?").
|
43
|
+
should make_wes_say("Yup")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "says hi informally when told to wake up" do
|
47
|
+
asking("you awake wes?").
|
48
|
+
should make_wes_say("Yo.")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "responds politely to thanks" do
|
52
|
+
asking("thanks, wes!").
|
53
|
+
should make_wes_say("No problem.")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
Binary file
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Plugin to allow Wes to update and reload himself
|
2
|
+
class ReloadPlugin < Campfire::PollingBot::Plugin
|
3
|
+
accepts :text_message, :addressed_to_me => true
|
4
|
+
|
5
|
+
def process(message)
|
6
|
+
case message.command
|
7
|
+
when /^reload/i
|
8
|
+
bot.say("k")
|
9
|
+
system("git pull origin master && bundle install")
|
10
|
+
if bot.debug
|
11
|
+
head = `git rev-parse HEAD`
|
12
|
+
bot.say("updated to: https://github.com/hackarts/wesabot/commit/#{head}")
|
13
|
+
end
|
14
|
+
bot.say("restarting...")
|
15
|
+
exec *INVOCATION
|
16
|
+
return HALT
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# return array of available commands and descriptions
|
21
|
+
def help
|
22
|
+
[['reload', "update and reload #{bot.name}"]]
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# Reminders plugin
|
2
|
+
require 'chronic'
|
3
|
+
class RemindMePlugin < Campfire::PollingBot::Plugin
|
4
|
+
accepts :text_message, :addressed_to_me => true
|
5
|
+
priority 10
|
6
|
+
|
7
|
+
NUMBER_WORDS = %w{zero one two three four five six seven eight nine ten}
|
8
|
+
PRONOUN_SUBS = [
|
9
|
+
["I'm", "you're"],
|
10
|
+
['my', 'your'],
|
11
|
+
['I', 'you'],
|
12
|
+
]
|
13
|
+
|
14
|
+
def process(message)
|
15
|
+
case message.command
|
16
|
+
when /remind\s+(.*?)\s+(in\s+(\S+)\s+(?:seconds|minutes|hours|days|weeks|years))\s+to\s+(.*)/i
|
17
|
+
reminder_person = $1
|
18
|
+
time_string = $2
|
19
|
+
time_value = $3
|
20
|
+
action = $4
|
21
|
+
if i = NUMBER_WORDS.index(time_value.downcase)
|
22
|
+
time_string.sub!(time_value, i.to_s)
|
23
|
+
end
|
24
|
+
when /remind\s+(.*?)\s+to\s+(.*?)\s+((?:in|on|at|next|this).*)/i
|
25
|
+
reminder_person = $1
|
26
|
+
action = $2
|
27
|
+
time_string = $3
|
28
|
+
when /remind\s+(.*?)\s+(.*?)\s+to\s+(.*)/i
|
29
|
+
reminder_person = $1
|
30
|
+
time_string = $2
|
31
|
+
action = $3
|
32
|
+
when /(?:list|show) (\S+) reminders/i, /(?:list|show) reminders for (\S+)/i
|
33
|
+
list_reminders(message.person, $1)
|
34
|
+
return HALT
|
35
|
+
when /(list|show) reminders$/
|
36
|
+
list_reminders(message.person, message.person)
|
37
|
+
return HALT
|
38
|
+
when /delete reminder (?:#\s*)?(\d+)/
|
39
|
+
delete_reminder(message.person, $1.to_i)
|
40
|
+
return HALT
|
41
|
+
else
|
42
|
+
return
|
43
|
+
end
|
44
|
+
|
45
|
+
PRONOUN_SUBS.each do |sub|
|
46
|
+
action.gsub!(/\b#{sub[0]}\b/i, sub[1])
|
47
|
+
end
|
48
|
+
|
49
|
+
begin
|
50
|
+
time = Chronic.parse(time_string.sub(/^at/, ''))
|
51
|
+
rescue RangeError
|
52
|
+
bot.say("Whatever, #{message.person}.")
|
53
|
+
return HALT
|
54
|
+
end
|
55
|
+
|
56
|
+
if !time || time.kind_of?(Chronic::Span)
|
57
|
+
bot.say("sorry, #{message.person}, but I don't know what '#{time_string}' means.")
|
58
|
+
return HALT
|
59
|
+
end
|
60
|
+
|
61
|
+
if reminder_person.downcase == 'me'
|
62
|
+
reminder_person = message.person
|
63
|
+
else
|
64
|
+
reminder_person.gsub!(/^(\w)/) {$1.upcase}
|
65
|
+
end
|
66
|
+
|
67
|
+
Reminder.create(:person => reminder_person, :action => action, :reminder_time => time)
|
68
|
+
time_now = Time.now
|
69
|
+
if [time.year,time.month,time.day] == [time_now.year,time_now.month,time_now.day]
|
70
|
+
converted_time_string = time.strftime('%I:%M %p').gsub(/^0/,'')
|
71
|
+
else
|
72
|
+
converted_time_string = time.strftime('%c')
|
73
|
+
end
|
74
|
+
|
75
|
+
bot.say("ok, #{message.person}, I will remind #{reminder_person == message.person ? 'you' : reminder_person} #{time_string} (#{converted_time_string}) to #{action}")
|
76
|
+
return HALT
|
77
|
+
end
|
78
|
+
|
79
|
+
def heartbeat
|
80
|
+
# check reminders every 9 seconds (heartbeat is called every 3 sec)
|
81
|
+
@heartbeat_counter ||= 0
|
82
|
+
@heartbeat_counter += 1
|
83
|
+
return unless (@heartbeat_counter % 3) == 1
|
84
|
+
due_reminders.each do |reminder|
|
85
|
+
bot.say("#{reminder.person}, I'm reminding you to #{reminder.action}")
|
86
|
+
reminder.destroy!
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# return array of available commands and descriptions
|
91
|
+
def help
|
92
|
+
[['remind (me|<person>) [in] <time string> to <message>', "set up a reminder"],
|
93
|
+
['remind (me|<person>) to <message> (in|on|at|next|this) <time string>', "set up a reminder"],
|
94
|
+
["[list|show] [person]['s] reminders", "display current reminders for yourself or person"],
|
95
|
+
["delete reminder <n>", "delete your reminder #n"],
|
96
|
+
]
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def due_reminders
|
102
|
+
Reminder.all(:reminder_time.lte => Time.now)
|
103
|
+
end
|
104
|
+
|
105
|
+
def list_reminders(current_person, request_person)
|
106
|
+
case request_person.downcase
|
107
|
+
when 'my', 'me'
|
108
|
+
request_person = current_person
|
109
|
+
else
|
110
|
+
request_person.gsub!(/'s$/i,'')
|
111
|
+
end
|
112
|
+
request_person.capitalize!
|
113
|
+
|
114
|
+
case request_person
|
115
|
+
when /everyone/i, /all/i
|
116
|
+
if (reminders = Reminder.all).any?
|
117
|
+
bot.say("Here are all the reminders I have:")
|
118
|
+
reminders.each do |reminder|
|
119
|
+
bot.say("#{reminder.id} - [#{reminder.reminder_time}] - #{reminder.person} - #{reminder.action}")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
return
|
123
|
+
else
|
124
|
+
reminders = Reminder.all(:conditions => {:person => request_person}, :order => [:reminder_time])
|
125
|
+
if reminders.any?
|
126
|
+
if request_person == current_person
|
127
|
+
bot.say("Here are the reminders I have for you, #{current_person}:")
|
128
|
+
else
|
129
|
+
bot.say("Here are the reminders for #{request_person}:")
|
130
|
+
end
|
131
|
+
reminders.each do |reminder|
|
132
|
+
bot.say("#{reminder.id} - [#{reminder.reminder_time}] - #{reminder.action}")
|
133
|
+
end
|
134
|
+
return
|
135
|
+
end
|
136
|
+
end
|
137
|
+
bot.say("I couldn't find any reminders for #{request_person}")
|
138
|
+
end
|
139
|
+
|
140
|
+
def delete_reminder(current_person, id)
|
141
|
+
reminder = Reminder.get(id)
|
142
|
+
if reminder.person == current_person
|
143
|
+
reminder.destroy
|
144
|
+
bot.say("Ok, I've deleted reminder ##{id}.")
|
145
|
+
else
|
146
|
+
bot.say("Sorry, #{current_person}, but I couldn't find a reminder with that id that belongs to you.")
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# used by HistoryPlugin, among others
|
2
|
+
class Message
|
3
|
+
include DataMapper::Resource
|
4
|
+
property :id, Serial
|
5
|
+
property :room, Integer, :required => true, :index => true
|
6
|
+
property :message_id, Integer, :required => true
|
7
|
+
property :message_type, String, :length => 20, :required => true, :index => true
|
8
|
+
property :person, String, :index => true
|
9
|
+
property :user_id, Integer, :required => false, :index => true
|
10
|
+
property :link, Text, :lazy => false
|
11
|
+
property :body, Text, :lazy => false
|
12
|
+
property :timestamp, Integer, :required => true, :index => true
|
13
|
+
|
14
|
+
belongs_to :user, :required => false
|
15
|
+
|
16
|
+
def self.last_message(name, time = nil)
|
17
|
+
first(:conditions => {
|
18
|
+
:person => name,
|
19
|
+
:message_type.not => 'Enter',
|
20
|
+
:timestamp.lt => (time || Time.now)
|
21
|
+
}, :order => [:timestamp.desc])
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.last_left(name, time = nil)
|
25
|
+
first(:conditions => {
|
26
|
+
:person => name,
|
27
|
+
:message_type => ['Leave','Kick'],
|
28
|
+
:timestamp.lt => (time || Time.now)
|
29
|
+
}, :order => [:timestamp.desc])
|
30
|
+
end
|
31
|
+
|
32
|
+
# guess whether the message is still on the screen by counting
|
33
|
+
# how many messages there are between it and now
|
34
|
+
def visible?
|
35
|
+
self.class.count(:id.gt => id) < 20
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class User
|
2
|
+
include DataMapper::Resource
|
3
|
+
property :id, Serial
|
4
|
+
property :name, String, :index => true
|
5
|
+
property :campfire_id, Integer, :index => true
|
6
|
+
|
7
|
+
has n, :messages
|
8
|
+
|
9
|
+
def short_name
|
10
|
+
name && name.split(' ').first
|
11
|
+
end
|
12
|
+
|
13
|
+
def wants_greeting?
|
14
|
+
greeting_setting = GreetingSetting.for_user(self)
|
15
|
+
|
16
|
+
if greeting_setting
|
17
|
+
greeting_setting.wants_greeting
|
18
|
+
else
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def wants_greeting=(yesno)
|
24
|
+
greeting_setting = GreetingSetting.for_user(self)
|
25
|
+
|
26
|
+
if greeting_setting
|
27
|
+
greeting_setting.update(:wants_greeting => yesno)
|
28
|
+
else
|
29
|
+
GreetingSetting.create(:user => self, :wants_greeting => yesno)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# Plugin to send SMS (well, email) messages
|
2
|
+
require 'net/smtp'
|
3
|
+
class SMSPlugin < Campfire::PollingBot::Plugin
|
4
|
+
accepts :text_message, :addressed_to_me => true
|
5
|
+
|
6
|
+
def process(message)
|
7
|
+
case message.command
|
8
|
+
when /(?:set)?\s+my\s+sms\s+address\s*(?:is|to|:)\s*(.+)/i
|
9
|
+
address = $1
|
10
|
+
if address =~ /"mailto:(.*?)"/
|
11
|
+
address = $1
|
12
|
+
end
|
13
|
+
update_sms_address(message.person, address)
|
14
|
+
bot.say("OK, #{message.person}, I've set your SMS address to #{address}")
|
15
|
+
return HALT
|
16
|
+
when /(?:set)?\s+(\w+?)'s\s+sms\s+address\s*(?:is|to|:)\s*(.+)/i
|
17
|
+
person = $1
|
18
|
+
address = $2
|
19
|
+
if address =~ /"mailto:(.*?)"/
|
20
|
+
address = $1
|
21
|
+
end
|
22
|
+
update_sms_address(person, address)
|
23
|
+
bot.say("OK, #{message.person}, I've set #{$1}'s SMS address to #{$2}")
|
24
|
+
return HALT
|
25
|
+
when /list sms addresses/i
|
26
|
+
if (settings = SMSSetting.all(:order => [:person])).any?
|
27
|
+
bot.say("Here are the SMS addresses I have:")
|
28
|
+
settings.each { |setting| bot.say(" #{setting.person} - #{setting.address}") }
|
29
|
+
else
|
30
|
+
bot.say("Sorry, I don't have any SMS addresses yet.")
|
31
|
+
end
|
32
|
+
return HALT
|
33
|
+
when /(?:sms|text|txt)\s+([^\s:]+):?\s*(.*)/i
|
34
|
+
if address = send_sms(message.person, $1, $2)
|
35
|
+
bot.say("Sent an SMS to #{$1} at #{address}")
|
36
|
+
end
|
37
|
+
return HALT
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# return array of available commands and descriptions
|
42
|
+
def help
|
43
|
+
[['set my sms address to: <address>', "set your sms address"],
|
44
|
+
["set <person>'s sms address to", "set someone else's sms address"],
|
45
|
+
['(sms|text|txt) <person>: <message>', "send an sms message"],
|
46
|
+
["list sms addresses", "list all sms addresses"]
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def update_sms_address(person, address)
|
53
|
+
person.downcase!
|
54
|
+
if setting = find_sms_setting(person)
|
55
|
+
setting.update_attributes(:address => address)
|
56
|
+
else
|
57
|
+
SMSSetting.create(:person => person, :address => address)
|
58
|
+
end
|
59
|
+
return address
|
60
|
+
end
|
61
|
+
|
62
|
+
def send_sms(sender, recipient, message)
|
63
|
+
if setting = find_sms_setting(recipient)
|
64
|
+
send_email(sender, setting.address, message)
|
65
|
+
return setting.address
|
66
|
+
else
|
67
|
+
bot.say("Sorry, I don't have an SMS address for #{recipient}.")
|
68
|
+
return nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def find_sms_setting(person)
|
73
|
+
SMSSetting.first(:person => person.downcase)
|
74
|
+
end
|
75
|
+
|
76
|
+
def send_email(from, to, message)
|
77
|
+
from += "@wesabe.com"
|
78
|
+
msg = "From: #{from}\nTo: #{to}\n\n#{message}"
|
79
|
+
begin
|
80
|
+
Net::SMTP.start('localhost', 25, 'wesabe.com') { |smtp| smtp.send_message(msg, from, to) }
|
81
|
+
return true
|
82
|
+
rescue Exception => e
|
83
|
+
bot.say("Hmm...couldn't send mail. Here's what happened when I tried:")
|
84
|
+
bot.paste(e.message)
|
85
|
+
return false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Simple sample plugin to just give the current time
|
2
|
+
class TimePlugin < Campfire::PollingBot::Plugin
|
3
|
+
accepts :text_message, :addressed_to_me => true
|
4
|
+
|
5
|
+
def process(message)
|
6
|
+
case message.command
|
7
|
+
when /time/i
|
8
|
+
bot.say("#{message.person}, the time is #{Time.now}")
|
9
|
+
return HALT
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# return array of available commands and descriptions
|
14
|
+
def help
|
15
|
+
[['time', "say the current time"]]
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# used by TweetPlugin
|
2
|
+
class Tweet
|
3
|
+
class TwitterError < RuntimeError
|
4
|
+
attr_reader :http_result
|
5
|
+
|
6
|
+
def initialize(http_result)
|
7
|
+
@http_result = http_result
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
include DataMapper::Resource
|
12
|
+
property :id, Serial
|
13
|
+
property :message, String, :required => true
|
14
|
+
property :person, String
|
15
|
+
property :timestamp, Integer, :required => true
|
16
|
+
|
17
|
+
def tweet(user, pass, proxy=nil)
|
18
|
+
self.class.tweet(message, user, pass, proxy)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.tweet(message, user, pass, proxy=nil)
|
22
|
+
result = post(message, user, pass, proxy)
|
23
|
+
if result.code == 200
|
24
|
+
return true
|
25
|
+
else
|
26
|
+
raise TwitterError.new(result.body)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.list
|
31
|
+
all(:order => [:timestamp.asc])
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
message
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# post a message to twitter
|
41
|
+
def self.post(message, user, pass, proxy=nil)
|
42
|
+
options = {
|
43
|
+
:query => {:status => message},
|
44
|
+
:basic_auth => {:username => user, :password => pass}
|
45
|
+
}
|
46
|
+
if proxy ||= ENV['HTTP_PROXY']
|
47
|
+
proxy_uri = URI.parse(proxy)
|
48
|
+
options.update(:http_proxyaddr => proxy_uri.host, :http_proxyport => proxy_uri.port)
|
49
|
+
end
|
50
|
+
HTTParty.post("http://twitter.com/statuses/update.xml", options)
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# plugin to send a tweet to a Twitter account
|
2
|
+
class TweetPlugin < Campfire::PollingBot::Plugin
|
3
|
+
accepts :text_message, :addressed_to_me => true
|
4
|
+
priority 20
|
5
|
+
|
6
|
+
def process(message)
|
7
|
+
case message.command
|
8
|
+
when /^(?:tweet|twitter):?\s*("?)(.*?)\1$/i
|
9
|
+
begin
|
10
|
+
text = strip_links($2)
|
11
|
+
post_tweet(text)
|
12
|
+
rescue TwitterError => ex
|
13
|
+
bot.say("Hmm...didn't work. Got this response:")
|
14
|
+
bot.paste(ex.http_result.content)
|
15
|
+
end
|
16
|
+
return HALT
|
17
|
+
when /^(?:save|queue)\s+(?:tweet|twitter):?\s*("?)(.*?)\1$/i
|
18
|
+
tweet = Tweet.create(:person => message.person, :message => strip_links($2), :timestamp => message.timestamp.to_i)
|
19
|
+
bot.say("Ok, saved for later: #{tweet}")
|
20
|
+
return HALT
|
21
|
+
when /^(?:show|list)\s+(?:all\s+)?(?:queued\s+)?(?:tweets|twitters)$/i
|
22
|
+
act_on_list do |list|
|
23
|
+
bot.say("Here are the tweets in the queue:")
|
24
|
+
i = 0
|
25
|
+
bot.paste(list.map{|tweet| "#{i+=1}. #{tweet}"}.join("\n"))
|
26
|
+
end
|
27
|
+
return HALT
|
28
|
+
when /^show\s+next\s+(?:tweet|twitter)$/i
|
29
|
+
act_on_list do |list|
|
30
|
+
bot.say("Next tweet to post: #{list.first}")
|
31
|
+
end
|
32
|
+
return HALT
|
33
|
+
when /^(?:post|send)\s+next\s+(?:tweet|twitter)$/i
|
34
|
+
act_on_list do |list|
|
35
|
+
tweet = list.first
|
36
|
+
post_tweet(tweet)
|
37
|
+
tweet.destroy
|
38
|
+
end
|
39
|
+
return HALT
|
40
|
+
when /^(?:post|send)\s+tweet\s+(\d+)$/i
|
41
|
+
act_on_tweet($1.to_i-1) do |tweet|
|
42
|
+
post_tweet(tweet)
|
43
|
+
tweet.destroy
|
44
|
+
end
|
45
|
+
return HALT
|
46
|
+
when /^delete\s+tweet\s+(\d+)$/i
|
47
|
+
act_on_tweet($1.to_i-1) do |tweet|
|
48
|
+
tweet.destroy
|
49
|
+
bot.say("Okay, deleted tweet #{$1}: #{tweet}")
|
50
|
+
end
|
51
|
+
return HALT
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# return array of available commands and descriptions
|
56
|
+
def help
|
57
|
+
user = username || 'the configured user'
|
58
|
+
[['tweet: <message>', "post <message> to #{user}'s twitter account"],
|
59
|
+
['save tweet: <message>', "save <message> for later"],
|
60
|
+
['show tweets', "shows the queued tweets for #{user}'s twitter account"],
|
61
|
+
['show next tweet', "shows the oldest queued twitter message"],
|
62
|
+
['post next tweet', "sends the oldest queued twitter message"],
|
63
|
+
['post tweet <n>', "sends the <n>th tweet from the list"],
|
64
|
+
['delete tweet <n>', "deletes the <n>th tweet from the list"]]
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def username
|
70
|
+
config && config['username']
|
71
|
+
end
|
72
|
+
|
73
|
+
def password
|
74
|
+
config && config['password']
|
75
|
+
end
|
76
|
+
|
77
|
+
def proxy
|
78
|
+
config && config['proxy']
|
79
|
+
end
|
80
|
+
|
81
|
+
def post_tweet(tweet)
|
82
|
+
if username.nil? || password.nil?
|
83
|
+
bot.say("The twitter plugin is not properly configured!")
|
84
|
+
return
|
85
|
+
end
|
86
|
+
|
87
|
+
begin
|
88
|
+
Tweet.tweet(tweet.to_s, username, password, proxy)
|
89
|
+
bot.say("Ok, tweeted: #{tweet}")
|
90
|
+
rescue Tweet::TwitterError => ex
|
91
|
+
bot.say("Hmm...didn't work. Got this response:")
|
92
|
+
bot.paste(ex.http_result.content)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def act_on_list
|
97
|
+
list = Tweet.list
|
98
|
+
if list.empty?
|
99
|
+
bot.say("There are no queued tweets.")
|
100
|
+
else
|
101
|
+
yield list
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def act_on_tweet(n)
|
106
|
+
if tweet = Tweet.list[n]
|
107
|
+
yield tweet
|
108
|
+
else
|
109
|
+
bot.say("Could not find tweet #{n}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# strip links from CF messages
|
114
|
+
def strip_links(msg)
|
115
|
+
msg.gsub(/<a href="([^"]*)".*?>.*?<\/a>/, '\1')
|
116
|
+
end
|
117
|
+
end
|