mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
138 lines
4.6 KiB
Erlang
138 lines
4.6 KiB
Erlang
%% SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
-module(guild_passive_sync_voice).
|
|
-typing([eqwalizer]).
|
|
|
|
-export([
|
|
build_current_voice_state_map/2,
|
|
compute_voice_state_updates/3
|
|
]).
|
|
|
|
-export_type([voice_state/0]).
|
|
|
|
-type voice_state() :: map().
|
|
|
|
-spec build_current_voice_state_map(sets:set(), map()) -> #{binary() => voice_state()}.
|
|
build_current_voice_state_map(ViewableChannels, VoiceStates) ->
|
|
maps:fold(
|
|
fun(ConnectionId, VoiceState, Acc) ->
|
|
maybe_include_voice_state(ConnectionId, VoiceState, ViewableChannels, Acc)
|
|
end,
|
|
#{},
|
|
VoiceStates
|
|
).
|
|
|
|
-spec maybe_include_voice_state(binary(), voice_state(), sets:set(), map()) -> map().
|
|
maybe_include_voice_state(ConnectionId, VoiceState, ViewableChannels, Acc) ->
|
|
case maps:get(<<"channel_id">>, VoiceState, null) of
|
|
null ->
|
|
Acc;
|
|
ChannelIdBin ->
|
|
maybe_include_if_viewable(
|
|
ConnectionId, VoiceState, ChannelIdBin, ViewableChannels, Acc
|
|
)
|
|
end.
|
|
|
|
-spec maybe_include_if_viewable(binary(), voice_state(), binary(), sets:set(), map()) -> map().
|
|
maybe_include_if_viewable(ConnectionId, VoiceState, ChannelIdBin, ViewableChannels, Acc) ->
|
|
case parse_snowflake(ChannelIdBin) of
|
|
undefined ->
|
|
Acc;
|
|
ChannelId ->
|
|
include_viewable_voice_state(
|
|
ConnectionId, VoiceState, ChannelId, ViewableChannels, Acc
|
|
)
|
|
end.
|
|
|
|
-spec include_viewable_voice_state(binary(), voice_state(), integer(), sets:set(), map()) ->
|
|
map().
|
|
include_viewable_voice_state(ConnectionId, VoiceState, ChannelId, ViewableChannels, Acc) ->
|
|
case sets:is_element(ChannelId, ViewableChannels) of
|
|
true -> Acc#{ConnectionId => VoiceState};
|
|
false -> Acc
|
|
end.
|
|
|
|
-spec compute_voice_state_updates(
|
|
#{binary() => voice_state()}, #{binary() => voice_state()}, integer()
|
|
) -> [voice_state()].
|
|
compute_voice_state_updates(Current, Previous, GuildId) ->
|
|
GuildIdBin = integer_to_binary(GuildId),
|
|
Updated = collect_updated(Current, Previous, GuildIdBin),
|
|
Removed = collect_removed(Current, Previous, GuildIdBin),
|
|
lists:reverse(Updated) ++ lists:reverse(Removed).
|
|
|
|
-spec collect_updated(map(), map(), binary()) -> [voice_state()].
|
|
collect_updated(Current, Previous, GuildIdBin) ->
|
|
maps:fold(
|
|
fun(ConnId, VoiceState, Acc) ->
|
|
collect_updated_state(ConnId, VoiceState, Previous, GuildIdBin, Acc)
|
|
end,
|
|
[],
|
|
Current
|
|
).
|
|
|
|
-spec collect_updated_state(binary(), voice_state(), map(), binary(), [voice_state()]) ->
|
|
[voice_state()].
|
|
collect_updated_state(ConnId, VoiceState, Previous, GuildIdBin, Acc) ->
|
|
PrevState = maps:get(ConnId, Previous, undefined),
|
|
case is_changed(VoiceState, PrevState) of
|
|
true ->
|
|
[
|
|
voice_state_utils:sanitize_voice_state_for_broadcast(
|
|
ensure_guild(VoiceState, GuildIdBin)
|
|
)
|
|
| Acc
|
|
];
|
|
false ->
|
|
Acc
|
|
end.
|
|
|
|
-spec collect_removed(map(), map(), binary()) -> [voice_state()].
|
|
collect_removed(Current, Previous, GuildIdBin) ->
|
|
maps:fold(
|
|
fun(ConnId, PrevState, Acc) ->
|
|
collect_removed_state(ConnId, PrevState, Current, GuildIdBin, Acc)
|
|
end,
|
|
[],
|
|
Previous
|
|
).
|
|
|
|
-spec collect_removed_state(binary(), voice_state(), map(), binary(), [voice_state()]) ->
|
|
[voice_state()].
|
|
collect_removed_state(ConnId, PrevState, Current, GuildIdBin, Acc) ->
|
|
case maps:is_key(ConnId, Current) of
|
|
true -> Acc;
|
|
false -> [build_removed(PrevState, GuildIdBin) | Acc]
|
|
end.
|
|
|
|
-spec is_changed(voice_state(), voice_state() | undefined) -> boolean().
|
|
is_changed(_Current, undefined) -> true;
|
|
is_changed(Current, Previous) -> version(Current) =/= version(Previous).
|
|
|
|
-spec version(voice_state()) -> integer().
|
|
version(VoiceState) ->
|
|
case map_utils:get_integer(VoiceState, <<"version">>, 0) of
|
|
V when is_integer(V) -> V;
|
|
_ -> 0
|
|
end.
|
|
|
|
-spec ensure_guild(voice_state(), binary()) -> voice_state().
|
|
ensure_guild(VoiceState, GuildIdBin) ->
|
|
case maps:get(<<"guild_id">>, VoiceState, undefined) of
|
|
undefined -> VoiceState#{<<"guild_id">> => GuildIdBin};
|
|
_ -> VoiceState
|
|
end.
|
|
|
|
-spec build_removed(voice_state(), binary()) -> voice_state().
|
|
build_removed(PrevState, GuildIdBin) ->
|
|
voice_state_utils:sanitize_voice_state_for_broadcast(
|
|
(ensure_guild(PrevState, GuildIdBin))#{<<"channel_id">> => null}
|
|
).
|
|
|
|
-spec parse_snowflake(term()) -> integer() | undefined.
|
|
parse_snowflake(Value) ->
|
|
case validation:validate_snowflake(<<"channel_id">>, Value) of
|
|
{ok, Id} -> Id;
|
|
{error, _, _} -> undefined
|
|
end.
|