Repository navigation
feat(bot): birthdays schema + /birthday set|clear - #738
Conversation
Phase 1 final gap from the competitive scan. Two tight subcommands, deferred scheduler/announcements to a follow-up PR to keep scope S. Schema (new model MemberBirthday): - month (1-12) + day (1-31) only — no year, no DOB, no age tracking - @@unique([guildId, userId]) so one row per guild-member - @@index([guildId, month, day]) ready for the daily-lookup scheduler Commands: - /birthday set month day — upserts, validates month bounds + per-month day cap (Feb 30 rejected, Feb 29 accepted as leap-day tolerant) - /birthday clear — idempotent delete, friendly message when not set Tests: 8 unit cases cover DM rejection, month/day validation, leap-day acceptance, upsert round-trip, clear both paths, error handling. Follow-up (separate PR): daily scheduler that queries the guildId_month_day index once per UTC day and posts to a guild-configured channel. Schema is already indexed for that lookup.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 30 minutes and 29 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Completes the birthdays feature on top of the schema + /birthday set|clear shipped in #738. Schema change: - GuildSettings.birthdayChannelId (String?) — null by default so the scheduler safely skips guilds that haven't opted in. New migration. Command: - /birthday channel <channel> — ManageGuild-gated, upserts GuildSettings. Empty channel disables announcements (same field set to null). Scheduler (packages/bot/src/utils/general/birthdayScheduler.ts): - Runs once at startup + every hour (BIRTHDAY_TICK_INTERVAL_MS override) - Queries MemberBirthday by today's UTC (month, day) via the existing @@index([guildId, month, day]) - Groups by guild, reads GuildSettings.birthdayChannelId, posts a celebration embed mentioning each user - Idempotent per UTC day (in-memory lastAnnouncedPerGuild map) so hourly reruns are no-ops; posts again when the date rolls over - Wired into clientHandler/service.ts alongside modDigestScheduler - Failures are logged and swallowed (won't crash the bot) Tests: 14 total across birthday.ts and birthdayScheduler.ts — covering empty results, multi-user guild, no-config skip, day-rollover idempotency, and db-error resilience.
Five commits landed on main since 2.6.147 but Unreleased was empty. Harvesting them now so the next version bump captures the right notes and users scanning the changelog see what shipped. Covered: #726 (CI pnpm→npm), #731 (/social), #733 (/leaderboard), #738 (birthday schema + /birthday set|clear), #741 (cached-guild log level). Queued PRs not yet on main (top.gg webhook stack, Phase 0 docs, dashboard vote badge, birthday scheduler + channel + list + role) will be added when they actually merge.
Five commits landed on main since 2.6.147 but Unreleased was empty. Harvesting them now so the next version bump captures the right notes and users scanning the changelog see what shipped. Covered: #726 (CI pnpm→npm), #731 (/social), #733 (/leaderboard), #738 (birthday schema + /birthday set|clear), #741 (cached-guild log level). Queued PRs not yet on main (top.gg webhook stack, Phase 0 docs, dashboard vote badge, birthday scheduler + channel + list + role) will be added when they actually merge.
* feat(bot): birthday scheduler + /birthday channel Completes the birthdays feature on top of the schema + /birthday set|clear shipped in #738. Schema change: - GuildSettings.birthdayChannelId (String?) — null by default so the scheduler safely skips guilds that haven't opted in. New migration. Command: - /birthday channel <channel> — ManageGuild-gated, upserts GuildSettings. Empty channel disables announcements (same field set to null). Scheduler (packages/bot/src/utils/general/birthdayScheduler.ts): - Runs once at startup + every hour (BIRTHDAY_TICK_INTERVAL_MS override) - Queries MemberBirthday by today's UTC (month, day) via the existing @@index([guildId, month, day]) - Groups by guild, reads GuildSettings.birthdayChannelId, posts a celebration embed mentioning each user - Idempotent per UTC day (in-memory lastAnnouncedPerGuild map) so hourly reruns are no-ops; posts again when the date rolls over - Wired into clientHandler/service.ts alongside modDigestScheduler - Failures are logged and swallowed (won't crash the bot) Tests: 14 total across birthday.ts and birthdayScheduler.ts — covering empty results, multi-user guild, no-config skip, day-rollover idempotency, and db-error resilience. * feat(bot): birthday role auto-grant + /birthday role (#743) Completes the birthday feature with the remaining Nekotina-style perk. Schema: - GuildSettings.birthdayRoleId (String?) — new migration. Null by default. Command: - /birthday role <role> — ManageGuild-gated, upserts GuildSettings. Empty role disables the grant entirely. Scheduler: - On each tick, for every guild with matches today: fetch role + grant to celebrators who don't already have it. - For guilds with birthdayRoleId set but NO matches today, iterate the role's current members and revoke. This means: - 24-hour auto-expiry happens naturally (next UTC day tick finds no match for the previous day's celebrators, revokes cleanly). - No separate tracking table / timer state needed. - Admin reconfiguration is idempotent (stale roleIds simply get logged and skipped). Scoped API calls — uses guild.roles.fetch(roleId).members iteration rather than fetching every guild member. No pagination, no rate-limit scaling risk at Lucky's current scale. Tests: 9 total (3 new role cases on top of the 6 announcement cases): - Grants the role to today's celebrators - Revokes from stale holders when no matches today - Skips re-grant when member already has the role * test(bot): cover birthday scheduler + /birthday channel/role commands (unblocks #740 Sonar) * fix(bot): remove stray merge conflict marker in birthday.ts Leftover <<<<<<< HEAD from the earlier merge resolution — no corresponding ======= / >>>>>>> pair, so the merged content is correct, just had a stray opening marker on line 203 that broke TypeScript compile.
Phase 1 final gap from the competitive scan. Two tight subcommands, deferred scheduler/announcements to a follow-up PR to keep scope S. Schema (new model MemberBirthday): - month (1-12) + day (1-31) only — no year, no DOB, no age tracking - @@unique([guildId, userId]) so one row per guild-member - @@index([guildId, month, day]) ready for the daily-lookup scheduler Commands: - /birthday set month day — upserts, validates month bounds + per-month day cap (Feb 30 rejected, Feb 29 accepted as leap-day tolerant) - /birthday clear — idempotent delete, friendly message when not set Tests: 8 unit cases cover DM rejection, month/day validation, leap-day acceptance, upsert round-trip, clear both paths, error handling. Follow-up (separate PR): daily scheduler that queries the guildId_month_day index once per UTC day and posts to a guild-configured channel. Schema is already indexed for that lookup.
Five commits landed on main since 2.6.147 but Unreleased was empty. Harvesting them now so the next version bump captures the right notes and users scanning the changelog see what shipped. Covered: #726 (CI pnpm→npm), #731 (/social), #733 (/leaderboard), #738 (birthday schema + /birthday set|clear), #741 (cached-guild log level). Queued PRs not yet on main (top.gg webhook stack, Phase 0 docs, dashboard vote badge, birthday scheduler + channel + list + role) will be added when they actually merge.
* feat(bot): birthday scheduler + /birthday channel Completes the birthdays feature on top of the schema + /birthday set|clear shipped in #738. Schema change: - GuildSettings.birthdayChannelId (String?) — null by default so the scheduler safely skips guilds that haven't opted in. New migration. Command: - /birthday channel <channel> — ManageGuild-gated, upserts GuildSettings. Empty channel disables announcements (same field set to null). Scheduler (packages/bot/src/utils/general/birthdayScheduler.ts): - Runs once at startup + every hour (BIRTHDAY_TICK_INTERVAL_MS override) - Queries MemberBirthday by today's UTC (month, day) via the existing @@index([guildId, month, day]) - Groups by guild, reads GuildSettings.birthdayChannelId, posts a celebration embed mentioning each user - Idempotent per UTC day (in-memory lastAnnouncedPerGuild map) so hourly reruns are no-ops; posts again when the date rolls over - Wired into clientHandler/service.ts alongside modDigestScheduler - Failures are logged and swallowed (won't crash the bot) Tests: 14 total across birthday.ts and birthdayScheduler.ts — covering empty results, multi-user guild, no-config skip, day-rollover idempotency, and db-error resilience. * feat(bot): birthday role auto-grant + /birthday role (#743) Completes the birthday feature with the remaining Nekotina-style perk. Schema: - GuildSettings.birthdayRoleId (String?) — new migration. Null by default. Command: - /birthday role <role> — ManageGuild-gated, upserts GuildSettings. Empty role disables the grant entirely. Scheduler: - On each tick, for every guild with matches today: fetch role + grant to celebrators who don't already have it. - For guilds with birthdayRoleId set but NO matches today, iterate the role's current members and revoke. This means: - 24-hour auto-expiry happens naturally (next UTC day tick finds no match for the previous day's celebrators, revokes cleanly). - No separate tracking table / timer state needed. - Admin reconfiguration is idempotent (stale roleIds simply get logged and skipped). Scoped API calls — uses guild.roles.fetch(roleId).members iteration rather than fetching every guild member. No pagination, no rate-limit scaling risk at Lucky's current scale. Tests: 9 total (3 new role cases on top of the 6 announcement cases): - Grants the role to today's celebrators - Revokes from stale holders when no matches today - Skips re-grant when member already has the role * test(bot): cover birthday scheduler + /birthday channel/role commands (unblocks #740 Sonar) * fix(bot): remove stray merge conflict marker in birthday.ts Leftover <<<<<<< HEAD from the earlier merge resolution — no corresponding ======= / >>>>>>> pair, so the merged content is correct, just had a stray opening marker on line 203 that broke TypeScript compile.



Summary
Phase 1 final gap from the competitive scan (Nekotina/Loritta pattern). Two tight subcommands now; scheduler/announcements deferred to a follow-up PR to keep scope S.
Schema — new `MemberBirthday` model
Migration: `prisma/migrations/20260420000000_add_member_birthdays/migration.sql`.
Commands
Test plan
Follow-up (separate PR)
Daily scheduler that queries `(guildId, month, day)` once per UTC day and posts to a guild-configured channel. Schema is already indexed for that lookup.