fluxer/fluxer_gateway/src/presence/presence_targets.erl
2026-02-17 12:22:36 +00:00

184 lines
5.5 KiB
Erlang

%% Copyright (C) 2026 Fluxer Contributors
%%
%% This file is part of Fluxer.
%%
%% Fluxer is free software: you can redistribute it and/or modify
%% it under the terms of the GNU Affero General Public License as published by
%% the Free Software Foundation, either version 3 of the License, or
%% (at your option) any later version.
%%
%% Fluxer is distributed in the hope that it will be useful,
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%% GNU Affero General Public License for more details.
%%
%% You should have received a copy of the GNU Affero General Public License
%% along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
-module(presence_targets).
-export([
friend_ids_from_state/1,
group_dm_recipients_from_state/1
]).
-type user_id() :: integer().
-type channel_id() :: integer().
-type relationship_type() :: integer().
-type state() :: #{
user_id := user_id(),
relationships => #{user_id() => relationship_type()},
channels => #{channel_id() => map()},
_ => _
}.
-spec friend_ids_from_state(state()) -> [user_id()].
friend_ids_from_state(State) ->
Relationships = maps:get(relationships, State, #{}),
[
UserId
|| {UserId, Type} <- maps:to_list(Relationships),
Type =:= 1 orelse Type =:= 3
].
-spec group_dm_recipients_from_state(state()) -> #{channel_id() => #{user_id() => true}}.
group_dm_recipients_from_state(State) ->
UserId = maps:get(user_id, State),
Channels = maps:get(channels, State, #{}),
maps:from_list(
[
{ChannelId, map_from_ids([Rid || Rid <- RecipientIds, Rid =/= UserId])}
|| {ChannelId, Channel} <- maps:to_list(Channels),
maps:get(<<"type">>, Channel, 0) =:= 3,
RecipientIds <- [extract_recipient_ids(Channel)]
]
).
-spec extract_recipient_ids(map()) -> [user_id()].
extract_recipient_ids(Channel) ->
Recipients = maps:get(<<"recipients">>, Channel, maps:get(<<"recipient_ids">>, Channel, [])),
Unique =
lists:foldl(
fun(Entry, Acc) ->
case extract_recipient_id(Entry) of
undefined ->
Acc;
Value ->
case lists:member(Value, Acc) of
true -> Acc;
false -> [Value | Acc]
end
end
end,
[],
Recipients
),
lists:reverse(Unique).
-spec extract_recipient_id(term()) -> user_id() | undefined.
extract_recipient_id(Entry) when is_map(Entry) ->
type_conv:extract_id(Entry, <<"id">>);
extract_recipient_id(Entry) ->
case Entry of
Bin when is_binary(Bin) ->
type_conv:extract_id(#{<<"id">> => Bin}, <<"id">>);
Int when is_integer(Int) ->
Int;
_ ->
undefined
end.
-spec map_from_ids([user_id()]) -> #{user_id() => true}.
map_from_ids(Ids) when is_list(Ids) ->
maps:from_list([{Id, true} || Id <- Ids]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
friend_ids_from_state_filters_relationship_types_test() ->
State = #{
relationships =>
#{
10 => 1,
11 => 3,
12 => 4,
13 => 2
}
},
Ids = lists:sort(friend_ids_from_state(State)),
?assertEqual([10, 11], Ids).
friend_ids_from_state_empty_test() ->
State = #{relationships => #{}},
?assertEqual([], friend_ids_from_state(State)).
friend_ids_from_state_missing_key_test() ->
State = #{},
?assertEqual([], friend_ids_from_state(State)).
group_dm_recipients_from_state_test() ->
State = #{
user_id => 1,
channels => #{
100 => #{
<<"type">> => 3,
<<"recipients">> => [
#{<<"id">> => <<"2">>},
#{<<"id">> => <<"3">>}
]
},
200 => #{
<<"type">> => 0,
<<"recipients">> => [#{<<"id">> => <<"4">>}]
}
}
},
Result = group_dm_recipients_from_state(State),
?assertEqual(#{100 => #{2 => true, 3 => true}}, Result).
group_dm_recipients_excludes_self_test() ->
State = #{
user_id => 2,
channels => #{
100 => #{
<<"type">> => 3,
<<"recipients">> => [
#{<<"id">> => <<"2">>},
#{<<"id">> => <<"3">>}
]
}
}
},
Result = group_dm_recipients_from_state(State),
?assertEqual(#{100 => #{3 => true}}, Result).
extract_recipient_id_map_test() ->
?assertEqual(123, extract_recipient_id(#{<<"id">> => <<"123">>})),
?assertEqual(undefined, extract_recipient_id(#{})).
extract_recipient_id_binary_test() ->
?assertEqual(456, extract_recipient_id(<<"456">>)).
extract_recipient_id_integer_test() ->
?assertEqual(789, extract_recipient_id(789)).
extract_recipient_id_invalid_test() ->
?assertEqual(undefined, extract_recipient_id(undefined)),
?assertEqual(undefined, extract_recipient_id([1, 2, 3])).
extract_recipient_ids_deduplicates_test() ->
Channel = #{
<<"recipients">> => [
#{<<"id">> => <<"1">>},
#{<<"id">> => <<"1">>},
#{<<"id">> => <<"2">>}
]
},
Ids = extract_recipient_ids(Channel),
?assertEqual([1, 2], Ids).
map_from_ids_test() ->
?assertEqual(#{}, map_from_ids([])),
?assertEqual(#{1 => true, 2 => true}, map_from_ids([1, 2])).
-endif.