diff --git a/apps/roster/src/api/push/roster_apns_api.erl b/apps/roster/src/api/push/roster_apns_api.erl index 18287f5b5b1c4a9521d982d7723816844647b3ee..1e571ff7eac48715c194d6d1bc48007bacfb985e 100644 --- a/apps/roster/src/api/push/roster_apns_api.erl +++ b/apps/roster/src/api/push/roster_apns_api.erl @@ -165,7 +165,7 @@ binary_push(Config, Packet, Token, Attempt) -> {error, Reason} when Attempt > 10 -> ?LOG_INFO("Socket error, giving up: ~p, Token ~P", [Reason, Token, 5]), - ok; + {error, not_delivered}; {error, Reason} -> ?LOG_INFO("Socket error: ~p, Retrying on ~P", [Reason, Token, 5]), diff --git a/apps/roster/src/api/push/roster_fcm_api.erl b/apps/roster/src/api/push/roster_fcm_api.erl index fb07f73a12b40e53d62e797bb23a30a273b5dad0..5ac1ba18dc303481c9eb39c31c05418a0dd945c1 100644 --- a/apps/roster/src/api/push/roster_fcm_api.erl +++ b/apps/roster/src/api/push/roster_fcm_api.erl @@ -2,44 +2,53 @@ -include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). --export([description/0, notify/3, test_push_notification/0]). +-export([ description/0 + , notify/2 + ]). description() -> "Android Push Notifications Module". --define(FCM_SERVER_KEY, proplists:get_value(fcm_server_key, application:get_env(roster, push_api, []))). --define(FCM_SEND_ENDPOINT, "https://fcm.googleapis.com/fcm/send"). --define(FCM_CONTENT_TYPE, "application/x-www-form-urlencoded;charset=UTF-8"). - %% ------------------------------------------------------------------ %% Android Push Notifications %% ------------------------------------------------------------------ -fcm_headers() -> - ContentType = "application/json", - Headers = [{"Authorization", binary_to_list(iolist_to_binary(["key=", ?FCM_SERVER_KEY]))}, {"Content-Type", ContentType}], - Headers. - -notify(MessageTitle, MessageBody, DeviceId) when is_binary(MessageTitle) -> - notify(binary_to_list(MessageTitle), MessageBody, DeviceId); -notify(MessageTitle, MessageBody, DeviceId) when is_binary(MessageBody) -> - notify(MessageTitle, binary_to_list(MessageBody), DeviceId); -notify(MessageTitle, MessageBody, DeviceId) when is_binary(DeviceId) -> - notify(MessageTitle, MessageBody, binary_to_list(DeviceId)); -notify(_, MessageBody, DeviceId) -> -%% create payload - Payload = binary_to_list(iolist_to_binary(["registration_id=", DeviceId, "&data=", MessageBody, "&priority=high"])), - ?LOG_INFO("Payload ~p~n~n", [Payload]), - Request = {?FCM_SEND_ENDPOINT, fcm_headers(), ?FCM_CONTENT_TYPE, Payload}, - {_, _} = roster_rest:send_request(post, Request, [], []), - ok. - -%% ------------------------------------------------------------------ -%% Tests -%% ------------------------------------------------------------------ - -%% Dima's phone --define(FCM_TEST_DEVICE_ID, <<"eBYJ3rgLdsc:APA91bHs_wR_SBrJxU7CjY0W4vwFN2LJugLwBefc1YLS2RWDVVUYLuT6g8x7TG640T7cmYmrxH_RvUv30bEZOAlZFLSwdLxy793sWC7L9YtRfow4Y9ro8Zwnz179YDnIzG5b8pky7QZV">>). - -test_push_notification() -> - MessageBody = "Notify Liubov about this push", - notify(MessageBody, MessageBody, ?FCM_TEST_DEVICE_ID). +notify(MessageBody, DeviceId) -> + Payload = binary_to_list( + iolist_to_binary(["registration_id=", DeviceId, + "&data=", MessageBody, + "&priority=high"])), + {ok, Config} = application:get_env(roster, push_api), + ServerKey = proplists:get_value(fcm_server_key, Config), + Headers = [{"Authorization", "key=" ++ ServerKey}, + {"Content-Type", "application/json"}], + Request = {"https://fcm.googleapis.com/fcm/send", + Headers, + "application/x-www-form-urlencoded;charset=UTF-8", + Payload}, + case httpc:request(post, Request, [], []) of + {ok, {{_, HttpCode, _}, _, Body}} when HttpCode =:= 200 -> + case re:split(Body, "=") of + [<<"id">> | _] -> + ?LOG_INFO("FCM push sent for ~P", [DeviceId, 5]), + ok; + [<<"Error">>, <<"NotRegistered">>] -> + ?LOG_INFO("FCM push to bad token ~P", + [DeviceId, 5]), + {error, bad_token}; + [<<"Error">> | _] -> + ?LOG_INFO("FCM push error for ~P, ~p", + [DeviceId, 5, Body]), + {error, not_delivered}; + _Other -> + ?LOG_INFO("FCM push unexpected reply ~P, ~p", + [DeviceId, 5, Body]), + {error, not_delivered} + end; + {ok, {{_, HttpCode, _}, _, Body}} -> + ?LOG_ERROR("FCM push error ~p:~p", [HttpCode, Body]), + {error, not_delivered}; + {error, What} -> + ?LOG_INFO("FCM push error for ~P: ~p", + [DeviceId, 5, What]), + {error, not_delivered} + end. diff --git a/apps/roster/src/protocol/roster_push.erl b/apps/roster/src/protocol/roster_push.erl index 5b675aca6bb076733f8a150834cab8ccc880b582..ca6e13e5f298ff41bad5c4303a04000c26257f60 100644 --- a/apps/roster/src/protocol/roster_push.erl +++ b/apps/roster/src/protocol/roster_push.erl @@ -2,7 +2,6 @@ -include_lib("kernel/include/logger.hrl"). -include("roster.hrl"). -include_lib("n2o/include/n2o.hrl"). --include_lib("kvs/include/kvs.hrl"). -export([ start/0 , send_push_notification/4 , proc/2 @@ -16,10 +15,19 @@ start() -> , state = init}). send_push_notification(Session, Payload, Alert, Type) -> - n2o_async:pid(system, ?MODULE) - ! {async_push, Session, Payload, Alert, Type}. + case is_push_session(Session) of + true -> + Pid = n2o_async:pid(system, ?MODULE), + Pid ! {async_push, Session, Payload, Alert, Type}, + ok; + false -> + skip + end. -%% TODO: Handle apns connection messages in proc/2 +is_push_session(#'Auth'{push = []}) -> false; +is_push_session(#'Auth'{os = ios}) -> true; +is_push_session(#'Auth'{os = android}) -> true; +is_push_session(#'Auth'{}) -> false. proc(init, #handler{name = ?MODULE} = Async) -> ConnState = #{ android => [] @@ -38,37 +46,35 @@ proc({reconnecting, _Pid}, #handler{} = H) -> %% APNS connection. Safe to ignore {noreply, H}; -proc({async_push, Session, Payload, PushAlert, PushType}, #handler{state = HS} = H) -> - send_push_notification(Session, Payload, PushAlert, PushType, HS), - {reply, [], H}. +proc({async_push, Session, Payload, Alert, Type}, Handler) -> + case do_push(Session, Payload, Alert, Type, Handler#handler.state) of + ok -> + {noreply, Handler}; + {error, bad_token} -> + ClientId = Session#'Auth'.client_id, + ?LOG_INFO("Deleting Auth for ~p: bad push token", [ClientId]), + kvs:delete('Auth', ClientId), + {noreply, Handler}; + {error, not_delivered} -> + {noreply, Handler} + end. + -send_push_notification(#'Auth'{ os = OS - , push = PushToken - , user_id = PhoneId - , settings = AuthSettings}, Payload, PushAlert, PushType, HS) -> - case PushToken of - [] -> skip; - _ -> - ?LOG_INFO("~p:~p:~PPushAlert:~p", - [PhoneId, OS, PushToken, 5, PushAlert]), - send_push_notification(OS, PushToken, Payload, PushAlert, PushType, AuthSettings, HS) +do_push(#'Auth'{ os = OS, push = Token, user_id = PhoneId, settings = Settings}, + Payload, Alert, Type, State) -> + ?LOG_INFO("~p:~p:~P PushAlert:~p", [PhoneId, OS, Token, 5, Alert]), + case OS of + ios -> + IOSPayload = base64:encode(term_to_binary(Payload)), + ConnectionState = maps:get(ios, State), + roster_apns_api:notify(Alert, IOSPayload, Type, Token, + Settings, ConnectionState); + android -> + AndroidPayload = android_push_payload(Payload, Alert, Type), + roster_fcm_api:notify(AndroidPayload, Token) end. -send_push_notification(ios, Push, Payload, PushAlert, PushType, AuthSettings, HS) -> - EncodedPayload = base64:encode(term_to_binary(Payload)), - IOS = maps:get(ios, HS), - case roster_apns_api:notify(PushAlert, EncodedPayload, - PushType, Push, AuthSettings, IOS) of - ok -> - ok; - {error, bad_token} -> - %% TODO: Remove auth. - ok; - {error,_What} -> - ok - end; -send_push_notification(android, Push, Payload, PushAlert, PushType, _AuthSettings,_HS) -> - PushModel = #push{model = Payload, type = PushType, alert = PushAlert, title = PushAlert, badge = 1}, - AndroidPush = http_uri:encode(binary_to_list(base64:encode(term_to_binary(PushModel)))), - roster_fcm_api:notify(PushAlert, AndroidPush, Push); -send_push_notification(_, _, _, _, _, _, _) -> skip. +android_push_payload(Payload, Alert, Type) -> + Model = #push{model = Payload, type = Type, alert = Alert, + title = Alert, badge = 1}, + http_uri:encode(binary_to_list(base64:encode(term_to_binary(Model)))). diff --git a/sys.config b/sys.config index 144dd9d752bb86637727059263c67bb4ff7b043f..caf1ae47b7d3bb61ccdffd3114a93206d87c4639 100644 --- a/sys.config +++ b/sys.config @@ -96,7 +96,10 @@ ]}, {push_api,[ {apns_force_http, false}, - {fcm_server_key,<<"AAAAAzb6_Zg:APA91bGN0jYv_4iqyk8IC4xUdPYXh0yPsTF9YYj_gd9oebRr_ZEoLuC5hCD9RfdqA3Y3AF_P_WbelqvzvgR3RsX_mHBLynV14Q6HakXAtrY_eWLK2xqamF2OC9uBXfKgxTFFqmyr1Kbw">>}, + {fcm_server_key, + "AAAAAzb6_Zg:APA91bGN0jYv_4iqyk8IC4xUdPYXh0yPsTF9YY" + "j_gd9oebRr_ZEoLuC5hCD9RfdqA3Y3AF_P_WbelqvzvgR3RsX_" + "mHBLynV14Q6HakXAtrY_eWLK2xqamF2OC9uBXfKgxTFFqmyr1Kbw"}, {apns_cert_dir, "etc/certs/apns_certificates"}, {apns_binary_port, 2195}, {apns_http_port, 443}]},