Skip to content

Commit

Permalink
Finished/fixed conference name announcements
Browse files Browse the repository at this point in the history
play_announce in participant_event handler

Select appropriate tone or use media for entry/exit

Fix compile & exit tone when on different media server

Remove participant count from this PR
  • Loading branch information
danielfinke committed Sep 30, 2015
1 parent e01eabd commit abc4c0c
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 47 deletions.
5 changes: 4 additions & 1 deletion applications/conference/src/conf_discovery_req.erl
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ handle_search_error(Conference, Call, Srv) ->
try amqp_util:basic_consume(Queue, [{'exclusive', 'true'}]) of
'ok' ->
lager:debug("initial participant creating conference on switch nodename '~p'", [whapps_call:switch_hostname(Call)]),
maybe_play_name(Conference, Call, Srv),
conf_participant:set_conference(Conference, Srv),
conf_participant:join_local(Srv),
wait_for_creation(Conference)
Expand Down Expand Up @@ -255,7 +256,9 @@ add_participant_to_conference(JObj, Conference, Call, Srv) ->
conf_participant:join_local(Srv);
_Else ->
lager:debug("running conference is on a different switch, bridging to ~s: ~p", [_Else, JObj]),
conf_participant:set_conference(Conference, Srv),
ParticipantHostname = wh_json:get_value(<<"Switch-Hostname">>, hd(wh_json:get_value(<<"Participants">>, JObj))),
Conference2 = whapps_conference:set_focus(ParticipantHostname, Conference),
conf_participant:set_conference(Conference2, Srv),
conf_participant:join_remote(Srv, JObj)
end.

Expand Down
119 changes: 78 additions & 41 deletions applications/conference/src/conf_participant.erl
Original file line number Diff line number Diff line change
Expand Up @@ -242,15 +242,15 @@ handle_call(_Request, _, P) ->
%%--------------------------------------------------------------------
handle_cast('hungup', #participant{in_conference='true'
,call=Call
,conference=Conference
}=Participant
) ->
_ = maybe_play_exit_tone(Conference),
play_hangup_announce(Participant),
_ = whapps_call_command:hangup(Call),
{'stop', {'shutdown', 'hungup'}, Participant};
handle_cast('hungup', #participant{in_conference='false'
,call=Call
}=Participant) ->
play_hangup_announce(Participant),
_ = whapps_call_command:hangup(Call),
{'stop', {'shutdown', 'hungup'}, Participant};
handle_cast({'gen_listener', {'created_queue', Q}}, #participant{conference='undefined'
Expand Down Expand Up @@ -292,15 +292,8 @@ handle_cast(_Message, #participant{conference='undefined'}=Participant) ->
,[_Message]
),
{'noreply', Participant};
handle_cast('play_announce', #participant{name_pronounced='undefined'} = Participant) ->
lager:debug("skipping announce"),
{'noreply', Participant};
handle_cast('play_announce', #participant{conference=Conference
,name_pronounced={_, AccountId, MediaId}
}=Participant) ->
lager:debug("playing announcement ~s to conference", [MediaId]),
Recording = wh_media_util:media_path(MediaId, AccountId),
whapps_conference_command:play(Recording, Conference),
handle_cast('play_announce', Participant) ->
play_announce(Participant),
{'noreply', Participant};
handle_cast('join_local', #participant{call=Call
,conference=Conference
Expand All @@ -318,12 +311,6 @@ handle_cast({'join_remote', JObj}, #participant{call=Call
{'noreply', Participant};
handle_cast({'sync_participant', JObj}, #participant{call=Call}=Participant) ->
{'noreply', sync_participant(JObj, Call, Participant)};
handle_cast('play_member_entry', #participant{conference=Conference}=Participant) ->
_ = maybe_play_entry_tone('member', Conference),
{'noreply', Participant};
handle_cast('play_moderator_entry', #participant{conference=Conference}=Participant) ->
_ = maybe_play_entry_tone('moderator', Conference),
{'noreply', Participant};
handle_cast({'dtmf', Digit}, #participant{last_dtmf = <<"*">>}=Participant) ->
case Digit of
<<"1">> -> toggle_mute(self());
Expand Down Expand Up @@ -419,11 +406,6 @@ handle_event(JObj, #participant{call_event_consumers=Consumers
gen_listener:cast(Srv, 'hungup');
{{<<"call_event">>, <<"CHANNEL_BRIDGE">>}, CallId} ->
gen_listener:cast(Srv, 'play_announce');
{{<<"call_event">>,<<"CHANNEL_EXECUTE">>}, CallId} ->
case wh_json:get_value(<<"Application-Name">>, JObj) of
<<"conference">> -> gen_listener:cast(Srv, 'play_announce');
_ -> 'ok'
end;
{_Else, CallId} ->
lager:debug("unhandled event: ~p", [_Else]);
{_Else, _OtherLeg} ->
Expand Down Expand Up @@ -496,7 +478,9 @@ sync_participant(JObj, Call, #participant{in_conference='false'
sync_member(Member, Call, Participant#participant{conference=C});
{'error', 'not_found'} ->
lager:debug("caller not found in the list of conference participants"),
Participant
Focus = wh_json:get_value(<<"Focus">>, JObj),
C = whapps_conference:set_focus(Focus, Conference),
Participant#participant{conference=C}
end;
sync_participant(JObj, Call, #participant{in_conference='true'}=Participant) ->
Participants = wh_json:get_value(<<"Participants">>, JObj, []),
Expand All @@ -520,7 +504,7 @@ sync_moderator(JObj, Call, #participant{conference=Conference
lager:debug("caller has joined the local conference as moderator ~p", [ParticipantId]),
Deaf = not wh_json:is_true(<<"Hear">>, JObj),
Muted = not wh_json:is_true(<<"Speak">>, JObj),
gen_listener:cast(self(), 'play_moderator_entry'),
gen_listener:cast(self(), 'play_announce'),
whapps_conference:moderator_join_muted(Conference)
andalso gen_listener:cast(self(), 'mute'),
whapps_conference:moderator_join_deaf(Conference)
Expand All @@ -545,7 +529,7 @@ sync_member(JObj, Call, #participant{conference=Conference
lager:debug("caller has joined the local conference as member ~p", [ParticipantId]),
Deaf = not wh_json:is_true(<<"Hear">>, JObj),
Muted = not wh_json:is_true(<<"Speak">>, JObj),
gen_listener:cast(self(), 'play_member_entry'),
gen_listener:cast(self(), 'play_announce'),
whapps_conference:member_join_muted(Conference)
andalso gen_listener:cast(self(), 'mute'),
whapps_conference:member_join_deaf(Conference)
Expand Down Expand Up @@ -575,6 +559,59 @@ notify_requestor(MyQ, MyId, DiscoveryEvent, ConferenceId) ->
wapi_conference:publish_discovery_resp(RequestorQ, Resp)
end.

-spec play_announce(participant()) -> 'ok'.
play_announce(#participant{name_pronounced='undefined'
,conference=Conference
}) ->
lager:debug("skipping announce"),
Moderator = case whapps_conference:moderator(Conference) of
'true' -> 'moderator';
'false' -> 'member'
end,
case play_entry_tone(Moderator, Conference) of
'false' -> 'ok';
Command -> whapps_conference_command:send_command(Command, Conference)
end;
play_announce(#participant{conference=Conference
,name_pronounced={_, AccountId, MediaId}
}) ->
lager:debug("playing announcement ~s to conference ~s", [MediaId, whapps_conference:id(Conference)]),
Recording = wh_media_util:media_path(MediaId, AccountId),
RecordingCommand = whapps_conference_command:play_command(Recording),
PromptCommand = whapps_conference_command:play_command(wh_media_util:get_prompt(<<"conf-has_joined">>, whapps_conference:call(Conference))),

Moderator = case whapps_conference:moderator(Conference) of
'true' -> 'moderator';
'false' -> 'member'
end,
case play_entry_tone(Moderator, Conference) of
'false' -> whapps_conference_command:macro([RecordingCommand, PromptCommand], Conference);
Command -> whapps_conference_command:macro([Command, RecordingCommand, PromptCommand], Conference)
end.

-spec play_hangup_announce(participant()) -> 'ok'.
play_hangup_announce(#participant{conference='undefined'}) ->
'ok';
play_hangup_announce(#participant{conference=Conference
,name_pronounced='undefined'
}) ->
case play_exit_tone(Conference) of
'false' -> 'ok';
Command -> whapps_conference_command:send_command(Command, Conference)
end;
play_hangup_announce(#participant{conference=Conference
,name_pronounced={_, AccountId, MediaId}
}) ->
lager:debug("playing announcement ~s to conference ~s", [MediaId, whapps_conference:id(Conference)]),
Recording = wh_media_util:media_path(MediaId, AccountId),
RecordingCommand = whapps_conference_command:play_command(Recording),
PromptCommand = whapps_conference_command:play_command(wh_media_util:get_prompt(<<"conf-has_left">>, whapps_conference:call(Conference))),

case play_exit_tone(Conference) of
'false' -> whapps_conference_command:macro([RecordingCommand, PromptCommand], Conference);
Command -> whapps_conference_command:macro([Command, RecordingCommand, PromptCommand], Conference)
end.

-spec bridge_to_conference(ne_binary(), whapps_conference:conference(), whapps_call:call()) -> 'ok'.
bridge_to_conference(Route, Conference, Call) ->
lager:debug("briding to conference running at '~s'", [Route]),
Expand Down Expand Up @@ -638,25 +675,25 @@ send_conference_command(Conference, Call) ->
).

%% @private
-spec maybe_play_exit_tone(whapps_conference:conference()) -> 'ok'.
maybe_play_exit_tone(Conference) ->
-spec play_exit_tone(whapps_conference:conference()) -> wh_proplist() | 'false'.
play_exit_tone(Conference) ->
case whapps_conference:play_exit_tone(Conference) of
'false' -> 'ok';
Media = ?NE_BINARY -> whapps_conference_command:play(Media, Conference);
_Else -> whapps_conference_command:play(?EXIT_TONE, Conference)
'false' -> 'false';
Media = ?NE_BINARY -> whapps_conference_command:play_command(Media);
_Else -> whapps_conference_command:play_command(?EXIT_TONE)
end.

%% @private
-spec maybe_play_entry_tone('member' | 'moderator', whapps_conference:conference()) -> 'ok'.
maybe_play_entry_tone('member', Conference) ->
-spec play_entry_tone('member' | 'moderator', whapps_conference:conference()) -> wh_proplist() | 'false'.
play_entry_tone('member', Conference) ->
play_entry_tone_media(?ENTRY_TONE, Conference);
play_entry_tone('moderator', Conference) ->
play_entry_tone_media(?MOD_ENTRY_TONE, Conference).

-spec play_entry_tone_media(ne_binary(), whapps_conference:conference()) -> wh_proplist() | 'false'.
play_entry_tone_media(Tone, Conference) ->
case whapps_conference:play_entry_tone(Conference) of
'false' -> 'ok';
Media = ?NE_BINARY -> whapps_conference_command:play(Media, Conference);
_Else -> whapps_conference_command:play(?ENTRY_TONE, Conference)
end;
maybe_play_entry_tone('moderator', Conference) ->
case whapps_conference:play_entry_tone(Conference) of
'false' -> 'ok';
Media = ?NE_BINARY -> whapps_conference_command:play(Media, Conference);
_Else -> whapps_conference_command:play(?MOD_ENTRY_TONE, Conference)
'false' -> 'false';
Media = ?NE_BINARY -> whapps_conference_command:play_command(Media);
_Else -> whapps_conference_command:play_command(Tone)
end.
11 changes: 10 additions & 1 deletion applications/conference/src/conf_route_req.erl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ handle_req(JObj, Props) ->
maybe_send_route_response(JObj, Q, Call) ->
case find_conference(Call) of
{'ok', Conference} ->
send_route_response(JObj, Q, Call, Conference);
send_route_response(JObj, Q, Call, bridged_conference(Conference));
{'error', _} -> 'ok'
end.

Expand Down Expand Up @@ -77,3 +77,12 @@ find_account_db(Call) ->
),
'undefined'
end.

-spec bridged_conference(whapps_conference:conference()) -> whapps_conference:conference().
bridged_conference(Conference) ->
%% We are relying on the original channel to play media
%% so that name announcements always work
Updaters = [fun(Conf) -> whapps_conference:set_play_entry_tone('false', Conf) end
,fun(Conf) -> whapps_conference:set_play_exit_tone('false', Conf) end
],
whapps_conference:update(Updaters, Conference).
12 changes: 12 additions & 0 deletions applications/ecallmgr/src/ecallmgr_fs_conference.erl
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,16 @@ exec(Focus, ConferenceId, JObj) ->
freeswitch:api(Focus, 'expand', Command)
end,
send_response(App, Result, wh_json:get_value(<<"Server-ID">>, JObj), JObj);
{<<"play_macro">>, AppData} ->
Commands = wh_json:get_value(<<"Commands">>, AppData, []),
Result = lists:foldl(fun(Command, _Acc) ->
{<<"play">>, AppData2} = get_conf_command(<<"play">>, Focus, ConferenceId, Command),
Command2 = list_to_binary([ConferenceId, " play ", AppData2]),
Focus =/= 'undefined' andalso lager:debug("execute on node ~s: conference ~s", [Focus, Command2]),
lager:debug("api to ~s: conference ~s", [Focus, Command2]),
freeswitch:api(Focus, 'conference', Command2)
end, 'undefined', Commands),
send_response(App, Result, wh_json:get_value(<<"Server-ID">>, JObj), JObj);
{AppName, AppData} ->
Command = wh_util:to_list(list_to_binary([ConferenceId, " ", AppName, " ", AppData])),
Focus =/= 'undefined' andalso lager:debug("execute on node ~s: conference ~s", [Focus, Command]),
Expand Down Expand Up @@ -560,6 +570,8 @@ get_conf_command(<<"play">>, _Focus, ConferenceId, JObj) ->
end,
{<<"play">>, Args}
end;
get_conf_command(<<"play_macro">>, _Focus, _ConferenceId, JObj) ->
{<<"play_macro">>, JObj};
get_conf_command(<<"stop_play">>, _Focus, _ConferenceId, JObj) ->
case wapi_conference:stop_play_v(JObj) of
'false' ->
Expand Down
26 changes: 26 additions & 0 deletions core/whistle-1.0.0/src/api/wapi_conference.erl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
-export([config_req/1, config_req_v/1
,config_resp/1, config_resp_v/1
]).
-export([play_macro_req/1, play_macro_req_v/1]).

-export([bind_q/2, unbind_q/2]).
-export([declare_exchanges/0]).
Expand Down Expand Up @@ -352,8 +353,15 @@
,{<<"tones">>, ?CONF_TONES_REQ_VALUES, fun ?MODULE:tones/1}
,{<<"say">>, ?CONF_SAY_REQ_VALUES, fun ?MODULE:say/1}
,{<<"tts">>, ?CONF_SAY_REQ_VALUES, fun ?MODULE:tts/1}
,{<<"play_macro">>, ?CONF_PLAY_MACRO_REQ_VALUES, fun ?MODULE:play_macro_req/1}
]).

-define(CONF_PLAY_MACRO_REQ_HEADERS, [<<"Application-Name">>, <<"Conference-ID">>, <<"Commands">>]).
-define(OPTIONAL_CONF_PLAY_MACRO_REQ_HEADERS, []).
-define(CONF_PLAY_MACRO_REQ_VALUES, []).
-define(CONF_PLAY_MACRO_REQ_TYPES, [{<<"Conference-ID">>, fun is_binary/1}
]).

focus_queue_name(Focus) -> <<(wh_util:to_binary(Focus))/binary, "_conference">>.

%%--------------------------------------------------------------------
Expand Down Expand Up @@ -630,6 +638,24 @@ play_v(Prop) when is_list(Prop) ->
wh_api:validate(Prop, ?PLAY_HEADERS, ?PLAY_VALUES, ?PLAY_TYPES);
play_v(JObj) -> play_v(wh_json:to_proplist(JObj)).

%%--------------------------------------------------------------------
%% @doc
%% Takes proplist, creates JSON string or error
%% @end
%%--------------------------------------------------------------------
-spec play_macro_req(api_terms()) -> {'ok', iolist()} | {'error', string()}.
play_macro_req(Prop) when is_list(Prop) ->
case play_macro_req_v(Prop) of
'true' -> wh_api:build_message(Prop, ?CONF_PLAY_MACRO_REQ_HEADERS, ?OPTIONAL_CONF_PLAY_MACRO_REQ_HEADERS);
'false' -> {'error', "Proplist failed validation for play_macro_req"}
end;
play_macro_req(JObj) -> play_macro_req(wh_json:to_proplist(JObj)).

-spec play_macro_req_v(api_terms()) -> boolean().
play_macro_req_v(Prop) when is_list(Prop) ->
wh_api:validate(Prop, ?CONF_PLAY_MACRO_REQ_HEADERS, ?CONF_PLAY_MACRO_REQ_VALUES, ?CONF_PLAY_MACRO_REQ_TYPES);
play_macro_req_v(JObj) -> play_macro_req_v(wh_json:to_proplist(JObj)).

%%--------------------------------------------------------------------
%% @doc
%% Takes proplist, creates JSON string or error
Expand Down
35 changes: 31 additions & 4 deletions core/whistle_apps-1.0.0/src/whapps_conference_command.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
-export([lock/1]).
-export([mute_participant/2]).
-export([prompt/2, prompt/3]).
-export([play_command/1, play_command/2]).
-export([play/2, play/3]).
-export([record/1, recordstop/1]).
-export([relate_participants/3, relate_participants/4]).
Expand All @@ -29,6 +30,7 @@
-export([participant_volume_out/3]).

-export([send_command/2]).
-export([macro/2]).

-spec search(whapps_conference:conference()) ->
{'ok', wh_json:object()} |
Expand Down Expand Up @@ -118,16 +120,24 @@ prompt(Media, Conference) ->
prompt(Media, ParticipantId, Conference) ->
play(wh_media_util:get_prompt(Media, whapps_conference:call(Conference)), ParticipantId, Conference).

-spec play_command(ne_binary()) -> wh_proplist().
-spec play_command(ne_binary(), non_neg_integer() | 'undefined') -> wh_proplist().

play_command(Media) ->
play_command(Media, 'undefined').
play_command(Media, ParticipantId) ->
[{<<"Application-Name">>, <<"play">>}
,{<<"Media-Name">>, Media}
,{<<"Participant">>, ParticipantId}
].

-spec play(ne_binary(), whapps_conference:conference()) -> 'ok'.
-spec play(ne_binary(), non_neg_integer() | 'undefined', whapps_conference:conference()) -> 'ok'.

play(Media, Conference) ->
play(Media, 'undefined', Conference).
play(Media, ParticipantId, Conference) ->
Command = [{<<"Application-Name">>, <<"play">>}
,{<<"Media-Name">>, Media}
,{<<"Participant">>, ParticipantId}
],
Command = play_command(Media, ParticipantId),
send_command(Command, Conference).

-spec record(whapps_conference:conference()) -> 'ok'.
Expand Down Expand Up @@ -216,8 +226,25 @@ send_command([_|_]=Command, Conference) ->
Prop = Command ++ [{<<"Conference-ID">>, ConferenceId}
| wh_api:default_headers(Q, <<"conference">>, <<"command">>, AppName, AppVersion)
],
lager:debug("prop ~p", [Prop]),
case wh_util:is_empty(Focus) of
'true' -> wapi_conference:publish_command(ConferenceId, Prop);
'false' -> wapi_conference:publish_targeted_command(Focus, Prop)
end;
send_command(JObj, Conference) -> send_command(wh_json:to_proplist(JObj), Conference).

macro(Commands, Conference) ->
Values = [{<<"Event-Category">>, <<"conference">>}
,{<<"Event-Name">>, <<"command">>}
,{<<"Conference-ID">>, whapps_conference:id(Conference)}
,{<<"Msg-ID">>, wh_util:rand_hex_binary(16)}
| wh_api:default_headers(?APP_NAME, ?APP_VERSION)
],
JsonCommands = lists:reverse(lists:foldl(fun(Command, Acc) ->
[wh_json:from_list(props:filter_undefined(Command ++ Values)) | Acc]
end, [], Commands)),
Prop = [{<<"Application-Name">>, <<"play_macro">>}
,{<<"Commands">>, JsonCommands}
| Values
],
send_command(Prop, Conference).
Binary file added system_media/en-us/conf-has_joined.wav
Binary file not shown.
Binary file added system_media/en-us/conf-has_left.wav
Binary file not shown.

0 comments on commit abc4c0c

Please sign in to comment.