Skip to content

Add voice log to track join and leave events - #210

Open
Rinzii wants to merge 5 commits into
TCCPP:mainfrom
Rinzii:vc-last-join-upstream
Open

Add voice log to track join and leave events#210
Rinzii wants to merge 5 commits into
TCCPP:mainfrom
Rinzii:vc-last-join-upstream

Conversation

@Rinzii

@Rinzii Rinzii commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR introduces a new voice log command that provides a recent history of voice join and leave activity for a channel. The intent is to give moderators a practical way to identify who entered or exited a voice channel when an incident happens too quickly to observe live.

It also includes dev-only tooling to generate voice join/leave events for testing. That tooling is isolated and can be removed after review if desired.

New

  • Added a new voice log command for viewing recent voice join/leave activity in a voice channel.
  • Added paginated output, non-pinging user mentions, and message controls for the voice log.
  • Added a dev-only /dev-voice-bounce command for generating voice join/leave events during testing.

Changes

  • Updated guild lookup output to use paginated embed-based navigation.

The previous guild lookup response was a flat text dump of channels. Honestly this approach does not scale well once the result set becomes large, and it can be quite awkward to navigate after the initial response is sent. Moving the command to a paginated embed makes the output easier to read, keeps it manageable for larger guilds, and provides a more consistent interaction model for commands that return structured lists.

  • Improved slash-command handling for unknown or stale command/subcommand interactions.

Previously, the slash-command handler assumed the incoming interaction always matched the bot’s currently loaded command registry. In practice that assumption does not always hold, particularly during deploys, after command changes, or while Discord is still serving older command definitions. The new handling guards those cases explicitly so the bot responds safely instead of raising an avoidable runtime error. This should make command handling more robust and reduce noise from stale interactions.

Rinzii added 4 commits March 11, 2026 02:25
Adds a /voice log view with join/leave events, paging controls, and issuer-only deletion. Extends the command layer with channel options and updates guild lookup to use paged embeds.
@sonarqubecloud

Copy link
Copy Markdown

};

export default class VoiceLog extends BotComponent {
private readonly event_history = new SelfClearingMap<string, voice_log_event[]>(JOIN_HISTORY_TTL);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This isn't going to be sufficient. In practice the bot runs for weeks at a time but this should not be relied on.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Originally I intended for this being stored in memory as a feature to avoid needing to change the db, but I can change that to use the db going forward.


if (this.wheatley.devmode_enabled) {
commands.add(
new TextBasedCommandBuilder("dev-voice-bounce", EarlyReplyMode.ephemeral)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If this is kept it would best be put somewhere like utility-tools to keep component focused - the logic for this command doubles the size of this component.

),
);

this.guild_lookup_page_button = commands.add(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please move changes to this file to a separate PR to keep this PR focused

// Sort primarily by position when available otherwise push to the end.
const channels = [...guild.channels.cache.values()].sort((a, b) => {
const pos_a =
"rawPosition" in a && typeof (a as any).rawPosition === "number"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do you need to any cast here?

}
}

const requested_n = n ?? 1;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Isn't this going to show only 1 item unless n is passed?

}
} else {
assert(false, "unhandled option type");
switch (option.type) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nesting a switch in this if-else-if-else chain handling all the option types in order to handle two cases isn't the right shape for this, please fix

@sonarqubecloud

Copy link
Copy Markdown

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.

2 participants