Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Voicemail Fast Forward and Rewind #5662

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about to use this menu

              ,keep = <<"1">> :: kz_term:ne_binary()
              ,replay = <<"2">> :: kz_term:ne_binary()
              ,forward = <<"3">> :: kz_term:ne_binary()
              ,prev = <<"4">> :: kz_term:ne_binary()
              ,callback = <<"5">> :: kz_term:ne_binary()
              ,next = <<"6">> :: kz_term:ne_binary()
              ,rewind = <<"7">> :: kz_term:ne_binary()
              ,delete = <<"8">> :: kz_term:ne_binary()
              ,fastforward = <<"9">> :: kz_term:ne_binary()

Callback feature on PR #5267

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the biggest problem with that is that it would change the default behavior for every deployed system and user (because you changed delete).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the point is that we should not change any existing keys, and since key mapping is available per mailbox, any operator could apply the mapping you suggest at any time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my view this change is acceptable for kazoo 5.x.x branch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jamesaimonetti or @lazedo can you please comment?


%% 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