%%%----------------------------------------------------------------------------- %%% @copyright (C) 2011-2019, 2600Hz %%% @doc Carrier for inums %%% @author Karl Anderson %%% @author Pierre Fenoll %%% @end %%%----------------------------------------------------------------------------- -module(knm_inum). -behaviour(knm_gen_carrier). -export([info/0]). -export([is_local/0]). -export([find_numbers/3]). -export([acquire_number/1]). -export([disconnect_number/1]). -export([is_number_billable/1]). -export([should_lookup_cnam/0]). -export([check_numbers/1]). -export([generate_numbers/3]). -include("knm.hrl"). %%------------------------------------------------------------------------------ %% @doc %% @end %%------------------------------------------------------------------------------ -spec info() -> map(). info() -> #{?CARRIER_INFO_MAX_PREFIX => 15 }. %%------------------------------------------------------------------------------ %% @doc Is this carrier handling numbers local to the system? %% %%
A non-local (foreign) carrier module makes HTTP requests.
%% @end %%------------------------------------------------------------------------------ -spec is_local() -> boolean(). is_local() -> 'true'. %%------------------------------------------------------------------------------ %% @doc Check with carrier if these numbers are registered with it. %% @end %%------------------------------------------------------------------------------ -spec check_numbers(kz_term:ne_binaries()) -> {ok, kz_json:object()} | {error, any()}. check_numbers(_Numbers) -> {error, not_implemented}. %%------------------------------------------------------------------------------ %% @doc Query the local system for a quantity of available numbers %% in a rate center %% @end %%------------------------------------------------------------------------------ -spec find_numbers(kz_term:ne_binary(), pos_integer(), knm_carriers:options()) -> {'ok', knm_number:knm_numbers()} | {'error', any()}. find_numbers(<<"+", _/binary>>=Prefix, Quantity, Options) -> AccountId = knm_carriers:account_id(Options), find_numbers_in_account(Prefix, Quantity, AccountId, Options); find_numbers(Prefix, Quantity, Options) -> find_numbers(<<"+",Prefix/binary>>, Quantity, Options). -spec find_numbers_in_account(kz_term:ne_binary(), pos_integer(), kz_term:api_ne_binary(), knm_carriers:options()) -> {'ok', knm_number:knm_numbers()} | {'error', any()}. find_numbers_in_account(_Prefix, _Quantity, 'undefined', _Options) -> {'ok', []}; find_numbers_in_account(Prefix, Quantity, AccountId, Options) -> Offset = knm_carriers:offset(Options), QID = knm_search:query_id(Options), case do_find_numbers_in_account(Prefix, Quantity, Offset, AccountId, QID) of {'error', 'not_available'} -> ResellerId = knm_carriers:reseller_id(Options), case AccountId =:= ResellerId of 'true' -> {'ok', []}; 'false' -> NewOptions = [{'offset', 0} | Options], find_numbers_in_account(Prefix, Quantity, ResellerId, NewOptions) end; Result -> Result end. -spec do_find_numbers_in_account(kz_term:ne_binary(), pos_integer(), non_neg_integer(), kz_term:ne_binary(), kz_term:ne_binary()) -> {'ok', knm_number:knm_numbers()} | {'error', any()}. do_find_numbers_in_account(Prefix, Quantity, Offset, AccountId, QID) -> ViewOptions = [{'startkey', [AccountId, ?NUMBER_STATE_AVAILABLE, Prefix]} ,{'endkey', [AccountId, ?NUMBER_STATE_AVAILABLE, <>]} ,{'limit', Quantity} ,{'skip', Offset} ,'include_docs' ], case kz_datamgr:get_results(?KZ_INUM_DB, <<"numbers_inum/status">>, ViewOptions) of {'ok', []} -> lager:debug("found no available inum numbers for account ~s", [AccountId]), {'error', 'not_available'}; {'ok', JObjs} -> lager:debug("found available inum numbers for account ~s", [AccountId]), {'ok', format_numbers_resp(QID, JObjs)}; {'error', _R}=E -> lager:debug("failed to lookup available local numbers: ~p", [_R]), E end. format_numbers_resp(QID, JObjs) -> [{QID, {Num, ?MODULE, ?NUMBER_STATE_AVAILABLE, kz_json:new()}} || JObj <- JObjs, Num <- [kz_doc:id(kz_json:get_value(<<"doc">>, JObj))] ]. %%------------------------------------------------------------------------------ %% @doc %% @end %%------------------------------------------------------------------------------ -spec is_number_billable(knm_phone_number:knm_phone_number()) -> boolean(). is_number_billable(_Number) -> 'false'. %%------------------------------------------------------------------------------ %% @doc Acquire a given number from the carrier %% @end %%------------------------------------------------------------------------------ -spec acquire_number(knm_number:knm_number()) -> knm_number:knm_number(). acquire_number(Number) -> PhoneNumber = knm_number:phone_number(Number), lager:debug("acquiring number ~s", [knm_phone_number:number(PhoneNumber)]), update_doc(Number, [{?PVT_STATE, knm_phone_number:state(PhoneNumber)} ,{?PVT_ASSIGNED_TO, knm_phone_number:assigned_to(PhoneNumber)} ]). %%------------------------------------------------------------------------------ %% @doc Release a number from the routing table %% @end %%------------------------------------------------------------------------------ -spec disconnect_number(knm_number:knm_number()) -> knm_number:knm_number(). disconnect_number(Number) -> lager:debug("disconnect number ~s in managed provider" ,[knm_phone_number:number(knm_number:phone_number(Number))]), update_doc(Number, [{?PVT_STATE, ?NUMBER_STATE_RELEASED} ,{?PVT_ASSIGNED_TO, 'undefined'} ,{?PVT_RESERVE_HISTORY, []} ]). -spec generate_numbers(kz_term:ne_binary(), pos_integer(), non_neg_integer()) -> 'ok'. generate_numbers(AccountId, <<"8835100",_/binary>> = Number, Quantity) when byte_size(Number) =:= 15 -> generate_numbers(AccountId, kz_term:to_integer(Number), kz_term:to_integer(Quantity)); generate_numbers(_AccountId, _Number, 0) -> 'ok'; generate_numbers(?MATCH_ACCOUNT_RAW(AccountId), Number, Quantity) when is_integer(Number), is_integer(Quantity), Quantity > 0 -> _R = save_doc(AccountId, <<"+",(kz_term:to_binary(Number))/binary>>), lager:info("number ~p/~p/~p", [Number, Quantity, _R]), generate_numbers(AccountId, Number+1, Quantity-1). -spec save_doc(kz_term:ne_binary(), kz_term:ne_binary()) -> {'ok', kz_json:object()} | {'error', any()}. save_doc(AccountId, Number) -> JObj = kz_json:from_list([{<<"_id">>, knm_converters:normalize(Number)} ,{<<"pvt_account_id">>, AccountId} ,{?PVT_STATE, ?NUMBER_STATE_AVAILABLE} ,{?PVT_TYPE, <<"number">>} ]), save_doc(JObj). -spec save_doc(kz_json:object()) -> {'ok', kz_json:object()} | {'error', any()}. save_doc(JObj) -> kz_datamgr:save_doc(?KZ_INUM_DB, JObj). -spec update_doc(knm_number:knm_number(), kz_term:proplist()) -> knm_number:knm_number(). update_doc(Number, UpdateProps) -> PhoneNumber = knm_number:phone_number(Number), Num = knm_phone_number:number(PhoneNumber), Updates = [{?PVT_MODULE_NAME, kz_term:to_binary(?MODULE)} | UpdateProps ], UpdateOptions = [{'update', Updates}], case kz_datamgr:update_doc(?KZ_INUM_DB, Num, UpdateOptions) of {'ok', _UpdatedDoc} -> Number; {'error', Reason} -> knm_errors:database_error(Reason, PhoneNumber) end. %%------------------------------------------------------------------------------ %% @doc %% @end %%------------------------------------------------------------------------------ -spec should_lookup_cnam() -> 'true'. should_lookup_cnam() -> 'true'.