campfire-bot 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,16 +0,0 @@
1
- require 'open-uri'
2
- require 'hpricot'
3
- require 'tempfile'
4
-
5
- class Fail < CampfireBot::Plugin
6
- on_command 'fail', :fail
7
-
8
- def fail(msg)
9
- # Scrape random fail
10
- fail = (Hpricot(open('http://failblog.org/?random#top'))/'div.entry img').first
11
-
12
- msg.speak(fail['src'])
13
- rescue => e
14
- msg.speak e
15
- end
16
- end
@@ -1,10 +0,0 @@
1
- require "#{BOT_ROOT}/vendor/escape/escape"
2
-
3
- class Fun < CampfireBot::Plugin
4
- on_command 'figlet', :figlet
5
-
6
- def figlet(msg)
7
- output = `#{Escape.shell_command(['figlet', '--', msg[:message]])}`
8
- msg.paste output
9
- end
10
- end
@@ -1,95 +0,0 @@
1
- class Fun < CampfireBot::Plugin
2
- on_command 'say', :say
3
- on_message Regexp.new("^#{bot.config['nickname']},\\s+(should|can|will|shall) (i|he|she|we|they) do it\\?", Regexp::IGNORECASE), :do_or_do_not
4
- on_message Regexp.new("^(good morning|morning|m0ink|hello|hi|hey|whassup|what's up|yo|hola|ola|'sup|sup)(,)*\\s*(#{bot.config['nickname']}).*$", Regexp::IGNORECASE), :greet
5
- on_message /(how's it|how are|how're) (ya |you )*(going|doing|doin).*/, :howareya
6
- on_command "blame", :blame
7
- on_command "trout", :trout
8
- on_command "slap", :trout
9
- on_command "troutslap", :trout
10
- # on_speaker 'Tim R.', :agree_with_tim
11
- # on_message /undo it/i, :do_it
12
- # on_message /(^|\s)do it/i, :undo_it
13
- # at_time 1.minute.from_now, :do_it
14
-
15
- def initialize
16
- @last_agreed = 20.minutes.ago
17
- @log = Logging.logger["CampfireBot::Plugin::Fun"]
18
- end
19
-
20
- def say(m)
21
- m.speak(m[:message])
22
- end
23
-
24
- def do_it(m = nil)
25
- m.speak('Do it!')
26
- end
27
-
28
- def undo_it(m)
29
- m.speak('Undo it!')
30
- end
31
-
32
- def do_or_do_not(m)
33
- responses = ['Do it!', 'Don\'t do it!', 'Undo it!']
34
- m.speak(responses.choice)
35
- end
36
-
37
- def agree_with_tim(m)
38
- m.speak('I agree with Tim.') unless @last_agreed > 15.minutes.ago
39
- @last_agreed = Time.now
40
- end
41
-
42
- def greet(m)
43
- messages = ['Howdy', 'Wassup', 'Greets', 'Hello', 'Hey there', "It's a", 'Good day']
44
- m.speak("#{messages.choice} #{m[:person].split(' ')[0]}")
45
- end
46
-
47
- def howareya(m)
48
- messages = ["just great", "peachy", "mas o menos",
49
- "you know how it is", "eh, ok", "pretty good. how about you?"]
50
- m.speak(messages[rand(messages.size)])
51
- end
52
-
53
- def blame(m)
54
- # TODO: capture user-submitted entries to a yaml file and regurgitate them
55
- # TODO: put all the default ones in a separate yaml
56
- if m[:message].strip.length > 0
57
- blamed = m[:message].strip
58
- else
59
- users = m[:room].users.delete_if {|u| u[:name] == bot.campfire.me[:name]}.map {|u| u[:name]}
60
- others = ["nobody", "my", "Microsoft", "Steve Jobs", "the terrorists", "your",
61
- "Project Management", "Development", "Management", "Corporate", "Cartman", "the user",
62
- "the liberal media", "Wall Street"]
63
-
64
- # mostly blame the other users
65
- if rand(10) >= 2
66
- blamed = users.choice
67
- else
68
- blamed = others.choice
69
- end
70
-
71
- end
72
-
73
- case blamed
74
- when "nobody"
75
- blamestring = "It's nobody's fault"
76
- when "your", "my"
77
- blamestring = "It's all #{blamed} fault"
78
- else
79
- blamestring = "It's all #{blamed}'s fault"
80
- blamestring = "It's all #{blamed}' fault" if blamed[-1].chr == "s"
81
- end
82
-
83
- m.speak blamestring
84
- end
85
-
86
- def trout(m)
87
- if m[:message].strip.length > 0
88
- selected_user_name = m[:message].strip
89
- else
90
- users = m[:room].users.map{|u| u[:name] }
91
- selected_user_name = users.choice
92
- end
93
- m.speak("#{m[:person]} slaps #{selected_user_name} #{["upside the head", "in the face", "on the rear", "where it counts", "in the knees", "ineffectually", "in the elbow", "on the funny bone", "in the ear", "on the nose", "in the teeth"].choice} with a #{%w(good-sized large decaying moldy spiked sabre-toothed surprised disappointed dramatic enraged rabid bug-eyed rotten foul-smelling demonic cluestick-holding).choice} trout")
94
- end
95
- end
@@ -1,43 +0,0 @@
1
- require 'open-uri'
2
- require 'hpricot'
3
- require 'tempfile'
4
-
5
- class Garfield < CampfireBot::Plugin
6
- BASE_URL = 'http://images.ucomics.com/comics/ga'
7
- START_DATE = Date.parse('1978-06-19')
8
- END_DATE = Date.today
9
-
10
- on_command 'garfield', :garfield
11
-
12
- def garfield(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')}/ga#{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
@@ -1,66 +0,0 @@
1
- require 'cgi'
2
- require 'net/http'
3
-
4
- #
5
- # Search sites like Google and Wikipedia
6
- #
7
- # You configure the command name and URL pattern. Given a search
8
- # term, it attempts to respond with the URL of the first search
9
- # result. It does so by simply inserting the term into the URL at the
10
- # '%s'. If the url redirects, it responds with the redirect target
11
- # instead, so you get a hint about what you'll see. Otherwise, you
12
- # get just the expanded url pattern.
13
- #
14
- # This is useful for a wide range of sites. Sample config:
15
- #
16
- # generic_search_commands:
17
- # wikipedia: "http://en.wikipedia.org/wiki/Special:Search?search=%s&go=Go"
18
- # google: "http://www.google.com/search?hl=en&q=%s&btnI=I'm+Feeling+Lucky&aq=f&oq="
19
- # php: "http://us3.php.net/manual-lookup.php?pattern=%s&lang=en"
20
- # letmegooglethatforyou: "http://letmegooglethatforyou.com/?q=%s"
21
- #
22
- # Note that the last site never redirects, which is fine.
23
- #
24
- class GenericSearch < CampfireBot::Plugin
25
- attr_reader :commands
26
-
27
- def initialize
28
- @commands = bot.config["generic_search_commands"] || {}
29
- commands.each { |c, s|
30
- method = "do_#{c}_command".to_sym
31
- self.class.send(:define_method, method) {|msg|
32
- msg.speak(http_peek(sprintf(s, CGI.escape(msg[:message]))))
33
- }
34
- self.class.on_command(c, method)
35
- }
36
- end
37
-
38
- protected
39
-
40
- # Follow the url just enough to see if it redirects. If so,
41
- # return the redirect. Otherwise, return the original.
42
- def http_peek(url)
43
- uri = URI.parse url
44
- http = Net::HTTP.new(uri.host, uri.port)
45
-
46
- # Unfortunately the net/http(s) API can't seem to do this for us,
47
- # even if we require net/https from the beginning (ruby 1.8)
48
- if uri.scheme == "https"
49
- require 'net/https'
50
- http.use_ssl = true
51
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
52
- end
53
-
54
- res = http.start { |http|
55
- req = Net::HTTP::Get.new uri.request_uri
56
- #req.basic_auth u, p
57
- response = http.request req
58
- }
59
- case res
60
- when Net::HTTPRedirection
61
- uri.merge res.header['Location']
62
- else # Net::HTTPSuccess or error
63
- url
64
- end
65
- end
66
- end
@@ -1,13 +0,0 @@
1
- # Rudimentary help system. Worth exploring further, though I am not sure how much access to the
2
- # rest of the sytem plugins should be allowed. Should they only be allowed to operate in their own
3
- # sandbox, or reach into the list of registered plugins like this one does?
4
-
5
- class Help < CampfireBot::Plugin
6
- on_command 'help', :help
7
-
8
- def help(msg)
9
- commands = CampfireBot::Plugin.registered_commands.map { |command| command.matcher.to_s + " " }
10
- msg.speak("To address me, type \"#{bot.config['nickname']},\" and a command, or just !command. \n
11
- Available commands: #{commands}")
12
- end
13
- end
@@ -1,58 +0,0 @@
1
- require 'yaml'
2
-
3
- class Infobot < CampfireBot::Plugin
4
-
5
- Infobot::DEFINE_REGEXP = /(no, )*(.+) is ([^\?]+)(?!\?)$/
6
- Infobot::RESPOND_REGEXP = /(what's|what is|who is|who's|where|where's|how's|how is) ([^\?]+)(?=\?)*/
7
-
8
- # if BOT_ENVIRONMENT == 'development'
9
- on_message Regexp.new("^#{bot.config['nickname']},\\s+#{RESPOND_REGEXP.source}", Regexp::IGNORECASE), :respond
10
- on_message Regexp.new("^#{bot.config['nickname']},\\s+#{DEFINE_REGEXP.source}", Regexp::IGNORECASE), :define
11
- on_command 'reload_facts', :reload
12
- # end
13
-
14
- def initialize
15
- # puts "entering initialize()"
16
-
17
- end
18
-
19
- def respond(msg)
20
- # puts "entering respond()"
21
- @facts = init()
22
- puts msg[:message]
23
- puts msg[:message] =~ RESPOND_REGEXP # Regexp.new("^#{Bot.instance.config['nickname']},\\s+#{RESPOND_REGEXP.source}", Regexp::IGNORECASE)
24
- puts $1, $2, $3
25
- if !@facts.has_key?($2.downcase)
26
- msg.speak("Sorry, I don't know what #{$2} is.")
27
- else
28
- fact = @facts[$2.downcase]
29
- msg.speak("#{msg[:person].split(" ")[0]}, #{$2} is #{fact}.")
30
- end
31
- end
32
-
33
- def define(msg)
34
- # puts 'entering define()'
35
- @facts = init()
36
- # puts @facts
37
- # puts msg[:message]
38
- puts msg[:message] =~ Regexp.new("^#{bot.config['nickname']},\\s+#{DEFINE_REGEXP.source}", Regexp::IGNORECASE)
39
- # puts @define_regexp
40
- # puts $1, $2, $3
41
- @facts[$2.downcase] = $3
42
- msg.speak("Okay, #{$2} is now #{$3}")
43
- File.open(File.join(File.dirname(__FILE__), 'infobot.yml'), 'w') do |out|
44
- YAML.dump(@facts, out)
45
- end
46
- end
47
-
48
- def init
49
- # puts "entering init()"
50
- YAML::load(File.read(File.join(BOT_ROOT, 'tmp', 'infobot.yml')))
51
- end
52
-
53
- def reload(msg)
54
- @facts = init()
55
- msg.speak("ok, reloaded #{@facts.size} facts")
56
- end
57
-
58
- end
@@ -1,87 +0,0 @@
1
- class Insult < CampfireBot::Plugin
2
-
3
- on_command 'insult', :insult
4
-
5
- def insult(msg)
6
- adj1 = adjective()
7
- adj2 = adjective()
8
- amount = amount()
9
- noun = noun()
10
- article = "a"
11
- article = "an" if (adj1[0] =~ /^[aeiou]/)
12
- out = "#{msg[:message]}, you are nothing but a #{adj1} #{amount} of #{adj2} #{noun}"
13
-
14
-
15
- msg.speak(out)
16
- end
17
-
18
-
19
- private
20
-
21
- def adjective()
22
- adjectives = %w(acidic antique contemptible culturally-unsound despicable evil fermented
23
- festering foul fulminating humid impure inept inferior industrial
24
- left-over low-quality malodorous off-color penguin-molesting
25
- petrified pointy-nosed salty sausage-snorfling tastless tempestuous
26
- tepid tofu-nibbling unintelligent unoriginal uninspiring weasel-smelling
27
- wretched spam-sucking egg-sucking decayed halfbaked infected squishy
28
- porous pickled coughed-up thick vapid hacked-up
29
- unmuzzled bawdy vain lumpish churlish fobbing rank craven puking
30
- jarring fly-bitten pox-marked fen-sucked spongy droning gleeking warped
31
- currish milk-livered surly mammering ill-borne beef-witted tickle-brained
32
- half-faced headless wayward rump-fed onion-eyed beslubbering villainous
33
- lewd-minded cockered full-gorged rude-snouted crook-pated pribbling
34
- dread-bolted fool-born puny fawning sheep-biting dankish goatish
35
- weather-bitten knotty-pated malt-wormy saucyspleened motley-mind
36
- it-fowling vassal-willed loggerheaded clapper-clawed frothy ruttish
37
- clouted common-kissing pignutted folly-fallen plume-plucked flap-mouthed
38
- swag-bellied dizzy-eyed gorbellied weedy reeky measled spur-galled mangled
39
- impertinent bootless toad-spotted hasty-witted horn-beat yeasty
40
- imp-bladdereddle-headed boil-brained tottering hedge-born hugger-muggered
41
- elf-skinned
42
-
43
- artless bawdy beslubbering bootless churlish cockered clouted craven currish dankish
44
- dissembling droning errant fawning fobbing froward gleeking goatish gorbellied
45
- impertinent infectious jarring loggerheaded lumpish mammering mangled mewling paunchy
46
- pribbling puking puny quailing rank reeky roguish ruttish saucy spleeny spongy surly
47
- tottering unmuzzled vain venomed villainous warped wayward weedy yeasty base-court
48
- bat-fowling beef-witted beetle-headed boil-brained clapper-clawed clay-brained
49
- common-kissing crook-pated dismal-dreaming dizzy-eyed doghearted dread-bolted
50
- earth-vexing elf-skinned fat-kidneyed flap-mouthed fly-bitten folly-fallen fool-born
51
- full-gorged guts-griping half-faced hasty-witted hedge-born hell-hated idle-headed
52
- ill-breeding ill-nurtured knotty-pated milk-livered motley-minded onion-eyed
53
- plume-plucked pottle-deep pox-marked reeling-ripe rough-hewn rude-growing rump-fed
54
- shard-borne sheep-biting spur-galled swag-bellied tardy-gaited tickle-brained
55
- toad-spotted urchin-snouted weather-bitten)
56
-
57
- adjectives[rand(adjectives.size)]
58
-
59
- end
60
-
61
- def amount()
62
- amounts = %w(accumulation bucket coagulation enema-bucketful gob half-mouthful
63
- heap mass mound petrification pile puddle stack thimbleful tongueful
64
- ooze quart bag plate ass-full assload load)
65
-
66
- amounts[rand(amounts.size)]
67
-
68
- end
69
-
70
- def noun()
71
- nouns = %w(bat|toenails bug|spit cat|hair chicken|piss dog|vomit dung
72
- fat-woman's|stomach-bile fish|heads guano gunk pond|scum rat|retch
73
- red|dye|number-9 Sun|IPC|manuals waffle-house|grits yoo-hoo
74
- dog|balls seagull|puke cat|bladders pus urine|samples
75
- squirrel|guts snake|rectums snake|bait buzzard|gizzards
76
- cat-hair-balls rat-farts pods armadillo|snouts entrails
77
- snake|snot eel|ooze slurpee-backwash toxic|waste Stimpy-drool
78
- poopy poop craptacular|carpet|droppings cold|sores warts)
79
-
80
- nouns[rand(nouns.size)].gsub("|", " ")
81
-
82
-
83
- end
84
-
85
- end
86
-
87
-
@@ -1,197 +0,0 @@
1
- require 'open-uri'
2
- require 'hpricot'
3
- require 'tempfile'
4
- require 'rexml/document'
5
- ##
6
- # JIRA plugin
7
- #
8
- # checks JIRA for new issues and posts them to the room
9
- #
10
- # in your config.yml you can either specify a single URL or a list of URLs, e.g.
11
- #
12
- # jira_url: http://your_jira_url
13
- # # OR
14
- # jira_url:
15
- # - http://your_jira_url
16
- # - http://your_jira_url2
17
- #
18
-
19
-
20
- class Jira < CampfireBot::Plugin
21
-
22
- at_interval 3.minutes, :check_jira
23
- on_command 'checkjira', :checkjira_command
24
- on_command 'jira', :checkjira_command
25
-
26
- def initialize
27
- # log "initializing... "
28
- @data_file = File.join(BOT_ROOT, 'tmp', "jira-#{BOT_ENVIRONMENT}.yml")
29
- @cached_ids = YAML::load(File.read(@data_file)) || {}
30
- @last_checked = @cached_ids[:last_checked] || 10.minutes.ago
31
- @log = Logging.logger["CampfireBot::Plugin::Jira"]
32
- end
33
-
34
- # respond to checkjira command-- same as interval except we answer with 'no issues found' if there are no issues
35
- def checkjira_command(msg)
36
- begin
37
- msg.speak "no new issues since I last checked #{@lastlast} ago" if !check_jira(msg)
38
- rescue
39
- msg.speak "sorry, we had trouble connecting to JIRA."
40
- end
41
- end
42
-
43
- def check_jira(msg)
44
-
45
- saw_an_issue = false
46
- old_cache = Marshal::load(Marshal.dump(@cached_ids)) # since ruby doesn't have deep copy
47
-
48
-
49
- @lastlast = time_ago_in_words(@last_checked)
50
- @last_checked = Time.now
51
-
52
-
53
- tix = fetch_jira_url
54
- raise if tix.nil?
55
-
56
- tix.each do |ticket|
57
- if seen?(ticket, old_cache)
58
- saw_an_issue = true
59
-
60
- @cached_ids = update_cache(ticket, @cached_ids)
61
-
62
-
63
- messagetext = "#{ticket[:type]} - #{ticket[:title]} - #{ticket[:link]} - reported by #{ticket[:reporter]} - #{ticket[:priority]}"
64
- msg.speak(messagetext)
65
- msg.play("vuvuzela") if ticket[:priority] == "Blocker"
66
- @log.info messagetext
67
-
68
- end
69
- end
70
-
71
- flush_cache(@cached_ids)
72
- @log.info "no new issues." if !saw_an_issue
73
-
74
- saw_an_issue
75
- end
76
-
77
- protected
78
-
79
- # fetch jira url and return a list of ticket Hashes
80
- def fetch_jira_url()
81
-
82
- jiraconfig = bot.config['jira_url']
83
-
84
- if jiraconfig.is_a?(Array)
85
- searchurls = jiraconfig
86
- else
87
- searchurls = [jiraconfig]
88
- end
89
-
90
- tix = []
91
-
92
- searchurls.each do |searchurl|
93
- begin
94
- @log.info "checking jira for new issues... #{searchurl}"
95
- xmldata = open(searchurl).read
96
- doc = REXML::Document.new(xmldata)
97
- raise Exception.new("response had no content") if doc.nil?
98
- doc.elements.inject('rss/channel/item', tix) do |tix, element|
99
- tix.push(parse_ticket_info(element))
100
- end
101
- rescue Exception => e
102
- @log.error "error connecting to jira: #{e.message}"
103
- # @log.error "#{e.backtrace}"
104
- end
105
- end
106
- return tix
107
- end
108
-
109
- # extract ticket hash from individual xml element
110
- def parse_ticket_info(xml_element)
111
- id = xml_element.elements['key'].text rescue ""
112
- id, spacekey = split_spacekey_and_id(id) rescue ""
113
-
114
- link = xml_element.elements['link'].text rescue ""
115
- title = xml_element.elements['title'].text rescue ""
116
- reporter = xml_element.elements['reporter'].text rescue ""
117
- type = xml_element.elements['type'].text rescue ""
118
- priority = xml_element.elements['priority'].text rescue ""
119
-
120
- return {
121
- :spacekey => spacekey,
122
- :id => id,
123
- :link => link,
124
- :title => title,
125
- :reporter => reporter,
126
- :type => type,
127
- :priority => priority
128
- }
129
- end
130
-
131
- # extract the spacekey and id from the ticket id
132
- def split_spacekey_and_id(key)
133
- spacekey = key.scan(/^([A-Z]+)/).to_s
134
- id = key.scan(/([0-9]+)$/)[0].to_s.to_i
135
- return id, spacekey
136
- end
137
-
138
- # has this ticket been seen before this run?
139
- def seen?(ticket, old_cache)
140
- !old_cache.key?(ticket[:spacekey]) or old_cache[ticket[:spacekey]] < ticket[:id]
141
- end
142
-
143
- # only update the cached highest ID if it is in fact the highest ID
144
- def update_cache(ticket, cache)
145
- cache[ticket[:spacekey]] = ticket[:id] if seen?(ticket, cache)
146
- cache
147
- end
148
-
149
- # write the cache to disk
150
- def flush_cache(cache)
151
- cache[:last_checked] = @last_checked
152
- File.open(@data_file, 'w') do |out|
153
- YAML.dump(cache, out)
154
- end
155
- end
156
-
157
-
158
- #
159
- # time/utility functions
160
- #
161
-
162
-
163
- def time_ago_in_words(from_time, include_seconds = false)
164
- distance_of_time_in_words(from_time, Time.now, include_seconds)
165
- end
166
-
167
- def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false)
168
- from_time = from_time.to_time if from_time.respond_to?(:to_time)
169
- to_time = to_time.to_time if to_time.respond_to?(:to_time)
170
- distance_in_minutes = (((to_time - from_time).abs)/60).round
171
- distance_in_seconds = ((to_time - from_time).abs).round
172
-
173
- case distance_in_minutes
174
- when 0..1
175
- return (distance_in_minutes == 0) ? 'less than a minute' : '1 minute' unless include_seconds
176
- case distance_in_seconds
177
- when 0..4 then 'less than 5 seconds'
178
- when 5..9 then 'less than 10 seconds'
179
- when 10..19 then 'less than 20 seconds'
180
- when 20..39 then 'half a minute'
181
- when 40..59 then 'less than a minute'
182
- else '1 minute'
183
- end
184
-
185
- when 2..44 then "#{distance_in_minutes} minutes"
186
- when 45..89 then 'about 1 hour'
187
- when 90..1439 then "about #{(distance_in_minutes.to_f / 60.0).round} hours"
188
- when 1440..2879 then '1 day'
189
- when 2880..43199 then "#{(distance_in_minutes / 1440).round} days"
190
- when 43200..86399 then 'about 1 month'
191
- when 86400..525599 then "#{(distance_in_minutes / 43200).round} months"
192
- when 525600..1051199 then 'about 1 year'
193
- else "over #{(distance_in_minutes / 525600).round} years"
194
- end
195
- end
196
-
197
- end