fix(auto-delete): guard nickname reset on role hierarchy + surface LLM parse errors

- resetOffensiveNickname: skip when target role sits above bot
  (member.manageable) instead of hammering a doomed setNickname PATCH
  that Discord rejects with 50013 'Missing Permissions'. Log the
  Discord error code on failure for clear diagnosis.
- llmCaller: include contentPreview (first 200 chars) in the parse-
  failure warning so non-JSON LLM responses are debuggable.
This commit is contained in:
asepharyana
2026-08-17 20:52:09 +07:00
parent 479f4719ba
commit b38616e051
2 changed files with 23 additions and 0 deletions
@@ -74,6 +74,23 @@ export async function resetOffensiveNickname(
return false; return false;
} }
const member = await guild.members.fetch(userId); const member = await guild.members.fetch(userId);
// Discord rejects setNickname with "Missing Permissions" (code 50013)
// whenever the target's top role sits above the bot in the role
// hierarchy — even when the bot has MANAGE_NICKNAMES. Guard on
// `manageable` (hierarchy-aware) so we skip with a clear reason
// instead of hammering a doomed PATCH on every message.
if (!member.manageable) {
logger.debug(
{
messageId,
guildId,
userId,
reason: "target role above bot in hierarchy (Discord 50013)",
},
"Nick reset skipped: member not manageable by bot",
);
return false;
}
// setNickname(null) = remove nickname → Discord shows global username // setNickname(null) = remove nickname → Discord shows global username
await member.setNickname(null, "[auto] nickname melanggar aturan server"); await member.setNickname(null, "[auto] nickname melanggar aturan server");
recentNicknameResets.set(cooldownKey, Date.now()); recentNicknameResets.set(cooldownKey, Date.now());
@@ -83,12 +100,17 @@ export async function resetOffensiveNickname(
); );
return true; return true;
} catch (error) { } catch (error) {
const errCode =
error instanceof Error && "code" in error
? (error as { code?: number | string }).code
: undefined;
logger.warn( logger.warn(
{ {
messageId, messageId,
guildId, guildId,
userId, userId,
error: error instanceof Error ? error.message : String(error), error: error instanceof Error ? error.message : String(error),
code: errCode,
}, },
"Nick reset failed", "Nick reset failed",
); );
@@ -119,6 +119,7 @@ export async function callModerationLLM(
{ {
error: state.lastParseError, error: state.lastParseError,
contentLength: rawContent.length, contentLength: rawContent.length,
contentPreview: rawContent.slice(0, 200),
targetIds, targetIds,
model: config.AI_LLM_MODEL, model: config.AI_LLM_MODEL,
}, },