Skip to content

Conversation

@vcarl
Copy link
Member

@vcarl vcarl commented Jan 23, 2026

Partially addresses #252

vcarl and others added 4 commits January 23, 2026 01:12
There’s a race condition in attributing bans, because the Audit Log Entry object isn’t created immediately as the Ban event fires. We’ll need to wait for a few seconds to make sure that the Audit Log Entry isn’t still pending before we post.

We could perhaps change this to post immediately and edit the message once attribution details come back.
Extends the mod action logging system to capture timeout and unban events.
Timeout logs include human-readable duration, and only manual timeout
removals (before natural expiry) are logged.

Refactors handlers to use Effect-TS with thin async wrappers that execute
Effects. Extracts shared audit log fetching logic into a reusable helper.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates public-facing promotion blockers (title + invite link) and expands moderation reporting to capture more actions (unban/timeout) with better attribution via audit logs.

Changes:

  • Update app page title metadata for production branding.
  • Update the “Add to server” invite link and supporting UI entry point.
  • Refactor moderation/audit-log reporting to use Effect-based handlers and add logging for unbans and timeouts.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
app/root.tsx Updates the app’s <title> metadata for branding/promotion readiness.
app/components/DiscordLayout.tsx Updates the Discord OAuth invite URL used by the dashboard UI.
app/commands/report/modActionLogger.ts Refactors mod-action event handling, adds audit log retries, and introduces unban/timeout tracking.
app/commands/report/modActionLog.ts Extends the mod-action report union and formats new action types in user-thread logs.
app/commands/report/automodLog.ts Exports logAutomod for reuse by the updated logger.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +53 to +59
const entry = findEntry(auditLogs.entries);
if (entry?.executor) {
yield* logEffect("debug", "ModActionLogger", `record found`, {
attempt: attempt + 1,
});
return entry;
}
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchAuditLogEntry only returns a found audit-log record when entry.executor is truthy. Audit log entries can exist without an executor (or the executor can be unavailable), which means this will incorrectly treat real bans/kicks/unbans as “not found” (e.g., kicks become left) and will also drop reason data. Return when an entry is found (and let downstream code handle executor === null) instead of gating on executor.

Copilot uses AI. Check for mistakes.
Comment on lines +303 to +307
// Determine if this is a timeout applied or removed
const isTimeoutApplied = newTimeout !== null && newTimeout > Date.now();
const isTimeoutRemoved =
oldTimeout !== null && oldTimeout > Date.now() && newTimeout === null;

Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isTimeoutApplied is true for any member update while a timeout is active (newTimeout > Date.now()), even when oldTimeout === newTimeout. Since GuildMemberUpdate fires for many unrelated changes, this will spam duplicate “timeout” logs. Detect an actual timeout change (e.g., compare oldTimeout vs newTimeout, or compute hadTimeout/hasTimeout and only log when transitioning).

Suggested change
// Determine if this is a timeout applied or removed
const isTimeoutApplied = newTimeout !== null && newTimeout > Date.now();
const isTimeoutRemoved =
oldTimeout !== null && oldTimeout > Date.now() && newTimeout === null;
// Determine if this is a timeout applied, updated, or removed
const hadTimeout =
oldTimeout !== null && oldTimeout > Date.now();
const hasTimeout =
newTimeout !== null && newTimeout > Date.now();
// Treat as "applied" if a timeout has become active or its expiry changed
const isTimeoutApplied =
hasTimeout && (!hadTimeout || oldTimeout !== newTimeout);
// Treat as "removed" if a previously active timeout is no longer active
const isTimeoutRemoved = hadTimeout && !hasTimeout;

Copilot uses AI. Check for mistakes.

const reasonText = reason ? ` ${reason}` : " for no reason";
const durationText =
actionType === "timeout" ? ` for ${report.duration}` : "";
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

durationText reads report.duration based on actionType === "timeout", but actionType was destructured from report, so TypeScript typically won’t narrow report here (and duration doesn’t exist on the other union variants). This is likely a type error under strict. Narrow on report.actionType (or use an if (report.actionType === "timeout") block) before accessing report.duration.

Suggested change
actionType === "timeout" ? ` for ${report.duration}` : "";
report.actionType === "timeout" ? ` for ${report.duration}` : "";

Copilot uses AI. Check for mistakes.
{
userId,
guildId: guild.id,
ruleId: autoModerationRule?.name,
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the structured log context for the timeout-skip branch, the key is ruleId but the value being logged is autoModerationRule?.name. This makes the log payload misleading; use ruleName for the name, or log the actual rule ID if you want ruleId.

Suggested change
ruleId: autoModerationRule?.name,
ruleName: autoModerationRule?.name,

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

github-actions bot commented Jan 23, 2026

Preview environment removed

The preview for this PR has been cleaned up.

@vcarl vcarl merged commit 250da5c into main Jan 23, 2026
5 checks passed
@vcarl vcarl deleted the vc-bugfixes branch January 23, 2026 06:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ban logs don't appear to correctly capture who initiated the ban Track more mod actions in #mod-log

2 participants