Skip to content

Commit

Permalink
Merge pull request #192 from plivo/VT-4683
Browse files Browse the repository at this point in the history
VT-4683 (Audio Streaming)
  • Loading branch information
abhishekGupta-Plivo authored Jun 29, 2023
2 parents 21d7bc6 + 37a38d3 commit af1a0b5
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 9 deletions.
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change Log

## [4.46.0](https://github.com/plivo/plivo-go/tree/v4.46.0) (2023-06-28)
**Feature - Audio Streaming**
- Added functionality to start, stop and fetch audio streams
- Added functionality to create stream XML

## [4.45.0](https://github.com/plivo/plivo-ruby/tree/v4.45.0) (2023-05-02)
**Feature - CNAM Lookup**
- Added New Param `cnam_lookup` in to the response of the [list all numbers API], [list single number API]
Expand Down Expand Up @@ -61,7 +66,6 @@
**Adding new attribute - 'message_expiry' in Send Message API**
- Added new attribute - message_expiry in Send Message API


## [4.34.0](https://github.com/plivo/plivo-ruby/tree/v4.34.0) (2022-12-16)
**10DLC: Update Campaign API**
- Added Update Campaign API
Expand All @@ -79,7 +83,6 @@
-Added 3 new keys to AccountPhoneNumber object:`tendlc_registration_status`, `tendlc_campaign_id` and `toll_free_sms_verification` (https://www.plivo.com/docs/numbers/api/account-phone-number#the-accountphonenumber-object)
-Added 3 new filters to AccountPhoneNumber - list all my numbers API:`tendlc_registration_status`, `tendlc_campaign_id` and `toll_free_sms_verification` (https://www.plivo.com/docs/numbers/api/account-phone-number#list-all-my-numbers)


## [4.30.2](https://github.com/plivo/plivo-ruby/tree/v4.30.2) (2022-09-28)
**10DLC: Campaign request**
- Added more attributes to create campaign request
Expand All @@ -88,7 +91,6 @@
**stability - faraday upgrade**
- faraday version upgrade


## [4.30.0](https://github.com/plivo/plivo-ruby/tree/v4.30.0) (2022-08-26)
**Feature - 10DLC APIs**
- Added new 10DLC APIs
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The Plivo Ruby SDK makes it simpler to integrate communications into your Ruby a
Add this line to your application's Gemfile:

```ruby
gem 'plivo', '>= 4.45.0'
gem 'plivo', '>= 4.46.0'
```

And then execute:
Expand Down
123 changes: 123 additions & 0 deletions lib/plivo/resources/calls.rb
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,73 @@ def cancel_request
@_client.send_request(resource_path, 'DELETE', nil, nil, false , is_voice_request: @_is_voice_request)
end

def start_stream(service_url, options = nil)
valid_param?(:service_url, service_url, [String, Symbol], true)
if options.nil?
return perform_action('Stream', 'POST', { service_url: service_url }, true)
end

valid_param?(:options, options, Hash, true)

params = { service_url: service_url }

if options.key?(:bidirectional) &&
valid_param?(:bidirectional, options[:bidirectional], [TrueClass, FalseClass], false )
params[:bidirectional] = options[:bidirectional]
end

if options.key?(:audio_track) &&
valid_param?(:audio_track, options[:audio_track],
[String, Symbol], false, %w[inbound outbound both])
params[:audio_track] = options[:audio_track]
end

if options.key?(:stream_timeout) &&
valid_param?(:stream_timeout, options[:stream_timeout], Integer, false)
params[:stream_timeout] = options[:stream_timeout]
end

if options.key?(:status_callback_url) &&
valid_param?(:status_callback_url, options[:status_callback_url], [String, Symbol], false)
params[:status_callback_url] = options[:status_callback_url]
end

if options.key?(:status_callback_method) &&
valid_param?(:status_callback_method, options[:status_callback_method],
[String, Symbol], false, %w[GET POST get post])
params[:status_callback_method] = options[:status_callback_method]
end

if options.key?(:content_type) &&
valid_param?(:content_type, options[:content_type], [String, Symbol, Integer], false)
params[:content_type] = options[:content_type]
end

if options.key?(:extra_headers) &&
valid_param?(:extra_headers, options[:extra_headers], [String], false)
params[:extra_headers] = options[:extra_headers]
end
perform_action('Stream', 'POST', params, true)
end

def stop_all_streams
perform_action('Stream', 'DELETE', nil, false)
end

def stop_stream(stream_id)
valid_param?(:stream_id, stream_id, [String, Symbol, Integer], true)
perform_action('Stream/' + stream_id, 'DELETE', nil, false)
end

def get_all_streams
perform_action('Stream', 'GET', nil, true )
end

def get_stream(stream_id)
valid_param?(:stream_id, stream_id, [String, Symbol, Integer], true)
perform_action('Stream/' + stream_id, 'GET', nil, true)
end

def to_s
call_details = {
answer_time: @answer_time,
Expand Down Expand Up @@ -577,6 +644,62 @@ def cancel_request(call_uuid)
valid_param?(:call_uuid, call_uuid, [String, Symbol], true)
Call.new(@_client, resource_id: call_uuid).cancel_request
end

# @param [String] service_url
# @param [Hash] options
# @option options [Boolean] :bidirectional
# @option options [String] :audio_track
# @option options [Int] :stream_timeout
# @option options [String] :status_callback_url
# @option options [String] :status_callback_method
# @option options [String] :content_type
# @option options [String] :extra_headers
def start_stream(call_uuid, service_url, options = {})
valid_param?(:call_uuid, call_uuid, [String, Symbol], true)
response = Call.new(@_client, resource_id: call_uuid).start_stream(service_url, options)
return Base::Response.new(Hash["api_id" => response.api_id,
"stream_id" => response.stream_id,
"message" => response.message])
end

def stop_all_streams(call_uuid)
valid_param?(:call_uuid, call_uuid, [String, Symbol], true )
Call.new(@_client, resource_id: call_uuid).stop_all_streams
end

def stop_stream(call_uuid, stream_id)
valid_param?(:call_uuid, call_uuid, [String, Symbol], true )
Call.new(@_client, resource_id: call_uuid).stop_stream(stream_id)
end

def get_all_streams(call_uuid)
valid_param?(:call_uuid, call_uuid, [String, Symbol], true )
response = Call.new(@_client, resource_id: call_uuid).get_all_streams
return Base::Response.new(Hash["api_id" => response.api_id,
"meta" => response.meta,
"objects" => response.objects])
end

def get_stream(call_uuid, stream_id)
valid_param?(:call_uuid, call_uuid, [String, Symbol], true )
response = Call.new(@_client, resource_id: call_uuid).get_stream(stream_id)
return Base::Response.new(Hash["api_id" => response.instance_variable_get(:@api_id),
"audio_track" => response.instance_variable_get(:@audio_track),
"bidirectional" => response.instance_variable_get(:@bidirectional),
"bill_duration" => response.instance_variable_get(:@bill_duration),
"billed_amount" => response.instance_variable_get(:@billed_amount),
"call_uuid" => response.instance_variable_get(:@call_uuid),
"created_at" => response.instance_variable_get(:@created_at),
"end_time" => response.instance_variable_get(:@end_time),
"plivo_auth_id" => response.instance_variable_get(:@plivo_auth_id),
"resource_uri" => response.instance_variable_get(:@resource_uri),
"rounded_bill_duration" => response.instance_variable_get(:@rounded_bill_duration),
"service_url" => response.instance_variable_get(:@service_url),
"start_time" => response.instance_variable_get(:@start_time),
"status" => response.instance_variable_get(:@status),
"status_callback_url" => response.instance_variable_get(:@status_callback_url),
"stream_id" => response.instance_variable_get(:@stream_id)])
end
end
end
end
2 changes: 1 addition & 1 deletion lib/plivo/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Plivo
VERSION = "4.45.0".freeze
VERSION = "4.46.0".freeze
end
1 change: 1 addition & 0 deletions lib/plivo/xml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
require_relative 'xml/plivo_xml'
require_relative 'xml/multipartycall'
require_relative 'xml/cont'
require_relative 'xml/stream'
include Plivo::Exceptions

module Plivo
Expand Down
4 changes: 2 additions & 2 deletions lib/plivo/xml/plivo_xml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ def initialize(response = nil)
end

def to_xml
'<?xml version="1.0" encoding="utf-8" ?>' + @response.to_xml
'<?xml version="1.0" encoding="utf-8" ?>' + @response.to_xml.gsub("&quot;", "\"")
end

def to_s
'<?xml version="1.0" encoding="utf-8" ?>' + @response.to_s
'<?xml version="1.0" encoding="utf-8" ?>' + @response.to_s.gsub("&quot;", "\"")
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/plivo/xml/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Plivo
module XML
class Response < Element
@nestables = %w[Speak Play GetDigits GetInput Record Dial Message
Redirect Wait Hangup PreAnswer Conference DTMF MultiPartyCall]
Redirect Wait Hangup PreAnswer Conference DTMF MultiPartyCall Stream]
@valid_attributes = []

def initialize
Expand Down
27 changes: 27 additions & 0 deletions lib/plivo/xml/stream.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module Plivo
module XML
class Stream < Element
@nestables = []
@valid_attributes = %w[bidirectional audioTrack streamTimeout statusCallbackUrl
statusCallbackMethod contentType extraHeaders]

SUPPORTED_BIDIRECTIONAL=%w(true false)
SUPPORTED_AUDIOTRACK=%w(inbound outbound both)
SUPPORTED_CALLBACKMETHOD=%w(GET POST)

def initialize(body, attributes = {})
if attributes[:bidirectional] && !SUPPORTED_BIDIRECTIONAL.include?(attributes[:bidirectional])
raise PlivoXMLError, "<Stream> bidirectional #{attributes[:bidirectional]} is not valid."
end
if attributes[:audioTrack] && !SUPPORTED_AUDIOTRACK.include?(attributes[:audioTrack])
raise PlivoXMLError, "<Stream> audioTrack #{attributes[:audioTrack]} is not valid."
end
if attributes[:statusCallbackMethod] && !SUPPORTED_CALLBACKMETHOD.include?(attributes[:statusCallbackMethod].upcase)
raise PlivoXMLError, "<Stream> statusCallbackMethod #{attributes[:statusCallbackMethod]} is not valid."
end
raise PlivoXMLError, 'No text set for Stream' unless body
super(body, attributes)
end
end
end
end
5 changes: 5 additions & 0 deletions spec/mocks/streamStartCreateResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"message": "stream started",
"api_id": "69487be8-3ef4-11e7-a51d-0245fa790d9e",
"stream_id": "69487be8-3ef4-11e7-a51d-0245fa790d77"
}
4 changes: 4 additions & 0 deletions spec/mocks/streamStartCreateResponses.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"message": "stream started",
"api_id": "69487be8-3ef4-11e7-a51d-0245fa790d9e"
}
16 changes: 16 additions & 0 deletions spec/resource_calls_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,20 @@ def to_json_list_live(list_object)
method: 'DELETE',
data: nil)
end

it 'start stream' do
id = 'MAXXXXXXXXXXXXXXXXXX'
contents = File.read(Dir.pwd + '/spec/mocks/streamStartCreateResponse.json')
response = File.read(Dir.pwd + '/spec/mocks/streamStartCreateResponses.json')
mock(202, JSON.parse(contents))
expect(JSON.parse(to_json_update(@api.calls
.start_stream(id,
'wss://mystream.ngrok.io/audiostream'))))
.to eql(JSON.parse(response))
compare_requests(uri: '/v1/Account/MAXXXXXXXXXXXXXXXXXX/Call/' + id + '/Stream/',
method: 'POST',
data: {
service_url: 'wss://mystream.ngrok.io/audiostream'
})
end
end
9 changes: 8 additions & 1 deletion spec/xml_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,15 @@
statusCallbackEvents: 'participant-speak-events, participant-digit-input-events, add-participant-api-events, participant-state-changes, mpc-state-changes'
)

resp.addStream('Starting a new stream.')
resp.addStream('Starting a new stream with params.',
{
bidirectional: 'true',
audioTrack: 'outbound'
})

xml = Plivo::XML::PlivoXML.new(resp)
puts xml.to_xml
expect(xml.to_xml).to eql("<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response><Conference callbackMethod='POST' callbackUrl='http://foo.com/confevents/' digitsMatch='#0,90,000'>My room</Conference><Dial confirmKey='3' confirmSound='http://foo.com/sound/'><Number sendDigits='wwww2410'>18217654321</Number><User sendDigits='wwww2410'>sip:[email protected]</User></Dial><Dial action='http://foo.com/dial_action/' timeout='20'><Number>18217654321</Number></Dial><Dial><Number>15671234567</Number></Dial><DTMF>12345</DTMF><GetDigits action='http://ww.foo.com/gather_pin/' method='POST'><Speak>Enter PIN number.</Speak></GetDigits><Speak>Input not recieved.</Speak><GetInput action='http://ww.foo.com/gather_feedback/' method='POST'><Speak>Tell us more about your experience.</Speak></GetInput><Speak>Statement not recieved.</Speak><Hangup reason='rejected' schedule='60'/><Speak loop='0'>Call will hangup after a min.</Speak><Message callbackMethod='POST' callbackUrl='http://foo.com/sms_status/' dst='15671234567' src='12023222222' type='sms'>Hi, message from Plivo.</Message><Play>https://amazonaws.com/Trumpet.mp3</Play><PreAnswer><Speak>This call will cost $2 a min.</Speak></PreAnswer><Speak>Thanks for dropping by.</Speak><Record action='http://foo.com/get_recording/' redirect='false' startOnDialAnswer='true'/><Dial><Number>15551234567</Number></Dial><Speak>Leave message after the beep.</Speak><Record action='http://foo.com/get_recording/' finishOnKey='*' maxLength='30'/><Speak>Recording not received.</Speak><Speak>Your call is being transferred.</Speak><Redirect>http://foo.com/redirect/</Redirect><Speak loop='3'>Go green, go plivo.</Speak><Speak>I will wait 7 seconds starting now!</Speak><Wait length='7'/><Speak>I just waited 7 seconds.</Speak><Wait beep='true' length='120'/><Play>https://s3.amazonaws.com/abc.mp3</Play><Wait length='10'/><Speak>Hello</Speak><Wait length='10' minSilence='3000' silence='true'/><Speak>Hello, welcome to the Jungle.</Speak><MultiPartyCall agentHoldMusicMethod='GET' coachMode='true' customerHoldMusicMethod='GET' endMpcOnExit='false' enterSound='beep:1' enterSoundMethod='GET' exitSound='beep:2' exitSoundMethod='GET' hold='false' maxDuration='1000' maxParticipants='10' mute='false' onExitActionMethod='POST' record='false' recordFileFormat='mp3' recordMinMemberCount='1' recordingCallbackMethod='GET' relayDTMFInputs='false' role='Agent' startMpcOnEnter='true' startRecordingAudioMethod='GET' statusCallbackEvents='participant-speak-events, participant-digit-input-events, add-participant-api-events, participant-state-changes, mpc-state-changes' statusCallbackMethod='POST' stayAlone='false' stopRecordingAudioMethod='GET' waitMusicMethod='GET'>Nairobi</MultiPartyCall></Response>")
expect(xml.to_xml).to eql("<?xml version=\"1.0\" encoding=\"utf-8\" ?><Response><Conference callbackMethod='POST' callbackUrl='http://foo.com/confevents/' digitsMatch='#0,90,000'>My room</Conference><Dial confirmKey='3' confirmSound='http://foo.com/sound/'><Number sendDigits='wwww2410'>18217654321</Number><User sendDigits='wwww2410'>sip:[email protected]</User></Dial><Dial action='http://foo.com/dial_action/' timeout='20'><Number>18217654321</Number></Dial><Dial><Number>15671234567</Number></Dial><DTMF>12345</DTMF><GetDigits action='http://ww.foo.com/gather_pin/' method='POST'><Speak>Enter PIN number.</Speak></GetDigits><Speak>Input not recieved.</Speak><GetInput action='http://ww.foo.com/gather_feedback/' method='POST'><Speak>Tell us more about your experience.</Speak></GetInput><Speak>Statement not recieved.</Speak><Hangup reason='rejected' schedule='60'/><Speak loop='0'>Call will hangup after a min.</Speak><Message callbackMethod='POST' callbackUrl='http://foo.com/sms_status/' dst='15671234567' src='12023222222' type='sms'>Hi, message from Plivo.</Message><Play>https://amazonaws.com/Trumpet.mp3</Play><PreAnswer><Speak>This call will cost $2 a min.</Speak></PreAnswer><Speak>Thanks for dropping by.</Speak><Record action='http://foo.com/get_recording/' redirect='false' startOnDialAnswer='true'/><Dial><Number>15551234567</Number></Dial><Speak>Leave message after the beep.</Speak><Record action='http://foo.com/get_recording/' finishOnKey='*' maxLength='30'/><Speak>Recording not received.</Speak><Speak>Your call is being transferred.</Speak><Redirect>http://foo.com/redirect/</Redirect><Speak loop='3'>Go green, go plivo.</Speak><Speak>I will wait 7 seconds starting now!</Speak><Wait length='7'/><Speak>I just waited 7 seconds.</Speak><Wait beep='true' length='120'/><Play>https://s3.amazonaws.com/abc.mp3</Play><Wait length='10'/><Speak>Hello</Speak><Wait length='10' minSilence='3000' silence='true'/><Speak>Hello, welcome to the Jungle.</Speak><MultiPartyCall agentHoldMusicMethod='GET' coachMode='true' customerHoldMusicMethod='GET' endMpcOnExit='false' enterSound='beep:1' enterSoundMethod='GET' exitSound='beep:2' exitSoundMethod='GET' hold='false' maxDuration='1000' maxParticipants='10' mute='false' onExitActionMethod='POST' record='false' recordFileFormat='mp3' recordMinMemberCount='1' recordingCallbackMethod='GET' relayDTMFInputs='false' role='Agent' startMpcOnEnter='true' startRecordingAudioMethod='GET' statusCallbackEvents='participant-speak-events, participant-digit-input-events, add-participant-api-events, participant-state-changes, mpc-state-changes' statusCallbackMethod='POST' stayAlone='false' stopRecordingAudioMethod='GET' waitMusicMethod='GET'>Nairobi</MultiPartyCall><Stream>Starting a new stream.</Stream><Stream audioTrack='outbound' bidirectional='true'>Starting a new stream with params.</Stream></Response>")
end
end

0 comments on commit af1a0b5

Please sign in to comment.