lita-sonos-commander 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 71a47b802acd8b74a12165728bd3b0fded035ab5
4
- data.tar.gz: 49ecca4439098ff7827f4d2b6ac78ddde816e4e3
3
+ metadata.gz: 8df08a9fd7ffbbe94ff51a5cc8ffbb77c26ab062
4
+ data.tar.gz: 6ce9bbb507eeec26eb03b8dc5715393762e487c6
5
5
  SHA512:
6
- metadata.gz: dae726c2765371f5459269bc7c95a6468f224d32cf0f64de0656c932241e3bc24c36028959114135512adcfa876a26ed2d390fb85acd500fe9bb22f8f9f106fd
7
- data.tar.gz: 4d22468f6a65ea06c68d426628d1908d68c53606c2cbc301ad0b26e3f6e776593a55099ce71eeea8d04e518a09f0e5dd016d8d1784720d4aa0e6353f6e5a926d
6
+ metadata.gz: e9699b15935e3210d61dcc4414097d3a176de14674ec8fbe5849ece61e0db6b751636a006ec71890d58e4126a21f9ac7dc1603ac6fb1d1a5bcb44f4290191310
7
+ data.tar.gz: 5ee1e48df4353a064d6edde6166de70ff064891f7fd1435ce653f4ceaae92b1b579de7f8a39ce1bebd83f007fb2ca69e0d1465288b17faaa91c692bb2bb509f9
@@ -1,47 +1,58 @@
1
- Lita::CommanderMiddleware = lambda do |env|
2
-
3
- if Faye::WebSocket.websocket?(env)
4
- ws = Faye::WebSocket.new(env)
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
- ws.on :open do |event|
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
- ws.on :connect do |event|
12
- end
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
- ws.on :message do |event|
15
- ws.send({ message: event.data }.to_json)
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
- sleep 0.5
18
- ws.send({ message: 'WE DID IT TWITCH', command: 'echo' }.to_json)
19
- end
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
- ws.on :close do |event|
22
- Lita::Handlers::SonosCommander.drop_socket(ws)
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
- p [:close, event.code, event.reason]
25
- ws = nil
26
- end
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
- http.get '/sonos/listen', :sonos_connector
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 /^play_url (.+)/, :sonos_play_url
12
- route /^say_text (.+)/, :sonos_say_text
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
- def sonos_play_url(message)
17
- text = message.matches.last.last
18
- emit_message command: 'play_url', data: URI.escape(text)
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
- emit_message command: 'play_text', data: text
24
- end
48
+ play_url message.matches.last.last
25
49
 
26
- def emit_message(command:, data:)
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 middlewares
36
- robot.registry.config.http.middleware
53
+ def play_url(url)
54
+ emit_message command: 'play_url', data: URI.escape(url)
37
55
  end
38
56
 
39
- def sockets
40
- self.class.sockets
41
- end
57
+ def handle_sonos_say_text(message)
58
+ return message.reply('No clients found!') unless sockets.any?
42
59
 
43
- def self.sockets
44
- @@_sockets ||= []
45
- end
60
+ text = message.matches.last.last
61
+ say_text text
46
62
 
47
- def self.add_socket(socket)
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 self.drop_socket(socket)
54
- puts "Forgetting socket #{socket}"
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
- def self.serialize(message)
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 register_faye(arg)
62
- @@_sockets ||= []
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
- def sonos_connector(request, response)
68
- middlewares.each do |mw|
69
- mw.middleware.call(request.env)
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.0.0'
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(:sonos_connector)
14
+ .to(:websocket_creator)
15
15
  }
16
16
 
17
- it { is_expected.to route('Lita sonos alpha bravo')
18
- .to(:send_to_sonos) }
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 'exploratory' do
23
- it "let's play" do
24
- subject.explorer
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
- expect(result.fetch(:message)).to eq body
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 { is_expected.to route_event(:save_alexa_message).to(:save_message) }
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
- # START:alexify
41
- describe ':alexify' do
42
- let(:message) do
43
- subject.save_message username: 'daniel', message: 'test message'
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 'should return a hash with an alexa-specific shape' do
47
- result = subject.alexify(message)
48
- expect(result.fetch(:mainText)).to eq('test message')
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
- it 'can grab a message from chat and store it' do
54
- send_message "lita newsfeed hello there #{DateTime.now}"
55
- response = replies.last
56
- expect(response =~ /hello there/i).to be_truthy
57
- expect(response =~ /Saved message for Alexa/).to be_truthy
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
- end
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.0.0
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-26 00:00:00.000000000 Z
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