%%%----------------------------------------------------------------------------- %%% @copyright (C) 2010-2019, 2600Hz %%% @doc This module looks up the Caller ID Name by matching %%% numbers/patters with the provided lists. %%% %%%

Data options:

%%%
%%%
`lists'
%%%
ID of list document to use.
%%%
%%% %%% @author Sponsored by Conversant Ltd, Implemented by SIPLABS, LLC (Ilya Ashchepkov) %%% @end %%%----------------------------------------------------------------------------- -module(cf_lookupcidname). -behaviour(gen_cf_action). -export([handle/2]). -include("callflow.hrl"). -type match_number_result() :: {'stop', kz_term:api_binary()} | 'continue'. -spec handle(kz_json:object(), kapps_call:call()) -> 'ok'. handle(Data, Call) -> CallerNumber = kapps_call:caller_id_number(Call), ListIds = kz_json:get_value(<<"lists">>, Data, []), AccountDb = kapps_call:account_db(Call), lager:debug("matching ~p in ~p", [CallerNumber, AccountDb]), CallerName = case match_number_in_lists(AccountDb, CallerNumber, ListIds) of 'continue' -> lager:debug("matching regexps"), match_regexp_in_lists(AccountDb, CallerNumber, ListIds); {'stop', Name} -> Name end, handle_caller_name(Call, CallerName). -spec handle_caller_name(kapps_call:call(), kz_term:api_ne_binary()) -> 'ok'. handle_caller_name(Call, 'undefined') -> cf_exe:continue(Call); handle_caller_name(Call, CallerName) -> lager:info("setting caller name to ~p", [CallerName]), cf_exe:continue(kapps_call:set_caller_id_name(CallerName, Call)). -spec match_number_in_lists(kz_term:ne_binary(), kz_term:ne_binary(), kz_term:ne_binaries()) -> match_number_result(). match_number_in_lists(AccountDb, Number, Lists) -> Prefixes = build_keys(Number), match_prefixes_in_lists(AccountDb, Prefixes, Lists). -spec match_prefixes_in_lists(kz_term:ne_binary(), kz_term:ne_binaries(), kz_term:ne_binaries()) -> match_number_result(). match_prefixes_in_lists(AccountDb, Prefixes, [ListId | Rest]) -> case match_prefixes_in_list(AccountDb, Prefixes, ListId) of {'stop', _Name} = Result -> Result; 'continue' -> match_prefixes_in_lists(AccountDb, Prefixes, Rest) end; match_prefixes_in_lists(_AccountDb, _Number, []) -> lager:debug("no matching prefix"), 'continue'. -spec match_prefixes_in_list(kz_term:ne_binary(), kz_term:ne_binaries(), kz_term:ne_binary()) -> match_number_result(). match_prefixes_in_list(AccountDb, Prefixes, ListId) -> Keys = [[ListId, Prefix] || Prefix <- Prefixes], ViewOptions = [{'keys', Keys} ,'include_docs' ], case kz_datamgr:get_results(AccountDb , <<"lists/match_prefix_in_list">>, ViewOptions) of {'ok', []} -> 'continue'; {'ok', Entries} -> lager:debug("matched ~p prefixes, getting longest", [length(Entries)]), [Entry|_] = lists:sort(fun compare_prefixes/2, Entries), Doc = kz_json:get_value(<<"doc">>, Entry), Name = kz_json:get_value([<<"displayname">>, <<"name">>, <<"cid_name">>], Doc), lager:debug("matched prefix ~p", [get_prefix(Entry)]), {'stop', Name}; {'error', Error} -> lager:warning("error while matching prefixes in list ~p: ~p", [ListId, Error]), 'continue' end. -spec compare_prefixes(kz_json:object(), kz_json:object()) -> boolean(). compare_prefixes(JObj1, JObj2) -> byte_size(get_prefix(JObj1)) >= byte_size(get_prefix(JObj2)). -spec get_prefix(kz_json:object()) -> kz_term:ne_binary(). get_prefix(JObj) -> Doc = kz_json:get_value(<<"doc">>, JObj), kz_json:get_ne_binary_value(<<"number">>, Doc, kz_json:get_ne_binary_value(<<"prefix">>, Doc)). %% TODO: this function from hon_util, may be place it somewhere in library? -spec build_keys(kz_term:ne_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:ne_binaries()) -> kz_term:ne_binaries(). build_keys(<>, Prefix, Acc) -> build_keys(Rest, <>, [<> | Acc]); build_keys(<<>>, _, Acc) -> Acc. -spec match_regexp_in_list(kz_term:ne_binary(), kz_term:ne_binary(), kz_term:ne_binary()) -> 'continue' | {'stop', kz_term:api_binary()}. match_regexp_in_list(AccountDb, Number, ListId) when is_binary(ListId) -> ViewOptions = [{'keys', [ListId]} ,'include_docs' ], case kz_datamgr:get_results(AccountDb, <<"lists/regexps_in_list">>, ViewOptions) of {'ok', Entries} -> match_regexp(Entries, Number); Error -> lager:warning("getting regexps error: ~p", [Error]), 'continue' end. -spec match_regexp_in_lists(kz_term:ne_binary(), kz_term:ne_binary(), kz_term:ne_binary() | kz_term:ne_binaries()) -> kz_term:api_ne_binary(). match_regexp_in_lists(AccountDb, Number, [ListId | Rest]) -> case match_regexp_in_list(AccountDb, Number, ListId) of 'continue' -> match_regexp_in_lists(AccountDb, Number, Rest); {'stop', Name} -> Name end; match_regexp_in_lists(_, _, []) -> 'undefined'. -spec match_regexp(kz_json:objects(), kz_term:ne_binary()) -> 'continue' | {'stop', kz_term:api_binary()}. match_regexp([Entry | Entries], Number) -> Regex = kz_json:get_ne_binary_value(<<"value">>, Entry), Doc = kz_json:get_value(<<"doc">>, Entry), case Regex =/= 'undefined' andalso re:run(Number, Regex) of 'false' -> match_regexp(Entries, Number); 'nomatch' -> match_regexp(Entries, Number); {'match', _} -> lager:debug("matched regexp ~p", [kz_doc:id(Doc)]), {'stop', kz_json:get_first_defined([<<"displayname">>, <<"name">>, <<"cid_name">>], Entry)} end; match_regexp([], _Number) -> 'continue'.