ceiling_cat 0.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/bin/ceiling_cat +26 -0
- data/lib/ceiling_cat.rb +4 -0
- data/lib/ceiling_cat/connection.rb +19 -0
- data/lib/ceiling_cat/errors.rb +17 -0
- data/lib/ceiling_cat/event.rb +29 -0
- data/lib/ceiling_cat/plugins/about.rb +44 -0
- data/lib/ceiling_cat/plugins/base.rb +98 -0
- data/lib/ceiling_cat/plugins/calc.rb +34 -0
- data/lib/ceiling_cat/plugins/call_and_response.rb +76 -0
- data/lib/ceiling_cat/plugins/campfire_account_monitor.rb +46 -0
- data/lib/ceiling_cat/plugins/days.rb +99 -0
- data/lib/ceiling_cat/plugins/general.rb +56 -0
- data/lib/ceiling_cat/plugins/greeter.rb +30 -0
- data/lib/ceiling_cat/plugins/notifo.rb +69 -0
- data/lib/ceiling_cat/room.rb +81 -0
- data/lib/ceiling_cat/services/campfire.rb +3 -0
- data/lib/ceiling_cat/services/campfire/connection.rb +24 -0
- data/lib/ceiling_cat/services/campfire/event.rb +18 -0
- data/lib/ceiling_cat/services/campfire/room.rb +110 -0
- data/lib/ceiling_cat/setup.rb +47 -0
- data/lib/ceiling_cat/storage/base.rb +18 -0
- data/lib/ceiling_cat/storage/yaml.rb +49 -0
- data/lib/ceiling_cat/user.rb +27 -0
- data/lib/ceiling_cat/version.rb +3 -0
- data/setup/Chatfile +20 -0
- data/setup/Rakefile +65 -0
- metadata +138 -0
data/bin/ceiling_cat
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'ceiling_cat'
|
4
|
+
|
5
|
+
if ARGV[0] == "setup"
|
6
|
+
cc=`gem which ceiling_cat`
|
7
|
+
`mkdir ./plugins` unless File.exists?('./plugins')
|
8
|
+
if File.exists?(File.join(cc.split("/")[0..-3], "setup", "Rakefile")) && !File.exists?('Rakefile')
|
9
|
+
`cp #{File.join(cc.split("/")[0..-3], "setup", "Rakefile")} Rakefile`
|
10
|
+
puts "Rakefile created."
|
11
|
+
end
|
12
|
+
if File.exists?(File.join(cc.split("/")[0..-3], "setup", "Chatfile")) && !File.exists?('Chatfile')
|
13
|
+
`cp #{File.join(cc.split("/")[0..-3], "setup", "Chatfile")} Chatfile`
|
14
|
+
puts "Chatfile created."
|
15
|
+
end
|
16
|
+
puts "To create your own plugin, run `rake plugin:create name=[plugin_name]` to get started."
|
17
|
+
else
|
18
|
+
file = ARGV[0] || './Chatfile'
|
19
|
+
|
20
|
+
if File.exists?(file)
|
21
|
+
load file
|
22
|
+
CeilingCat::Setup.new.connect
|
23
|
+
else
|
24
|
+
puts "Couldn't find a Chatfile at #{file}! Create one at `./Chatfile`, provide a path to one, or run `ceiling_cat setup`"
|
25
|
+
end
|
26
|
+
end
|
data/lib/ceiling_cat.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
class Connection
|
3
|
+
attr_accessor :config
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@config=config
|
7
|
+
@config.storage ||= CeilingCat::Storage::Hash
|
8
|
+
end
|
9
|
+
|
10
|
+
def plugins
|
11
|
+
self.config.plugins
|
12
|
+
end
|
13
|
+
|
14
|
+
def storage
|
15
|
+
self.config.storage
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
class Error < StandardError
|
3
|
+
|
4
|
+
def initialize(message)
|
5
|
+
super message
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# Gem Specific Errors
|
10
|
+
class CeilingCatError < StandardError; end
|
11
|
+
|
12
|
+
class UnsupportedChatServiceError < CeilingCatError; end
|
13
|
+
|
14
|
+
class NotImplementedError < CeilingCatError; end
|
15
|
+
|
16
|
+
class ReloadException < CeilingCatError; end
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
class Event
|
3
|
+
attr_accessor :room, :body, :user, :type, :time
|
4
|
+
|
5
|
+
def initialize(room, body,user,opts={})
|
6
|
+
@room = room
|
7
|
+
@body = body.to_s.strip
|
8
|
+
@user = user
|
9
|
+
@type = opts[:type]
|
10
|
+
@time = opts[:time] || Time.now
|
11
|
+
end
|
12
|
+
|
13
|
+
def handle
|
14
|
+
@room.plugins.each do |plugin|
|
15
|
+
puts "running #{plugin}"
|
16
|
+
begin
|
17
|
+
response = plugin.new(self).handle
|
18
|
+
break if response.present?
|
19
|
+
rescue => e
|
20
|
+
@room.say("Whoops - there was a problem with #{plugin}: #{e}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def type # assume that all messages are just text unless the specific room type overrides it.
|
26
|
+
:chat
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
module Plugin
|
3
|
+
class About < CeilingCat::Plugin::Base
|
4
|
+
def self.commands
|
5
|
+
[{:command => "plugins", :description => "List of installed plugins.", :method => "list_plugins"},
|
6
|
+
{:command => "commands", :description => "List of available commands.", :method => "list_commands", :public => true},
|
7
|
+
{:command => "users", :description => "List of registered in the room.", :method => "list_users", :public => true},
|
8
|
+
{:command => "guests", :description => "List of guests in the room.", :method => "list_guests", :public => true}]
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.description
|
12
|
+
"About the currently running Ceiling Cat"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.name
|
16
|
+
"About"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.public?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def list_plugins
|
24
|
+
reply room.plugin_descriptions(user.is_registered?)
|
25
|
+
end
|
26
|
+
|
27
|
+
def list_commands
|
28
|
+
messages = room.available_commands(user.is_registered?) # Show all commands if the user is registered, otherwise only show private commands
|
29
|
+
messages << "Run commands with '![command]' or '#{room.me.name}: [command]'"
|
30
|
+
reply messages
|
31
|
+
end
|
32
|
+
|
33
|
+
def list_users
|
34
|
+
members = room.list_of_users_in_room(:type => "member")
|
35
|
+
reply "#{members} #{pluralize(members.size, "is a", "are")} #{pluralize(members.size, "registered user")}."
|
36
|
+
end
|
37
|
+
|
38
|
+
def list_guests
|
39
|
+
guests = room.list_of_users_in_room(:type => "guest")
|
40
|
+
reply "#{guests} #{pluralize(members.size, "is a", "are")} #{pluralize(members.size, "guest")}."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
module Plugin
|
3
|
+
class Base
|
4
|
+
attr_accessor :event
|
5
|
+
|
6
|
+
def initialize(event)
|
7
|
+
@event = event
|
8
|
+
end
|
9
|
+
|
10
|
+
def handle
|
11
|
+
if command = commands.find{|command| body =~ /^(!|#{room.me.name}:?\s*)#{command[:command]}/i}
|
12
|
+
begin
|
13
|
+
if command[:public] || user.is_registered?
|
14
|
+
self.send command[:method]
|
15
|
+
end
|
16
|
+
rescue => e
|
17
|
+
reply "There was an error: #{$!}"
|
18
|
+
raise e
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.name
|
24
|
+
self.to_s.gsub("CeilingCat::Plugin::","")
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.description
|
28
|
+
"No description"
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.public?
|
32
|
+
false #assume we don't want people to know a plugin is available.
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.commands
|
36
|
+
[]
|
37
|
+
end
|
38
|
+
|
39
|
+
def commands
|
40
|
+
self.class.commands || []
|
41
|
+
end
|
42
|
+
|
43
|
+
def event
|
44
|
+
@event
|
45
|
+
end
|
46
|
+
|
47
|
+
def room
|
48
|
+
event.room
|
49
|
+
end
|
50
|
+
|
51
|
+
def store
|
52
|
+
self.class.store
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.store
|
56
|
+
CeilingCat::Setup.config.storage
|
57
|
+
end
|
58
|
+
|
59
|
+
def body
|
60
|
+
event.body
|
61
|
+
end
|
62
|
+
|
63
|
+
def user
|
64
|
+
event.user
|
65
|
+
end
|
66
|
+
|
67
|
+
def reply(message)
|
68
|
+
room.say(message)
|
69
|
+
end
|
70
|
+
|
71
|
+
def words
|
72
|
+
body.split
|
73
|
+
end
|
74
|
+
|
75
|
+
def body_without_command(command,text=body)
|
76
|
+
text.sub(/^!#{command}:?\s*/i,"").strip
|
77
|
+
end
|
78
|
+
|
79
|
+
def body_without_nick(text=body)
|
80
|
+
text.sub(/^#{room.me.name}:?\s*/i,'').strip
|
81
|
+
end
|
82
|
+
|
83
|
+
def body_without_nick_or_command(command,text=body)
|
84
|
+
body_without_command(command, body_without_nick(text).sub(/^#{command}/i,"!#{command}"))
|
85
|
+
end
|
86
|
+
|
87
|
+
def pluralize(n, singular, plural=nil)
|
88
|
+
if n == 1
|
89
|
+
"#{singular}"
|
90
|
+
elsif plural
|
91
|
+
"#{plural}"
|
92
|
+
else
|
93
|
+
"#{singular}s"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
module Plugin
|
3
|
+
class Calc < CeilingCat::Plugin::Base
|
4
|
+
def self.commands
|
5
|
+
[{:command => "calculate", :description => "Performs basic math functions - '!calculate 7*5'", :method => "calculate", :public => true}]
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.description
|
9
|
+
"A basic calculator, for all your mathin' needs!"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.name
|
13
|
+
"Calculator"
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.public?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def calculate
|
21
|
+
begin
|
22
|
+
math = body.gsub(/[^\d+-\/*\(\)\.]/,"") # Removing anything but numbers, operators, and parens
|
23
|
+
if math.length > 0 && math =~ /^\d.+\d$/
|
24
|
+
reply "#{math} = #{eval math}"
|
25
|
+
else
|
26
|
+
reply "I don't think that's an equation. Want to try something else?"
|
27
|
+
end
|
28
|
+
rescue => e
|
29
|
+
raise e
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
module Plugin
|
3
|
+
class CallAndResponse < CeilingCat::Plugin::Base
|
4
|
+
def handle
|
5
|
+
if event.type == :chat
|
6
|
+
super
|
7
|
+
if match = self.class.list.find{|car| body =~ Regexp.new(Regexp.escape(car[:call]),true) }
|
8
|
+
reply match[:url]
|
9
|
+
return nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.commands
|
16
|
+
[{:command => "list calls", :description => "List current calls and their responses", :method => "list"},
|
17
|
+
{:command => "add call", :description => "Add an response to display when a phrase is said, split by a | - '!add call I see what you did there... | You caught me!'", :method => "add"},
|
18
|
+
{:command => "remove call", :description => "Remove an call and response by call '!remove call I see what you did there...'", :method => "remove"}]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.description
|
22
|
+
"Watches for certain phrases and inserts a relevant image"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.name
|
26
|
+
"Call and Response"
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.public?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def list
|
34
|
+
messages = []
|
35
|
+
store["call_and_responses"] ||= []
|
36
|
+
if store["call_and_responses"].size > 0
|
37
|
+
messages << "Current Calls and Responses"
|
38
|
+
messages += store["call_and_responses"].collect{|car| "-- #{car[:call]} | #{car[:response]}"}
|
39
|
+
else
|
40
|
+
messages << "There are no call and respones set up yet! You should add one with '!add call When someone says this | I say this'"
|
41
|
+
end
|
42
|
+
reply messages
|
43
|
+
end
|
44
|
+
|
45
|
+
def add
|
46
|
+
message = body_without_nick_or_command("add call").split
|
47
|
+
call, response = message.split("|")
|
48
|
+
if self.class.add(:call => call.strip,:response => response.strip)
|
49
|
+
reply "Call and Response added."
|
50
|
+
else
|
51
|
+
reply "Unable to add that call. A call and a response are required, split by a | - 'When someones says this | I say this'"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def remove
|
56
|
+
self.class.remove(body_without_nick_or_command("remove image phrase"))
|
57
|
+
reply "Call removed."
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.list
|
61
|
+
store["call_and_responses"] ||= []
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.add(opts={})
|
65
|
+
return false unless opts[:call] && opts[:response]
|
66
|
+
store["call_and_responses"] ||= []
|
67
|
+
store["call_and_responses"] = (store["call_and_responses"] + [{:call => opts[:call], :response => opts[:response]}]).uniq
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.remove(phrase)
|
71
|
+
store["call_and_responses"] ||= []
|
72
|
+
store["call_and_responses"] = store["call_and_responses"].reject{|car| car[:call].downcase == call.downcase }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
module Plugin
|
3
|
+
class CampfireAccountMonitor < CeilingCat::Plugin::Base
|
4
|
+
# See lib/ceiling_cat/plugins/base.rb for the methods available by default.
|
5
|
+
# Plugins are run in the order they are listed in the Chatfile.
|
6
|
+
# When a plugin returns anything other than nil the plugin execution chain is halted.
|
7
|
+
# If you want your plugin to do something but let the remaining plugins execute, return nil at the end of your method.
|
8
|
+
|
9
|
+
# handle manages a plugin's entire interaction with an event.
|
10
|
+
# If you only want to execute commands - "![command]" - leave handle alone (or remove it and define commands below)
|
11
|
+
def handle
|
12
|
+
if room.connection.config.service.downcase == "campfire" && event.type == :entrance
|
13
|
+
user_count = room.connection.total_user_count
|
14
|
+
max_users = room.connection.config.max_users || 100
|
15
|
+
room.plugin("notifo").new(@event).deliver("#{user_count} of #{max_users} max connections to Campfire.") if room.plugin_installed?("notifo") && user_count > max_users-2
|
16
|
+
end
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
# If you want the plugin to run when certain text is sent use commands instead of handle.
|
21
|
+
# Ceiling Cat will watch for "![command]" or "[name]: [command" and execute the method for that command.
|
22
|
+
def self.commands
|
23
|
+
[{:command => "total users", :description => "Gets the total number of current users in chat", :method => "total_users", :public => false}]
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.description
|
27
|
+
"For monitoring Campfire connection limits"
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.name
|
31
|
+
"Campfire account monitor"
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.public?
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def total_users
|
39
|
+
if room.connection.config.service.downcase == "campfire"
|
40
|
+
reply "#{room.connection.total_user_count} connections currently used"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
class NotADateError < CeilingCatError; end
|
3
|
+
|
4
|
+
module Plugin
|
5
|
+
class Days < CeilingCat::Plugin::Base
|
6
|
+
def self.commands
|
7
|
+
[{:command => "today", :description => "Find out if there's anything special about today.", :method => "about", :public => true},
|
8
|
+
{:command => "add holiday", :description => "Add a holiday - '!add holiday 1/19/2011'", :method => "add_to_holidays"}]
|
9
|
+
end
|
10
|
+
|
11
|
+
def about(date=Date.today)
|
12
|
+
begin
|
13
|
+
if self.class.is_a_holiday?(date)
|
14
|
+
reply "Today is a holiday!"
|
15
|
+
elsif self.class.is_a_weekend?(date)
|
16
|
+
reply "Today is a weekend!"
|
17
|
+
else
|
18
|
+
reply "Just a normal day today."
|
19
|
+
end
|
20
|
+
rescue NotADateError
|
21
|
+
reply "Sorry, that's not a valid date."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.description
|
26
|
+
"Holidays and times you shouldn't expect to see us in chat."
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_to_holidays
|
30
|
+
date = body_without_command("add holiday")
|
31
|
+
if date.empty?
|
32
|
+
reply "You need to provide a date to add to the holiday list, like 'add holiday 1/19/2011'"
|
33
|
+
else
|
34
|
+
begin
|
35
|
+
self.class.add_to_holidays(body_without_command("add holiday"))
|
36
|
+
reply "#{date.to_s} has been added to the holiday list."
|
37
|
+
rescue NotADateError
|
38
|
+
reply "Sorry, that's not a valid date."
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.is_a_weekend?(date=Date.today)
|
44
|
+
if is_a_date?(date)
|
45
|
+
date = Date.parse(date.to_s)
|
46
|
+
date.wday == 0 || date.wday == 6
|
47
|
+
else
|
48
|
+
raise NotADateError
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.holidays
|
53
|
+
store["holidays"] ||= []
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.add_to_holidays(days)
|
57
|
+
dates = Array(days).collect do |day|
|
58
|
+
if is_a_date?(day)
|
59
|
+
Date.parse(day.to_s)
|
60
|
+
else
|
61
|
+
raise NotADateError
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
store["holidays"] ||= []
|
66
|
+
store["holidays"] = (store["holidays"] + dates).uniq
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.remove_from_holidays(days)
|
70
|
+
dates = Array(days).collect do |day|
|
71
|
+
if is_a_date?(day)
|
72
|
+
Date.parse(day.to_s)
|
73
|
+
else
|
74
|
+
raise NotADateError
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
store["holidays"] ||= []
|
79
|
+
store["holidays"] = store["holidays"] - dates
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.is_a_holiday?(date=Date.today)
|
83
|
+
if is_a_date?(date)
|
84
|
+
store["holidays"].include? Date.parse(date.to_s)
|
85
|
+
else
|
86
|
+
raise NotADateError
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.is_a_date?(date_string)
|
91
|
+
begin
|
92
|
+
return true if Date.parse(date_string.to_s)
|
93
|
+
rescue ArgumentError => e
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
module Plugin
|
3
|
+
class General < CeilingCat::Plugin::Base
|
4
|
+
# See lib/ceiling_cat/plugins/base.rb for the methods available by default.
|
5
|
+
# Plugins are run in the order they are listed in the Chatfile.
|
6
|
+
# When a plugin returns anything other than nil the plugin execution chain is halted.
|
7
|
+
# If you want your plugin to do something but let the remaining plugins execute, return nil at the end of your method.
|
8
|
+
|
9
|
+
# handle manages a plugin's entire interaction with an event.
|
10
|
+
# If you only want to execute commands - "![command]" - leave handle alone (or remove it and define commands below)
|
11
|
+
def handle
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
# If you want the plugin to run when certain text is sent use commands instead of handle.
|
16
|
+
# Ceiling Cat will watch for "![command]" or "[name]: [command" and execute the method for that command.
|
17
|
+
def self.commands
|
18
|
+
[{:command => "debugger", :description => "Load the debugger", :method => "run_debug", :public => false},
|
19
|
+
{:command => "enable debug", :description => "Enable debuggers", :method => "enable_debug", :public => false},
|
20
|
+
{:command => "disable debug", :description => "Disable debuggers", :method => "disable_debug", :public => false}]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.description
|
24
|
+
"General tasks for development"
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.name
|
28
|
+
"General"
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.public?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def run_debug
|
36
|
+
if room.debug_mode?
|
37
|
+
reply "Opening debugger in terminal"
|
38
|
+
debugger
|
39
|
+
else
|
40
|
+
reply "Debugging is disabled - !enable debug to activate it."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def enable_debug
|
45
|
+
store["debug_mode"] ||= "true"
|
46
|
+
reply "debugging enabled"
|
47
|
+
end
|
48
|
+
|
49
|
+
def disable_debug
|
50
|
+
store["debug_mode"] ||= "false"
|
51
|
+
reply "debugging disabled"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
module Plugin
|
3
|
+
class Greeter < CeilingCat::Plugin::Base
|
4
|
+
def handle
|
5
|
+
message = []
|
6
|
+
|
7
|
+
if event.type == :entrance
|
8
|
+
members = room.users_in_room(:type => "member")
|
9
|
+
guests = room.users_in_room(:type => "guest")
|
10
|
+
if user.is_guest?
|
11
|
+
messages << "Hey #{user.name}!"
|
12
|
+
elsif user.is_registered?
|
13
|
+
messages << "Nice to see you again #{user.name}"
|
14
|
+
end
|
15
|
+
reply message unless message.empty?
|
16
|
+
elsif event.type == :chat
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.name
|
22
|
+
"Greeter"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.description
|
26
|
+
"Welcomes visitors and memebrs to chat."
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module CeilingCat
|
4
|
+
module Plugin
|
5
|
+
class Notifo < CeilingCat::Plugin::Base
|
6
|
+
def self.commands
|
7
|
+
[{:command => "notifo", :description => "Send a message with Notifo - '!notifo Hey, get in here!'.", :method => "deliver"},
|
8
|
+
{:command => "add notifo users", :description => "Add users to get Notifos - '!add notifo users username1 username2'.", :method => "add_users"},
|
9
|
+
{:command => "remove notifo users", :description => "Add users to get Notifos - '!remove notifo users username1 username2'.", :method => "remove_users"},
|
10
|
+
{:command => "list notifo users", :description => "List users who get Notifos - '!list notifo users'.", :method => "list_users"}]
|
11
|
+
end
|
12
|
+
|
13
|
+
def deliver(message=nil)
|
14
|
+
message ||= body_without_nick_or_command("notifo")
|
15
|
+
if active?
|
16
|
+
Array(store["notifo_users"]).each do |user|
|
17
|
+
HTTParty.post("https://api.notifo.com/v1/send_notification",
|
18
|
+
:body => { :to => user, :msg => message },
|
19
|
+
:basic_auth => {:username => store["notifo_credentials"][:username], :password => store["notifo_credentials"][:api_secret]})
|
20
|
+
end
|
21
|
+
else
|
22
|
+
debugger
|
23
|
+
puts "here"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def active?
|
28
|
+
store["notifo_credentials"] && store["notifo_credentials"][:username].present? && store["notifo_credentials"][:api_secret].present? && Array(store["notifo_users"]).size > 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.name
|
32
|
+
"Notifo"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.description
|
36
|
+
"Sends messages to users with Notifo"
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.add_users(users)
|
40
|
+
store["notifo_users"] ||= []
|
41
|
+
store["notifo_users"] = (Array(store["notifo_users"]) + Array(users)).uniq
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.set_credentials(username, api_secret)
|
45
|
+
store["notifo_credentials"] = {:username => username, :api_secret => api_secret}
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_users
|
49
|
+
users = body_without_nick_or_command("add notifo users").split(" ")
|
50
|
+
self.class.add_users(users)
|
51
|
+
reply "#{users.join(" ")} added to notifo alerts."
|
52
|
+
end
|
53
|
+
|
54
|
+
def remove_users
|
55
|
+
users = body_without_nick_or_command("remove notifo users").split(" ")
|
56
|
+
store["notifo_users"] ||= []
|
57
|
+
store["notifo_users"] = store["notifo_users"] - users
|
58
|
+
reply "#{users.join(" ")} removed from notifo alerts."
|
59
|
+
end
|
60
|
+
|
61
|
+
def list_users
|
62
|
+
messages = []
|
63
|
+
messages << "Notifo Users"
|
64
|
+
Array(store["notifo_users"]).each{|user| messages << "-- #{user}"}
|
65
|
+
reply messages
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
class Room
|
3
|
+
attr_accessor :connection, :room, :users_in_room, :me
|
4
|
+
|
5
|
+
def initialize(opts={})
|
6
|
+
@connection = opts[:connection]
|
7
|
+
end
|
8
|
+
|
9
|
+
def watch
|
10
|
+
raise NotImplementedError, "Implement in chat service files!"
|
11
|
+
end
|
12
|
+
|
13
|
+
def say
|
14
|
+
raise NotImplementedError, "Implement in chat service files!"
|
15
|
+
end
|
16
|
+
|
17
|
+
def me
|
18
|
+
raise NotImplementedError, "Implement in chat service files or define config.nickname!" unless config.nickname
|
19
|
+
@me ||= CeilingCat::User.new(config.nickname)
|
20
|
+
end
|
21
|
+
|
22
|
+
def users_in_room(type=nil)
|
23
|
+
raise NotImplementedError, "Implement in chat service files!"
|
24
|
+
end
|
25
|
+
|
26
|
+
def plugins
|
27
|
+
@connection.plugins
|
28
|
+
end
|
29
|
+
|
30
|
+
def config
|
31
|
+
@connection.config
|
32
|
+
end
|
33
|
+
|
34
|
+
def plugin_installed?(name)
|
35
|
+
!plugin(name).nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
def plugin(name)
|
39
|
+
plugins.find{|plugin| plugin.name.downcase == name.to_s.downcase || plugin.class == name}
|
40
|
+
end
|
41
|
+
|
42
|
+
def store
|
43
|
+
@connection.storage
|
44
|
+
end
|
45
|
+
|
46
|
+
def debug_mode?
|
47
|
+
!store["debug_mode"].nil? && store["debug_mode"] == "true"
|
48
|
+
end
|
49
|
+
|
50
|
+
def plugin_descriptions(show_private=false)
|
51
|
+
messages = []
|
52
|
+
plugins.each do |plugin|
|
53
|
+
messages << "#{plugin.name}: #{plugin.description}" if show_private || plugin.public?
|
54
|
+
end
|
55
|
+
messages
|
56
|
+
end
|
57
|
+
|
58
|
+
def available_commands(show_private=false)
|
59
|
+
messages = []
|
60
|
+
plugins.each do |plugin|
|
61
|
+
if !plugin.commands.empty? && (plugin.public? || show_private)
|
62
|
+
messages << "Commands for #{plugin.name}"
|
63
|
+
plugin.commands.each do |command|
|
64
|
+
messages << "-- #{command[:command]}: #{command[:description]}" if show_private || command[:public]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
messages
|
69
|
+
end
|
70
|
+
|
71
|
+
def list_of_users_in_room(type=nil)
|
72
|
+
users = users_in_room(type)
|
73
|
+
last_user = users.pop
|
74
|
+
if users.size > 0
|
75
|
+
return users.collect{|user| user["name"] }.join(", ") + " and #{ last_user["name"]}"
|
76
|
+
else
|
77
|
+
return last_user["name"]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'tinder'
|
2
|
+
|
3
|
+
module CeilingCat
|
4
|
+
module Campfire
|
5
|
+
class Connection < CeilingCat::Connection
|
6
|
+
def initialize(config)
|
7
|
+
@config=config
|
8
|
+
@config.ssl ||= "true"
|
9
|
+
end
|
10
|
+
|
11
|
+
def campfire
|
12
|
+
@campfire = Tinder::Campfire.new(self.config.username, :token => self.config.token, :ssl => self.config.ssl)
|
13
|
+
end
|
14
|
+
|
15
|
+
def total_user_count
|
16
|
+
users = 0
|
17
|
+
@campfire.rooms.each do |room|
|
18
|
+
users += room.users.size
|
19
|
+
end
|
20
|
+
users
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
module Campfire
|
3
|
+
class Room < CeilingCat::Room
|
4
|
+
|
5
|
+
def initialize(opts={})
|
6
|
+
super
|
7
|
+
@campfire_room = @connection.campfire.find_room_by_name(opts[:room_name])
|
8
|
+
end
|
9
|
+
|
10
|
+
def watch
|
11
|
+
puts "Watching room..."
|
12
|
+
setup_interrupts
|
13
|
+
begin
|
14
|
+
loop do
|
15
|
+
begin
|
16
|
+
Timeout::timeout(90) do
|
17
|
+
@campfire_room.listen do |event|
|
18
|
+
begin
|
19
|
+
if event[:type] != "TimestampMessage"
|
20
|
+
user = CeilingCat::User.new(event[:user][:name], :id => event[:user][:id], :role => event[:user][:type])
|
21
|
+
|
22
|
+
unless is_me?(user) # Otherwise CC will talk to itself
|
23
|
+
event = CeilingCat::Campfire::Event.new(self,event[:body], user, :type => event[:type])
|
24
|
+
event.handle
|
25
|
+
users_in_room(:reload => true) if event.type != :chat # If someone comes or goes, reload the list.
|
26
|
+
end
|
27
|
+
end
|
28
|
+
rescue => e
|
29
|
+
say "An error occurred with Campfire: #{e}" if debug_mode?
|
30
|
+
raise e
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
rescue Timeout::Error
|
35
|
+
puts "timeout! trying again..."
|
36
|
+
retry
|
37
|
+
end
|
38
|
+
end
|
39
|
+
rescue Faraday::Error::ParsingError
|
40
|
+
puts "Error parsing response. Campfire may be down."
|
41
|
+
break
|
42
|
+
rescue ReloadException => e
|
43
|
+
retry
|
44
|
+
rescue NoMethodError => e
|
45
|
+
puts "No Method Error"
|
46
|
+
e.backtrace.each do |line|
|
47
|
+
puts "Backtrace: #{line}"
|
48
|
+
end
|
49
|
+
retry
|
50
|
+
rescue StandardError => e
|
51
|
+
puts e.class
|
52
|
+
debugger if debug_mode?
|
53
|
+
e.backtrace.each do |line|
|
54
|
+
puts "Backtrace: #{line}"
|
55
|
+
end
|
56
|
+
retry
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def is_me?(user)
|
61
|
+
user.id == me.id
|
62
|
+
end
|
63
|
+
|
64
|
+
def me
|
65
|
+
@me ||= CeilingCat::User.new(@connection.campfire.me[:name], :id => @connection.campfire.me[:id], :role => @connection.campfire.me[:type])
|
66
|
+
end
|
67
|
+
|
68
|
+
def users_in_room(opts={})
|
69
|
+
if opts[:reload] || @users_in_room.nil?
|
70
|
+
puts "Requesting user list"
|
71
|
+
@users_in_room = @campfire_room.users
|
72
|
+
end
|
73
|
+
|
74
|
+
@users_in_room.find_all do |user|
|
75
|
+
unless is_me?(CeilingCat::User.new(user[:name], :id => user[:id], :role => user[:type]))
|
76
|
+
if opts[:type].present?
|
77
|
+
if user[:type].to_s.downcase == opts[:type].downcase
|
78
|
+
CeilingCat::User.new(user[:name], :id => user[:id], :role => user[:type])
|
79
|
+
end
|
80
|
+
else
|
81
|
+
CeilingCat::User.new(user[:name], :id => user[:id], :role => user[:type])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def say(something_to_say)
|
88
|
+
Array(something_to_say).each do |line|
|
89
|
+
@campfire_room.speak(line)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def setup_interrupts
|
94
|
+
trap('INT') do
|
95
|
+
puts "Leaving room...."
|
96
|
+
@campfire_room.leave if @campfire_room
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
|
100
|
+
trap('USR1') do
|
101
|
+
puts "Leaving room"
|
102
|
+
@campfire_room.leave if @campfire_room
|
103
|
+
# logger.info "Reloading config"
|
104
|
+
# config(true)
|
105
|
+
# raise ReloadException.new("Rejoin room please, ceiling cat")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module CeilingCat
|
4
|
+
class Setup
|
5
|
+
|
6
|
+
attr_accessor :config
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Class-level config. This is set by the +configure+ class method,
|
10
|
+
# and is used if no configuration is passed to the +initialize+
|
11
|
+
# method.
|
12
|
+
attr_accessor :config
|
13
|
+
end
|
14
|
+
|
15
|
+
# Configures the connection at the class level. When the +ceiling_cat+ bin
|
16
|
+
# file is loaded, it evals the file referenced by the first
|
17
|
+
# command-line parameter. This file can configure the connection
|
18
|
+
# instance later created by +robut+ by setting parameters in the
|
19
|
+
# CeilingCat::Connection.configure block.
|
20
|
+
def self.configure
|
21
|
+
self.config = OpenStruct.new
|
22
|
+
yield config
|
23
|
+
end
|
24
|
+
|
25
|
+
# Sets the instance config to +config+, converting it into an
|
26
|
+
# OpenStruct if necessary.
|
27
|
+
def config=(config)
|
28
|
+
@config = config.kind_of?(Hash) ? OpenStruct.new(config) : config
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(_config = nil)
|
32
|
+
self.config = _config || self.class.config
|
33
|
+
end
|
34
|
+
|
35
|
+
def connect
|
36
|
+
case self.config.service.downcase
|
37
|
+
when 'campfire'
|
38
|
+
connection = CeilingCat::Campfire::Connection.new(self.config)
|
39
|
+
room = CeilingCat::Campfire::Room.new(:connection => connection, :room_name => self.config.room)
|
40
|
+
room.watch
|
41
|
+
else
|
42
|
+
raise CeilingCat::UnsupportedChatServiceError.new("#{self.config.service} is not a supported chat service.")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
module Storage
|
3
|
+
class Base
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# Sets the key +k+ to the value +v+ in the current storage system
|
7
|
+
def []=(k,v)
|
8
|
+
raise NotImplementedError, "Implement in storage type file!"
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns the value at the key +k+.
|
12
|
+
def [](k)
|
13
|
+
raise NotImplementedError, "Implement in storage type file!"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module CeilingCat
|
4
|
+
module Storage
|
5
|
+
class Yaml < CeilingCat::Storage::Base
|
6
|
+
class << self
|
7
|
+
attr_reader :file
|
8
|
+
|
9
|
+
# Sets the path to the file this store will persist to, and forces
|
10
|
+
# a reload of all of the data.
|
11
|
+
def file=(f)
|
12
|
+
@file = f
|
13
|
+
@internal = nil # force reload
|
14
|
+
@file
|
15
|
+
end
|
16
|
+
|
17
|
+
# Sets the key +k+ to the value +v+
|
18
|
+
def []=(k, v)
|
19
|
+
internal[k] = v
|
20
|
+
persist!
|
21
|
+
v
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the value at the key +k+.
|
25
|
+
def [](k)
|
26
|
+
internal[k]
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# The internal in-memory representation of the yaml file
|
32
|
+
def internal
|
33
|
+
@internal ||= begin
|
34
|
+
YAML.load_file(file) rescue {}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Persists the data in this store to disk. Throws an exception if
|
39
|
+
# we don't have a file set.
|
40
|
+
def persist!
|
41
|
+
raise "CeilingCat::Storage::Yaml.file must be set" unless file
|
42
|
+
f = File.open(file, "w")
|
43
|
+
f.puts internal.to_yaml
|
44
|
+
f.close
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module CeilingCat
|
2
|
+
class User
|
3
|
+
attr_accessor :name, :role, :id
|
4
|
+
|
5
|
+
def initialize(name, opts={})
|
6
|
+
@name = name
|
7
|
+
@id = opts[:id]
|
8
|
+
@role = opts[:role]
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
short_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def short_name
|
16
|
+
@name.to_s.split.compact.first
|
17
|
+
end
|
18
|
+
|
19
|
+
def is_registered?
|
20
|
+
@role.to_s.downcase == 'member'
|
21
|
+
end
|
22
|
+
|
23
|
+
def is_guest?
|
24
|
+
@role.to_s.downcase == 'guest' || @type.to_s.nil?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/setup/Chatfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Require your plugins here
|
2
|
+
require 'ceiling_cat/plugins/about'
|
3
|
+
require 'ceiling_cat/plugins/calc'
|
4
|
+
require 'ceiling_cat/plugins/greeter'
|
5
|
+
|
6
|
+
CeilingCat::Setup.configure do |config|
|
7
|
+
config.service = 'campfire'
|
8
|
+
config.username = 'username'
|
9
|
+
config.token = '12345abcde'
|
10
|
+
config.room = 'Test Room'
|
11
|
+
config.ssl = true
|
12
|
+
|
13
|
+
config.plugins = [CeilingCat::Plugin::About,
|
14
|
+
CeilingCat::Plugin::Greeter,
|
15
|
+
CeilingCat::Plugin::Calc]
|
16
|
+
|
17
|
+
# Some plugins require storage
|
18
|
+
CeilingCat::Storage::Yaml.file = "ceilingcat.yml"
|
19
|
+
config.storage = CeilingCat::Storage::Yaml
|
20
|
+
end
|
data/setup/Rakefile
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
namespace :plugin do
|
2
|
+
desc "Create a new plugin"
|
3
|
+
task :create do
|
4
|
+
raise "You need to set a name! `rake plugin:create name=[plugin_name]`" unless ENV["name"]
|
5
|
+
name = ENV["name"].downcase
|
6
|
+
file = File.join("plugins", "#{name}.rb")
|
7
|
+
|
8
|
+
unless File.exists? file
|
9
|
+
contents = plugin_base(name)
|
10
|
+
File.open(file, 'w') {|f| f.write(contents) }
|
11
|
+
puts "Plugin '#{name}' created at #{file}."
|
12
|
+
puts "Make sure you require it in your Chatfile and add it to your config.plugins so it runs."
|
13
|
+
else
|
14
|
+
puts "A plugin named '#{name}' already exists. Try another name."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def plugin_base(name)
|
20
|
+
%Q{module CeilingCat
|
21
|
+
module Plugin
|
22
|
+
class #{name.camelize} < CeilingCat::Plugin::Base
|
23
|
+
# See lib/ceiling_cat/plugins/base.rb for the methods available by default.
|
24
|
+
# Plugins are run in the order they are listed in the Chatfile.
|
25
|
+
# When a plugin returns anything other than nil the plugin execution chain is halted.
|
26
|
+
# If you want your plugin to do something but let the remaining plugins execute, return nil at the end of your method.
|
27
|
+
|
28
|
+
# handle manages a plugin's entire interaction with an event.
|
29
|
+
# If you only want to execute commands - "![command]" - leave handle alone (or remove it and define commands below)
|
30
|
+
def handle
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
# If you want the plugin to run when certain text is sent use commands instead of handle.
|
35
|
+
# Ceiling Cat will watch for "![command]" or "[name]: [command" and execute the method for that command.
|
36
|
+
def self.commands
|
37
|
+
[{:command => "#{name}", :description => "Does something", :method => "#{name}", :public => false}]
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.description
|
41
|
+
"A plugin called #{name.capitalize}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.name
|
45
|
+
"#{name.gsub("_"," ").capitalize}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.public?
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def #{name}
|
53
|
+
reply "You've created the '#{name.capitalize}' plugin. Now make it do something awesome!"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
class String
|
62
|
+
def camelize
|
63
|
+
self.split("_").each{|z|z.capitalize!}.join("")
|
64
|
+
end
|
65
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ceiling_cat
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Chris Warren
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-08-18 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: tinder
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - "="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 15
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 4
|
32
|
+
- 4
|
33
|
+
version: 1.4.4
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: httparty
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - "="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 19
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
- 7
|
48
|
+
- 8
|
49
|
+
version: 0.7.8
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: crack
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - "="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 11
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
- 1
|
64
|
+
- 8
|
65
|
+
version: 0.1.8
|
66
|
+
type: :runtime
|
67
|
+
version_requirements: *id003
|
68
|
+
description: Ceiling Cat is watching you chat. A Campfire chat bot!
|
69
|
+
email:
|
70
|
+
- chris@zencoder.com
|
71
|
+
executables:
|
72
|
+
- ceiling_cat
|
73
|
+
extensions: []
|
74
|
+
|
75
|
+
extra_rdoc_files: []
|
76
|
+
|
77
|
+
files:
|
78
|
+
- lib/ceiling_cat.rb
|
79
|
+
- lib/ceiling_cat/setup.rb
|
80
|
+
- lib/ceiling_cat/version.rb
|
81
|
+
- lib/ceiling_cat/connection.rb
|
82
|
+
- lib/ceiling_cat/errors.rb
|
83
|
+
- lib/ceiling_cat/event.rb
|
84
|
+
- lib/ceiling_cat/user.rb
|
85
|
+
- lib/ceiling_cat/room.rb
|
86
|
+
- lib/ceiling_cat/storage/base.rb
|
87
|
+
- setup/Chatfile
|
88
|
+
- setup/Rakefile
|
89
|
+
- lib/ceiling_cat/plugins/base.rb
|
90
|
+
- lib/ceiling_cat/plugins/about.rb
|
91
|
+
- lib/ceiling_cat/plugins/calc.rb
|
92
|
+
- lib/ceiling_cat/plugins/call_and_response.rb
|
93
|
+
- lib/ceiling_cat/plugins/campfire_account_monitor.rb
|
94
|
+
- lib/ceiling_cat/plugins/days.rb
|
95
|
+
- lib/ceiling_cat/plugins/general.rb
|
96
|
+
- lib/ceiling_cat/plugins/greeter.rb
|
97
|
+
- lib/ceiling_cat/plugins/notifo.rb
|
98
|
+
- lib/ceiling_cat/storage/yaml.rb
|
99
|
+
- lib/ceiling_cat/services/campfire.rb
|
100
|
+
- lib/ceiling_cat/services/campfire/connection.rb
|
101
|
+
- lib/ceiling_cat/services/campfire/event.rb
|
102
|
+
- lib/ceiling_cat/services/campfire/room.rb
|
103
|
+
- bin/ceiling_cat
|
104
|
+
homepage: http://zencoder.com
|
105
|
+
licenses: []
|
106
|
+
|
107
|
+
post_install_message: " ********************************************************************************\n Run `ceiling_cat setup` to create a Chatfile and a Rakefile - everything you\n need to start watching your chats and making ceiling_cat do your bidding!\n \n Run `rake plugin:create name=plugin_name` to generate a new plugin.\n\n Update your Chatfile with your credentials and you'll be ready to go!\n ********************************************************************************\n"
|
108
|
+
rdoc_options: []
|
109
|
+
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
hash: 3
|
127
|
+
segments:
|
128
|
+
- 0
|
129
|
+
version: "0"
|
130
|
+
requirements: []
|
131
|
+
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 1.8.8
|
134
|
+
signing_key:
|
135
|
+
specification_version: 3
|
136
|
+
summary: Ceiling Cat is watching you chat. A Campfire chat bot.
|
137
|
+
test_files: []
|
138
|
+
|