lita-sonos-commander 1.0.0 → 1.1.0
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.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8df08a9fd7ffbbe94ff51a5cc8ffbb77c26ab062
|
4
|
+
data.tar.gz: 6ce9bbb507eeec26eb03b8dc5715393762e487c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9699b15935e3210d61dcc4414097d3a176de14674ec8fbe5849ece61e0db6b751636a006ec71890d58e4126a21f9ac7dc1603ac6fb1d1a5bcb44f4290191310
|
7
|
+
data.tar.gz: 5ee1e48df4353a064d6edde6166de70ff064891f7fd1435ce653f4ceaae92b1b579de7f8a39ce1bebd83f007fb2ca69e0d1465288b17faaa91c692bb2bb509f9
|
@@ -1,47 +1,58 @@
|
|
1
|
-
Lita::CommanderMiddleware
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
Lita::Handlers::SonosCommander.add_socket(ws)
|
1
|
+
class Lita::CommanderMiddleware
|
2
|
+
def self.build(open_sockets:)
|
3
|
+
new.build open_sockets: open_sockets
|
4
|
+
end
|
7
5
|
|
8
|
-
|
6
|
+
attr_reader :env, :open_sockets
|
7
|
+
|
8
|
+
def build(open_sockets:)
|
9
|
+
@open_sockets = open_sockets
|
10
|
+
|
11
|
+
return lambda do |env|
|
12
|
+
if Faye::WebSocket.websocket?(env)
|
13
|
+
@env = env
|
14
|
+
handle_env_has_socket
|
15
|
+
else
|
16
|
+
[
|
17
|
+
200,
|
18
|
+
{ 'Content-Type' => 'text/plain' },
|
19
|
+
['Hello from a Lita chatbot! Feed me a websocket connection!']
|
20
|
+
]
|
21
|
+
end
|
9
22
|
end
|
23
|
+
end
|
10
24
|
|
11
|
-
|
12
|
-
|
25
|
+
def build_socket(env)
|
26
|
+
ws = Faye::WebSocket.new(env)
|
27
|
+
open_sockets << ws
|
28
|
+
Lita.logger.debug "Sonos client count: #{open_sockets.count}"
|
29
|
+
ws
|
30
|
+
end
|
13
31
|
|
14
|
-
|
15
|
-
|
32
|
+
def close_socket(ws)
|
33
|
+
open_sockets.delete_if { |s| s == ws }
|
34
|
+
Lita.logger.debug "Sonos client count: #{open_sockets.count}"
|
35
|
+
p [:close, event.code, event.reason]
|
36
|
+
ws = nil
|
37
|
+
end
|
16
38
|
|
17
|
-
|
18
|
-
|
19
|
-
|
39
|
+
def handle_message(ws, event)
|
40
|
+
ws.send({ message: event.data }.to_json)
|
41
|
+
maybe_send_debug_message(ws)
|
42
|
+
end
|
20
43
|
|
21
|
-
|
22
|
-
|
44
|
+
def maybe_send_debug_message(ws)
|
45
|
+
sleep 0.5
|
46
|
+
ws.send({ message: 'WE DID IT TWITCH', command: 'echo' }.to_json)
|
47
|
+
end
|
23
48
|
|
24
|
-
|
25
|
-
|
26
|
-
|
49
|
+
def handle_env_has_socket
|
50
|
+
ws = build_socket(env)
|
51
|
+
|
52
|
+
ws.on(:message) { |event| handle_message(ws, event) }
|
53
|
+
ws.on(:close) { |event| close_socket(ws) }
|
27
54
|
|
28
55
|
# Return async Rack response
|
29
56
|
ws.rack_response
|
30
|
-
|
31
|
-
else
|
32
|
-
# Normal HTTP request
|
33
|
-
[200, {'Content-Type' => 'text/plain'}, ['Hello']]
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def register_faye(arg)
|
38
|
-
@@_sockets ||= []
|
39
|
-
middleware = robot.registry.config.http.middleware
|
40
|
-
result = middleware.use App
|
41
|
-
end
|
42
|
-
|
43
|
-
def sonos_connector(request, response)
|
44
|
-
middlewares.each do |mw|
|
45
|
-
mw.middleware.call(request.env)
|
46
57
|
end
|
47
58
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'pry'
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
require 'faye/websocket'
|
3
5
|
require 'uri'
|
@@ -6,70 +8,93 @@ module Lita
|
|
6
8
|
module Handlers
|
7
9
|
class SonosCommander < Handler
|
8
10
|
|
9
|
-
|
11
|
+
# START:socket_registry
|
12
|
+
@_sockets ||= []
|
13
|
+
|
14
|
+
def self.sockets
|
15
|
+
@_sockets
|
16
|
+
end
|
17
|
+
|
18
|
+
def sockets
|
19
|
+
self.class.sockets
|
20
|
+
end
|
21
|
+
# END:socket_registry
|
22
|
+
|
23
|
+
# START:routing
|
24
|
+
http.get '/sonos/listen', :websocket_creator
|
10
25
|
|
11
|
-
route
|
12
|
-
route
|
26
|
+
route(/^play url (http.+)/i, :handle_sonos_play_url)
|
27
|
+
route(/^speak words (.+)/i, :handle_sonos_say_text)
|
13
28
|
|
14
29
|
on :loaded, :register_faye
|
30
|
+
# END:routing
|
15
31
|
|
16
|
-
|
17
|
-
|
18
|
-
|
32
|
+
# START:create_sockets
|
33
|
+
def websocket_creator(request, _response)
|
34
|
+
# could probably skip straight to the Commander middleware
|
35
|
+
# but it's cleaner to leave them all in play.
|
36
|
+
|
37
|
+
middleware_registry.each do |mw|
|
38
|
+
mw.middleware.call(request.env)
|
39
|
+
end
|
19
40
|
end
|
41
|
+
# END:create_sockets
|
42
|
+
|
43
|
+
# START:chat_handlers
|
44
|
+
def handle_sonos_play_url(message)
|
45
|
+
return message.reply('No clients found!') unless sockets.any?
|
20
46
|
|
21
|
-
def sonos_say_text(message)
|
22
47
|
text = message.matches.last.last
|
23
|
-
|
24
|
-
end
|
48
|
+
play_url message.matches.last.last
|
25
49
|
|
26
|
-
|
27
|
-
puts "emitting #{command} \t #{data}"
|
28
|
-
sockets.each do |ws|
|
29
|
-
ws.send(
|
30
|
-
{ command: command, data: { text: data, volume: 20 } }.to_json
|
31
|
-
)
|
32
|
-
end
|
50
|
+
message.reply "Command sent: [play_url] #{text}!"
|
33
51
|
end
|
34
52
|
|
35
|
-
def
|
36
|
-
|
53
|
+
def play_url(url)
|
54
|
+
emit_message command: 'play_url', data: URI.escape(url)
|
37
55
|
end
|
38
56
|
|
39
|
-
def
|
40
|
-
|
41
|
-
end
|
57
|
+
def handle_sonos_say_text(message)
|
58
|
+
return message.reply('No clients found!') unless sockets.any?
|
42
59
|
|
43
|
-
|
44
|
-
|
45
|
-
end
|
60
|
+
text = message.matches.last.last
|
61
|
+
say_text text
|
46
62
|
|
47
|
-
|
48
|
-
puts "Tracking socket #{socket}"
|
49
|
-
@@_sockets ||= []
|
50
|
-
@@_sockets << socket
|
63
|
+
message.reply "Command sent: [play_text] #{text}!"
|
51
64
|
end
|
52
65
|
|
53
|
-
def
|
54
|
-
|
55
|
-
sockets.delete_if { |s| s == socket }
|
66
|
+
def say_text(text)
|
67
|
+
emit_message command: 'play_text', data: text
|
56
68
|
end
|
69
|
+
# END:chat_handlers
|
57
70
|
|
58
|
-
|
71
|
+
# START:faye_hookup
|
72
|
+
def register_faye(_arg)
|
73
|
+
socket_manager = Lita::CommanderMiddleware.build(open_sockets: sockets)
|
74
|
+
middleware_registry.use socket_manager
|
59
75
|
end
|
60
76
|
|
61
|
-
def
|
62
|
-
|
63
|
-
middleware = robot.registry.config.http.middleware
|
64
|
-
result = middleware.use Lita::CommanderMiddleware
|
77
|
+
def middleware_registry
|
78
|
+
robot.registry.config.http.middleware
|
65
79
|
end
|
80
|
+
# END:faye_hookup
|
66
81
|
|
67
|
-
|
68
|
-
|
69
|
-
|
82
|
+
# START:message_emission
|
83
|
+
def emit_message(command:, data:)
|
84
|
+
puts "emitting #{command} \t #{data}"
|
85
|
+
|
86
|
+
sockets.each do |ws|
|
87
|
+
ws.send serialize(command: command, text: data)
|
70
88
|
end
|
71
89
|
end
|
72
90
|
|
91
|
+
def serialize(command:, text:)
|
92
|
+
{
|
93
|
+
command: command,
|
94
|
+
data: { text: text, volume: 20 }
|
95
|
+
}.to_json
|
96
|
+
end
|
97
|
+
# END:message_emission
|
73
98
|
|
74
99
|
Lita.register_handler(self)
|
75
100
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = 'lita-sonos-commander'
|
3
|
-
spec.version = '1.
|
3
|
+
spec.version = '1.1.0'
|
4
4
|
spec.authors = ['Daniel J. Pritchett']
|
5
5
|
spec.email = ['dpritchett@gmail.com']
|
6
6
|
spec.description = 'Control your Sonos with Lita chatbot commands'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
describe Lita::CommanderMiddleware do
|
5
|
+
let(:handler) { double 'handler' }
|
6
|
+
let(:result) { subject.build({}) }
|
7
|
+
|
8
|
+
it 'returns a lambda' do
|
9
|
+
result = subject.class.build(open_sockets: [])
|
10
|
+
expect(result.is_a?(Proc)).to be_truthy
|
11
|
+
end
|
12
|
+
end
|
@@ -11,51 +11,104 @@ describe Lita::Handlers::SonosCommander, lita_handler: true do
|
|
11
11
|
describe 'routes' do
|
12
12
|
it {
|
13
13
|
is_expected.to route_http(:get, '/sonos/listen')
|
14
|
-
.to(:
|
14
|
+
.to(:websocket_creator)
|
15
15
|
}
|
16
16
|
|
17
|
-
it {
|
18
|
-
.to(
|
17
|
+
it {
|
18
|
+
is_expected.to(route('Lita play url http://zombo.com')
|
19
|
+
.to(:handle_sonos_play_url))
|
20
|
+
}
|
21
|
+
it {
|
22
|
+
is_expected.to(route('Lita play url https://www.youtube.com/watch?v=dQw4w9WgXcQ')
|
23
|
+
.to(:handle_sonos_play_url))
|
24
|
+
}
|
25
|
+
it {
|
26
|
+
is_expected.to(route('Lita speak words i like turtles')
|
27
|
+
.to(:handle_sonos_say_text))
|
28
|
+
}
|
29
|
+
|
30
|
+
it { is_expected.to route_event(:loaded).to(:register_faye) }
|
19
31
|
end
|
20
32
|
# END:routes
|
21
33
|
|
22
|
-
describe '
|
23
|
-
|
24
|
-
|
34
|
+
describe 'sending messages to clients' do
|
35
|
+
let(:client) { double('socket client') }
|
36
|
+
before { subject.stub(:sockets).and_return [client] }
|
37
|
+
|
38
|
+
it 'should :send some commanding json to the client' do
|
39
|
+
expect(client).to receive(:send).with(/play_url.+http.+volume/)
|
40
|
+
|
41
|
+
subject.play_url 'http://bana.nas'
|
25
42
|
end
|
26
|
-
end
|
27
|
-
# START:save_message
|
28
|
-
describe ':save_message' do
|
29
|
-
let(:body) { 'hello, alexa!' }
|
30
|
-
it 'saves a message and acknowledges' do
|
31
|
-
result = subject.save_message(username: 'dpritchett', message: body)
|
32
43
|
|
33
|
-
|
44
|
+
it 'should :send some commanding json to the client' do
|
45
|
+
expect(client).to receive(:send).with(/play_text.+turtles.+volume/)
|
46
|
+
|
47
|
+
subject.say_text 'i like turtles'
|
34
48
|
end
|
35
49
|
|
36
|
-
it
|
50
|
+
it 'can grab a message from chat and store it' do
|
51
|
+
send_message 'lita speak words i like turtles'
|
52
|
+
response = replies.last
|
53
|
+
|
54
|
+
expect(response).to match(/No clients found/i)
|
55
|
+
end
|
37
56
|
end
|
38
|
-
# END:save_message
|
39
57
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
58
|
+
describe 'socket registry' do
|
59
|
+
let(:sockets) { subject.sockets }
|
60
|
+
it 'has a socket registry shared amongst instances of the handler' do
|
61
|
+
sockets << 'banana'
|
62
|
+
another_one = Lita::Handlers::SonosCommander.new({})
|
63
|
+
|
64
|
+
expect(another_one.sockets).to include('banana')
|
44
65
|
end
|
45
66
|
|
46
|
-
it '
|
47
|
-
|
48
|
-
|
67
|
+
it 'allows addition and deletion to the socket registry via array methods' do
|
68
|
+
canary = rand(10_000).to_s
|
69
|
+
sockets << canary
|
70
|
+
expect(sockets).to include(canary)
|
71
|
+
|
72
|
+
sockets.delete_if { |socket| socket.eql? canary }
|
73
|
+
expect(sockets).to_not include(canary)
|
49
74
|
end
|
50
75
|
end
|
51
|
-
# END:alexify
|
52
76
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
77
|
+
describe 'text serializer' do
|
78
|
+
it 'creates a json payload' do
|
79
|
+
result = subject.serialize(command: 'phone_call', text: 'call your mother')
|
80
|
+
|
81
|
+
deserialized = JSON.parse(result)
|
82
|
+
|
83
|
+
expect(deserialized.fetch('command')).to eq 'phone_call'
|
84
|
+
expect(deserialized.dig('data', 'volume')).to_not be_falsey
|
85
|
+
end
|
58
86
|
end
|
59
87
|
|
60
|
-
|
88
|
+
describe 'socket middleware registration' do
|
89
|
+
let(:middlewares) { double 'middlewares' }
|
90
|
+
before { subject.stub(:middleware_registry).and_return(middlewares) }
|
91
|
+
|
92
|
+
let(:commander) { double 'commander' }
|
93
|
+
before { Lita::CommanderMiddleware.stub(:build).and_return(commander) }
|
94
|
+
|
95
|
+
it 'should register the commander middleware' do
|
96
|
+
expect(middlewares).to receive(:use).with(commander)
|
97
|
+
|
98
|
+
subject.register_faye(nil)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe ':websocket_creator' do
|
103
|
+
let(:request) { double 'request' }
|
104
|
+
let(:request_env) { double 'request' }
|
105
|
+
let(:middleware) { double 'middleware' }
|
106
|
+
|
107
|
+
before { subject.stub_chain(:middleware_registry, :each).and_return [middleware] }
|
108
|
+
before { request.stub(:env).and_return(request_env) }
|
61
109
|
|
110
|
+
it 'passes incoming any request environments to registered middlewares' do
|
111
|
+
subject.websocket_creator(request, nil)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lita-sonos-commander
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel J. Pritchett
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: lita
|
@@ -156,6 +156,7 @@ files:
|
|
156
156
|
- lib/lita/handlers/sonos_commander.rb
|
157
157
|
- lita-sonos-commander.gemspec
|
158
158
|
- locales/en.yml
|
159
|
+
- spec/lita/commander_middleware_spec.rb
|
159
160
|
- spec/lita/handlers/sonos_commander_spec.rb
|
160
161
|
- spec/spec_helper.rb
|
161
162
|
homepage: https://github.com/dpritchett/lita-sonos-commander
|
@@ -184,5 +185,6 @@ signing_key:
|
|
184
185
|
specification_version: 4
|
185
186
|
summary: Control your Sonos with Lita chatbot commands
|
186
187
|
test_files:
|
188
|
+
- spec/lita/commander_middleware_spec.rb
|
187
189
|
- spec/lita/handlers/sonos_commander_spec.rb
|
188
190
|
- spec/spec_helper.rb
|