Skip to content

Commit

Permalink
Added voicemail fast forward and rewind (2600hz#5662)
Browse files Browse the repository at this point in the history
  • Loading branch information
noahmehl authored and jamesaimonetti committed Jun 6, 2019
1 parent 906c5d3 commit c9dc911
Show file tree
Hide file tree
Showing 18 changed files with 444 additions and 10 deletions.
78 changes: 72 additions & 6 deletions applications/callflow/src/module/cf_voicemail.erl
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@
-define(KEY_DELETE_AFTER_NOTIFY, <<"delete_after_notify">>).
-define(KEY_SAVE_AFTER_NOTIFY, <<"save_after_notify">>).
-define(KEY_FORCE_REQUIRE_PIN, <<"force_require_pin">>).
-define(KEY_ALLOW_FF_RW, <<"is_voicemail_ff_rw_enabled">>).
-define(KEY_SEEK_DURATION, <<"seek_duration_ms">>).
-define(MAX_INVALID_PIN_LOOPS, 3).
-define(DEFAULT_SEEK_DURATION, 10 * ?MILLISECONDS_IN_SECOND).

-define(MAILBOX_DEFAULT_SIZE
,kapps_config:get_integer(?CF_CONFIG_CAT
Expand Down Expand Up @@ -88,6 +91,17 @@
,'false'
)).

-define(IS_FF_RW_ENABLED
,kapps_config:get_is_true(?CF_CONFIG_CAT
,[?KEY_VOICEMAIL, ?KEY_ALLOW_FF_RW]
,'false'
)).

-define(MAILBOX_SEEK_DURATION
,kapps_config:get_non_neg_integer(?CF_CONFIG_CAT
,[?KEY_VOICEMAIL, ?KEY_SEEK_DURATION]
,?DEFAULT_SEEK_DURATION
)).
-define(DEFAULT_FORWARD_TYPE
,kapps_config:get_ne_binary(?CF_CONFIG_CAT
,[?KEY_VOICEMAIL, <<"vm_message_forward_type">>]
Expand Down Expand Up @@ -133,8 +147,10 @@
,prev = <<"4">> :: kz_term:ne_binary()
,next = <<"6">> :: kz_term:ne_binary()
,delete = <<"7">> :: kz_term:ne_binary()
,rewind = <<"5">> :: kz_term:ne_binary()
,fastforward = <<"8">> :: kz_term:ne_binary()

%% Greeting or instructions
%% Greeting or instructions
,continue = 'undefined' :: kz_term:api_ne_binary()
}).
-type vm_keys() :: #keys{}.
Expand Down Expand Up @@ -165,6 +181,8 @@
,transcribe_voicemail = 'false' :: boolean()
,notifications :: kz_term:api_object()
,after_notify_action = 'nothing' :: 'nothing' | 'delete' | 'save'
,is_ff_rw_enabled = 'false' :: boolean()
,seek_duration = ?DEFAULT_SEEK_DURATION :: non_neg_integer()
,interdigit_timeout = kapps_call_command:default_interdigit_timeout() :: pos_integer()
,play_greeting_intro = 'false' :: boolean()
,use_person_not_available = 'false' :: boolean()
Expand Down Expand Up @@ -805,22 +823,30 @@ message_count_prompts(New, Saved) ->
kapps_call_command:audio_macro_prompts().
message_prompt([H|_]=Messages, Message, Count, #mailbox{timezone=Timezone
,skip_envelope='false'
,keys=Keys
,is_ff_rw_enabled=AllowFfRw
}) ->
[{'prompt', <<"vm-message_number">>}
,{'say', kz_term:to_binary(Count - length(Messages) + 1), <<"number">>}
,{'play', Message}
,play_prompt(Message, AllowFfRw, Keys)
,{'prompt', <<"vm-received">>}
,{'say', get_unix_epoch(kz_json:get_integer_value(<<"timestamp">>, H), Timezone), <<"current_date_time">>}
,{'prompt', <<"vm-message_menu">>}
];
message_prompt(Messages, Message, Count, #mailbox{skip_envelope='true'}) ->
message_prompt(Messages, Message, Count, #mailbox{is_ff_rw_enabled=AllowFfRw
,keys=Keys
,skip_envelope='true'}) ->
lager:debug("mailbox is set to skip playing message envelope"),
[{'prompt', <<"vm-message_number">>}
,{'say', kz_term:to_binary(Count - length(Messages) + 1), <<"number">>}
,{'play', Message}
,play_prompt(Message, AllowFfRw, Keys)
,{'prompt', <<"vm-message_menu">>}
].

play_prompt(Message, 'true'=_AllowFfRw, #keys{rewind=RW, fastforward=FF}=_Keys) ->
{'play', Message, ?ANY_DIGIT -- [RW, FF]};
play_prompt(Message, 'false', _Keys) ->
{'play', Message}.

%%------------------------------------------------------------------------------
%% @doc Plays back a message then the menu, and continues to loop over the
Expand All @@ -837,43 +863,59 @@ play_messages(Messages, Count, Box, Call) ->

-spec play_messages(kz_json:objects(), kz_json:objects(), non_neg_integer(), mailbox(), kapps_call:call()) ->
'ok' | 'complete'.
play_messages([H|T]=Messages, PrevMessages, Count, Box, Call) ->
play_messages([H|T]=Messages, PrevMessages, Count, #mailbox{seek_duration=SeekDuration}=Box, Call) ->
AccountId = kapps_call:account_id(Call),
Message = kvm_message:media_url(AccountId, H),
lager:info("playing mailbox message ~p (~s)", [Count, Message]),
Prompt = message_prompt(Messages, Message, Count, Box),
case message_menu(Prompt, Box, Call) of
{'ok', 'keep'} ->
lager:info("caller chose to save the message"),
_ = kapps_call_command:flush(Call),
_ = kapps_call_command:b_prompt(<<"vm-saved">>, Call),
{_, NMessage} = kvm_message:set_folder(?VM_FOLDER_SAVED, H, AccountId),
play_messages(T, [NMessage|PrevMessages], Count, Box, Call);
{'ok', 'prev'} ->
lager:info("caller chose to listen to previous message"),
_ = kapps_call_command:flush(Call),
play_prev_message(Messages, PrevMessages, Count, Box, Call);
{'ok', 'next'} ->
lager:info("caller chose to listen to next message"),
_ = kapps_call_command:flush(Call),
play_next_message(Messages, PrevMessages, Count, Box, Call);
{'ok', 'delete'} ->
lager:info("caller chose to delete the message"),
_ = kapps_call_command:flush(Call),
_ = kapps_call_command:b_prompt(<<"vm-deleted">>, Call),
_ = kvm_message:set_folder({?VM_FOLDER_DELETED, 'false'}, H, AccountId),
play_messages(T, PrevMessages, Count, Box, Call);
{'ok', 'return'} ->
lager:info("caller chose to return to the main menu"),
_ = kapps_call_command:flush(Call),
_ = kapps_call_command:b_prompt(<<"vm-saved">>, Call),
_ = kvm_message:set_folder(?VM_FOLDER_SAVED, H, AccountId),
'complete';
{'ok', 'replay'} ->
lager:info("caller chose to replay"),
_ = kapps_call_command:flush(Call),
play_messages(Messages, PrevMessages, Count, Box, Call);
{'ok', 'forward'} ->
lager:info("caller chose to forward the message"),
_ = kapps_call_command:flush(Call),
forward_message(H, Box, Call),
{_, NMessage} = kvm_message:set_folder(?VM_FOLDER_SAVED, H, AccountId),
_ = kapps_call_command:prompt(<<"vm-saved">>, Call),
play_messages(T, [NMessage|PrevMessages], Count, Box, Call);
{'ok', 'rewind'} ->
lager:info("caller chose to rewind part of the message"),
_ = kapps_call_command:seek('rewind', SeekDuration, Call),
play_messages(Messages, PrevMessages, Count, Box, Call);
{'ok', 'fastforward'} ->
lager:info("caller chose to fast forward part of the message"),
_ = kapps_call_command:seek('fastforward', SeekDuration, Call),
play_messages(Messages, PrevMessages, Count, Box, Call);
{'error', _} ->
_ = kapps_call_command:flush(Call),
lager:info("error during message playback")
end;
play_messages([], _, _, _, _) ->
Expand Down Expand Up @@ -1029,7 +1071,7 @@ forward_message(AttachmentName, Length, Message, SrcBoxId, #mailbox{mailbox_numb
%% user provides a valid option
%% @end
%%------------------------------------------------------------------------------
-type message_menu_returns() :: {'ok', 'keep' | 'delete' | 'return' | 'replay' | 'prev' | 'next' | 'forward'}.
-type message_menu_returns() :: {'ok', 'keep' | 'delete' | 'return' | 'replay' | 'prev' | 'next' | 'forward' | 'rewind' | 'fastforward'}.

-spec message_menu(mailbox(), kapps_call:call()) ->
{'error', 'channel_hungup' | 'channel_unbridge' | kz_json:object()} |
Expand All @@ -1047,7 +1089,10 @@ message_menu(Prompt, #mailbox{keys=#keys{replay=Replay
,prev=Prev
,next=Next
,return_main=ReturnMain
,rewind=RW
,fastforward=FF
}
,is_ff_rw_enabled=AllowFfRw
,interdigit_timeout=Interdigit
}=Box, Call) ->
lager:info("playing message menu"),
Expand All @@ -1057,6 +1102,8 @@ message_menu(Prompt, #mailbox{keys=#keys{replay=Replay
,kapps_call_command:default_collect_timeout()
,Interdigit
,NoopId
,[<<"#">>]
,'false'
,Call
)
of
Expand All @@ -1067,6 +1114,8 @@ message_menu(Prompt, #mailbox{keys=#keys{replay=Replay
{'ok', Replay} -> {'ok', 'replay'};
{'ok', Prev} -> {'ok', 'prev'};
{'ok', Next} -> {'ok', 'next'};
{'ok', RW} when AllowFfRw -> {'ok', 'rewind'};
{'ok', FF} when AllowFfRw -> {'ok', 'fastforward'};
{'error', _}=E -> E;
_ ->
_ = kapps_call_command:b_prompt(<<"menu-invalid_entry">>, Call),
Expand Down Expand Up @@ -1568,7 +1617,9 @@ get_mailbox_profile(Data, Call) ->
,[MaxMessageCount, MsgCount]
),

SeekDuration = seek_duration(MailboxJObj),
AfterNotifyAction = after_notify_action(MailboxJObj),
IsFfRwEnabled = is_ff_rw_enabled(MailboxJObj),

#mailbox{mailbox_id = MailboxId
,exists = 'true'
Expand Down Expand Up @@ -1609,6 +1660,8 @@ get_mailbox_profile(Data, Call) ->
,notifications =
kz_json:get_json_value(<<"notifications">>, MailboxJObj)
,after_notify_action = AfterNotifyAction
,is_ff_rw_enabled = IsFfRwEnabled
,seek_duration = SeekDuration
,interdigit_timeout =
kz_json:find(<<"interdigit_timeout">>, [MailboxJObj, Data], kapps_call_command:default_interdigit_timeout())
,play_greeting_intro =
Expand All @@ -1634,6 +1687,17 @@ should_require_pin(MailboxJObj) ->
'false' -> kzd_voicemail_box:pin_required(MailboxJObj)
end.

-spec is_ff_rw_enabled(kz_json:object()) -> boolean().
is_ff_rw_enabled(MailboxJObj) ->
case ?IS_FF_RW_ENABLED of
'true' -> kzd_vmboxes:is_voicemail_ff_rw_enabled(MailboxJObj);
'false' -> 'false'
end.

-spec seek_duration(kz_json:object()) -> non_neg_integer().
seek_duration(MailboxJObj) ->
kzd_vmboxes:seek_duration_ms(MailboxJObj, ?MAILBOX_SEEK_DURATION).

-spec after_notify_action(kz_json:object()) -> atom().
after_notify_action(MailboxJObj) ->
Delete = kz_json:is_true(?KEY_DELETE_AFTER_NOTIFY, MailboxJObj, ?DEFAULT_DELETE_AFTER_NOTIFY),
Expand Down Expand Up @@ -1702,6 +1766,8 @@ populate_keys(Call) ->
,replay = kz_json:get_binary_value(<<"replay">>, JObj, Default#keys.replay)
,prev = kz_json:get_binary_value(<<"prev">>, JObj, Default#keys.prev)
,next = kz_json:get_binary_value(<<"next">>, JObj, Default#keys.next)
,fastforward = kz_json:get_binary_value(<<"fastforward">>, JObj, Default#keys.fastforward)
,rewind = kz_json:get_binary_value(<<"rewind">>, JObj, Default#keys.rewind)
,delete = kz_json:get_binary_value(<<"delete">>, JObj, Default#keys.delete)
,continue = kz_json:get_binary_value(<<"continue">>, JObj, Default#keys.continue)
}.
Expand Down
2 changes: 2 additions & 0 deletions applications/crossbar/doc/ref/vmboxes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Key | Description | Type | Default | Required | Support Level
`check_if_owner` | Determines if when the user calls their own voicemail they should be prompted to sign in | `boolean()` | `true` | `false` | `supported`
`delete_after_notify` | Move the voicemail to delete folder after the notification has been sent | `boolean()` | `false` | `false` | `supported`
`is_setup` | Determines if the user has completed the initial configuration | `boolean()` | `false` | `false` | `supported`
`is_voicemail_ff_rw_enabled` | callflow allow fastforward and rewind during voicemail message playback | `boolean()` | `false` | `false` |
`mailbox` | The voicemail box number | `string(1..30)` | | `true` | `supported`
`media.unavailable` | The ID of a media object that should be used as the unavailable greeting | `string(32)` | | `false` | `supported`
`media` | The media (prompt) parameters | `object()` | `{}` | `false` | `supported`
Expand All @@ -28,6 +29,7 @@ Key | Description | Type | Default | Required | Support Level
`pin` | The pin number for the voicemail box | `string(4..15)` | | `false` | `supported`
`require_pin` | Determines if a pin is required to check the voicemail from the users devices | `boolean()` | `false` | `false` | `supported`
`save_after_notify` | Move the voicemail to save folder after the notification has been sent (This setting will override delete_after_notify) | `boolean()` | `false` | `false` | `supported`
`seek_duration_ms` | callflow fastforward and rewind seek duration | `integer()` | `10000` | `false` |
`skip_envelope` | Determines if the envelope should be skipped | `boolean()` | `false` | `false` | `beta`
`skip_greeting` | Determines if the greeting should be skipped | `boolean()` | `false` | `false` | `supported`
`skip_instructions` | Determines if the instructions after the greeting and prior to composing a message should be played | `boolean()` | `false` | `false` | `supported`
Expand Down
2 changes: 2 additions & 0 deletions applications/crossbar/doc/voicemail.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Key | Description | Type | Default | Required | Support Level
`check_if_owner` | Determines if when the user calls their own voicemail they should be prompted to sign in | `boolean()` | `true` | `false` | `supported`
`delete_after_notify` | Move the voicemail to delete folder after the notification has been sent | `boolean()` | `false` | `false` | `supported`
`is_setup` | Determines if the user has completed the initial configuration | `boolean()` | `false` | `false` | `supported`
`is_voicemail_ff_rw_enabled` | callflow allow fastforward and rewind during voicemail message playback | `boolean()` | `false` | `false` |
`mailbox` | The voicemail box number | `string(1..30)` | | `true` | `supported`
`media.unavailable` | The ID of a media object that should be used as the unavailable greeting | `string(32)` | | `false` | `supported`
`media` | The media (prompt) parameters | `object()` | `{}` | `false` | `supported`
Expand All @@ -39,6 +40,7 @@ Key | Description | Type | Default | Required | Support Level
`pin` | The pin number for the voicemail box | `string(4..15)` | | `false` | `supported`
`require_pin` | Determines if a pin is required to check the voicemail from the users devices | `boolean()` | `false` | `false` | `supported`
`save_after_notify` | Move the voicemail to save folder after the notification has been sent (This setting will override delete_after_notify) | `boolean()` | `false` | `false` | `supported`
`seek_duration_ms` | callflow fastforward and rewind seek duration | `integer()` | `10000` | `false` |
`skip_envelope` | Determines if the envelope should be skipped | `boolean()` | `false` | `false` | `beta`
`skip_greeting` | Determines if the greeting should be skipped | `boolean()` | `false` | `false` | `supported`
`skip_instructions` | Determines if the instructions after the greeting and prior to composing a message should be played | `boolean()` | `false` | `false` | `supported`
Expand Down
Loading

0 comments on commit c9dc911

Please sign in to comment.