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.
- 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
|