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.
@@ -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
@@ -0,0 +1,6 @@
1
+
2
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
3
+
4
+ describe Firetower do
5
+ end
6
+
@@ -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