telephony 1.0.3
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 +15 -0
- data/README.md +105 -0
- data/Rakefile +39 -0
- data/app/assets/fonts/telephony/zest-telephony.eot +0 -0
- data/app/assets/fonts/telephony/zest-telephony.svg +73 -0
- data/app/assets/fonts/telephony/zest-telephony.ttf +0 -0
- data/app/assets/fonts/telephony/zest-telephony.woff +0 -0
- data/app/assets/images/telephony/backspace.png +0 -0
- data/app/assets/images/telephony/icon-spinner.gif +0 -0
- data/app/assets/javascripts/telephony/application.js +12 -0
- data/app/assets/javascripts/telephony/collections/agents.js +4 -0
- data/app/assets/javascripts/telephony/config.js.erb +11 -0
- data/app/assets/javascripts/telephony/models/agent.js +103 -0
- data/app/assets/javascripts/telephony/models/conversation.js +226 -0
- data/app/assets/javascripts/telephony/models/device.js +30 -0
- data/app/assets/javascripts/telephony/models/transfer.js +65 -0
- data/app/assets/javascripts/telephony/namespace.js +6 -0
- data/app/assets/javascripts/telephony/push.js +80 -0
- data/app/assets/javascripts/telephony/vendor/backbone.js +1159 -0
- data/app/assets/javascripts/telephony/vendor/chosen.jquery.js +1018 -0
- data/app/assets/javascripts/telephony/vendor/jquery.form.js +1117 -0
- data/app/assets/javascripts/telephony/vendor/pusher.js +1304 -0
- data/app/assets/javascripts/telephony/vendor/test_dependencies.js +18 -0
- data/app/assets/javascripts/telephony/vendor/underscore.js +839 -0
- data/app/assets/javascripts/telephony/views/agents_view.js +69 -0
- data/app/assets/javascripts/telephony/views/application_view.js +78 -0
- data/app/assets/javascripts/telephony/views/call_queue_view.js +21 -0
- data/app/assets/javascripts/telephony/views/conversation_buttons_view.js +15 -0
- data/app/assets/javascripts/telephony/views/conversation_view.js +322 -0
- data/app/assets/javascripts/telephony/views/status_view.js +36 -0
- data/app/assets/javascripts/telephony/views/transfer_view.js +87 -0
- data/app/assets/javascripts/telephony/views/twilio_client_view.js +117 -0
- data/app/assets/javascripts/telephony/views/widget_view.js +55 -0
- data/app/assets/javascripts/telephony/widget.js +5 -0
- data/app/assets/javascripts/templates/telephony/agents_view.jst.ejs +8 -0
- data/app/assets/javascripts/templates/telephony/call_queue_view.jst.ejs +2 -0
- data/app/assets/javascripts/templates/telephony/conversation_buttons_view.jst.ejs +23 -0
- data/app/assets/javascripts/templates/telephony/conversation_view.jst.ejs +10 -0
- data/app/assets/javascripts/templates/telephony/status_view.jst.ejs +4 -0
- data/app/assets/javascripts/templates/telephony/transfer_view.jst.ejs +38 -0
- data/app/assets/javascripts/templates/telephony/twilio_client_view.jst.ejs +10 -0
- data/app/assets/stylesheets/telephony/font.css +43 -0
- data/app/assets/stylesheets/telephony/reset.css.scss +10 -0
- data/app/assets/stylesheets/telephony/widget.css.scss +512 -0
- data/app/controllers/telephony/agents_controller.rb +46 -0
- data/app/controllers/telephony/application_controller.rb +4 -0
- data/app/controllers/telephony/call_centers_controller.rb +9 -0
- data/app/controllers/telephony/conversations_controller.rb +74 -0
- data/app/controllers/telephony/inbound/conversation_queues_controller.rb +21 -0
- data/app/controllers/telephony/playable_listeners_controller.rb +43 -0
- data/app/controllers/telephony/providers/twilio/application_controller.rb +26 -0
- data/app/controllers/telephony/providers/twilio/calls_controller.rb +130 -0
- data/app/controllers/telephony/providers/twilio/inbound_calls_controller.rb +54 -0
- data/app/controllers/telephony/providers/twilio/musics_controller.rb +12 -0
- data/app/controllers/telephony/providers/twilio/voicemails_controller.rb +29 -0
- data/app/controllers/telephony/signals/agents/presences_controller.rb +64 -0
- data/app/controllers/telephony/transfers_controller.rb +18 -0
- data/app/controllers/telephony/twilio_client_controller.rb +37 -0
- data/app/controllers/telephony/voicemails_controller.rb +13 -0
- data/app/controllers/telephony/widget_controller.rb +10 -0
- data/app/helpers/telephony/application_helper.rb +4 -0
- data/app/helpers/telephony/calls_helper.rb +13 -0
- data/app/models/telephony/agent.rb +186 -0
- data/app/models/telephony/agent_state_machine.rb +54 -0
- data/app/models/telephony/base.rb +7 -0
- data/app/models/telephony/blacklisted_number.rb +5 -0
- data/app/models/telephony/call.rb +94 -0
- data/app/models/telephony/call_center.rb +26 -0
- data/app/models/telephony/call_state_machine.rb +98 -0
- data/app/models/telephony/conversation.rb +273 -0
- data/app/models/telephony/conversation_state_machine.rb +109 -0
- data/app/models/telephony/conversations_presenter.rb +83 -0
- data/app/models/telephony/events.rb +6 -0
- data/app/models/telephony/events/answer.rb +6 -0
- data/app/models/telephony/events/base.rb +118 -0
- data/app/models/telephony/events/busy.rb +6 -0
- data/app/models/telephony/events/call_answered.rb +22 -0
- data/app/models/telephony/events/call_fail.rb +6 -0
- data/app/models/telephony/events/complete_hold.rb +28 -0
- data/app/models/telephony/events/complete_one_step_transfer.rb +17 -0
- data/app/models/telephony/events/complete_resume.rb +28 -0
- data/app/models/telephony/events/complete_two_step_transfer.rb +6 -0
- data/app/models/telephony/events/conference.rb +6 -0
- data/app/models/telephony/events/connect.rb +22 -0
- data/app/models/telephony/events/customer_left_two_step_transfer.rb +6 -0
- data/app/models/telephony/events/dial_agent.rb +6 -0
- data/app/models/telephony/events/ended.rb +19 -0
- data/app/models/telephony/events/enqueue.rb +6 -0
- data/app/models/telephony/events/fail_one_step_transfer.rb +9 -0
- data/app/models/telephony/events/fail_two_step_transfer.rb +6 -0
- data/app/models/telephony/events/initialize_widget.rb +15 -0
- data/app/models/telephony/events/initiate_hold.rb +6 -0
- data/app/models/telephony/events/initiate_one_step_transfer.rb +6 -0
- data/app/models/telephony/events/initiate_resume.rb +6 -0
- data/app/models/telephony/events/initiate_two_step_transfer.rb +6 -0
- data/app/models/telephony/events/leave_two_step_transfer.rb +17 -0
- data/app/models/telephony/events/leave_voicemail.rb +22 -0
- data/app/models/telephony/events/no_answer.rb +6 -0
- data/app/models/telephony/events/play_closed_greeting.rb +6 -0
- data/app/models/telephony/events/play_message.rb +6 -0
- data/app/models/telephony/events/reject.rb +6 -0
- data/app/models/telephony/events/rona.rb +6 -0
- data/app/models/telephony/events/start.rb +17 -0
- data/app/models/telephony/events/straight_to_voicemail.rb +6 -0
- data/app/models/telephony/events/terminate.rb +6 -0
- data/app/models/telephony/events/transfer.rb +35 -0
- data/app/models/telephony/inbound_conversation_queue.rb +101 -0
- data/app/models/telephony/playable.rb +7 -0
- data/app/models/telephony/playable_listener.rb +42 -0
- data/app/models/telephony/pusher_event_publisher.rb +26 -0
- data/app/models/telephony/recording.rb +4 -0
- data/app/models/telephony/voicemail.rb +36 -0
- data/app/observers/telephony/agent_observer.rb +8 -0
- data/app/observers/telephony/call_observer.rb +15 -0
- data/app/observers/telephony/conversation_observer.rb +9 -0
- data/app/observers/telephony/event_observer.rb +9 -0
- data/app/views/layouts/telephony/application.html.erb +14 -0
- data/app/views/telephony/providers/twilio/calls/child_detached.builder +5 -0
- data/app/views/telephony/providers/twilio/calls/complete_hold.builder +6 -0
- data/app/views/telephony/providers/twilio/calls/connect.builder +13 -0
- data/app/views/telephony/providers/twilio/calls/dial.builder +14 -0
- data/app/views/telephony/providers/twilio/calls/done.builder +5 -0
- data/app/views/telephony/providers/twilio/calls/join_conference.builder +7 -0
- data/app/views/telephony/providers/twilio/calls/whisper_tone.builder +3 -0
- data/app/views/telephony/providers/twilio/inbound_calls/closed_hours.builder +7 -0
- data/app/views/telephony/providers/twilio/inbound_calls/create.builder +8 -0
- data/app/views/telephony/providers/twilio/inbound_calls/enqueue.builder +8 -0
- data/app/views/telephony/providers/twilio/inbound_calls/reject.builder +5 -0
- data/app/views/telephony/providers/twilio/inbound_calls/wait_music.builder +5 -0
- data/app/views/telephony/providers/twilio/musics/hold.builder +5 -0
- data/app/views/telephony/providers/twilio/voicemails/new.builder +6 -0
- data/app/views/telephony/twilio_client/index.html.erb +74 -0
- data/app/views/telephony/widget/index.erb +3 -0
- data/config/cucumber.yml +8 -0
- data/config/database.yml +57 -0
- data/config/environment.rb +67 -0
- data/config/initializers/pusher.rb +21 -0
- data/config/initializers/telephony.rb +44 -0
- data/config/routes.rb +94 -0
- data/config/wopr.yml.example +38 -0
- data/db/migrate/20130806213053_bootstrap_db.rb +105 -0
- data/db/migrate/20131009204026_create_telephony_blacklisted_numbers.rb +11 -0
- data/lib/agent_generator.rb +24 -0
- data/lib/tasks/cucumber.rake +65 -0
- data/lib/tasks/jasmine.rake +8 -0
- data/lib/tasks/telephony_tasks.rake +4 -0
- data/lib/telephony.rb +34 -0
- data/lib/telephony/concerns/controllers/twilio_request_verifier.rb +39 -0
- data/lib/telephony/conversation_data.rb +67 -0
- data/lib/telephony/engine.rb +19 -0
- data/lib/telephony/error.rb +8 -0
- data/lib/telephony/error/agent_on_a_call.rb +2 -0
- data/lib/telephony/error/base.rb +3 -0
- data/lib/telephony/error/connection.rb +2 -0
- data/lib/telephony/error/not_in_progress.rb +2 -0
- data/lib/telephony/error/queue_empty.rb +2 -0
- data/lib/telephony/helper.rb +11 -0
- data/lib/telephony/jobs/agent_offline.rb +28 -0
- data/lib/telephony/jobs/pusher_event.rb +21 -0
- data/lib/telephony/providers/twilio_provider.rb +162 -0
- data/lib/telephony/version.rb +3 -0
- data/spec/controllers/telephony/agents_controller_spec.rb +117 -0
- data/spec/controllers/telephony/call_centers_controller_spec.rb +25 -0
- data/spec/controllers/telephony/conversations_controller_spec.rb +229 -0
- data/spec/controllers/telephony/playable_listeners_controller_spec.rb +138 -0
- data/spec/controllers/telephony/providers/twilio/calls_controller_spec.rb +33 -0
- data/spec/controllers/telephony/providers/twilio/musics_controller_spec.rb +20 -0
- data/spec/controllers/telephony/signals/agents/presences_controller_spec.rb +154 -0
- data/spec/controllers/telephony/twilio_client_controller_spec.rb +58 -0
- data/spec/controllers/telephony/widget_controller_spec.rb +25 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +6 -0
- data/spec/dummy/app/assets/javascripts/lib/event_logger.js +82 -0
- data/spec/dummy/app/assets/javascripts/vendor/backbone.js +1159 -0
- data/spec/dummy/app/assets/javascripts/vendor/chosen.jquery.js +1018 -0
- data/spec/dummy/app/assets/javascripts/vendor/jquery.form.js +1117 -0
- data/spec/dummy/app/assets/javascripts/vendor/underscore.js +839 -0
- data/spec/dummy/app/assets/stylesheets/application.css +23 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/widget_host_controller.rb +2 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +12 -0
- data/spec/dummy/app/views/widget_host/index.html.erb +53 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +61 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/call_centers.yml +35 -0
- data/spec/dummy/config/call_centers.yml.example +35 -0
- data/spec/dummy/config/database.yml +56 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/cucumber.rb +39 -0
- data/spec/dummy/config/environments/development.rb +39 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/pusher.yml +14 -0
- data/spec/dummy/config/pusher.yml.example +14 -0
- data/spec/dummy/config/routes.rb +5 -0
- data/spec/dummy/config/sms_whitelist.json.example +4 -0
- data/spec/dummy/config/twilio.yml +57 -0
- data/spec/dummy/config/twilio.yml.example +57 -0
- data/spec/dummy/db/schema.rb +122 -0
- data/spec/dummy/db/seeds.rb +6 -0
- data/spec/dummy/log/production.log +156 -0
- data/spec/dummy/log/test.log +13812 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/assets/application-628309db7ce8c0abdd91f8f796881599.js +32 -0
- data/spec/dummy/public/assets/application-628309db7ce8c0abdd91f8f796881599.js.gz +0 -0
- data/spec/dummy/public/assets/application-6c551430f3bdd32ba57c9c41e5f91846.css +24 -0
- data/spec/dummy/public/assets/application-6c551430f3bdd32ba57c9c41e5f91846.css.gz +0 -0
- data/spec/dummy/public/assets/telephony/backspace-9f5b195e049ed90eb20ecdda6e264b0a.png +0 -0
- data/spec/dummy/public/assets/telephony/icon-spinner-ddbae473a70c5425810aeee31d4a8ad7.gif +0 -0
- data/spec/dummy/public/assets/telephony/zest-telephony-0e6b13673634da80de9ae09bcca253e3.eot +0 -0
- data/spec/dummy/public/assets/telephony/zest-telephony-37f394757ccb11b978f16d9fd32cb3b5.ttf +0 -0
- data/spec/dummy/public/assets/telephony/zest-telephony-e7efdbc60c0a1c8404951bc79c7fb3a8.woff +0 -0
- data/spec/dummy/public/assets/telephony/zest-telephony-f34287df626936908408f53a33db9e83.svg +73 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/tmp/cache/assets/C98/020/sprockets%2Fa83f8254d688334ff179984e423f502f +0 -0
- data/spec/dummy/tmp/cache/assets/CA4/550/sprockets%2F407f9191fe573f2435c55c017cb0d022 +0 -0
- data/spec/dummy/tmp/cache/assets/CAA/6A0/sprockets%2F3265550e2d2c7f5a864ca85d255504c2 +0 -0
- data/spec/dummy/tmp/cache/assets/CB0/F60/sprockets%2F462f9efed31e6710732376ae06c27208 +0 -0
- data/spec/dummy/tmp/cache/assets/CB6/E20/sprockets%2Fa7092cd9f16917099d754f1380af13e1 +0 -0
- data/spec/dummy/tmp/cache/assets/CC7/9F0/sprockets%2F0517100815cd45c43fec1d8510fcb630 +0 -0
- data/spec/dummy/tmp/cache/assets/CCA/BF0/sprockets%2F16ea15e0b5d367c34b812612a4207e3e +0 -0
- data/spec/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
- data/spec/dummy/tmp/cache/assets/CF1/960/sprockets%2F36ef26985821ec702c74dae4af789903 +0 -0
- data/spec/dummy/tmp/cache/assets/D07/F30/sprockets%2Fd12862f20df0c9ef7aa2b4216d247079 +0 -0
- data/spec/dummy/tmp/cache/assets/D09/BF0/sprockets%2F5c8595efaa8cc5235a09801c60e25a16 +0 -0
- data/spec/dummy/tmp/cache/assets/D0D/250/sprockets%2F5e759d66e374910dae5a51b221e6e3a4 +0 -0
- data/spec/dummy/tmp/cache/assets/D0D/E30/sprockets%2Fa282879e0788ba133f5bdc211cf134e8 +0 -0
- data/spec/dummy/tmp/cache/assets/D11/8E0/sprockets%2F2a193a8af29963f1113bef05788e02ff +0 -0
- data/spec/dummy/tmp/cache/assets/D14/270/sprockets%2F7f95a752910ed33d728532f8fabd340d +0 -0
- data/spec/dummy/tmp/cache/assets/D17/BE0/sprockets%2F58749d7aea4ca766c2520801f0f96b9b +0 -0
- data/spec/dummy/tmp/cache/assets/D18/270/sprockets%2Fbb2d88813b1795a97e9a7c14100eef77 +0 -0
- data/spec/dummy/tmp/cache/assets/D22/310/sprockets%2Fbae9148ce1229214dfbb0c425b0054d0 +0 -0
- data/spec/dummy/tmp/cache/assets/D30/EB0/sprockets%2F2c668b88beb4c9805d91ed4587f549e7 +0 -0
- data/spec/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/tmp/cache/assets/D3C/690/sprockets%2F681ba175fdd2da8d590b7e624b6135a6 +0 -0
- data/spec/dummy/tmp/cache/assets/D44/BC0/sprockets%2F7214f66dd91fdd51f00c3882293f5eea +0 -0
- data/spec/dummy/tmp/cache/assets/D46/FA0/sprockets%2F4f9543cd9e68ba3141fd488afb13681b +0 -0
- data/spec/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/spec/dummy/tmp/cache/assets/D51/9A0/sprockets%2Fb6e4cbcf30f6f611671695f79ea846d5 +0 -0
- data/spec/dummy/tmp/cache/assets/D55/920/sprockets%2F0a02242aa4c4febcc796419aba103c69 +0 -0
- data/spec/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/spec/dummy/tmp/cache/assets/D5F/6F0/sprockets%2F155e7bc44f326371cce0ca8bc5b2414e +0 -0
- data/spec/dummy/tmp/cache/assets/D6B/250/sprockets%2Fe4eecce7a3371b11038c92f07cb28e67 +0 -0
- data/spec/dummy/tmp/cache/assets/D6B/E70/sprockets%2F103a456ae3692e3cca20b7696fdee8f0 +0 -0
- data/spec/dummy/tmp/cache/assets/D7F/AD0/sprockets%2Fb1c02d12303b1fe2f981771cbb0c4cbd +0 -0
- data/spec/dummy/tmp/cache/assets/D81/D00/sprockets%2F0c68b6d9dce98681a2a710d9fc3f7e39 +0 -0
- data/spec/dummy/tmp/cache/assets/D94/4D0/sprockets%2F4608a5bdf27ddf11bcaed1375911e9a2 +0 -0
- data/spec/dummy/tmp/cache/assets/D9A/580/sprockets%2F433005b19aaf7ed5cba9c7f8cd30b775 +0 -0
- data/spec/dummy/tmp/cache/assets/DA4/270/sprockets%2F860ab3bd30204e02c0a5d82bd4bbabe1 +0 -0
- data/spec/dummy/tmp/cache/assets/DA4/480/sprockets%2F53e39285bb57a2ddb6ff856b2eb491da +0 -0
- data/spec/dummy/tmp/cache/assets/DA6/3C0/sprockets%2F587c7e35f47442eadaa530acc5e9f5e6 +0 -0
- data/spec/dummy/tmp/cache/assets/DAB/DA0/sprockets%2Fb8fb5e8518ed3d38a7f6a8f18a9208aa +0 -0
- data/spec/dummy/tmp/cache/assets/DB2/700/sprockets%2Ffa85cb79b94fce1b8344d983ea863d2e +0 -0
- data/spec/dummy/tmp/cache/assets/DCC/4F0/sprockets%2Fbc0f3c61e16afb5a285ba8b39846ed8b +0 -0
- data/spec/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/spec/dummy/tmp/cache/assets/DDC/BE0/sprockets%2F2c0a4d4675a61b014aedbbabb3f071fc +0 -0
- data/spec/dummy/tmp/cache/assets/E04/640/sprockets%2F3c5ddf2bbefd9d70f1b506b519cf856e +0 -0
- data/spec/dummy/tmp/cache/assets/E04/7F0/sprockets%2F35f1b2ff729a5ec21f6dc5f7d6faa67b +0 -0
- data/spec/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/tmp/cache/assets/E07/230/sprockets%2Febd4f2dd708879d593aff008ccb75acc +0 -0
- data/spec/dummy/tmp/cache/assets/E17/9D0/sprockets%2F8db2b79f6c4eabe7a9ebe7a7898ec066 +0 -0
- data/spec/dummy/tmp/cache/assets/E31/F10/sprockets%2Fcebacf56667da1cc2961b59fd922eafb +0 -0
- data/spec/factories/agent.rb +26 -0
- data/spec/factories/calls.rb +106 -0
- data/spec/factories/conversations.rb +171 -0
- data/spec/factories/events.rb +250 -0
- data/spec/factories/playable_listeners.rb +6 -0
- data/spec/factories/recordings.rb +6 -0
- data/spec/factories/voicemails.rb +8 -0
- data/spec/fixtures/vcr_cassettes/Authenticating_an_online_user.yml +90 -0
- data/spec/fixtures/vcr_cassettes/Creating_a_conversation/creates_a_new_call.yml +42 -0
- data/spec/fixtures/vcr_cassettes/Creating_a_conversation/creates_a_new_conversation.yml +42 -0
- data/spec/fixtures/vcr_cassettes/Creating_a_conversation/returns_the_conversation_as_JSON.yml +42 -0
- data/spec/fixtures/vcr_cassettes/Pusher_channel_as_JSON.yml +90 -0
- data/spec/fixtures/vcr_cassettes/Pusher_to_publish_the_event.yml +32 -0
- data/spec/fixtures/vcr_cassettes/Reloading_the_widget_during_a_call/_create/during_a_call/after_a_failed_transfer/pushes_the_event_for_the_active_call.yml +1110 -0
- data/spec/fixtures/vcr_cassettes/Telephony_ConversationsController/_create/by_default/creates_a_new_call.yml +45 -0
- data/spec/fixtures/vcr_cassettes/Telephony_ConversationsController/_create/by_default/creates_a_new_conversation.yml +43 -0
- data/spec/fixtures/vcr_cassettes/Telephony_ConversationsController/_create/by_default/returns_the_conversation_as_JSON.yml +44 -0
- data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_call/allows_the_phone_to_ring_for_60_seconds.yml +42 -0
- data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_call/includes_a_status_change_callback_url.yml +42 -0
- data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_call/places_a_call.yml +42 -0
- data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_dial_into_conference/allows_the_phone_to_ring_for_15_seconds.yml +42 -0
- data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_dial_into_conference/places_a_call_to_redirect_the_participant_to_a_conference.yml +42 -0
- data/spec/fixtures/vcr_cassettes/Telephony_Providers_TwilioProvider/_hangup/by_default/hangs_up_the_call.yml +42 -0
- data/spec/fixtures/vcr_cassettes/buying_number_for_an_area_code.yml +42 -0
- data/spec/fixtures/vcr_cassettes/returns_success.yml +90 -0
- data/spec/javascripts/helpers/jasmine-jquery.js +546 -0
- data/spec/javascripts/helpers/mock-ajax.js +207 -0
- data/spec/javascripts/support/jasmine.yml +80 -0
- data/spec/javascripts/telephony/models/agent_spec.js +40 -0
- data/spec/javascripts/telephony/models/conversation_spec.js +515 -0
- data/spec/javascripts/telephony/models/device_spec.js +80 -0
- data/spec/javascripts/telephony/models/transfer_spec.js +22 -0
- data/spec/javascripts/telephony/push_spec.js +427 -0
- data/spec/javascripts/telephony/views/agents_view_spec.js +101 -0
- data/spec/javascripts/telephony/views/application_view_spec.js +74 -0
- data/spec/javascripts/telephony/views/conversation_view_spec.js +626 -0
- data/spec/javascripts/telephony/views/status_view_spec.js +30 -0
- data/spec/javascripts/telephony/views/transfer_view_spec.js +187 -0
- data/spec/javascripts/telephony/views/twilio_client_view_spec.js +77 -0
- data/spec/javascripts/telephony/views/widget_view_spec.js +20 -0
- data/spec/lib/telephony/concerns/controllers/twilio_request_verifier_spec.rb +77 -0
- data/spec/lib/telephony/conversation_data_spec.rb +342 -0
- data/spec/lib/telephony/helper_spec.rb +24 -0
- data/spec/lib/telephony/jobs/agent_offline_spec.rb +37 -0
- data/spec/lib/telephony/jobs/pusher_event_spec.rb +39 -0
- data/spec/lib/telephony/providers/twilio_provider_spec.rb +439 -0
- data/spec/lib/telephony_spec.rb +104 -0
- data/spec/models/telephony/agent_spec.rb +479 -0
- data/spec/models/telephony/agent_state_machine_spec.rb +131 -0
- data/spec/models/telephony/call_center_spec.rb +32 -0
- data/spec/models/telephony/call_spec.rb +597 -0
- data/spec/models/telephony/call_state_machine_spec.rb +472 -0
- data/spec/models/telephony/conversation_spec.rb +751 -0
- data/spec/models/telephony/conversation_state_machine_spec.rb +387 -0
- data/spec/models/telephony/conversations_presenter_spec.rb +40 -0
- data/spec/models/telephony/event_spec.rb +716 -0
- data/spec/models/telephony/inbound_conversation_queue_spec.rb +243 -0
- data/spec/models/telephony/playable_listener_spec.rb +43 -0
- data/spec/models/telephony/pusher_event_publisher_spec.rb +36 -0
- data/spec/models/telephony/recording_spec.rb +6 -0
- data/spec/models/telephony/voicemail_spec.rb +96 -0
- data/spec/observers/telephony/agent_observer_spec.rb +21 -0
- data/spec/observers/telephony/event_observer_spec.rb +19 -0
- data/spec/requests/create_conversation_spec.rb +39 -0
- data/spec/requests/dequeue_call_spec.rb +42 -0
- data/spec/requests/list_conversations_spec.rb +33 -0
- data/spec/requests/presences_spec.rb +51 -0
- data/spec/requests/providers/twilio/calls/child_answered_spec.rb +24 -0
- data/spec/requests/providers/twilio/calls/child_detached_spec.rb +249 -0
- data/spec/requests/providers/twilio/calls/dial_spec.rb +132 -0
- data/spec/requests/providers/twilio/calls/done_spec.rb +69 -0
- data/spec/requests/providers/twilio/calls/join_conference_spec.rb +56 -0
- data/spec/requests/providers/twilio/calls/leave_queue_spec.rb +44 -0
- data/spec/requests/providers/twilio/calls/parent_answered_spec.rb +59 -0
- data/spec/requests/providers/twilio/inbound/connect_dequeued_call_spec.rb +124 -0
- data/spec/requests/providers/twilio/inbound/enqueue_inbound_call_spec.rb +108 -0
- data/spec/requests/providers/twilio/inbound/enqueued_call_wait_music_spec.rb +13 -0
- data/spec/requests/providers/twilio/voicemails/leave_voicemail_spec.rb +78 -0
- data/spec/requests/search_conversations_spec.rb +101 -0
- data/spec/requests/transfer_spec.rb +65 -0
- data/spec/requests/voicemail_api_spec.rb +58 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/factory_girl.rb +5 -0
- data/spec/support/matchers/be_complete_hold.rb +20 -0
- data/spec/support/matchers/be_whisper_tone.rb +35 -0
- data/spec/support/matchers/join_conference.rb +23 -0
- data/spec/support/nokogiri.rb +1 -0
- data/spec/support/pusher_helper.rb +24 -0
- data/spec/support/vcr.rb +12 -0
- data/spec/support/webmock.rb +1 -0
- metadata +692 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
!binary "U0hBMQ==":
|
|
3
|
+
metadata.gz: !binary |-
|
|
4
|
+
OTQ3MjJkODgzYzMxN2M2YzliMjk1NzU2ODdjNTNhMWI1YmRiYzVhNQ==
|
|
5
|
+
data.tar.gz: !binary |-
|
|
6
|
+
NGNmNDBhYWI4ODM5NzA0YjIxYjBhMjRhZTdiOTVmYTJlNTExMmY4NA==
|
|
7
|
+
SHA512:
|
|
8
|
+
metadata.gz: !binary |-
|
|
9
|
+
NzFlNzMwMjZhMDQ2NWYxZTk5MWM4NmQ2ZTdhNWJiYjBmZDYzMmIzNTM5OWVh
|
|
10
|
+
NjRmNTQ1YTBlNzQ2NGMyZWNmOGY5ZDBmMGIwMGRlZGJiYjhhYTY5MWM1ZGFi
|
|
11
|
+
NDhhNDJhY2E1NGQyNzVkNGJkN2IwZTJkNDVkOWY1NDE0ZTUxZDA=
|
|
12
|
+
data.tar.gz: !binary |-
|
|
13
|
+
YzQ4ZDE4MjY4ODdjMjNkODE5ZTEwODNlMTNiMzczMmJhOGVmZWE4NTkxMTc5
|
|
14
|
+
YTEyNzIwMTgzYWE5NjRiOWQxYzQ2OTI3ZTNmMjY1ZDI2ZWQxODhiYWE2ZmQ4
|
|
15
|
+
OTZjYmQwMjgwYjI5ZDdlYmU3Mjc0ZmM5N2NlMTg4MmY3YjdlNWI=
|
data/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
Telephony call-center software
|
|
2
|
+
=======
|
|
3
|
+
ZestPhone
|
|
4
|
+
================
|
|
5
|
+
|
|
6
|
+
## Who we are
|
|
7
|
+
We are Zestfinance (zestfinance.com) a financial services company that helps lenders with underwriting and loan servicing. In addition to building cutting-edge underwriting algorithms, we’ve also built a host of home-grown CRM tools and loan servicing products for our lending partners. We recently added call-center style phone controls into our existing CRM by leveraging Twilio’s cloud-based communication technology, which we call ZestPhone.
|
|
8
|
+
|
|
9
|
+
## What is ZestPhone?
|
|
10
|
+
ZestPhone is a drop-in phone widget that can be simply added to any web application to give you full call center functionality.
|
|
11
|
+
|
|
12
|
+
## Features:
|
|
13
|
+
- Inbound and outbound calling
|
|
14
|
+
- Call outbound to anywhere in the world*
|
|
15
|
+
- Set up your existing business phone number with Twilio or purchase one from Twilio to get started
|
|
16
|
+
- Have multiple business phone numbers in use for marketing attribution
|
|
17
|
+
- Cold and warm transfer
|
|
18
|
+
- Allow your agents transfer calls to each other either directly or with introduction
|
|
19
|
+
- Put a call on hold / resume a call
|
|
20
|
+
- Agent status
|
|
21
|
+
- Ability to respond to telephony events in your application
|
|
22
|
+
- Uses existing agent names/numbers/ids
|
|
23
|
+
- Embeds into your existing javascript-enabled application
|
|
24
|
+
- PSTN or VoIP (i.e. use a regular phone, SIP, or twilio client)
|
|
25
|
+
- Call recording
|
|
26
|
+
- All calls are recorded while the customer is on the line with an agent and are stored by Twilio (storage rates apply)
|
|
27
|
+
- Voicemail
|
|
28
|
+
- Customers can be transferred to a specific agent’s voicemail if the agent is busy with another customer or offline
|
|
29
|
+
|
|
30
|
+
## Why use ZestPhone / Am I (or my company) a good candidate?
|
|
31
|
+
- **Quick integration** You want to quickly and easily add phone capabilities to your application
|
|
32
|
+
- **1:1 Customer Service Experience** You want to empower your call center agents to provide 1:1 support to customers with easy-to-use transfer and voicemail capabilities
|
|
33
|
+
- **Use any type of phone** You have call centers with different phone setups and want software to support them all (PSTN, SIP, VoIP)
|
|
34
|
+
- **Low cost per minute** You want to take advantage of twilio’s competitive usage pricing
|
|
35
|
+
|
|
36
|
+
## Running ZestPhone server
|
|
37
|
+
|
|
38
|
+
ZestPhone is a ruby on rails application, backed by a MySql database.
|
|
39
|
+
It's only known to work for sure using ruby 1.9.3, but any 1.9.x ruby version is probably fine.
|
|
40
|
+
|
|
41
|
+
- You need a Twilio account of course.
|
|
42
|
+
- A Pusher account
|
|
43
|
+
- A database that is supported by rails, and that supports locking (only MySql is known to work)
|
|
44
|
+
- Your application needs to include backbone.js
|
|
45
|
+
|
|
46
|
+
The complete setup guide is here: https://github.com/ZestFinance/zestphone/blob/master/SETUP.md
|
|
47
|
+
|
|
48
|
+
## Javascript events your app can respond to
|
|
49
|
+
|
|
50
|
+
All events have this basic data:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
event_id:
|
|
55
|
+
conversation_id:
|
|
56
|
+
conversation_state:
|
|
57
|
+
call_id: # This will be empty for events that happen at the conversation level
|
|
58
|
+
number: # The customer's number
|
|
59
|
+
owner: # Is the agent receiving this event the conversation owner?
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- **telephony:Answer**:
|
|
64
|
+
Fired when the agent answers his phone
|
|
65
|
+
- **telephony:Busy**:
|
|
66
|
+
Fired to an agent whose phone was busy
|
|
67
|
+
- **telephony:CallFail**:
|
|
68
|
+
Sent to an agent when the agent leg fails
|
|
69
|
+
- **telephony:CompleteHold**:
|
|
70
|
+
Sent to all agents when the customer has been put on hold
|
|
71
|
+
- **telephony:CompleteOneStepTransfer**:
|
|
72
|
+
Sent to the agent who just answered a one-step transfer
|
|
73
|
+
- **telephony:CompleteResume**:
|
|
74
|
+
Sent to all agents when the customer is taken off of hold
|
|
75
|
+
- **telephony:CompleteTwoStepTransfer**:
|
|
76
|
+
Sent to all agents after the 2nd agent picks up in a two-step transfer. The data sent to agent1 will include agent2's name, extension, and csr_type. The data sent to agent2 will include that data for agent1.
|
|
77
|
+
|
|
78
|
+
- **telephony:Conference**
|
|
79
|
+
Sent to an agent who was just moved into a conference room. This can happen during hold or transfer actions, and is not very interesting on it's own.
|
|
80
|
+
- **telephony:Connect**
|
|
81
|
+
- **telephony:csrDidChangeStatus**
|
|
82
|
+
- **telephony:csrNotAvailable**
|
|
83
|
+
- **telephony:CustomerLeftTwoStepTransfer**
|
|
84
|
+
- **telephony:FailOneStepTransfer**
|
|
85
|
+
- **telephony:FailTwoStepTransfer**
|
|
86
|
+
- **telephony:InitiateOneStepTransfer**
|
|
87
|
+
- **telephony:InitiateTwoStepTransfer**
|
|
88
|
+
- **telephony:InitializeWidget**
|
|
89
|
+
- **telephony:LeaveTwoStepTransfer**
|
|
90
|
+
- **telephony:LeaveVoicemail**
|
|
91
|
+
- **telephony:NoAnswer**
|
|
92
|
+
- **telephony:QueueChange**
|
|
93
|
+
- **telephony:RemoveFromConference**
|
|
94
|
+
- **telephony:Start**
|
|
95
|
+
- **telephony:Terminate**
|
|
96
|
+
- **telephony:toggleCsrStatus**
|
|
97
|
+
- **telephony:WidgetReady**
|
|
98
|
+
- **telephony:conversationCreated**
|
|
99
|
+
- **transferInitiated**
|
|
100
|
+
|
|
101
|
+
## Javascript events you can trigger from your app
|
|
102
|
+
- **telephony:ClickToCall**:
|
|
103
|
+
Sample usage: ```javascript
|
|
104
|
+
$(this).trigger("telephony:ClickToCall", to: $(event.currentTarget).text());
|
|
105
|
+
```
|
data/Rakefile
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
begin
|
|
8
|
+
require 'rdoc/task'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
require 'rdoc/rdoc'
|
|
11
|
+
require 'rake/rdoctask'
|
|
12
|
+
RDoc::Task = Rake::RDocTask
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
17
|
+
rdoc.title = 'Telephony'
|
|
18
|
+
rdoc.options << '--line-numbers'
|
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
|
24
|
+
load 'rails/tasks/engine.rake'
|
|
25
|
+
|
|
26
|
+
Bundler::GemHelper.install_tasks
|
|
27
|
+
|
|
28
|
+
require 'rspec/core/rake_task'
|
|
29
|
+
|
|
30
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
require 'jasmine/headless'
|
|
34
|
+
|
|
35
|
+
Jasmine::Headless::Task.new do |t|
|
|
36
|
+
t.colors = true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
task default: [:spec, 'jasmine:headless']
|
|
Binary file
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<?xml version="1.0" standalone="no"?>
|
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
|
3
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
|
4
|
+
<metadata>
|
|
5
|
+
Created by FontForge 20100429 at Sat Dec 22 19:34:50 2012
|
|
6
|
+
By root
|
|
7
|
+
Copyright (C) 2012 by original authors @ fontello.com
|
|
8
|
+
</metadata>
|
|
9
|
+
<defs>
|
|
10
|
+
<font id="zest-telephony" horiz-adv-x="852" >
|
|
11
|
+
<font-face
|
|
12
|
+
font-family="zest-telephony"
|
|
13
|
+
font-weight="500"
|
|
14
|
+
font-stretch="normal"
|
|
15
|
+
units-per-em="1000"
|
|
16
|
+
panose-1="2 0 6 3 0 0 0 0 0 0"
|
|
17
|
+
ascent="850"
|
|
18
|
+
descent="-150"
|
|
19
|
+
bbox="15 -150 1015 850"
|
|
20
|
+
underline-thickness="50"
|
|
21
|
+
underline-position="-100"
|
|
22
|
+
unicode-range="U+26A0-1F514"
|
|
23
|
+
/>
|
|
24
|
+
<missing-glyph horiz-adv-x="364"
|
|
25
|
+
d="M33 0v666h265v-666h-265zM66 33h199v600h-199v-600z" />
|
|
26
|
+
<glyph glyph-name=".notdef" horiz-adv-x="364"
|
|
27
|
+
d="M33 0v666h265v-666h-265zM66 33h199v600h-199v-600z" />
|
|
28
|
+
<glyph glyph-name=".null" horiz-adv-x="0"
|
|
29
|
+
/>
|
|
30
|
+
<glyph glyph-name="nonmarkingreturn" horiz-adv-x="333"
|
|
31
|
+
/>
|
|
32
|
+
<glyph glyph-name="uni26A0" unicode="⚠" horiz-adv-x="944"
|
|
33
|
+
d="M77 -53q-25 0 -43 12t-18 35q0 21 11 38l395 684q22 37 49.5 37t49.5 -37l396 -684q12 -19 12 -38q0 -23 -18 -35t-43 -12h-791zM430 537q-18 0 -18 -17l6 -326q0 -7 5.5 -12.5t12.5 -5.5h72q7 0 12 5.5t5 12.5l8 326q0 7 -5.5 12t-12.5 5h-85zM415 33q0 -18 18 -18h79
|
|
34
|
+
q18 0 18 18v76q0 19 -18 19h-79q-18 0 -18 -19v-76z" />
|
|
35
|
+
<glyph glyph-name="uni2714" unicode="✔"
|
|
36
|
+
d="M16 350q0 171 120 291t290.5 120t290.5 -120t120 -291t-120 -291t-290.5 -120t-290.5 120t-120 291zM139 325q0 -11 7 -18l165 -164q18 -18 42 -18h28q24 0 42 18l284 283q17 18 0 35l-54 54q-8 8 -18.5 8t-17.5 -8l-233 -232q-7 -8 -17 -8t-17 8l-114 113q-7 8 -17 8
|
|
37
|
+
q-11 0 -19 -8l-54 -54q-7 -8 -7 -17z" />
|
|
38
|
+
<glyph glyph-name="uni2716" unicode="✖"
|
|
39
|
+
d="M16 350q0 171 120 291t290.5 120t290.5 -120t120 -291t-120 -291t-290.5 -120t-290.5 120t-120 291zM214 214q0 -5 5 -12l59 -60q13 -13 24 0l126 125l112 -114q5 -5 13 -5q6 0 11 5l58 58q12 12 0 24l-113 113l126 126q12 12 0 24l-60 59q-13 13 -24 0l-126 -125
|
|
40
|
+
l-113 114q-6 5 -12 5q-5 0 -13 -5l-56 -58q-13 -12 0 -24l112 -113l-124 -125q-5 -6 -5 -12z" />
|
|
41
|
+
<glyph glyph-name="uniE782" unicode="" horiz-adv-x="979"
|
|
42
|
+
d="M56 -45q-17 0 -28.5 11.5t-11.5 28.5v59q0 17 11.5 28t28.5 11h79q16 0 27.5 -11.5t11.5 -27.5v-59q0 -17 -11 -28.5t-28 -11.5h-79zM56 172q-17 0 -28.5 11.5t-11.5 28.5v59q0 17 11.5 28.5t28.5 11.5h79q17 0 28 -11.5t11 -28.5v-59q0 -17 -11 -28.5t-28 -11.5h-79z
|
|
43
|
+
M56 390q-17 0 -28.5 11t-11.5 28v59q0 17 11.5 28.5t28.5 11.5h79q17 0 28 -11.5t11 -28.5v-59q0 -17 -11 -28t-28 -11h-79zM56 607q-17 0 -28.5 11.5t-11.5 27.5v60q0 17 11.5 28t28.5 11h79q16 0 27.5 -11.5t11.5 -27.5v-60q0 -16 -11.5 -27.5t-27.5 -11.5h-79zM293 -45
|
|
44
|
+
q-17 0 -28.5 11.5t-11.5 28.5v59q0 17 11.5 28t28.5 11h632q15 0 26.5 -11.5t11.5 -27.5v-59q0 -17 -11 -28.5t-27 -11.5h-632zM293 172q-17 0 -28.5 11.5t-11.5 28.5v59q0 17 11.5 28.5t28.5 11.5h632q16 0 27 -11.5t11 -28.5v-59q0 -17 -11 -28.5t-27 -11.5h-632zM293 390
|
|
45
|
+
q-17 0 -28.5 11t-11.5 28v59q0 17 11.5 28.5t28.5 11.5h632q16 0 27 -11.5t11 -28.5v-59q0 -17 -11 -28t-27 -11h-632zM253 646v60q0 17 11.5 28t28.5 11h632q15 0 26.5 -11.5t11.5 -27.5v-60q0 -16 -11.5 -27.5t-26.5 -11.5h-632q-17 0 -28.5 11.5t-11.5 27.5z" />
|
|
46
|
+
<glyph glyph-name="uniE838" unicode="" horiz-adv-x="1030"
|
|
47
|
+
d="M335 586q-11 -5 -16 5l-91 157q-6 10 4 16l61 35q11 5 16 -5l91 -157q6 -10 -4 -16zM276 525l-35 -60q-5 -10 -16 -5l-158 92q-9 5 -4 15l35 61q5 10 16 4l158 -91q12 -6 4 -16zM220 381v-69q0 -11 -11 -11h-182q-12 0 -12 11v1v69q0 4 3.5 8t8.5 4h182q11 0 11 -12v-1z
|
|
48
|
+
M279 170q5 -11 -5 -16l-157 -91q-10 -6 -16 4l-35 61q-5 11 5 16l157 91q10 6 16 -4zM340 111l60 -35q10 -5 5 -16l-92 -158q-5 -9 -15 -4l-61 35q-10 5 -4 16l91 158q6 12 16 4zM484 55h69q11 0 11 -11v-182q0 -12 -11 -12h-1h-69q-5 0 -8.5 4t-3.5 8v182q0 11 12 11h1z
|
|
49
|
+
M695 114q11 5 16 -5l91 -157q6 -10 -4 -16l-61 -35q-11 -5 -16 5l-91 157q-6 10 4 16zM754 175l35 60q5 10 16 5l158 -92q9 -5 4 -15l-35 -61q-5 -10 -16 -4l-158 91q-12 6 -4 16zM810 319v69q0 11 11 11h182q12 0 12 -11v-1v-69q0 -4 -3.5 -8t-8.5 -4h-182q-11 0 -11 12v1z
|
|
50
|
+
M751 530q-5 11 5 16l157 91q10 6 16 -4l35 -61q5 -11 -5 -16l-157 -91q-10 -6 -16 4zM690 589l-60 35q-10 5 -5 16l92 158q5 9 15 4l61 -35q10 -5 4 -16l-91 -158q-6 -12 -16 -4zM546 645h-69q-11 0 -11 11v182q0 12 11 12h1h69q5 0 8.5 -4t3.5 -8v-182q0 -11 -12 -11h-1z
|
|
51
|
+
" />
|
|
52
|
+
<glyph glyph-name="uniF098" unicode="" horiz-adv-x="821"
|
|
53
|
+
d="M115 -45q-41 0 -70 29t-29 70v592q0 63 60 90q19 9 39 9h592q41 0 70 -29t29 -70v-592q0 -63 -62 -90q-20 -9 -37 -9h-592zM115 490q1 -13 3 -25t8 -27l8 -22l12 -24t10 -19q95 -189 258 -273q17 -8 82 -32q56 -22 99 -11q39 10 76 35q26 19 36 80q1 8 -2.5 14.5t-7 9
|
|
54
|
+
t-12 7t-11.5 6.5q-79 45 -90 53q-12 7 -20 7q-4 0 -9 -2.5t-10.5 -8t-8.5 -9.5l-9 -10q-5 -6 -6 -7q-2 -2 -8.5 -10.5t-10.5 -12t-12.5 -10t-13.5 -5.5l-15 5q-3 3 -9.5 6.5t-9.5 5.5q-110 61 -173 180q-8 8 -4 18t7 14t11.5 11t11.5 10q1 2 6 7.5t8 8.5t6.5 8.5t4.5 10.5
|
|
55
|
+
t1 11.5t-2 13.5q-35 84 -43 121q-2 7 -7 12.5t-15 7t-15.5 2t-20 0t-18.5 -0.5h-4q-57 -27 -76 -104q-6 -28 -5 -52z" />
|
|
56
|
+
<glyph glyph-name="uniF0E0" unicode="" horiz-adv-x="979"
|
|
57
|
+
d="M75 -45q-24 0 -41.5 17.5t-17.5 41.5v466q6 -7 21 -17l261 -199q82 -66 138 -82q27 -9 53 -9q73 0 149 57q32 23 89 68.5t84 65.5l142 107q6 4 10 9v-466q0 -24 -17 -41.5t-41 -17.5h-830zM17 692q0 20 18.5 36.5t39.5 16.5h830q21 0 39 -16.5t18 -36.5q0 -28 -32 -73
|
|
58
|
+
q-29 -38 -57 -61l-248 -187q-10 -7 -29.5 -23t-37 -28t-34 -20.5t-33.5 -8.5h-2q-28 0 -71 31q-12 8 -34 26l-30 23l-124 94l-124 93q-53 43 -80 99q-9 19 -9 35z" />
|
|
59
|
+
<glyph glyph-name="u1F464" unicode="👤" horiz-adv-x="837"
|
|
60
|
+
d="M16 -24v199q0 39 33 87q36 49 71 60q24 7 86 14q13 2 46.5 6t47.5 6q-45 28 -73 76t-28 109q0 89 64 155q63 65 155 65q91 0 156 -65q64 -64 64 -155q0 -60 -28.5 -108.5t-72.5 -76.5l69 -8q52 -7 76 -11t31.5 -5.5t17.5 -7t22 -15.5q40 -37 59 -84q9 -24 9 -42v-199
|
|
61
|
+
q-7 -3 -20.5 -13t-18.5 -12q-8 -4 -11 -4h-705q-18 0 -27 11q-8 10 -23 18z" />
|
|
62
|
+
<glyph glyph-name="u1F4DE" unicode="📞"
|
|
63
|
+
d="M16 556q0 94 65 168q22 24 47 36q6 -1 16 0t12 1h15l21 -1q-1 -1 33 -7q7 -4 10.5 -12.5t7.5 -21.5t6 -17q7 -21 24 -68l23 -64q7 -21 7 -27q0 -30 -40 -65q-21 -18 -28 -26q-13 -12 -13 -23q0 -8 22 -48q45 -76 98 -129t129 -96q39 -23 49.5 -23t27.5 17q7 7 28.5 33.5
|
|
64
|
+
t33.5 38.5q17 16 30 16q3 0 27 -11q56 -31 93 -54q36 -22 55 -31q22 -11 22 -31q0 -32 -19 -81q-26 -59 -131 -84q-28 -7 -48 -7q-14 0 -29.5 2.5t-25.5 4.5t-27.5 7.5t-23 8t-25.5 10t-23 8.5q-174 65 -314 247q-83 106 -127 217q-28 67 -28 112z" />
|
|
65
|
+
<glyph glyph-name="u1F50D" unicode="🔍"
|
|
66
|
+
d="M547 133q-90 -57 -189 -57q-140 0 -242 100q-100 100 -100 242q0 144 100 242q101 101 242 101q138 0 241 -101q101 -102 101 -242q0 -99 -57 -188l185 -184q9 -10 9 -25q0 -16 -32.5 -49t-49.5 -33q-15 0 -24 10zM153 418q0 -68 35 -116.5t91 -72.5q38 -16 79 -16
|
|
67
|
+
q67 0 115.5 35.5t73.5 89.5q16 40 16 80q0 84 -61 145q-59 61 -144 61q-67 0 -115.5 -36t-72.5 -91q-17 -40 -17 -79zM229 418q0 43 22.5 74t55.5 45q27 11 51 11q11 0 18 -8t7 -18.5t-7 -18t-18 -7.5q-33 0 -55.5 -22.5t-22.5 -55.5q0 -10 -7.5 -17.5t-17.5 -7.5
|
|
68
|
+
q-12 0 -19 7.5t-7 17.5z" />
|
|
69
|
+
<glyph glyph-name="u1F514" unicode="🔔" horiz-adv-x="936"
|
|
70
|
+
d="M106 36q-35 0 -61.5 24.5t-28.5 57.5q144 109 176 308q8 54 13 123q6 84 94 150q83 62 170 62q83 0 169 -62q93 -66 93 -148q15 -299 189 -433q-2 -34 -28 -58t-62 -24h-241q-10 -41 -42.5 -69t-78 -28t-78.5 28.5t-43 68.5h-241zM798 118q-128 142 -148 423
|
|
71
|
+
q-5 53 -67 98q-57 40 -114 40q-54 0 -114 -40q-64 -43 -68 -94q-13 -273 -149 -427h660zM382 63q0 -36 25.5 -61.5t61.5 -25.5q9 0 9 10.5t-9 10.5q-28 0 -47.5 19.5t-19.5 46.5q0 10 -9 10q-11 0 -11 -10z" />
|
|
72
|
+
</font>
|
|
73
|
+
</defs></svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//= require "./vendor/pusher"
|
|
2
|
+
|
|
3
|
+
//= require_tree "../templates/"
|
|
4
|
+
//= require "telephony/namespace"
|
|
5
|
+
//= require "telephony/config"
|
|
6
|
+
//= require "telephony/push"
|
|
7
|
+
//= require "telephony/models/device"
|
|
8
|
+
//= require "telephony/models/agent"
|
|
9
|
+
//= require "telephony/models/transfer"
|
|
10
|
+
//= require "telephony/models/conversation"
|
|
11
|
+
//= require "telephony/collections/agents"
|
|
12
|
+
//= require_tree "./views"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Zest.Telephony.Config = {
|
|
2
|
+
PUSHER_APP_KEY: "<%= PUSHER_CONFIG['app_key'] %>",
|
|
3
|
+
STYLESHEET_PATH: "<%= asset_path('telephony/widget.css') %>",
|
|
4
|
+
BASE_PATH: "/zestphone",
|
|
5
|
+
AGENT_PATH: "/zestphone/agents",
|
|
6
|
+
TWILIO_CLIENT_ENABLED: $('#telephony-widget').data('agent_phone_type') == 'twilio_client',
|
|
7
|
+
TWILIO_CLIENT_URL: "//static.twilio.com/libs/twiliojs/1.1/twilio.min.js",
|
|
8
|
+
TWILIO_CLIENT_TOKEN_PATH: "/zestphone/twilio_client/token",
|
|
9
|
+
CONVERSATION_PATH: "/zestphone/conversations"
|
|
10
|
+
}
|
|
11
|
+
Pusher.channel_auth_endpoint = "/zestphone/signals/agents/presences/authenticate";
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Zest.Telephony.Models.Agent = Backbone.Model.extend({
|
|
2
|
+
urlRoot: Zest.Telephony.Config.AGENT_PATH,
|
|
3
|
+
|
|
4
|
+
defaults: {
|
|
5
|
+
"status": "loading...",
|
|
6
|
+
"in_transition": false
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
url: function() {
|
|
10
|
+
return this.urlRoot + "/" + this.get("csr_id");
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
statusUrl: function() {
|
|
14
|
+
return this.url() + "/status"
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
status: function() {
|
|
18
|
+
return this.get('status');
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
displayText: function() {
|
|
22
|
+
return this.get("csr_type") + " " + this.get("name") + " x" + this.get("phone_ext");
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
toggleAvailable: function() {
|
|
26
|
+
this.updateStatus("toggle_available");
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
updateStatus: function(eventName) {
|
|
30
|
+
var data = {};
|
|
31
|
+
data["event"] = eventName || "not_available";
|
|
32
|
+
|
|
33
|
+
this.set({in_transition: true});
|
|
34
|
+
$.ajax(this.statusUrl(), {data: data, type: 'PUT'})
|
|
35
|
+
.done($.proxy(this.done, this))
|
|
36
|
+
.fail($.proxy(this.fail, this))
|
|
37
|
+
.always($.proxy(this.always, this));
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
isValid: function(opts) {
|
|
41
|
+
opts = opts || {}
|
|
42
|
+
$.ajax(this.url(), {data: this.toJSON(), type: 'PUT'})
|
|
43
|
+
.done(opts['done_callback'])
|
|
44
|
+
.fail(opts['fail_callback']);
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
disabled: function() {
|
|
48
|
+
if (this.onACall() || this.offline() || this.inTransition()) {
|
|
49
|
+
return "disabled";
|
|
50
|
+
}
|
|
51
|
+
return "";
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
display: function() {
|
|
55
|
+
if (this.inTransition() || this.loading()) {
|
|
56
|
+
return "display";
|
|
57
|
+
}
|
|
58
|
+
return "";
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
inTransition: function() {
|
|
62
|
+
return this.get("in_transition");
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
onACall: function() {
|
|
66
|
+
return this.get("status") === "on_a_call";
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
offline: function() {
|
|
70
|
+
return this.get("status") === "offline";
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
available: function() {
|
|
74
|
+
return this.get("status") === "available";
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
notAvailable: function() {
|
|
78
|
+
return this.get("status") === "not_available";
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
loading: function() {
|
|
82
|
+
return this.get("status") === "loading...";
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
humanizedStatus: function() {
|
|
86
|
+
var humanizedStatus = this.get('status').replace(/_/g, ' ');
|
|
87
|
+
return humanizedStatus.substr(0,1).toUpperCase() + humanizedStatus.substr(1);
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
done: function(data) {
|
|
91
|
+
this.set(data, {silent: true});
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
always: function() {
|
|
95
|
+
this.set({in_transition: false});
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
fail: function(xhr, textStatus, errorThrown) {
|
|
99
|
+
if (typeof console === "object" && typeof console.log === "function") {
|
|
100
|
+
console.log("Unable to update the status: " + textStatus);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
Zest.Telephony.Models.Conversation = Backbone.Model.extend({
|
|
2
|
+
urlRoot: Zest.Telephony.Config.CONVERSATION_PATH,
|
|
3
|
+
|
|
4
|
+
defaults: {
|
|
5
|
+
state: "disabled_by_default"
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
initialize: function() {
|
|
9
|
+
this.enabledHoldStates = ['in_progress', 'in_progress_two_step_transfer'];
|
|
10
|
+
this.disabledHoldStates = ['two_step_transferring', 'initiating_hold', 'agents_only'];
|
|
11
|
+
this.enabledResumeStates = ['in_progress_hold', 'in_progress_two_step_transfer_hold'];
|
|
12
|
+
this.disabledResumeStates = ['initiating_resume', 'initiating_two_step_transfer_resume', 'two_step_transferring_hold'];
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
create: function() {
|
|
16
|
+
var data = {
|
|
17
|
+
loan_id: this.get("loanId"),
|
|
18
|
+
from: this.get("from"),
|
|
19
|
+
from_id: this.get("fromId"),
|
|
20
|
+
from_type: 'csr',
|
|
21
|
+
to: this.get("to"),
|
|
22
|
+
to_id: this.get("toId"),
|
|
23
|
+
to_type: this.get("toType"),
|
|
24
|
+
owner: this.get("owner")
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
this.set({ state: "connecting" });
|
|
28
|
+
$.ajax(this.url(), { data: data, type: 'POST' })
|
|
29
|
+
.done($.proxy(this.createSuccess, this))
|
|
30
|
+
.fail($.proxy(this.createFail, this));
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
hold: function() {
|
|
34
|
+
var oldState = this.get('state');
|
|
35
|
+
var newState = "initiating_hold";
|
|
36
|
+
|
|
37
|
+
this.set({ state: newState });
|
|
38
|
+
$.ajax(this.url() + "/hold", {type: 'POST'})
|
|
39
|
+
.done($.proxy(this.holdResumeSuccess, this))
|
|
40
|
+
.fail($.proxy(this.holdResumeFail(oldState, newState), this));
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
resume: function() {
|
|
44
|
+
var oldState = this.get('state');
|
|
45
|
+
var newState = "initiating_resume";
|
|
46
|
+
|
|
47
|
+
this.set({ state: newState });
|
|
48
|
+
$.ajax(this.url() + "/resume", {type: 'POST'})
|
|
49
|
+
.done($.proxy(this.holdResumeSuccess, this))
|
|
50
|
+
.fail($.proxy(this.holdResumeFail(oldState, newState), this));
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
holdResumeSuccess: function(data) {
|
|
54
|
+
this.set(data);
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
holdResumeFail: function(oldState, newState) {
|
|
58
|
+
return function(xhr, textStatus, errorThrown) {
|
|
59
|
+
var body = JSON.parse(xhr.responseText);
|
|
60
|
+
var data = {
|
|
61
|
+
error: body.errors[0],
|
|
62
|
+
oldState: oldState,
|
|
63
|
+
newState: newState
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
$(document).trigger('holdResumeFail', data);
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
uiDisableCall: function() {
|
|
71
|
+
if (this.get('callingDisabled')) {
|
|
72
|
+
return 'disabled';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return this._enabledForStates(['not_initiated', 'terminated']);
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
uiShowCall: function() {
|
|
79
|
+
var displayableStates = ['not_initiated', 'connecting', 'terminated'];
|
|
80
|
+
return this._shownForStates(displayableStates);
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
uiDisableTransfer: function() {
|
|
84
|
+
var enabledStates = ['in_progress', 'in_progress_hold'];
|
|
85
|
+
return this._enabledForStates(enabledStates);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
uiShowTransfer: function() {
|
|
89
|
+
if (this.get("isCancelable")) {
|
|
90
|
+
return "hidden";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
var displayableStates = [
|
|
94
|
+
'in_progress',
|
|
95
|
+
'one_step_transferring',
|
|
96
|
+
'two_step_transferring',
|
|
97
|
+
'two_step_transferring_hold',
|
|
98
|
+
'initiating_two_step_transfer_resume',
|
|
99
|
+
'initiating_two_step_transfer_hold',
|
|
100
|
+
'in_progress_two_step_transfer_hold',
|
|
101
|
+
'in_progress_two_step_transfer',
|
|
102
|
+
'initiating_hold',
|
|
103
|
+
'in_progress_hold',
|
|
104
|
+
'initiating_resume',
|
|
105
|
+
'agents_only'
|
|
106
|
+
];
|
|
107
|
+
return this._shownForStates(displayableStates);
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
uiShowCancelTransfer: function() {
|
|
111
|
+
return this.get("isCancelable") ? "" : "hidden";
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
uiShowResume: function() {
|
|
115
|
+
var displayableStates = this.enabledResumeStates.concat(this.disabledResumeStates);
|
|
116
|
+
return this._shownForStates(displayableStates);
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
uiDisableResume: function() {
|
|
120
|
+
return this._disabledIf(this._statesIn(this.disabledResumeStates) || !this.get("owner"));
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
uiShowHold: function() {
|
|
124
|
+
var displayableStates = this.enabledHoldStates.concat(this.disabledHoldStates);
|
|
125
|
+
return this._shownForStates(displayableStates);
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
uiDisableHold: function() {
|
|
129
|
+
return this._disabledIf(this._statesIn(this.disabledHoldStates) || !this.get("owner"));
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
uiShowCallSpinner: function() {
|
|
133
|
+
return this._displayedForStates(['connecting']);
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
uiShowHoldSpinner: function() {
|
|
137
|
+
return this._displayedForStates(['initiating_hold']);
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
uiShowResumeSpinner: function() {
|
|
141
|
+
return this._displayedForStates(['initiating_resume']);
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
state: function() {
|
|
145
|
+
return this.get("state");
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
createSuccess: function(data) {
|
|
149
|
+
$(document).trigger("telephony:conversationCreated", data);
|
|
150
|
+
this.set(data);
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
createFail: function(xhr, textStatus, errorThrown) {
|
|
154
|
+
var body = JSON.parse(xhr.responseText);
|
|
155
|
+
$(document).trigger('callFailed', body.errors[0]);
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
fail: function(xhr, textStatus, errorThrown) {
|
|
160
|
+
if (typeof console === "object" && typeof console.log === "function") {
|
|
161
|
+
console.log("Unable to complete the request: " + textStatus);
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
isValid: function() {
|
|
166
|
+
if (! this._isValidPhoneNumberFormat()) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
if (! this._isValidCallee()) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
_disabledIf: function(disable) {
|
|
176
|
+
return disable ? 'disabled' : '';
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
_statesIn: function(states) {
|
|
180
|
+
return _.contains(states, this.state());
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
_disabledForStates: function(states) {
|
|
184
|
+
return this._disabledIf(this._statesIn(states));
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
_enabledForStates: function(states) {
|
|
188
|
+
return _.contains(states, this.state()) ? '' : 'disabled';
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
_shownForStates: function(states) {
|
|
192
|
+
return _.contains(states, this.state()) ? '' : 'hidden';
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
_displayedForStates: function(states) {
|
|
196
|
+
return _.contains(states, this.state()) ? 'display' : '';
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
_isValidPhoneNumberFormat: function() {
|
|
200
|
+
var phoneNumber = this._normalizePhoneNumber(this.get('to'));
|
|
201
|
+
if (phoneNumber.length !== 10) {
|
|
202
|
+
this.errorMessage = 'Please enter a 10-digit phone number';
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
return true;
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
_isValidCallee: function() {
|
|
209
|
+
var from = this._normalizePhoneNumber(this.get('from'));
|
|
210
|
+
var to = this._normalizePhoneNumber(this.get('to'));
|
|
211
|
+
if (from === to) {
|
|
212
|
+
this.errorMessage = 'Please input a customer phone number';
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
_normalizePhoneNumber: function(phoneNumber) {
|
|
219
|
+
var number = phoneNumber || '';
|
|
220
|
+
var digits = number.toString().replace(/\D/g, '');
|
|
221
|
+
if (digits[0] === '1') {
|
|
222
|
+
digits = digits.slice(1);
|
|
223
|
+
}
|
|
224
|
+
return digits;
|
|
225
|
+
}
|
|
226
|
+
});
|