%%%----------------------------------------------------------------------------- %%% @copyright (C) 2011-2019, 2600Hz %%% @doc Handles inspection of incoming caller ID and branching to a child %%% callflow node accordingly. %%% %%%

Data options:

%%%
%%%
`id'
%%%
Document ID of the match list
%%%
%%% %%% Sample for children section of Callflow: %%% ``` %%% "children": { %%% "match": { // callflow node to branch to when absolute mode is false and regex matches }, %%% "nomatch": { // callflow node to branch to when regex does not match or no child node defined for incoming caller id }, %%% } %%% ''' %%% %%% %%% @author Kozlov Yakov %%% @end %%%----------------------------------------------------------------------------- -module(cf_cidlistmatch). -behaviour(gen_cf_action). -export([handle/2]). -include("callflow.hrl"). %%------------------------------------------------------------------------------ %% @doc Entry point for this module %% @end %%------------------------------------------------------------------------------ -spec handle(kz_json:object(), kapps_call:call()) -> 'ok'. handle(Data, Call) -> CallerIdNumber = kapps_call:caller_id_number(Call), ListId = kz_json:get_ne_binary_value(<<"id">>, Data), AccountDb = kapps_call:account_db(Call), lager:debug("comparing caller id ~s with match list ~s entries in ~s", [CallerIdNumber, ListId, AccountDb]), case is_matching_prefix(AccountDb, ListId, CallerIdNumber) orelse is_matching_regexp(AccountDb, ListId, CallerIdNumber) of 'true' -> handle_match(Call); 'false' -> handle_no_match(Call) end. -spec is_matching_prefix(kz_term:ne_binary(), kz_term:ne_binary(), kz_term:ne_binary()) -> boolean(). is_matching_prefix(AccountDb, ListId, Number) -> NumberPrefixes = build_keys(Number), Keys = [[ListId, X] || X <- NumberPrefixes], case kz_datamgr:get_results(AccountDb, <<"lists/match_prefix_in_list">>, [{'keys', Keys}]) of {'ok', [_ | _]} -> 'true'; _ -> 'false' end. -spec is_matching_regexp(kz_term:ne_binary(), kz_term:ne_binary(), kz_term:ne_binary()) -> boolean(). is_matching_regexp(AccountDb, ListId, Number) -> case kz_datamgr:get_results(AccountDb, <<"lists/regexps_in_list">>, [{'key', ListId}]) of {'ok', Regexps} -> Patterns = [kz_json:get_value(<<"value">>, X) || X <- Regexps, X =/= null ], match_regexps(Patterns, Number); _ -> 'false' end. -spec match_regexps(kz_term:binaries(), kz_term:ne_binary()) -> boolean(). match_regexps([Pattern | Rest], Number) -> case re:run(Number, Pattern) of {'match', _} -> 'true'; 'nomatch' -> match_regexps(Rest, Number) end; match_regexps([], _Number) -> 'false'. %% TODO: this function from hon_util, may be place it somewhere in library? -spec build_keys(binary()) -> kz_term:binaries(). build_keys(<<"+", E164/binary>>) -> build_keys(E164); build_keys(<>) -> build_keys(Rest, D, [D]). -spec build_keys(binary(), binary(), kz_term:binaries()) -> kz_term:binaries(). build_keys(<>, Prefix, Acc) -> build_keys(Rest, <>, [<> | Acc]); build_keys(<<>>, _, Acc) -> Acc. %%------------------------------------------------------------------------------ %% @doc Handle a caller id "match" condition %% @end %%------------------------------------------------------------------------------ -spec handle_match(kapps_call:call()) -> 'ok'. handle_match(Call) -> case is_callflow_child(<<"match">>, Call) of 'true' -> 'ok'; 'false' -> cf_exe:continue(Call) end. %%------------------------------------------------------------------------------ %% @doc Handle a caller id "no match" condition %% @end %%------------------------------------------------------------------------------ -spec handle_no_match(kapps_call:call()) -> 'ok'. handle_no_match(Call) -> case is_callflow_child(<<"nomatch">>, Call) of 'true' -> 'ok'; 'false' -> cf_exe:continue(Call) end. %%------------------------------------------------------------------------------ %% @doc Check if the given node name is a callflow child %% @end %%------------------------------------------------------------------------------ -spec is_callflow_child(kz_term:ne_binary(), kapps_call:call()) -> boolean(). is_callflow_child(Name, Call) -> lager:debug("looking for callflow child ~s", [Name]), case cf_exe:attempt(Name, Call) of {'attempt_resp', 'ok'} -> lager:debug("found callflow child"), 'true'; {'attempt_resp', {'error', _}} -> lager:debug("failed to find callflow child"), 'false' end.