firetower 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/.bnsignore +18 -0
- data/History.txt +4 -0
- data/README.html +406 -0
- data/README.org +135 -0
- data/Rakefile +30 -0
- data/bin/firetower +336 -0
- data/example/bot.rb +8 -0
- data/images/BaldMountainLookout.jpg +0 -0
- data/images/campfire-logo-for-fluid.png +0 -0
- data/lib/firetower.rb +80 -0
- data/lib/firetower/account.rb +52 -0
- data/lib/firetower/firetower.conf.erb +22 -0
- data/lib/firetower/plugins/core/init_v1.rb +1 -0
- data/lib/firetower/plugins/core/notify_plugin.rb +42 -0
- data/lib/firetower/room.rb +15 -0
- data/lib/firetower/server.rb +62 -0
- data/lib/firetower/session.rb +104 -0
- data/spec/firetower_spec.rb +6 -0
- data/spec/spec_helper.rb +15 -0
- data/test/test_firetower.rb +0 -0
- data/version.txt +1 -0
- metadata +206 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
module Firetower
|
2
|
+
class Account
|
3
|
+
attr_reader :subdomain, :token, :session, :users
|
4
|
+
|
5
|
+
def initialize(subdomain, token, session, options = {})
|
6
|
+
@subdomain = subdomain
|
7
|
+
@token = token
|
8
|
+
@session = session
|
9
|
+
@ssl = options.fetch(:ssl) { false }
|
10
|
+
@users = Hash.new do |cache, user_id|
|
11
|
+
data = session.get(subdomain, "/users/#{user_id}.json")
|
12
|
+
cache[user_id] = data['user']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def rooms
|
17
|
+
return @rooms if defined?(@rooms)
|
18
|
+
@rooms = Hash.new do |h, k|
|
19
|
+
raise "No room named #{k} in #{subdomain}"
|
20
|
+
end
|
21
|
+
data = session.get(subdomain, "/rooms.json")
|
22
|
+
data['rooms'].each do |room_data|
|
23
|
+
room = Room.new(self, room_data)
|
24
|
+
@rooms[room.name] = room
|
25
|
+
end
|
26
|
+
@rooms
|
27
|
+
end
|
28
|
+
|
29
|
+
def say!(room_name, text)
|
30
|
+
room_id = rooms[room_name].id
|
31
|
+
session.post(subdomain, "/room/#{room_id}/speak.json", {
|
32
|
+
'message' => {
|
33
|
+
'body' => text
|
34
|
+
}
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
def paste!(room_name, text)
|
39
|
+
room_id = rooms[room_name].id
|
40
|
+
session.post(subdomain, "/room/#{room_id}/speak.json", {
|
41
|
+
'message' => {
|
42
|
+
'body' => text,
|
43
|
+
'type' => 'PasteMessage'
|
44
|
+
}
|
45
|
+
})
|
46
|
+
end
|
47
|
+
|
48
|
+
def ssl?
|
49
|
+
@ssl
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# <%= program %> configuration initially generated <%= Time.now %>
|
2
|
+
|
3
|
+
# Go ahead and customize this file to your heart's content! The content is Ruby
|
4
|
+
# code, and all code is evaluated in the context of a Firetower::Session object.
|
5
|
+
|
6
|
+
# CONNECTIONS
|
7
|
+
|
8
|
+
# You can connect to as many Campfire accounts as you like :-)
|
9
|
+
account '<%= subdomain %>', '<%= token %>', :ssl => <%= use_ssl %>
|
10
|
+
|
11
|
+
# ROOMS
|
12
|
+
# Activity in these rooms will tracked by the <%= program %> server
|
13
|
+
|
14
|
+
<% room_names.each do |room_name| %>
|
15
|
+
join '<%= subdomain %>', '<%= room_name %>'
|
16
|
+
<% end %>
|
17
|
+
|
18
|
+
# DEFAULT ROOM
|
19
|
+
default '<%= subdomain %>', '<%= room_names.first %>'
|
20
|
+
|
21
|
+
# Plugins
|
22
|
+
use NotifyPlugin
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'firetower/plugins/core/notify_plugin.rb'
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Firetower
|
2
|
+
module Plugins
|
3
|
+
class NotifyPlugin < Firetower::Session::Listener
|
4
|
+
|
5
|
+
def startup(session)
|
6
|
+
if session.kind == :server
|
7
|
+
notify("Firetower", "Firetower is vigilant!")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def join(session, room)
|
12
|
+
notify("Firetower", "Joined room \"#{room.name}\"")
|
13
|
+
end
|
14
|
+
|
15
|
+
def receive(session, event)
|
16
|
+
case event['type']
|
17
|
+
when "TextMessage"
|
18
|
+
user = event.room.account.users[event['user_id']]
|
19
|
+
notify(user['name'], event['body'])
|
20
|
+
else
|
21
|
+
# NOOP
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def error(session, description)
|
26
|
+
notify("Campfire Error", description)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def notify(*args)
|
32
|
+
system('notify-send', '--icon',
|
33
|
+
File.expand_path(
|
34
|
+
'../images/campfire-logo-for-fluid.png',
|
35
|
+
File.dirname(__FILE__)),
|
36
|
+
'-c', 'Firetower',
|
37
|
+
*args) or raise "Desktop notification failed"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Firetower
|
2
|
+
class Room
|
3
|
+
attr_reader :id, :name, :account
|
4
|
+
|
5
|
+
def initialize(account, attributes={})
|
6
|
+
@account = account
|
7
|
+
@id = attributes['id']
|
8
|
+
@name = attributes['name']
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"#{account.subdomain}/#{id} (#{name})"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Firetower
|
2
|
+
class Server
|
3
|
+
attr_reader :session, :log_path, :pid_path
|
4
|
+
|
5
|
+
def initialize(session, options={})
|
6
|
+
@session = session
|
7
|
+
@log_path = Pathname(options.fetch(:log_path))
|
8
|
+
@pid_path = Pathname(options.fetch(:pid_path))
|
9
|
+
@logger = options.fetch(:logger) {
|
10
|
+
::Logger.new(@log_path, 4, 1024000)
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
trap('INT') do
|
16
|
+
@logger.info "Received INT; shutting down."
|
17
|
+
EventMachine.stop_event_loop
|
18
|
+
@pid_path.unlink if @pid_path.exist?
|
19
|
+
end
|
20
|
+
EventMachine::run do
|
21
|
+
@logger.info "Firetower is starting up"
|
22
|
+
open(@pid_path, 'w+') do |pid_file|
|
23
|
+
pid_file.puts $$
|
24
|
+
end
|
25
|
+
@session.subscribed_rooms.each do |room|
|
26
|
+
subscribe_to_room(room)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
session.close!
|
30
|
+
end
|
31
|
+
|
32
|
+
def subscribe_to_room(room)
|
33
|
+
@logger.info "Subscribing to #{room}"
|
34
|
+
stream = Twitter::JSONStream.connect(
|
35
|
+
:path => "/room/#{room.id}/live.json",
|
36
|
+
:host => 'streaming.campfirenow.com',
|
37
|
+
:auth => "#{room.account.token}:x")
|
38
|
+
|
39
|
+
stream.each_item do |event|
|
40
|
+
@logger.info "Processing event:\n #{event}"
|
41
|
+
event = JSON.parse(event)
|
42
|
+
|
43
|
+
(class << event; self; end).send(:define_method, :room){room}
|
44
|
+
session.execute_hook(:receive, session, event)
|
45
|
+
end
|
46
|
+
|
47
|
+
stream.on_error do |message|
|
48
|
+
@logger.error message
|
49
|
+
session.execute_hook(:error, session, message)
|
50
|
+
end
|
51
|
+
|
52
|
+
stream.on_max_reconnects do |timeout, retries|
|
53
|
+
@logger.error "Unable to connect after #{retries} attempts"
|
54
|
+
session.execute_hook(:error, session,
|
55
|
+
"Unable to connect after #{retries} attempts")
|
56
|
+
stop_event_loop
|
57
|
+
end
|
58
|
+
session.execute_hook(:join, session, room)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Firetower
|
2
|
+
module Plugins
|
3
|
+
end
|
4
|
+
|
5
|
+
class Session
|
6
|
+
include ::Firetower::Plugins
|
7
|
+
|
8
|
+
include HookR::Hooks
|
9
|
+
|
10
|
+
attr_reader :accounts
|
11
|
+
attr_reader :connections
|
12
|
+
attr_reader :subscribed_rooms
|
13
|
+
attr_reader :kind
|
14
|
+
attr_reader :default_room
|
15
|
+
|
16
|
+
attr_accessor :logger
|
17
|
+
|
18
|
+
define_hook :startup, :session
|
19
|
+
define_hook :connect, :session, :account
|
20
|
+
define_hook :join, :session, :room
|
21
|
+
define_hook :receive, :session, :event
|
22
|
+
define_hook :error, :session, :description
|
23
|
+
define_hook :leave, :session, :room
|
24
|
+
define_hook :disconnect, :session, :account
|
25
|
+
define_hook :shutdown, :session
|
26
|
+
|
27
|
+
def initialize(kind=:command, options={})
|
28
|
+
@accounts = Hash.new do |hash, subdomain|
|
29
|
+
raise "Unknown subdomain '#{subdomain}'"
|
30
|
+
end
|
31
|
+
@connections = Hash.new do |hash, subdomain|
|
32
|
+
connection =
|
33
|
+
if accounts[subdomain].ssl?
|
34
|
+
connection = Net::HTTP.new("#{subdomain}.campfirenow.com", Net::HTTP.https_default_port)
|
35
|
+
connection.use_ssl = true
|
36
|
+
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
37
|
+
connection
|
38
|
+
else
|
39
|
+
Net::HTTP.new("#{subdomain}.campfirenow.com", Net::HTTP.http_default_port)
|
40
|
+
end
|
41
|
+
hash[subdomain] = connection
|
42
|
+
end
|
43
|
+
@subscribed_rooms = []
|
44
|
+
@kind = kind
|
45
|
+
@logger = options.fetch(:logger) { ::Logger.new($stderr) }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Declare an account
|
49
|
+
def account(subdomain, token, options={})
|
50
|
+
accounts[subdomain] = Account.new(subdomain, token, self, options)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Enable a plugin or extension
|
54
|
+
def use(class_or_instance, *args)
|
55
|
+
case class_or_instance
|
56
|
+
when Class then add_listener(class_or_instance.new(*args))
|
57
|
+
else add_listener(class_or_instance)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def join(subdomain, room_name)
|
62
|
+
@subscribed_rooms << accounts[subdomain].rooms[room_name]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Set default room
|
66
|
+
def default(subdomain, room_name)
|
67
|
+
@default_room = accounts[subdomain].rooms[room_name]
|
68
|
+
end
|
69
|
+
|
70
|
+
def default_room
|
71
|
+
@default_room ||= @subscribed_rooms.first
|
72
|
+
end
|
73
|
+
|
74
|
+
def post(subdomain, path, data)
|
75
|
+
request = Net::HTTP::Post.new(path)
|
76
|
+
request.body = data.to_json
|
77
|
+
request['Content-Type'] = 'application/json'
|
78
|
+
perform_request(subdomain, request)
|
79
|
+
end
|
80
|
+
|
81
|
+
def get(subdomain, path)
|
82
|
+
request = Net::HTTP::Get.new(path)
|
83
|
+
request['Accept'] = 'application/json'
|
84
|
+
response = perform_request(subdomain, request)
|
85
|
+
JSON.parse(response.body)
|
86
|
+
end
|
87
|
+
|
88
|
+
def perform_request(subdomain, request)
|
89
|
+
request.basic_auth(accounts[subdomain].token, '')
|
90
|
+
response = connections[subdomain].request(request)
|
91
|
+
case response
|
92
|
+
when Net::HTTPSuccess
|
93
|
+
response
|
94
|
+
when Net::HTTPClientError, Net::HTTPServerError
|
95
|
+
raise "Error (#{response.message}): \n#{JSON.parse(response.body).to_yaml}"
|
96
|
+
end
|
97
|
+
response
|
98
|
+
end
|
99
|
+
|
100
|
+
def close!
|
101
|
+
# TODO
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
require File.expand_path(
|
3
|
+
File.join(File.dirname(__FILE__), %w[.. lib firetower]))
|
4
|
+
|
5
|
+
Spec::Runner.configure do |config|
|
6
|
+
# == Mock Framework
|
7
|
+
#
|
8
|
+
# RSpec uses it's own mocking framework by default. If you prefer to
|
9
|
+
# use mocha, flexmock or RR, uncomment the appropriate line:
|
10
|
+
#
|
11
|
+
# config.mock_with :mocha
|
12
|
+
# config.mock_with :flexmock
|
13
|
+
# config.mock_with :rr
|
14
|
+
end
|
15
|
+
|
File without changes
|
data/version.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
metadata
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: firetower
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Avdi Grimm
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-07-12 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: twitter-stream
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
- 1
|
30
|
+
- 6
|
31
|
+
version: 0.1.6
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: eventmachine
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
- 12
|
44
|
+
- 10
|
45
|
+
version: 0.12.10
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: json
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 1
|
57
|
+
- 4
|
58
|
+
version: "1.4"
|
59
|
+
type: :runtime
|
60
|
+
version_requirements: *id003
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: addressable
|
63
|
+
prerelease: false
|
64
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 2
|
70
|
+
- 1
|
71
|
+
version: "2.1"
|
72
|
+
type: :runtime
|
73
|
+
version_requirements: *id004
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: main
|
76
|
+
prerelease: false
|
77
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ~>
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
segments:
|
82
|
+
- 4
|
83
|
+
- 2
|
84
|
+
version: "4.2"
|
85
|
+
type: :runtime
|
86
|
+
version_requirements: *id005
|
87
|
+
- !ruby/object:Gem::Dependency
|
88
|
+
name: servolux
|
89
|
+
prerelease: false
|
90
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ~>
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
- 9
|
97
|
+
- 4
|
98
|
+
version: 0.9.4
|
99
|
+
type: :runtime
|
100
|
+
version_requirements: *id006
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: hookr
|
103
|
+
prerelease: false
|
104
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ~>
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
segments:
|
109
|
+
- 1
|
110
|
+
- 0
|
111
|
+
version: "1.0"
|
112
|
+
type: :runtime
|
113
|
+
version_requirements: *id007
|
114
|
+
- !ruby/object:Gem::Dependency
|
115
|
+
name: highline
|
116
|
+
prerelease: false
|
117
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ~>
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
segments:
|
122
|
+
- 1
|
123
|
+
- 5
|
124
|
+
version: "1.5"
|
125
|
+
type: :runtime
|
126
|
+
version_requirements: *id008
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: bones
|
129
|
+
prerelease: false
|
130
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
segments:
|
135
|
+
- 3
|
136
|
+
- 4
|
137
|
+
- 7
|
138
|
+
version: 3.4.7
|
139
|
+
type: :development
|
140
|
+
version_requirements: *id009
|
141
|
+
description: ""
|
142
|
+
email: avdi@avdi.org
|
143
|
+
executables:
|
144
|
+
- firetower
|
145
|
+
extensions: []
|
146
|
+
|
147
|
+
extra_rdoc_files:
|
148
|
+
- History.txt
|
149
|
+
- bin/firetower
|
150
|
+
- lib/firetower/firetower.conf.erb
|
151
|
+
- version.txt
|
152
|
+
files:
|
153
|
+
- .bnsignore
|
154
|
+
- History.txt
|
155
|
+
- README.html
|
156
|
+
- README.org
|
157
|
+
- Rakefile
|
158
|
+
- bin/firetower
|
159
|
+
- example/bot.rb
|
160
|
+
- images/BaldMountainLookout.jpg
|
161
|
+
- images/campfire-logo-for-fluid.png
|
162
|
+
- lib/firetower.rb
|
163
|
+
- lib/firetower/account.rb
|
164
|
+
- lib/firetower/firetower.conf.erb
|
165
|
+
- lib/firetower/plugins/core/init_v1.rb
|
166
|
+
- lib/firetower/plugins/core/notify_plugin.rb
|
167
|
+
- lib/firetower/room.rb
|
168
|
+
- lib/firetower/server.rb
|
169
|
+
- lib/firetower/session.rb
|
170
|
+
- spec/firetower_spec.rb
|
171
|
+
- spec/spec_helper.rb
|
172
|
+
- test/test_firetower.rb
|
173
|
+
- version.txt
|
174
|
+
has_rdoc: true
|
175
|
+
homepage: http://github.com/avdi/firetower
|
176
|
+
licenses: []
|
177
|
+
|
178
|
+
post_install_message:
|
179
|
+
rdoc_options:
|
180
|
+
- --main
|
181
|
+
- README.org
|
182
|
+
require_paths:
|
183
|
+
- lib
|
184
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
segments:
|
189
|
+
- 0
|
190
|
+
version: "0"
|
191
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
segments:
|
196
|
+
- 0
|
197
|
+
version: "0"
|
198
|
+
requirements: []
|
199
|
+
|
200
|
+
rubyforge_project: firetower
|
201
|
+
rubygems_version: 1.3.6
|
202
|
+
signing_key:
|
203
|
+
specification_version: 3
|
204
|
+
summary: A command-line interface to Campfire chats
|
205
|
+
test_files:
|
206
|
+
- test/test_firetower.rb
|