diff --git a/fluxer_gateway/src/push/push_message_params.erl b/fluxer_gateway/src/push/push_message_params.erl index 846b40ee3..cc0f2e36c 100644 --- a/fluxer_gateway/src/push/push_message_params.erl +++ b/fluxer_gateway/src/push/push_message_params.erl @@ -8,6 +8,9 @@ -export_type([context/0]). -define(MAX_GUILD_FEATURES, 64). +-define(MESSAGE_TYPE_DEFAULT, 0). +-define(MESSAGE_TYPE_REPLY, 19). +-define(PUSHABLE_MESSAGE_TYPES, [?MESSAGE_TYPE_DEFAULT, ?MESSAGE_TYPE_REPLY]). -type context() :: #{ message_data := map(), @@ -110,7 +113,24 @@ validate(#{message_id := undefined}) -> validate(#{guild_default_notifications := undefined}) -> {error, invalid_guild_default_notifications}; validate(Context) -> - {ok, Context}. + validate_message_type(message_type(Context), Context). + +-spec validate_message_type(integer(), context()) -> {ok, context()} | {error, term()}. +validate_message_type(Type, Context) -> + case lists:member(Type, ?PUSHABLE_MESSAGE_TYPES) of + true -> {ok, Context}; + false -> {error, {unpushable_message_type, Type}} + end. + +-spec message_type(context()) -> integer(). +message_type(#{message_data := MessageData}) -> + normalize_message_type(maps:get(<<"type">>, MessageData, ?MESSAGE_TYPE_DEFAULT)). + +-spec normalize_message_type(term()) -> integer(). +normalize_message_type(Type) when is_integer(Type) -> + Type; +normalize_message_type(_Type) -> + ?MESSAGE_TYPE_DEFAULT. -spec owner_fallback_key(map()) -> term(). owner_fallback_key(Params) -> @@ -163,3 +183,51 @@ markdown_context(MessageData, GuildId, RoleNames, _RawContext) when push_notification_format:build_markdown_context(MessageData, GuildId, RoleNames, #{}); markdown_context(_MessageData, _GuildId, _RoleNames, RawContext) -> optional_map(RawContext). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +params_with_message_type(Type) -> + #{ + message_data => #{ + <<"channel_id">> => <<"1472201127385612376">>, + <<"id">> => <<"1472201127385612377">>, + <<"type">> => Type + }, + user_ids => [<<"1474262819227156566">>], + guild_id => <<"1472200708085309475">>, + author_id => <<"1472583967301656587">>, + guild_default_notifications => 0 + }. + +context_allows_a_default_message_test() -> + ?assertMatch({ok, _}, context(params_with_message_type(?MESSAGE_TYPE_DEFAULT))). + +context_allows_a_reply_test() -> + ?assertMatch({ok, _}, context(params_with_message_type(?MESSAGE_TYPE_REPLY))). + +context_rejects_a_call_system_message_test() -> + ?assertEqual( + {error, {unpushable_message_type, 3}}, context(params_with_message_type(3)) + ). + +context_rejects_every_non_authored_message_type_test() -> + lists:foreach( + fun(Type) -> + ?assertEqual( + {error, {unpushable_message_type, Type}}, + context(params_with_message_type(Type)) + ) + end, + [1, 2, 3, 4, 5, 6, 7, 99] + ). + +context_treats_a_missing_message_type_as_pushable_test() -> + Params = params_with_message_type(?MESSAGE_TYPE_DEFAULT), + MessageData = maps:remove(<<"type">>, maps:get(message_data, Params)), + ?assertMatch({ok, _}, context(Params#{message_data := MessageData})). + +context_treats_a_malformed_message_type_as_pushable_test() -> + ?assertMatch({ok, _}, context(params_with_message_type(<<"nonsense">>))). + +-endif. diff --git a/fluxer_gateway/src/push/push_notification_format.erl b/fluxer_gateway/src/push/push_notification_format.erl index 8203d2129..81f0d44b1 100644 --- a/fluxer_gateway/src/push/push_notification_format.erl +++ b/fluxer_gateway/src/push/push_notification_format.erl @@ -33,7 +33,7 @@ build_content_preview(MessageData, MarkdownContext) -> Preview = push_markdown_plaintext:render_push_preview(Content, MarkdownContext), case Preview of <<>> -> - truncate_preview(build_content_fallback_preview(MessageData)); + truncate_preview(build_content_fallback_preview(MessageData, MarkdownContext)); _ -> truncate_preview(Preview) end. @@ -372,13 +372,37 @@ valid_utf8_prefix(Content) -> {error, Valid, _Rest} -> Valid end. --spec build_content_fallback_preview(map()) -> binary(). -build_content_fallback_preview(MessageData) -> +-spec build_content_fallback_preview(map(), map()) -> binary(). +build_content_fallback_preview(MessageData, MarkdownContext) -> case first_nonempty_binary([ build_sticker_preview(maps:get(<<"stickers">>, MessageData, [])), build_attachment_preview(maps:get(<<"attachments">>, MessageData, [])), - build_embed_preview(maps:get(<<"embeds">>, MessageData, [])) + build_embed_preview(maps:get(<<"embeds">>, MessageData, [])), + build_snapshot_preview( + maps:get(<<"message_snapshots">>, MessageData, []), MarkdownContext + ) + ]) + of + Preview when is_binary(Preview) -> Preview; + undefined -> <<>> + end. + +-spec build_snapshot_preview(term(), map()) -> binary(). +build_snapshot_preview([Snapshot | _Rest], MarkdownContext) when is_map(Snapshot) -> + snapshot_preview(Snapshot, MarkdownContext); +build_snapshot_preview(_Snapshots, _MarkdownContext) -> + <<>>. + +-spec snapshot_preview(map(), map()) -> binary(). +snapshot_preview(Snapshot, MarkdownContext) -> + Content = push_utils:normalize_binary(maps:get(<<"content">>, Snapshot, <<>>), <<>>), + case + first_nonempty_binary([ + push_markdown_plaintext:render_push_preview(Content, MarkdownContext), + build_sticker_preview(maps:get(<<"stickers">>, Snapshot, [])), + build_attachment_preview(maps:get(<<"attachments">>, Snapshot, [])), + build_embed_preview(maps:get(<<"embeds">>, Snapshot, [])) ]) of Preview when is_binary(Preview) -> Preview; @@ -639,6 +663,48 @@ truncate_preview_keeps_short_valid_content_identical_test() -> Content = <<"hello \xC3\xA9 world">>, ?assertEqual(Content, truncate_preview(Content)). +a_forwarded_message_previews_its_snapshot_content_test() -> + MessageData = #{ + <<"content">> => <<>>, + <<"message_snapshots">> => [#{<<"content">> => <<"forwarded text">>}] + }, + ?assertEqual(<<"forwarded text">>, build_content_preview(MessageData)). + +a_forwarded_attachment_previews_its_filename_test() -> + MessageData = #{ + <<"content">> => <<>>, + <<"message_snapshots">> => [ + #{<<"content">> => null, <<"attachments">> => [#{<<"filename">> => <<"cat.png">>}]} + ] + }, + ?assertEqual(<<"Attachment: cat.png">>, build_content_preview(MessageData)). + +a_forwarded_sticker_previews_its_name_test() -> + MessageData = #{ + <<"content">> => <<>>, + <<"message_snapshots">> => [ + #{<<"content">> => null, <<"stickers">> => [#{<<"name">> => <<"Wave">>}]} + ] + }, + ?assertEqual(<<"Sticker: Wave">>, build_content_preview(MessageData)). + +own_content_wins_over_a_snapshot_test() -> + MessageData = #{ + <<"content">> => <<"my words">>, + <<"message_snapshots">> => [#{<<"content">> => <<"forwarded text">>}] + }, + ?assertEqual(<<"my words">>, build_content_preview(MessageData)). + +an_empty_snapshot_list_previews_nothing_test() -> + ?assertEqual( + <<>>, build_content_preview(#{<<"content">> => <<>>, <<"message_snapshots">> => []}) + ). + +a_null_snapshot_field_previews_nothing_test() -> + ?assertEqual( + <<>>, build_content_preview(#{<<"content">> => <<>>, <<"message_snapshots">> => null}) + ). + build_url_dm_test() -> ?assertEqual(<<"/channels/@me/456/789">>, build_url(0, 456, 789)).