Repository navigation
V3 - Tidy up and rewrite - #729
Conversation
Add .editorconfig and reformat src/**/*.ts (no functional changes). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- node:test harness (tsconfig.test.json, npm test, CI step) with env, Logger and in-memory sequelize tests; createSequelize factory - src/config/env.ts validates runtime config at startup; .env.example updated - Logger accepts unknown errors (prints stack), adds level helpers, ISO timestamps - typed linting with no-floating-promises / no-misused-promises; fix violations - enable strictNullChecks and fix fallout Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… events Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…king columns
Replaces sequelize.sync({ alter: true }) at startup with versioned
migrations; test helper runs the migrator on :memory: databases.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Activity score is +1 per human guild message with no decrement; honeypot logic moved to handleHoneypot. Message/member events use AuditEmbed and handle partials. /post, /noroles and /rolesince defer first, check permissions, and paginate long listings into a text attachment. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…rofile and lifecycle events Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ug, simulate and member events Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…Registrar to src/framework Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…alidator, bump to 3.0.0 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Comprehensive guide based on actual code. Covers setup (local & Docker), environment configuration (all vars with defaults), Discord setup (intents and permissions), all commands organized by category with descriptions, behaviour (onboarding flow, audit logs, honeypot, activity score, birthdays with timezone and Feb 29 handling, scheduled job order), data storage and retention policy, and contributing guidelines with code examples. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…ite migrator Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- ES modules, run src/*.ts directly with Node type stripping (no build step) - explicit .ts import extensions, import type, erasable syntax only - replace runtime file discovery with static command/event registries and validateRegistry (with tests) - replace node-schedule with croner; Scheduler.stop() is now async and waits for an in-flight run - load .env via --env-file-if-exists; drop dotenv, tslib, copyfiles, rimraf, tsc-watch; single tsconfig covering src and tests Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ctories Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Replace the env Proxy, lazy database singleton, honeypot static map and module-level shutdown hooks with an App built once in index.ts. Handlers receive it as run(app, client, ...) for events and ctx.app for commands; services become plain functions taking the db or app. The clock is injected where it decides behaviour. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The dispatcher replies with a UserError's message and logs it at info, while other errors keep the generic reply and error log. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Add fakeUser, fakeMember, fakeGuild, fakeConfig, fakeAuditChannel and a shared fakeCommandContext, centralise structural casts in stub(), and drop the duplicated ad-hoc builders across test files. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… script Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Read the guild's profiles before the member fetch so profiles created while it runs are not mistaken for departed members. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 07d880a79b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…state Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 27639d1b08
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const status = await refreshCalendar(ctx.app, client, ctx.config); | ||
| if (previousPath && previousPath !== path) { | ||
| await deleteCalendarMessage(client, previousPath); |
There was a problem hiding this comment.
Keep the old calendar when initial population fails
When the new message is sent and saved but its initial refreshCalendar returns failed—for example, because Discord transiently rejects the edit—this still deletes the previous working calendar and leaves the stored replacement showing only the placeholder until a later sync succeeds. Fresh evidence beyond the earlier send/save ordering issue is that refreshCalendar now converts fetch/edit errors into non-updated statuses, but those statuses still fall through to this unconditional deletion; remove the old message only after the replacement reports updated, or restore the previous path on failure.
Useful? React with 👍 / 👎.
| await deleteCalendarMessage(ctx.interaction.client, ctx.config.birthdayCalendarMessagePath); | ||
| ctx.config = await updateConfig(ctx.app, ctx.config.serverId, { | ||
| birthdayCalendarMessagePath: null, | ||
| }); |
There was a problem hiding this comment.
Preserve the calendar path when message deletion fails
When Discord transiently rejects the fetch or deletion of an existing calendar message, deleteCalendarMessage suppresses the error, so this still clears the only stored path and replies that the calendar was removed. The visible message is then orphaned and can no longer be updated or retried by the bot; distinguish an already-missing message from other failures and retain the path or report the failure when deletion did not succeed.
Useful? React with 👍 / 👎.
| await updateConfig(app, config.serverId, { | ||
| accessFailureCount: next.accessFailureCount, | ||
| firstAccessFailureAt: next.firstAccessFailureAt, | ||
| }); |
There was a problem hiding this comment.
Avoid recreating a guild config after a concurrent purge
If the bot is removed from a guild while this data check is awaiting Discord, the guildDelete handler can purge the guild after getConfigs() took its snapshot, and this subsequent updateConfig then upserts a fresh row for the already-purged guild. That defeats the documented immediate deletion and leaves the bot repeatedly checking retained orphan state until the grace policy purges it again; retention updates should only update an existing row or confirm the guild was not concurrently removed before writing.
Useful? React with 👍 / 👎.
| | 1.0.x | :no_entry: | | ||
| | 1.1.x | :no_entry: | | ||
| | 1.2.x | :no_entry: | | ||
| | 1.3.x | :no_entry: | |
There was a problem hiding this comment.
List the current v3 release as supported
The change releases the project as version 3.0.0, but the security policy still identifies only 2.0.x as supported and does not mention v3. Users of the newly documented release therefore cannot tell whether security reports and fixes apply to their version; add the 3.0.x support status and update the legacy status if v2 is no longer maintained.
Useful? React with 👍 / 👎.
If the new calendar cannot be populated, restore the previous path, delete the new placeholder and keep the existing calendar. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
deleteCalendarMessage now reports deleted, already-missing or failed; remove only clears the stored path when the message is gone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Add updateExistingConfig (update-only) and use it for the data check's access-state writes, skipping the guild if its row is gone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 210f9aed0b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if ( | ||
| (cronFields.length !== 5 && cronFields.length !== 6) || | ||
| !cronFields.every((field) => CRON_FIELD.test(field)) |
There was a problem hiding this comment.
Reject cron expressions Croner cannot schedule
When JOB_SCHEDULE contains five syntactically simple but invalid fields, such as 99 99 99 99 99, this character-only check accepts it even though Croner rejects the out-of-range values. ready.ts then catches the scheduler construction error and leaves cleanup, birthday notifications, and calendar refreshes disabled while the bot otherwise remains online; validate by constructing/parsing the Croner expression during environment loading so invalid configuration fails startup.
Useful? React with 👍 / 👎.
| if ( | ||
| !perms?.has(PermissionFlagsBits.ViewChannel) || | ||
| !perms.has(PermissionFlagsBits.SendMessages) |
There was a problem hiding this comment.
Require history access before creating a calendar
When a channel override grants the bot View Channel and Send Messages but denies Read Message History, creation can initially succeed because the just-sent message is cached, but after a restart or cache eviction refreshCalendar() cannot fetch that message and every scheduled/manual sync reports it missing. Include PermissionFlagsBits.ReadMessageHistory in this permission check so the command does not persist a calendar that cannot be maintained.
Useful? React with 👍 / 👎.
| .onConflictDoUpdate({ | ||
| target: [userProfiles.serverId, userProfiles.userId], | ||
| set: { screeningPendingAt: at, updatedAt: at }, |
There was a problem hiding this comment.
Reset stale onboarding state for a new screening cycle
When a previously onboarded member leaves while the bot is offline and rejoins a screening-enabled guild before cleanup, their profile still contains onboardedAt. Recording the new pending state here preserves that old marker, and guildMemberUpdate subsequently skips both full and partial screening-completion events as “already recorded,” so the returning member never receives configured roles or a welcome message. Treat a definitively new pending join as a new onboarding cycle and clear or otherwise supersede the stale completion marker.
Useful? React with 👍 / 👎.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Document the onboarding state machine, claim the single onboarding per membership atomically before any step runs, drop state from previous memberships using the join time, catch up on screenings that finished while offline, and replace the piecemeal event tests with a table-driven state machine test. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Classify refresh failures (missing, no access, transient) instead of reporting every error as missing, serialise refreshes per guild so the last edit has the latest profiles, replace the calendar a create actually overwrote (not the one it first saw), never clobber a concurrent create on rollback, skip purged guilds in the scheduled refresh, and refresh the calendar when a member with a birthday leaves. Consolidate the calendar tests into a table-driven state machine test. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Trust a stored hash only while the recorded scope matches the current one (an unrecorded scope re-registers to record it), and stop retrying the old dev guild cleanup on every start once the bot can no longer reach that guild. Replace the piecemeal registrar tests with a table-driven state machine test. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Re-read each config before the data check acts on it, never purge a guild the gateway still lists, delete departed members' profiles by row id so a rejoiner's new profile survives, and re-record pending screenings when the bot is added to a guild. Consolidate the data check tests into a table-driven state machine test with the concurrent guildDelete/guildCreate cases. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Add fitMessage to truncate user-configured welcome text with a note, and use it for welcome preview/get and the simulate onboard dry run. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Wrap the full member fetch in noroles and rolesince so a discord.js GatewayRateLimitError becomes a UserError with the retry time. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reject on an oversized content-length header, then stream the body and cancel as soon as 10 MB is exceeded, all under the existing 10s timeout. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Chunk the mention list so each announcement stays within Discord's 2000 character limit, with allowedMentions limited to each chunk. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Updated native dependencies note to mention both better-sqlite3 and @napi-rs/canvas - Clarified onboarding audit titles: "New member joined" and "Member completed onboarding" - Rewrote v2 → v3 command changes list with accurate details for each command - Fixed example code imports to include .ts extension (Node runs TS with ESM) - Fixed updateConfig signature in example code - Added notes about welcome preview/get truncation, rate-limit handling for /noroles and /rolesince, welcome image download cap, and birthday announcement splitting Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
No description provided.