refactor progress
This commit is contained in:
@@ -27,6 +27,8 @@
|
||||
|
||||
-type session_state() :: map().
|
||||
-type request_data() :: map().
|
||||
-type member() :: map().
|
||||
-type presence() :: map().
|
||||
|
||||
-ifdef(TEST).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
@@ -34,13 +36,10 @@
|
||||
|
||||
-spec handle_request(request_data(), pid(), session_state()) -> ok | {error, atom()}.
|
||||
handle_request(Data, SocketPid, SessionState) when is_map(Data), is_pid(SocketPid) ->
|
||||
logger:debug("[guild_request_members] Handling guild members request: ~p", [Data]),
|
||||
case parse_request(Data) of
|
||||
{ok, Request} ->
|
||||
logger:debug("[guild_request_members] Request parsed successfully: ~p", [Request]),
|
||||
process_request(Request, SocketPid, SessionState);
|
||||
{error, Reason} ->
|
||||
logger:warning("[guild_request_members] Failed to parse request: ~p", [Reason]),
|
||||
{error, Reason}
|
||||
end;
|
||||
handle_request(_, _, _) ->
|
||||
@@ -55,7 +54,6 @@ parse_request(Data) ->
|
||||
Presences = maps:get(<<"presences">>, Data, false),
|
||||
Nonce = maps:get(<<"nonce">>, Data, null),
|
||||
NormalizedNonce = normalize_nonce(Nonce),
|
||||
|
||||
case validate_guild_id(GuildIdRaw) of
|
||||
{ok, GuildId} ->
|
||||
case validate_user_ids(UserIdsRaw) of
|
||||
@@ -137,25 +135,16 @@ process_request(Request, SocketPid, SessionState) ->
|
||||
#{guild_id := GuildId, query := Query, limit := Limit, user_ids := UserIds} = Request,
|
||||
UserIdBin = maps:get(user_id, SessionState),
|
||||
UserId = type_conv:to_integer(UserIdBin),
|
||||
|
||||
logger:debug(
|
||||
"[guild_request_members] Processing request for guild ~p, user ~p, user_ids: ~p",
|
||||
[GuildId, UserId, UserIds]
|
||||
),
|
||||
|
||||
case check_permission(UserId, GuildId, Query, Limit, UserIds, SessionState) of
|
||||
ok ->
|
||||
logger:debug("[guild_request_members] Permission check passed, fetching members"),
|
||||
fetch_and_send_members(Request, SocketPid, SessionState);
|
||||
{error, Reason} ->
|
||||
logger:warning(
|
||||
"[guild_request_members] Permission check failed: ~p",
|
||||
[Reason]
|
||||
),
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
-spec check_permission(integer(), integer(), binary(), non_neg_integer(), [integer()], session_state()) ->
|
||||
-spec check_permission(
|
||||
integer(), integer(), binary(), non_neg_integer(), [integer()], session_state()
|
||||
) ->
|
||||
ok | {error, atom()}.
|
||||
check_permission(UserId, GuildId, Query, Limit, UserIds, SessionState) ->
|
||||
RequiresPermission = Query =:= <<>> andalso Limit =:= 0 andalso UserIds =:= [],
|
||||
@@ -177,7 +166,6 @@ check_management_permission(UserId, _GuildId, GuildPid) ->
|
||||
KickMembers = constants:kick_members_permission(),
|
||||
BanMembers = constants:ban_members_permission(),
|
||||
RequiredPermission = ManageRoles bor KickMembers bor BanMembers,
|
||||
|
||||
PermRequest = #{
|
||||
user_id => UserId,
|
||||
permission => RequiredPermission,
|
||||
@@ -215,65 +203,45 @@ fetch_and_send_members(Request, _SocketPid, SessionState) ->
|
||||
nonce := Nonce
|
||||
} = Request,
|
||||
SessionId = maps:get(session_id, SessionState),
|
||||
|
||||
logger:debug(
|
||||
"[guild_request_members] Looking up guild ~p for member request",
|
||||
[GuildId]
|
||||
),
|
||||
|
||||
case lookup_guild(GuildId, SessionState) of
|
||||
{ok, GuildPid} ->
|
||||
logger:debug("[guild_request_members] Guild ~p found, fetching members", [GuildId]),
|
||||
Members = fetch_members(GuildPid, Query, Limit, UserIds),
|
||||
logger:debug("[guild_request_members] Found ~p members", [length(Members)]),
|
||||
PresencesList = maybe_fetch_presences(Presences, GuildPid, Members),
|
||||
send_member_chunks(GuildPid, SessionId, Members, PresencesList, Nonce),
|
||||
logger:debug(
|
||||
"[guild_request_members] Sent ~p member chunks for guild ~p with nonce ~p",
|
||||
[max(1, (length(Members) + ?CHUNK_SIZE - 1) div ?CHUNK_SIZE), GuildId, Nonce]
|
||||
),
|
||||
ok;
|
||||
{error, Reason} ->
|
||||
logger:warning(
|
||||
"[guild_request_members] Failed to lookup guild ~p: ~p",
|
||||
[GuildId, Reason]
|
||||
),
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
-spec fetch_members(pid(), binary(), non_neg_integer(), [integer()]) -> [map()].
|
||||
-spec fetch_members(pid(), binary(), non_neg_integer(), [integer()]) -> [member()].
|
||||
fetch_members(GuildPid, _Query, _Limit, UserIds) when UserIds =/= [] ->
|
||||
logger:debug("[guild_request_members] Fetching members by user_ids: ~p", [UserIds]),
|
||||
case gen_server:call(GuildPid, {list_guild_members, #{limit => 100000, offset => 0}}, 10000) of
|
||||
#{members := AllMembers} ->
|
||||
logger:debug("[guild_request_members] Got ~p members from guild, filtering by user_ids", [length(AllMembers)]),
|
||||
Filtered = filter_members_by_ids(AllMembers, UserIds),
|
||||
logger:debug("[guild_request_members] Filtered to ~p members", [length(Filtered)]),
|
||||
Filtered;
|
||||
Other ->
|
||||
logger:warning("[guild_request_members] Unexpected response from guild: ~p", [Other]),
|
||||
filter_members_by_ids(AllMembers, UserIds);
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
fetch_members(GuildPid, Query, Limit, []) ->
|
||||
ActualLimit = case Limit of 0 -> 100000; L -> L end,
|
||||
logger:debug("[guild_request_members] Fetching members with query '~s', limit ~p", [Query, ActualLimit]),
|
||||
case gen_server:call(GuildPid, {list_guild_members, #{limit => ActualLimit, offset => 0}}, 10000) of
|
||||
ActualLimit =
|
||||
case Limit of
|
||||
0 -> 100000;
|
||||
L -> L
|
||||
end,
|
||||
case
|
||||
gen_server:call(GuildPid, {list_guild_members, #{limit => ActualLimit, offset => 0}}, 10000)
|
||||
of
|
||||
#{members := AllMembers} ->
|
||||
logger:debug("[guild_request_members] Got ~p members from guild", [length(AllMembers)]),
|
||||
Result = case Query of
|
||||
case Query of
|
||||
<<>> ->
|
||||
lists:sublist(AllMembers, ActualLimit);
|
||||
_ ->
|
||||
filter_members_by_query(AllMembers, Query, ActualLimit)
|
||||
end,
|
||||
logger:debug("[guild_request_members] Returning ~p members after query/filter", [length(Result)]),
|
||||
Result;
|
||||
Other ->
|
||||
logger:warning("[guild_request_members] Unexpected response from guild: ~p", [Other]),
|
||||
end;
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
-spec filter_members_by_ids([map()], [integer()]) -> [map()].
|
||||
-spec filter_members_by_ids([member()], [integer()]) -> [member()].
|
||||
filter_members_by_ids(Members, UserIds) ->
|
||||
UserIdSet = sets:from_list(UserIds),
|
||||
lists:filter(
|
||||
@@ -284,7 +252,7 @@ filter_members_by_ids(Members, UserIds) ->
|
||||
Members
|
||||
).
|
||||
|
||||
-spec filter_members_by_query([map()], binary(), non_neg_integer()) -> [map()].
|
||||
-spec filter_members_by_query([member()], binary(), non_neg_integer()) -> [member()].
|
||||
filter_members_by_query(Members, Query, Limit) ->
|
||||
NormalizedQuery = string:lowercase(binary_to_list(Query)),
|
||||
Matches = lists:filter(
|
||||
@@ -297,50 +265,47 @@ filter_members_by_query(Members, Query, Limit) ->
|
||||
),
|
||||
lists:sublist(Matches, Limit).
|
||||
|
||||
-spec get_display_name(map()) -> binary().
|
||||
-spec get_display_name(member()) -> binary().
|
||||
get_display_name(Member) when is_map(Member) ->
|
||||
Nick = maps:get(<<"nick">>, Member, undefined),
|
||||
case Nick of
|
||||
undefined -> nick_isundefined(Member);
|
||||
null -> nick_isundefined(Member);
|
||||
undefined -> get_fallback_name(Member);
|
||||
null -> get_fallback_name(Member);
|
||||
_ when is_binary(Nick) -> Nick;
|
||||
_ -> nick_isundefined(Member)
|
||||
_ -> get_fallback_name(Member)
|
||||
end;
|
||||
get_display_name(_) ->
|
||||
<<>>.
|
||||
|
||||
nick_isundefined(Member) ->
|
||||
-spec get_fallback_name(member()) -> binary().
|
||||
get_fallback_name(Member) ->
|
||||
User = maps:get(<<"user">>, Member, #{}),
|
||||
GlobalName = maps:get(<<"global_name">>, User, undefined),
|
||||
case GlobalName of
|
||||
undefined ->
|
||||
Username = maps:get(<<"username">>, User, <<>>),
|
||||
case Username of
|
||||
null -> <<>>;
|
||||
undefined -> <<>>;
|
||||
_ when is_binary(Username) -> Username;
|
||||
_ -> <<>>
|
||||
end;
|
||||
null ->
|
||||
Username = maps:get(<<"username">>, User, <<>>),
|
||||
case Username of
|
||||
null -> <<>>;
|
||||
undefined -> <<>>;
|
||||
_ when is_binary(Username) -> Username;
|
||||
_ -> <<>>
|
||||
end;
|
||||
undefined -> get_username(User);
|
||||
null -> get_username(User);
|
||||
_ when is_binary(GlobalName) -> GlobalName;
|
||||
_ -> get_username(User)
|
||||
end.
|
||||
|
||||
-spec get_username(map()) -> binary().
|
||||
get_username(User) ->
|
||||
Username = maps:get(<<"username">>, User, <<>>),
|
||||
case Username of
|
||||
null -> <<>>;
|
||||
undefined -> <<>>;
|
||||
_ when is_binary(Username) -> Username;
|
||||
_ -> <<>>
|
||||
end.
|
||||
|
||||
-spec extract_user_id(map()) -> integer() | undefined.
|
||||
-spec extract_user_id(member()) -> integer() | undefined.
|
||||
extract_user_id(Member) when is_map(Member) ->
|
||||
User = maps:get(<<"user">>, Member, #{}),
|
||||
map_utils:get_integer(User, <<"id">>, undefined);
|
||||
extract_user_id(_) ->
|
||||
undefined.
|
||||
|
||||
-spec maybe_fetch_presences(boolean(), pid(), [map()]) -> [map()].
|
||||
-spec maybe_fetch_presences(boolean(), pid(), [member()]) -> [presence()].
|
||||
maybe_fetch_presences(false, _GuildPid, _Members) ->
|
||||
[];
|
||||
maybe_fetch_presences(true, _GuildPid, Members) ->
|
||||
@@ -361,41 +326,30 @@ maybe_fetch_presences(true, _GuildPid, Members) ->
|
||||
[P || P <- Cached, presence_visible(P)]
|
||||
end.
|
||||
|
||||
-spec presence_visible(map()) -> boolean().
|
||||
-spec presence_visible(presence()) -> boolean().
|
||||
presence_visible(P) ->
|
||||
Status = maps:get(<<"status">>, P, <<"offline">>),
|
||||
Status =/= <<"offline">> andalso Status =/= <<"invisible">>.
|
||||
|
||||
-spec send_member_chunks(pid(), binary(), [map()], [map()], term()) -> ok.
|
||||
-spec send_member_chunks(pid(), binary(), [member()], [presence()], term()) -> ok.
|
||||
send_member_chunks(GuildPid, SessionId, Members, Presences, Nonce) ->
|
||||
TotalChunks = max(1, (length(Members) + ?CHUNK_SIZE - 1) div ?CHUNK_SIZE),
|
||||
MemberChunks = chunk_list(Members, ?CHUNK_SIZE),
|
||||
PresenceChunks = chunk_presences(Presences, MemberChunks),
|
||||
|
||||
logger:debug(
|
||||
"[guild_request_members] Sending ~p member chunks (total members: ~p, nonce: ~p)",
|
||||
[TotalChunks, length(Members), Nonce]
|
||||
),
|
||||
|
||||
lists:foldl(
|
||||
fun({MemberChunk, PresenceChunk}, ChunkIndex) ->
|
||||
ChunkData = build_chunk_data(
|
||||
MemberChunk, PresenceChunk, ChunkIndex, TotalChunks, Nonce
|
||||
),
|
||||
logger:debug(
|
||||
"[guild_request_members] Sending chunk ~p/~p with ~p members, nonce: ~p",
|
||||
[ChunkIndex + 1, TotalChunks, length(MemberChunk), Nonce]
|
||||
),
|
||||
gen_server:cast(GuildPid, {send_members_chunk, SessionId, ChunkData}),
|
||||
ChunkIndex + 1
|
||||
end,
|
||||
0,
|
||||
lists:zip(MemberChunks, PresenceChunks)
|
||||
),
|
||||
logger:debug("[guild_request_members] All chunks sent successfully"),
|
||||
ok.
|
||||
|
||||
-spec build_chunk_data([map()], [map()], non_neg_integer(), non_neg_integer(), term()) ->
|
||||
-spec build_chunk_data([member()], [presence()], non_neg_integer(), non_neg_integer(), term()) ->
|
||||
map().
|
||||
build_chunk_data(Members, Presences, ChunkIndex, TotalChunks, Nonce) ->
|
||||
Base = #{
|
||||
@@ -403,15 +357,15 @@ build_chunk_data(Members, Presences, ChunkIndex, TotalChunks, Nonce) ->
|
||||
<<"chunk_index">> => ChunkIndex,
|
||||
<<"chunk_count">> => TotalChunks
|
||||
},
|
||||
WithPresences = case Presences of
|
||||
[] -> Base;
|
||||
_ -> maps:put(<<"presences">>, Presences, Base)
|
||||
end,
|
||||
WithNonce = case Nonce of
|
||||
WithPresences =
|
||||
case Presences of
|
||||
[] -> Base;
|
||||
_ -> maps:put(<<"presences">>, Presences, Base)
|
||||
end,
|
||||
case Nonce of
|
||||
null -> WithPresences;
|
||||
_ -> maps:put(<<"nonce">>, Nonce, WithPresences)
|
||||
end,
|
||||
WithNonce.
|
||||
end.
|
||||
|
||||
-spec chunk_list([T], pos_integer()) -> [[T]] when T :: term().
|
||||
chunk_list([], _Size) ->
|
||||
@@ -419,13 +373,14 @@ chunk_list([], _Size) ->
|
||||
chunk_list(List, Size) ->
|
||||
chunk_list(List, Size, []).
|
||||
|
||||
-spec chunk_list([T], pos_integer(), [[T]]) -> [[T]] when T :: term().
|
||||
chunk_list([], _Size, Acc) ->
|
||||
lists:reverse(Acc);
|
||||
chunk_list(List, Size, Acc) ->
|
||||
{Chunk, Rest} = lists:split(min(Size, length(List)), List),
|
||||
chunk_list(Rest, Size, [Chunk | Acc]).
|
||||
|
||||
-spec chunk_presences([map()], [[map()]]) -> [[map()]].
|
||||
-spec chunk_presences([presence()], [[member()]]) -> [[presence()]].
|
||||
chunk_presences(Presences, MemberChunks) ->
|
||||
lists:map(
|
||||
fun(MemberChunk) ->
|
||||
@@ -491,15 +446,18 @@ display_name_priority_test() ->
|
||||
<<"nick">> => <<"Nick">>
|
||||
},
|
||||
?assertEqual(<<"Nick">>, get_display_name(MemberWithNick)),
|
||||
|
||||
MemberWithGlobal = #{
|
||||
<<"user">> => #{<<"username">> => <<"user">>, <<"global_name">> => <<"Global">>}
|
||||
},
|
||||
?assertEqual(<<"Global">>, get_display_name(MemberWithGlobal)),
|
||||
|
||||
MemberWithUsername = #{
|
||||
<<"user">> => #{<<"username">> => <<"user">>}
|
||||
},
|
||||
?assertEqual(<<"user">>, get_display_name(MemberWithUsername)).
|
||||
|
||||
normalize_nonce_test() ->
|
||||
?assertEqual(<<"abc">>, normalize_nonce(<<"abc">>)),
|
||||
?assertEqual(null, normalize_nonce(<<"this_nonce_is_way_too_long_to_be_valid">>)),
|
||||
?assertEqual(null, normalize_nonce(undefined)).
|
||||
|
||||
-endif.
|
||||
|
||||
Reference in New Issue
Block a user