punchblock 1.7.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +3 -0
- data/lib/punchblock/translator/asterisk/component/output.rb +2 -2
- data/lib/punchblock/translator/freeswitch/call.rb +1 -1
- data/lib/punchblock/version.rb +1 -1
- data/spec/punchblock/translator/asterisk/component/output_spec.rb +309 -309
- data/spec/punchblock/translator/freeswitch/call_spec.rb +11 -5
- metadata +3 -3
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# [develop](https://github.com/adhearsion/punchblock)
|
2
2
|
|
3
|
+
# [v1.7.1](https://github.com/adhearsion/punchblock/compare/v1.7.0...v1.7.1) - [2012-12-17](https://rubygems.org/gems/punchblock/versions/1.7.1)
|
4
|
+
* Bugfix: Deal with nil media engines on FS/* properly
|
5
|
+
|
3
6
|
# [v1.7.0](https://github.com/adhearsion/punchblock/compare/v1.6.1...v1.7.0) - [2012-12-13](https://rubygems.org/gems/punchblock/versions/1.7.0)
|
4
7
|
* Feature: Support for the renderer attribute added to the Output component.
|
5
8
|
* Feature: FreeSWITCH and Asterisk translators now use the :renderer attribute on Output
|
@@ -27,10 +27,10 @@ module Punchblock
|
|
27
27
|
|
28
28
|
output_component = current_actor
|
29
29
|
|
30
|
-
rendering_engine = @component_node.renderer
|
30
|
+
rendering_engine = @component_node.renderer || @media_engine || :asterisk
|
31
31
|
|
32
32
|
case rendering_engine.to_sym
|
33
|
-
when :asterisk
|
33
|
+
when :asterisk
|
34
34
|
raise OptionError, "A voice value is unsupported on Asterisk." if @component_node.voice
|
35
35
|
raise OptionError, 'Interrupt digits are not allowed with early media.' if early && @component_node.interrupt_on
|
36
36
|
|
@@ -198,7 +198,7 @@ module Punchblock
|
|
198
198
|
hangup REJECT_TO_HANGUP_REASON[command.reason]
|
199
199
|
command.response = true
|
200
200
|
when Punchblock::Component::Output
|
201
|
-
media_renderer = command.renderer
|
201
|
+
media_renderer = command.renderer || media_engine || :freeswitch
|
202
202
|
case media_renderer.to_sym
|
203
203
|
when :freeswitch, :native, nil
|
204
204
|
execute_component Component::Output, command
|
data/lib/punchblock/version.rb
CHANGED
@@ -355,407 +355,407 @@ module Punchblock
|
|
355
355
|
end
|
356
356
|
end
|
357
357
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
end
|
364
|
-
|
365
|
-
def expect_playback_noanswer
|
366
|
-
mock_call.should_receive(:send_agi_action!).once.with 'EXEC Playback', audio_filename + ',noanswer'
|
367
|
-
end
|
368
|
-
|
369
|
-
let(:audio_filename) { 'http://foo.com/bar.mp3' }
|
358
|
+
[:asterisk, nil].each do |media_engine|
|
359
|
+
context "with a media engine of #{media_engine.inspect}" do
|
360
|
+
def expect_playback(filename = audio_filename)
|
361
|
+
mock_call.should_receive(:send_agi_action!).once.with 'EXEC Playback', filename
|
362
|
+
end
|
370
363
|
|
371
|
-
|
372
|
-
|
373
|
-
audio :src => audio_filename
|
364
|
+
def expect_playback_noanswer
|
365
|
+
mock_call.should_receive(:send_agi_action!).once.with 'EXEC Playback', audio_filename + ',noanswer'
|
374
366
|
end
|
375
|
-
end
|
376
367
|
|
377
|
-
|
368
|
+
let(:audio_filename) { 'http://foo.com/bar.mp3' }
|
378
369
|
|
379
|
-
|
380
|
-
|
381
|
-
|
370
|
+
let :ssml_doc do
|
371
|
+
RubySpeech::SSML.draw do
|
372
|
+
audio :src => audio_filename
|
373
|
+
end
|
374
|
+
end
|
382
375
|
|
383
|
-
|
384
|
-
Punchblock::Component::Output.new command_options
|
385
|
-
end
|
376
|
+
let(:command_opts) { {} }
|
386
377
|
|
387
|
-
|
388
|
-
|
389
|
-
let(:command_opts) { { :ssml => nil } }
|
390
|
-
it "should return an error and not execute any actions" do
|
391
|
-
subject.execute
|
392
|
-
error = ProtocolError.new.setup 'option error', 'An SSML document is required.'
|
393
|
-
original_command.response(0.1).should be == error
|
394
|
-
end
|
378
|
+
let :command_options do
|
379
|
+
{ :ssml => ssml_doc }.merge(command_opts)
|
395
380
|
end
|
396
381
|
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
{
|
401
|
-
:ssml => RubySpeech::SSML.draw { audio :src => audio_filename }
|
402
|
-
}
|
403
|
-
end
|
382
|
+
let :original_command do
|
383
|
+
Punchblock::Component::Output.new command_options
|
384
|
+
end
|
404
385
|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
386
|
+
describe 'ssml' do
|
387
|
+
context 'unset' do
|
388
|
+
let(:command_opts) { { :ssml => nil } }
|
389
|
+
it "should return an error and not execute any actions" do
|
390
|
+
subject.execute
|
391
|
+
error = ProtocolError.new.setup 'option error', 'An SSML document is required.'
|
392
|
+
original_command.response(0.1).should be == error
|
393
|
+
end
|
409
394
|
end
|
410
395
|
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
396
|
+
context 'with a single audio SSML node' do
|
397
|
+
let(:audio_filename) { 'http://foo.com/bar.mp3' }
|
398
|
+
let :command_options do
|
399
|
+
{
|
400
|
+
:ssml => RubySpeech::SSML.draw { audio :src => audio_filename }
|
401
|
+
}
|
417
402
|
end
|
418
|
-
subject.execute
|
419
|
-
original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
|
420
|
-
end
|
421
|
-
end
|
422
403
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
}
|
429
|
-
end
|
404
|
+
it 'should playback the audio file using Playback' do
|
405
|
+
expect_answered
|
406
|
+
expect_playback
|
407
|
+
subject.execute
|
408
|
+
end
|
430
409
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
410
|
+
it 'should send a complete event when the file finishes playback' do
|
411
|
+
def mock_call.answered?
|
412
|
+
true
|
413
|
+
end
|
414
|
+
def mock_call.send_agi_action!(*args, &block)
|
415
|
+
block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1)
|
416
|
+
end
|
417
|
+
subject.execute
|
418
|
+
original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
|
419
|
+
end
|
435
420
|
end
|
436
421
|
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
422
|
+
context 'with a single text node without spaces' do
|
423
|
+
let(:audio_filename) { 'tt-monkeys' }
|
424
|
+
let :command_options do
|
425
|
+
{
|
426
|
+
:ssml => RubySpeech::SSML.draw { string audio_filename }
|
427
|
+
}
|
441
428
|
end
|
442
|
-
subject.execute
|
443
|
-
original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
|
444
|
-
end
|
445
429
|
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
expect_playback_noanswer
|
450
|
-
mock_call.should_receive(:send_progress)
|
430
|
+
it 'should playback the audio file using Playback' do
|
431
|
+
expect_answered
|
432
|
+
expect_playback
|
451
433
|
subject.execute
|
452
434
|
end
|
453
435
|
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
:ssml => RubySpeech::SSML.draw { string audio_filename },
|
459
|
-
:interrupt_on => :any
|
460
|
-
}
|
436
|
+
it 'should send a complete event when the file finishes playback' do
|
437
|
+
expect_answered
|
438
|
+
def mock_call.send_agi_action!(*args, &block)
|
439
|
+
block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1)
|
461
440
|
end
|
462
|
-
|
441
|
+
subject.execute
|
442
|
+
original_command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
|
443
|
+
end
|
444
|
+
|
445
|
+
context "with early media playback" do
|
446
|
+
it "should play the file with Playback" do
|
463
447
|
expect_answered false
|
464
|
-
|
448
|
+
expect_playback_noanswer
|
449
|
+
mock_call.should_receive(:send_progress)
|
465
450
|
subject.execute
|
466
|
-
original_command.response(0.1).should be == error
|
467
451
|
end
|
468
|
-
end
|
469
|
-
end
|
470
|
-
end
|
471
452
|
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
let(:audio_filename2) { 'http://foo.com/baz.mp3' }
|
487
|
-
let :command_options do
|
488
|
-
{
|
489
|
-
:ssml => RubySpeech::SSML.draw do
|
490
|
-
audio :src => audio_filename1
|
491
|
-
audio :src => audio_filename2
|
453
|
+
context "with interrupt_on set to something that is not nil" do
|
454
|
+
let(:audio_filename) { 'tt-monkeys' }
|
455
|
+
let :command_options do
|
456
|
+
{
|
457
|
+
:ssml => RubySpeech::SSML.draw { string audio_filename },
|
458
|
+
:interrupt_on => :any
|
459
|
+
}
|
460
|
+
end
|
461
|
+
it "should return an error when the output is interruptible and it is early media" do
|
462
|
+
expect_answered false
|
463
|
+
error = ProtocolError.new.setup 'option error', 'Interrupt digits are not allowed with early media.'
|
464
|
+
subject.execute
|
465
|
+
original_command.response(0.1).should be == error
|
466
|
+
end
|
492
467
|
end
|
493
|
-
|
468
|
+
end
|
494
469
|
end
|
495
470
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
471
|
+
context 'with a string (not SSML)' do
|
472
|
+
let :command_options do
|
473
|
+
{ :text => 'Foo Bar' }
|
474
|
+
end
|
475
|
+
|
476
|
+
it "should return an unrenderable document error" do
|
477
|
+
subject.execute
|
478
|
+
error = ProtocolError.new.setup 'unrenderable document error', 'The provided document could not be rendered. See http://adhearsion.com/docs/common_problems#unrenderable-document-error for details.'
|
479
|
+
original_command.response(0.1).should be == error
|
480
|
+
end
|
503
481
|
end
|
504
482
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
483
|
+
context 'with multiple audio SSML nodes' do
|
484
|
+
let(:audio_filename1) { 'http://foo.com/bar.mp3' }
|
485
|
+
let(:audio_filename2) { 'http://foo.com/baz.mp3' }
|
486
|
+
let :command_options do
|
487
|
+
{
|
488
|
+
:ssml => RubySpeech::SSML.draw do
|
489
|
+
audio :src => audio_filename1
|
490
|
+
audio :src => audio_filename2
|
491
|
+
end
|
492
|
+
}
|
509
493
|
end
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
494
|
+
|
495
|
+
it 'should playback all audio files using Playback' do
|
496
|
+
latch = CountDownLatch.new 2
|
497
|
+
expect_playback [audio_filename1, audio_filename2].join('&')
|
498
|
+
expect_answered
|
499
|
+
subject.execute
|
500
|
+
latch.wait 2
|
501
|
+
sleep 2
|
514
502
|
end
|
515
|
-
subject.execute
|
516
|
-
latch.wait(2).should be_true
|
517
|
-
end
|
518
|
-
end
|
519
503
|
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
string "Foo Bar"
|
504
|
+
it 'should send a complete event after the final file has finished playback' do
|
505
|
+
expect_answered
|
506
|
+
def mock_call.send_agi_action!(*args, &block)
|
507
|
+
block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1)
|
525
508
|
end
|
526
|
-
|
509
|
+
latch = CountDownLatch.new 1
|
510
|
+
original_command.should_receive(:add_event).once.with do |e|
|
511
|
+
e.reason.should be_a Punchblock::Component::Output::Complete::Success
|
512
|
+
latch.countdown!
|
513
|
+
end
|
514
|
+
subject.execute
|
515
|
+
latch.wait(2).should be_true
|
516
|
+
end
|
527
517
|
end
|
528
518
|
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
519
|
+
context "with an SSML document containing elements other than <audio/>" do
|
520
|
+
let :command_options do
|
521
|
+
{
|
522
|
+
:ssml => RubySpeech::SSML.draw do
|
523
|
+
string "Foo Bar"
|
524
|
+
end
|
525
|
+
}
|
526
|
+
end
|
536
527
|
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
expect_playback
|
543
|
-
subject.execute
|
528
|
+
it "should return an unrenderable document error" do
|
529
|
+
subject.execute
|
530
|
+
error = ProtocolError.new.setup 'unrenderable document error', 'The provided document could not be rendered. See http://adhearsion.com/docs/common_problems#unrenderable-document-error for details.'
|
531
|
+
original_command.response(0.1).should be == error
|
532
|
+
end
|
544
533
|
end
|
545
534
|
end
|
546
535
|
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
536
|
+
describe 'start-offset' do
|
537
|
+
context 'unset' do
|
538
|
+
let(:command_opts) { { :start_offset => nil } }
|
539
|
+
it 'should not pass any options to Playback' do
|
540
|
+
expect_answered
|
541
|
+
expect_playback
|
542
|
+
subject.execute
|
543
|
+
end
|
553
544
|
end
|
554
|
-
end
|
555
|
-
end
|
556
545
|
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
546
|
+
context 'set' do
|
547
|
+
let(:command_opts) { { :start_offset => 10 } }
|
548
|
+
it "should return an error and not execute any actions" do
|
549
|
+
subject.execute
|
550
|
+
error = ProtocolError.new.setup 'option error', 'A start_offset value is unsupported on Asterisk.'
|
551
|
+
original_command.response(0.1).should be == error
|
552
|
+
end
|
564
553
|
end
|
565
554
|
end
|
566
555
|
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
556
|
+
describe 'start-paused' do
|
557
|
+
context 'false' do
|
558
|
+
let(:command_opts) { { :start_paused => false } }
|
559
|
+
it 'should not pass any options to Playback' do
|
560
|
+
expect_answered
|
561
|
+
expect_playback
|
562
|
+
subject.execute
|
563
|
+
end
|
573
564
|
end
|
574
|
-
end
|
575
|
-
end
|
576
565
|
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
566
|
+
context 'true' do
|
567
|
+
let(:command_opts) { { :start_paused => true } }
|
568
|
+
it "should return an error and not execute any actions" do
|
569
|
+
subject.execute
|
570
|
+
error = ProtocolError.new.setup 'option error', 'A start_paused value is unsupported on Asterisk.'
|
571
|
+
original_command.response(0.1).should be == error
|
572
|
+
end
|
584
573
|
end
|
585
574
|
end
|
586
575
|
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
576
|
+
describe 'repeat-interval' do
|
577
|
+
context 'unset' do
|
578
|
+
let(:command_opts) { { :repeat_interval => nil } }
|
579
|
+
it 'should not pass any options to Playback' do
|
580
|
+
expect_answered
|
581
|
+
expect_playback
|
582
|
+
subject.execute
|
583
|
+
end
|
593
584
|
end
|
594
|
-
end
|
595
|
-
end
|
596
585
|
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
586
|
+
context 'set' do
|
587
|
+
let(:command_opts) { { :repeat_interval => 10 } }
|
588
|
+
it "should return an error and not execute any actions" do
|
589
|
+
subject.execute
|
590
|
+
error = ProtocolError.new.setup 'option error', 'A repeat_interval value is unsupported on Asterisk.'
|
591
|
+
original_command.response(0.1).should be == error
|
592
|
+
end
|
604
593
|
end
|
605
594
|
end
|
606
595
|
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
596
|
+
describe 'repeat-times' do
|
597
|
+
context 'unset' do
|
598
|
+
let(:command_opts) { { :repeat_times => nil } }
|
599
|
+
it 'should not pass any options to Playback' do
|
600
|
+
expect_answered
|
601
|
+
expect_playback
|
602
|
+
subject.execute
|
603
|
+
end
|
613
604
|
end
|
614
|
-
end
|
615
|
-
end
|
616
605
|
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
606
|
+
context 'set' do
|
607
|
+
let(:command_opts) { { :repeat_times => 2 } }
|
608
|
+
it "should return an error and not execute any actions" do
|
609
|
+
subject.execute
|
610
|
+
error = ProtocolError.new.setup 'option error', 'A repeat_times value is unsupported on Asterisk.'
|
611
|
+
original_command.response(0.1).should be == error
|
612
|
+
end
|
624
613
|
end
|
625
614
|
end
|
626
615
|
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
616
|
+
describe 'max-time' do
|
617
|
+
context 'unset' do
|
618
|
+
let(:command_opts) { { :max_time => nil } }
|
619
|
+
it 'should not pass any options to Playback' do
|
620
|
+
expect_answered
|
621
|
+
expect_playback
|
622
|
+
subject.execute
|
623
|
+
end
|
633
624
|
end
|
634
|
-
end
|
635
|
-
end
|
636
625
|
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
626
|
+
context 'set' do
|
627
|
+
let(:command_opts) { { :max_time => 30 } }
|
628
|
+
it "should return an error and not execute any actions" do
|
629
|
+
subject.execute
|
630
|
+
error = ProtocolError.new.setup 'option error', 'A max_time value is unsupported on Asterisk.'
|
631
|
+
original_command.response(0.1).should be == error
|
632
|
+
end
|
644
633
|
end
|
645
634
|
end
|
646
635
|
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
636
|
+
describe 'voice' do
|
637
|
+
context 'unset' do
|
638
|
+
let(:command_opts) { { :voice => nil } }
|
639
|
+
it 'should not pass the v option to Playback' do
|
640
|
+
expect_answered
|
641
|
+
expect_playback
|
642
|
+
subject.execute
|
643
|
+
end
|
653
644
|
end
|
654
|
-
end
|
655
|
-
end
|
656
645
|
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
646
|
+
context 'set' do
|
647
|
+
let(:command_opts) { { :voice => 'alison' } }
|
648
|
+
it "should return an error and not execute any actions" do
|
649
|
+
subject.execute
|
650
|
+
error = ProtocolError.new.setup 'option error', 'A voice value is unsupported on Asterisk.'
|
651
|
+
original_command.response(0.1).should be == error
|
652
|
+
end
|
663
653
|
end
|
664
654
|
end
|
665
655
|
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
let :ami_event do
|
674
|
-
RubyAMI::Event.new('AsyncAGI').tap do |e|
|
675
|
-
e['SubEvent'] = "Start"
|
676
|
-
e['Channel'] = channel
|
677
|
-
e['Env'] = "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2F1234-00000000%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201320835995.0%0Aagi_version%3A%201.8.4.1%0Aagi_callerid%3A%205678%0Aagi_calleridname%3A%20Jane%20Smith%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%201000%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20default%0Aagi_extension%3A%201000%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%204366221312%0A%0A"
|
656
|
+
describe 'interrupt_on' do
|
657
|
+
def ami_event_for_dtmf(digit, position)
|
658
|
+
RubyAMI::Event.new('DTMF').tap do |e|
|
659
|
+
e['Digit'] = digit.to_s
|
660
|
+
e['Start'] = position == :start ? 'Yes' : 'No'
|
661
|
+
e['End'] = position == :end ? 'Yes' : 'No'
|
662
|
+
end
|
678
663
|
end
|
679
|
-
end
|
680
664
|
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
expect_answered
|
685
|
-
expect_playback
|
686
|
-
mock_call.should_receive(:redirect_back!).never
|
687
|
-
subject.execute
|
688
|
-
original_command.response(0.1).should be_a Ref
|
689
|
-
send_ami_events_for_dtmf 1
|
665
|
+
def send_ami_events_for_dtmf(digit)
|
666
|
+
mock_call.process_ami_event ami_event_for_dtmf(digit, :start)
|
667
|
+
mock_call.process_ami_event ami_event_for_dtmf(digit, :end)
|
690
668
|
end
|
691
|
-
end
|
692
669
|
|
693
|
-
|
694
|
-
let(:
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
670
|
+
let(:reason) { original_command.complete_event(5).reason }
|
671
|
+
let(:channel) { "SIP/1234-00000000" }
|
672
|
+
let :ami_event do
|
673
|
+
RubyAMI::Event.new('AsyncAGI').tap do |e|
|
674
|
+
e['SubEvent'] = "Start"
|
675
|
+
e['Channel'] = channel
|
676
|
+
e['Env'] = "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2F1234-00000000%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201320835995.0%0Aagi_version%3A%201.8.4.1%0Aagi_callerid%3A%205678%0Aagi_calleridname%3A%20Jane%20Smith%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%201000%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20default%0Aagi_extension%3A%201000%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%204366221312%0A%0A"
|
677
|
+
end
|
699
678
|
end
|
700
679
|
|
701
|
-
context "
|
702
|
-
|
703
|
-
|
680
|
+
context "set to nil" do
|
681
|
+
let(:command_opts) { { :interrupt_on => nil } }
|
682
|
+
it "does not redirect the call" do
|
683
|
+
expect_answered
|
684
|
+
expect_playback
|
685
|
+
mock_call.should_receive(:redirect_back!).never
|
704
686
|
subject.execute
|
705
687
|
original_command.response(0.1).should be_a Ref
|
706
|
-
original_command.should_not be_complete
|
707
688
|
send_ami_events_for_dtmf 1
|
708
|
-
mock_call.process_ami_event! ami_event
|
709
|
-
sleep 0.2
|
710
|
-
original_command.should be_complete
|
711
|
-
reason.should be_a Punchblock::Component::Output::Complete::Success
|
712
689
|
end
|
690
|
+
end
|
713
691
|
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
692
|
+
context "set to :any" do
|
693
|
+
let(:command_opts) { { :interrupt_on => :any } }
|
694
|
+
|
695
|
+
before do
|
696
|
+
expect_answered
|
697
|
+
expect_playback
|
719
698
|
end
|
720
|
-
end
|
721
|
-
end
|
722
699
|
|
723
|
-
|
724
|
-
|
700
|
+
context "when a DTMF digit is received" do
|
701
|
+
it "sends the correct complete event" do
|
702
|
+
mock_call.should_receive :redirect_back!
|
703
|
+
subject.execute
|
704
|
+
original_command.response(0.1).should be_a Ref
|
705
|
+
original_command.should_not be_complete
|
706
|
+
send_ami_events_for_dtmf 1
|
707
|
+
mock_call.process_ami_event! ami_event
|
708
|
+
sleep 0.2
|
709
|
+
original_command.should be_complete
|
710
|
+
reason.should be_a Punchblock::Component::Output::Complete::Success
|
711
|
+
end
|
725
712
|
|
726
|
-
|
727
|
-
|
728
|
-
|
713
|
+
it "redirects the call back to async AGI" do
|
714
|
+
mock_call.should_receive(:redirect_back!).once
|
715
|
+
subject.execute
|
716
|
+
original_command.response(0.1).should be_a Ref
|
717
|
+
send_ami_events_for_dtmf 1
|
718
|
+
end
|
719
|
+
end
|
729
720
|
end
|
730
721
|
|
731
|
-
context "
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
send_ami_events_for_dtmf 1
|
738
|
-
mock_call.process_ami_event! ami_event
|
739
|
-
sleep 0.2
|
740
|
-
original_command.should be_complete
|
741
|
-
reason.should be_a Punchblock::Component::Output::Complete::Success
|
722
|
+
context "set to :dtmf" do
|
723
|
+
let(:command_opts) { { :interrupt_on => :dtmf } }
|
724
|
+
|
725
|
+
before do
|
726
|
+
expect_answered
|
727
|
+
expect_playback
|
742
728
|
end
|
743
729
|
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
730
|
+
context "when a DTMF digit is received" do
|
731
|
+
it "sends the correct complete event" do
|
732
|
+
mock_call.should_receive :redirect_back!
|
733
|
+
subject.execute
|
734
|
+
original_command.response(0.1).should be_a Ref
|
735
|
+
original_command.should_not be_complete
|
736
|
+
send_ami_events_for_dtmf 1
|
737
|
+
mock_call.process_ami_event! ami_event
|
738
|
+
sleep 0.2
|
739
|
+
original_command.should be_complete
|
740
|
+
reason.should be_a Punchblock::Component::Output::Complete::Success
|
741
|
+
end
|
742
|
+
|
743
|
+
it "redirects the call back to async AGI" do
|
744
|
+
mock_call.should_receive(:redirect_back!).once
|
745
|
+
subject.execute
|
746
|
+
original_command.response(0.1).should be_a Ref
|
747
|
+
send_ami_events_for_dtmf 1
|
748
|
+
end
|
749
749
|
end
|
750
750
|
end
|
751
|
-
end
|
752
751
|
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
752
|
+
context "set to :speech" do
|
753
|
+
let(:command_opts) { { :interrupt_on => :speech } }
|
754
|
+
it "should return an error and not execute any actions" do
|
755
|
+
subject.execute
|
756
|
+
error = ProtocolError.new.setup 'option error', 'An interrupt-on value of speech is unsupported.'
|
757
|
+
original_command.response(0.1).should be == error
|
758
|
+
end
|
759
759
|
end
|
760
760
|
end
|
761
761
|
end
|
@@ -754,11 +754,17 @@ module Punchblock
|
|
754
754
|
|
755
755
|
let(:mock_component) { mock 'Freeswitch::Component::Output', :id => 'foo' }
|
756
756
|
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
757
|
+
['freeswitch', nil].each do |media_engine|
|
758
|
+
let(:media_engine) { media_engine }
|
759
|
+
|
760
|
+
context "with a media engine of #{media_engine}" do
|
761
|
+
it 'should create an Output component and execute it asynchronously' do
|
762
|
+
Component::Output.should_receive(:new_link).once.with(command, subject).and_return mock_component
|
763
|
+
mock_component.should_receive(:execute!).once
|
764
|
+
subject.execute_command command
|
765
|
+
subject.component_with_id('foo').should be mock_component
|
766
|
+
end
|
767
|
+
end
|
762
768
|
end
|
763
769
|
|
764
770
|
context 'with the media engine of :flite' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: punchblock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-12-
|
14
|
+
date: 2012-12-17 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: niceogiri
|
@@ -499,7 +499,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
499
499
|
version: '0'
|
500
500
|
segments:
|
501
501
|
- 0
|
502
|
-
hash: -
|
502
|
+
hash: -2704704399660670461
|
503
503
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
504
504
|
none: false
|
505
505
|
requirements:
|