campfire-bot 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +11 -0
- data/.gitignore +6 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +57 -0
- data/README.textile +52 -0
- data/TODO +72 -0
- data/bin/bot +13 -0
- data/campfire-bot.gemspec +27 -0
- data/cfbot-stop.sh +8 -0
- data/config.example.yml +31 -0
- data/lib/bot.rb +194 -0
- data/lib/event.rb +114 -0
- data/lib/message.rb +30 -0
- data/lib/plugin.rb +77 -0
- data/lib/version.rb +3 -0
- data/plugins/accountability.rb +45 -0
- data/plugins/austin.rb +29 -0
- data/plugins/basecamp.rb +48 -0
- data/plugins/beer.rb +214 -0
- data/plugins/beijing_tally.rb +30 -0
- data/plugins/boop.rb +127 -0
- data/plugins/bruce.rb +15 -0
- data/plugins/bugzilla.rb +198 -0
- data/plugins/calvin.rb +43 -0
- data/plugins/chuck.rb +23 -0
- data/plugins/dilbert.rb +51 -0
- data/plugins/excuse.rb +478 -0
- data/plugins/fail.rb +16 -0
- data/plugins/figlet.rb +10 -0
- data/plugins/fun.rb +95 -0
- data/plugins/garfield.rb +43 -0
- data/plugins/generic_search.rb +66 -0
- data/plugins/help.rb +13 -0
- data/plugins/infobot.rb +58 -0
- data/plugins/insult.rb +87 -0
- data/plugins/jira.rb +197 -0
- data/plugins/lolcats.rb +17 -0
- data/plugins/our_quotes.rb +195 -0
- data/plugins/quote.rb +31 -0
- data/plugins/schneier.rb +28 -0
- data/plugins/seen.rb +88 -0
- data/plugins/signal_filter.rb +9 -0
- data/plugins/svn.rb +167 -0
- data/plugins/twitter_echo.rb +54 -0
- data/plugins/unfuddle.rb +69 -0
- data/plugins/weather.rb +25 -0
- data/plugins/xkcd.rb +43 -0
- data/spec/beer_spec.rb +224 -0
- data/spec/bugzilla_spec.rb +90 -0
- data/spec/command_spec.rb +96 -0
- data/spec/jira_spec.rb +264 -0
- data/spec/our_quotes_spec.rb +186 -0
- data/spec/plugin_spec.rb +43 -0
- data/spec/spec.opts +1 -0
- data/vendor/escape/ChangeLog +30 -0
- data/vendor/escape/Makefile +5 -0
- data/vendor/escape/README +81 -0
- data/vendor/escape/VERSION +1 -0
- data/vendor/escape/escape.rb +302 -0
- data/vendor/escape/install.rb +109 -0
- data/vendor/escape/misc/README.erb +85 -0
- data/vendor/escape/rdoc/classes/Escape.html +427 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000022.html +19 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000023.html +32 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000024.html +24 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000025.html +19 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000026.html +48 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000027.html +19 -0
- data/vendor/escape/rdoc/classes/Escape.src/M000028.html +19 -0
- data/vendor/escape/rdoc/classes/Escape/HTMLAttrValue.html +113 -0
- data/vendor/escape/rdoc/classes/Escape/HTMLEscaped.html +113 -0
- data/vendor/escape/rdoc/classes/Escape/PercentEncoded.html +113 -0
- data/vendor/escape/rdoc/classes/Escape/ShellEscaped.html +113 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.html +242 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000029.html +18 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000030.html +18 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000031.html +18 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000032.html +18 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000033.html +18 -0
- data/vendor/escape/rdoc/classes/Escape/StringWrapper.src/M000035.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapeHTML.html +182 -0
- data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000008.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000009.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000010.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapeHTML.src/M000011.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.html +182 -0
- data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000012.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000013.html +19 -0
- data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000014.html +20 -0
- data/vendor/escape/rdoc/classes/TestEscapePercentEncoded.src/M000015.html +22 -0
- data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.html +167 -0
- data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.src/M000016.html +18 -0
- data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.src/M000017.html +20 -0
- data/vendor/escape/rdoc/classes/TestEscapeShellEscaped.src/M000018.html +20 -0
- data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.html +167 -0
- data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.src/M000019.html +20 -0
- data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.src/M000020.html +24 -0
- data/vendor/escape/rdoc/classes/TestEscapeStringWrapper.src/M000021.html +22 -0
- data/vendor/escape/rdoc/files/escape_rb.html +136 -0
- data/vendor/escape/rdoc/files/install_rb.html +250 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000001.html +23 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000002.html +31 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000003.html +27 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000004.html +27 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000005.html +21 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000006.html +23 -0
- data/vendor/escape/rdoc/files/install_rb.src/M000007.html +21 -0
- data/vendor/escape/rdoc/files/test/test-escape_rb.html +109 -0
- data/vendor/escape/rdoc/fr_class_index.html +36 -0
- data/vendor/escape/rdoc/fr_file_index.html +29 -0
- data/vendor/escape/rdoc/fr_method_index.html +61 -0
- data/vendor/escape/rdoc/index.html +24 -0
- data/vendor/escape/rdoc/rdoc-style.css +208 -0
- data/vendor/escape/test/test-escape.rb +90 -0
- metadata +259 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'hpricot'
|
3
|
+
|
4
|
+
class BeijingTally < CampfireBot::Plugin
|
5
|
+
|
6
|
+
on_command 'tally', :tally
|
7
|
+
|
8
|
+
def tally(msg)
|
9
|
+
output = "#{'Pos.'.rjust(6)} - #{'Country'.ljust(25)} - G - S - B - Total\n"
|
10
|
+
rows = ((Hpricot(open('http://results.beijing2008.cn/WRM/ENG/INF/GL/95A/GL0000000.shtml'))/'//table')[1]/'tr')[2..-1]
|
11
|
+
rows.each_with_index do |row, i|
|
12
|
+
cells = row/'td'
|
13
|
+
output += "#{strip_tags_or_zero(cells[0].inner_html).rjust(6)} - " # position
|
14
|
+
output += "#{((i == rows.length - 1) ? '' : strip_tags_or_zero(cells[1].inner_html)).ljust(25)} - " # country
|
15
|
+
output += "#{strip_tags_or_zero(cells[-5].inner_html).rjust(3)} - " # gold
|
16
|
+
output += "#{strip_tags_or_zero(cells[-4].inner_html).rjust(3)} - " # silver
|
17
|
+
output += "#{strip_tags_or_zero(cells[-3].inner_html).rjust(3)} - " # bronze
|
18
|
+
output += "#{strip_tags_or_zero(cells[-2].inner_html).rjust(3)}\n" # total
|
19
|
+
end
|
20
|
+
|
21
|
+
msg.paste(output)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# Take away the HTML tags from the string and insert a '0' if it is empty
|
27
|
+
def strip_tags_or_zero(str)
|
28
|
+
(out = str.gsub(/<\/?[^>]*>/, "").strip).blank? ? '0' : out
|
29
|
+
end
|
30
|
+
end
|
data/plugins/boop.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# TODO
|
2
|
+
#
|
3
|
+
# - convert/filter out HTML entities like "
|
4
|
+
# - add a callback to prime the chains when the bot is ready, and do this in a thread.
|
5
|
+
|
6
|
+
class Boop < CampfireBot::Plugin
|
7
|
+
|
8
|
+
# Markov chain implementation courtesy of http://blog.segment7.net/articles/2006/02/25/markov-chain
|
9
|
+
|
10
|
+
on_message /.*/, :listen
|
11
|
+
on_command 'speak', :random_chatter
|
12
|
+
on_command 'prime_chains', :load_transcripts
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@phrases = Hash.new { |hash, key| hash[key] = [] } # phrase => next-word possibilities
|
16
|
+
@word_count = 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def listen(msg)
|
20
|
+
add_line(msg[:message])
|
21
|
+
end
|
22
|
+
|
23
|
+
def random_chatter(msg)
|
24
|
+
puts "random_chatter"
|
25
|
+
msg.speak(generate_line)
|
26
|
+
end
|
27
|
+
|
28
|
+
def focused_chatter(msg)
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_transcripts(msg)
|
33
|
+
msg.speak("Filling my brain with transcripts...")
|
34
|
+
|
35
|
+
puts "available transcripts - #{bot.room.available_transcripts.to_yaml}"
|
36
|
+
|
37
|
+
bot.room.available_transcripts.to_a.each do |date|
|
38
|
+
puts "loading transcript #{date}"
|
39
|
+
|
40
|
+
transcript = bot.room.transcript(date)
|
41
|
+
|
42
|
+
transcript.each do |message|
|
43
|
+
puts "message: #{message[:message]}"
|
44
|
+
|
45
|
+
filtered_text = strip_message(message)
|
46
|
+
|
47
|
+
filtered_text.split("\n").each { |line| add_line(line) unless line.blank? }
|
48
|
+
filtered_text.split("\n").each { |line| puts "ACCEPTED: " + line unless line.blank? }
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
msg.speak("Primed!")
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def add_line(line)
|
59
|
+
words = line.scan(/\S+/)
|
60
|
+
@word_count += words.length
|
61
|
+
|
62
|
+
words.each_with_index do |word, index|
|
63
|
+
phrase = words[index, phrase_length] # current phrase
|
64
|
+
@phrases[phrase] << words[index + phrase_length] # next possibility
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_line
|
69
|
+
# our seed phrase
|
70
|
+
# phrase = words[0, phrase_length]
|
71
|
+
phrase = [random_word]
|
72
|
+
|
73
|
+
output = []
|
74
|
+
|
75
|
+
@word_count.times do
|
76
|
+
# grab all possibilities for our state
|
77
|
+
options = @phrases[phrase]
|
78
|
+
|
79
|
+
# add the first word to our output and discard
|
80
|
+
output << phrase.shift
|
81
|
+
|
82
|
+
# select at random and add it to our phrase
|
83
|
+
phrase.push(options.rand)
|
84
|
+
|
85
|
+
# the last phrase of the input text will map to an empty array of
|
86
|
+
# possibilities so exit cleanly.
|
87
|
+
break if phrase.compact.empty? # all out of words
|
88
|
+
end
|
89
|
+
|
90
|
+
# return our output
|
91
|
+
output.join(' ')
|
92
|
+
end
|
93
|
+
|
94
|
+
def random_word
|
95
|
+
@phrases.keys.rand.first
|
96
|
+
end
|
97
|
+
|
98
|
+
# amount of state (order-k)
|
99
|
+
def phrase_length
|
100
|
+
1
|
101
|
+
end
|
102
|
+
|
103
|
+
def strip_message(msg)
|
104
|
+
str = msg[:message].to_s
|
105
|
+
|
106
|
+
return '' if str.blank?
|
107
|
+
|
108
|
+
# return nothing if the line is a bot command
|
109
|
+
return '' if str[0..0] == '!' || str =~ Regexp.new("^#{bot.config['nickname']},", Regexp::IGNORECASE)
|
110
|
+
|
111
|
+
# and get rid of the messages that are generated by the campfire system itself
|
112
|
+
return '' if str =~ /has (entered|left) the room/
|
113
|
+
|
114
|
+
# keep the contents of the pastes, but strip out the 'view paste' link.
|
115
|
+
str.gsub!(/<a href=.*?>View paste<\/a>/, '')
|
116
|
+
|
117
|
+
# also get rid of the stuff spoken by the bot
|
118
|
+
return '' if msg[:person] == bot.config['nickname']
|
119
|
+
|
120
|
+
# now strip out all image tags completely
|
121
|
+
str.gsub!(/<img\s.*?\/>/, '')
|
122
|
+
|
123
|
+
# and now strip out all other html tags, leaving their contents intact
|
124
|
+
str.gsub(/<\/?[^>]*>/, "")
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
data/plugins/bruce.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'hpricot'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
class Bruce < CampfireBot::Plugin
|
6
|
+
on_command 'bruce', :fail
|
7
|
+
|
8
|
+
def fail(msg)
|
9
|
+
# Scrape random fail
|
10
|
+
bruce = (Hpricot(open('http://www.schneierfacts.com/'))/'p.fact').first
|
11
|
+
msg.speak(CGI.unescapeHTML(bruce.inner_html))
|
12
|
+
rescue => e
|
13
|
+
msg.speak e
|
14
|
+
end
|
15
|
+
end
|
data/plugins/bugzilla.rb
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
# Lookup bug titles and URLs when their number is mentioned or on command.
|
4
|
+
#
|
5
|
+
# You'll probably want to at least configure bugzilla_bug_url with the
|
6
|
+
# URL to your bug tracking tool. Put a '%s' where the bug ID should be
|
7
|
+
# inserted.
|
8
|
+
#
|
9
|
+
# Several other options are available, including the interval in which
|
10
|
+
# to avoid volunteering the same information, and whether to show the
|
11
|
+
# url with the title.
|
12
|
+
#
|
13
|
+
# HTMLEntities will be used for better entity (–) decoding if
|
14
|
+
# present, but is not required.
|
15
|
+
#
|
16
|
+
# Similarly, net/netrc will be used to supply HTTP Basic Auth
|
17
|
+
# credentials, but only if it's available.
|
18
|
+
#
|
19
|
+
# While is designed to work with Bugzilla, it also works fine with:
|
20
|
+
# * Debian bug tracking system
|
21
|
+
# * KDE Bug tracking system
|
22
|
+
# * Trac - if you configure to recognize "tickets" instead of "bugs"
|
23
|
+
# * Redmine - if you configure to recognize "issues" instead of "bugs"
|
24
|
+
class Bugzilla < CampfireBot::Plugin
|
25
|
+
on_command 'bug', :describe_command
|
26
|
+
# on_message registered below...
|
27
|
+
|
28
|
+
config_var :data_file, File.join(BOT_ROOT, 'tmp', 'bugzilla.yml')
|
29
|
+
config_var :min_period, 30.minutes
|
30
|
+
config_var :debug_enabled, false
|
31
|
+
config_var :bug_url, "https://bugzilla/show_bug.cgi?id=%s"
|
32
|
+
config_var :link_enabled, true
|
33
|
+
config_var :bug_id_pattern, '(?:[0-9]{3,6})'
|
34
|
+
config_var :bug_word_pattern, 'bugs?:?\s+'
|
35
|
+
config_var :mention_pattern,
|
36
|
+
'%2$s%1$s(?:(?:,\s*|,?\sand\s|,?\sor\s|\s+)%1$s)*'
|
37
|
+
|
38
|
+
attr_reader :bug_timestamps, :bug_id_regexp, :mention_regexp,
|
39
|
+
:use_htmlentities, :use_netrc
|
40
|
+
|
41
|
+
def initialize()
|
42
|
+
super
|
43
|
+
@bug_id_regexp = Regexp.new(bug_id_pattern, Regexp::IGNORECASE)
|
44
|
+
@mention_regexp = Regexp.new(sprintf(mention_pattern,
|
45
|
+
bug_id_pattern, bug_word_pattern),
|
46
|
+
Regexp::IGNORECASE)
|
47
|
+
self.class.on_message mention_regexp, :describe_mention
|
48
|
+
|
49
|
+
@bug_timestamps = YAML::load(File.read(@data_file)) rescue {}
|
50
|
+
if link_enabled
|
51
|
+
require 'shorturl'
|
52
|
+
end
|
53
|
+
|
54
|
+
# Put this in the constructor so we don't fail to find htmlentities
|
55
|
+
# every time we fetch a bug title.
|
56
|
+
begin
|
57
|
+
require 'htmlentities'
|
58
|
+
@use_htmlentities = true
|
59
|
+
rescue LoadError
|
60
|
+
debug "Falling back to 'cgi', install 'htmlentities' better unescaping"
|
61
|
+
require 'cgi'
|
62
|
+
end
|
63
|
+
begin
|
64
|
+
require 'net/netrc'
|
65
|
+
@use_netrc = true
|
66
|
+
rescue LoadError
|
67
|
+
debug "Can't load 'net/netrc': HTTP Auth from .netrc will be unavailable"
|
68
|
+
require 'cgi'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def debug(spew)
|
73
|
+
$stderr.puts "#{self.class.name}: #{spew}" if debug_enabled
|
74
|
+
end
|
75
|
+
|
76
|
+
def describe_mention(msg)
|
77
|
+
debug "heard a mention"
|
78
|
+
match = msg[:message].match(mention_regexp)
|
79
|
+
describe_bugs msg, match.to_s, true
|
80
|
+
end
|
81
|
+
|
82
|
+
def describe_command(msg)
|
83
|
+
debug "received a command"
|
84
|
+
debug "msg[:message] = #{msg[:message].inspect}"
|
85
|
+
describe_bugs msg, msg[:message], false
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
def describe_bugs(msg, text, check_timestamp)
|
91
|
+
summaries = text.to_s.scan(bug_id_regexp).collect { |bug|
|
92
|
+
debug "mentioned bug #{bug}"
|
93
|
+
now = Time.new
|
94
|
+
last_spoke = (bug_timestamps[msg[:room].name] ||= {})[bug]
|
95
|
+
if check_timestamp && !last_spoke.nil? && last_spoke > now - min_period
|
96
|
+
debug "keeping quiet, last spoke at #{last_spoke}"
|
97
|
+
nil
|
98
|
+
else
|
99
|
+
debug "fetching title for #{bug}"
|
100
|
+
url = sprintf(bug_url, bug)
|
101
|
+
html = http_fetch_body(url)
|
102
|
+
if !m = html.match("<title>([^<]+)</title>")
|
103
|
+
raise "no title for bug #{bug}!"
|
104
|
+
end
|
105
|
+
debug "fetched."
|
106
|
+
title = html_decode(m[1])
|
107
|
+
title += " (#{ShortURL.shorten(url)})" if link_enabled
|
108
|
+
bug_timestamps[msg[:room].name][bug] = now
|
109
|
+
title
|
110
|
+
end
|
111
|
+
}.reject { |s| s.nil? }
|
112
|
+
if !summaries.empty?
|
113
|
+
expire_timestamps
|
114
|
+
n = bug_timestamps.inject(0) { |sum, pair| sum + pair[1].size }
|
115
|
+
debug "Writing #{n} timestamps"
|
116
|
+
File.open(data_file, 'w') do |out|
|
117
|
+
YAML.dump(bug_timestamps, out)
|
118
|
+
end
|
119
|
+
# Speak the summaries all at once so they're more readable and
|
120
|
+
# not interleaved with someone else's speach
|
121
|
+
summaries.each { |s|
|
122
|
+
debug "sending response: #{s}"
|
123
|
+
msg.speak s
|
124
|
+
}
|
125
|
+
else
|
126
|
+
debug "nothing to say."
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Don't let the datafile or the in-memory list grow too
|
131
|
+
# large over long periods of time. Remove entries that are
|
132
|
+
# well over min_period.
|
133
|
+
def expire_timestamps
|
134
|
+
debug "Expiring bug timestamps"
|
135
|
+
cutoff = Time.new - (2 * min_period)
|
136
|
+
recent = {}
|
137
|
+
bug_timestamps.each { |room, hash|
|
138
|
+
recent[room] = {}
|
139
|
+
hash.each { |bug, ts|
|
140
|
+
recent[room][bug] = ts if ts > cutoff
|
141
|
+
}
|
142
|
+
recent.delete(room) if recent[room].empty?
|
143
|
+
}
|
144
|
+
@bug_timestamps = recent
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns the non-HTML version of the given string using
|
148
|
+
# htmlentities if available, or else unescapeHTML
|
149
|
+
def html_decode(html)
|
150
|
+
if use_htmlentities
|
151
|
+
HTMLEntities.new.decode(html)
|
152
|
+
else
|
153
|
+
CGI.unescapeHTML(html)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Return the HTTPResponse
|
158
|
+
#
|
159
|
+
# Use SSL if necessary, and check .netrc for
|
160
|
+
# passwords.
|
161
|
+
def http_fetch(url)
|
162
|
+
uri = URI.parse url
|
163
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
164
|
+
|
165
|
+
# Unfortunately the net/http(s) API can't seem to do this for us,
|
166
|
+
# even if we require net/https from the beginning (ruby 1.8)
|
167
|
+
if uri.scheme == "https"
|
168
|
+
require 'net/https'
|
169
|
+
http.use_ssl = true
|
170
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
171
|
+
end
|
172
|
+
|
173
|
+
res = http.start { |http|
|
174
|
+
req = Net::HTTP::Get.new uri.request_uri
|
175
|
+
cred = netrc_credentials uri.host
|
176
|
+
req.basic_auth *cred if cred
|
177
|
+
http.request req
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
# Returns only the document body
|
182
|
+
def http_fetch_body(url)
|
183
|
+
res = http_fetch(url)
|
184
|
+
case res
|
185
|
+
when Net::HTTPSuccess
|
186
|
+
res.body
|
187
|
+
else res.error!
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Returns [username, password] for the given host or nil
|
192
|
+
def netrc_credentials(host)
|
193
|
+
# Don't crash just b/c the gem is not installed
|
194
|
+
return nil if !use_netrc
|
195
|
+
obj = Net::Netrc.locate(host)
|
196
|
+
obj ? [obj.login, obj.password] : nil
|
197
|
+
end
|
198
|
+
end
|
data/plugins/calvin.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'hpricot'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
class Calvin < CampfireBot::Plugin
|
6
|
+
BASE_URL = 'http://marcel-oehler.marcellosendos.ch/comics/ch/'
|
7
|
+
START_DATE = Date.parse('1985-11-18')
|
8
|
+
END_DATE = Date.parse('1995-12-31') # A sad day
|
9
|
+
|
10
|
+
on_command 'calvin', :calvin
|
11
|
+
|
12
|
+
def calvin(msg)
|
13
|
+
comic = case msg[:message].split(/\s+/)[0]
|
14
|
+
when 'random'
|
15
|
+
fetch_random
|
16
|
+
when /d+/
|
17
|
+
fetch_comic(msg[:message].split(/\s+/)[0])
|
18
|
+
else
|
19
|
+
fetch_random
|
20
|
+
end
|
21
|
+
|
22
|
+
msg.speak(comic)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def fetch_random
|
28
|
+
fetch_comic(rand(number_of_comics))
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch_comic(id = nil)
|
32
|
+
date = id_to_date(id)
|
33
|
+
"#{BASE_URL}#{date.strftime('%Y')}/#{date.strftime('%m')}/#{date.strftime('%Y%m%d')}.gif"
|
34
|
+
end
|
35
|
+
|
36
|
+
def id_to_date(id)
|
37
|
+
(START_DATE + id.days).to_date
|
38
|
+
end
|
39
|
+
|
40
|
+
def number_of_comics
|
41
|
+
(END_DATE - START_DATE).to_i
|
42
|
+
end
|
43
|
+
end
|
data/plugins/chuck.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'hpricot'
|
3
|
+
|
4
|
+
class Chuck < CampfireBot::Plugin
|
5
|
+
on_command 'chuck', :chuck
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@log = Logging.logger["CampfireBot::Plugin::Chuck"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def chuck(msg)
|
12
|
+
url = "http://www.chucknorrisfacts.com/all-chuck-norris-facts?page=#{rand(172)+1}"
|
13
|
+
doc = Hpricot(open(url))
|
14
|
+
|
15
|
+
facts = []
|
16
|
+
|
17
|
+
(doc/".item-list a.createYourOwn").each do |a_tag|
|
18
|
+
facts << CGI.unescapeHTML(a_tag.inner_html)
|
19
|
+
end
|
20
|
+
|
21
|
+
msg.speak(facts[rand(facts.size)])
|
22
|
+
end
|
23
|
+
end
|
data/plugins/dilbert.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'hpricot'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
|
6
|
+
class Dilbert < CampfireBot::Plugin
|
7
|
+
BASE_URL = 'http://dilbert.com/'
|
8
|
+
START_DATE = Date.parse('1996-01-01')
|
9
|
+
|
10
|
+
on_command 'dilbert', :dilbert
|
11
|
+
|
12
|
+
def dilbert(msg)
|
13
|
+
comic = case msg[:message].split(/\s+/)[0]
|
14
|
+
when 'latest'
|
15
|
+
fetch_latest
|
16
|
+
when 'random'
|
17
|
+
fetch_random
|
18
|
+
when /d+/
|
19
|
+
fetch_comic(msg[:message].split(/\s+/)[1])
|
20
|
+
else
|
21
|
+
fetch_random
|
22
|
+
end
|
23
|
+
|
24
|
+
msg.speak(BASE_URL + comic['src'])
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def fetch_latest
|
31
|
+
fetch_comic
|
32
|
+
end
|
33
|
+
|
34
|
+
def fetch_random
|
35
|
+
fetch_comic(rand(number_of_comics))
|
36
|
+
end
|
37
|
+
|
38
|
+
def fetch_comic(id = nil)
|
39
|
+
# Rely on the comic being the last image on the page not nested
|
40
|
+
(Hpricot(open("#{BASE_URL}fast#{'/' + id_to_date(id) + '/' if id}"))/'//img').last
|
41
|
+
end
|
42
|
+
|
43
|
+
def id_to_date(id)
|
44
|
+
(START_DATE + id.days).to_date.to_s(:db)
|
45
|
+
end
|
46
|
+
|
47
|
+
def number_of_comics
|
48
|
+
(Date.today - START_DATE).to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|