Skip to content

refactor: split autoplay.ts into queueHandlers, settingsHandlers, artistHandlers - #814

Merged
LucasSantana-Dev merged 15 commits into
mainfrom
refactor/split-autoplay-command
May 6, 2026
Merged

LucasSantana-Dev merged 15 commits into
mainfrom
refactor/split-autoplay-command

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented May 6, 2026 •

Copy link
Copy Markdown
Owner

Summary

Modularizes the 1074-line autoplay.ts command file into three focused handler modules, reducing complexity and improving maintainability.

Changes

  • queueHandlers.ts (120 lines): Queue operations (skip, clear, status)
  • settingsHandlers.ts (280 lines): Settings management (mode, genre subcommands, analytics)
  • artistHandlers.ts (222 lines): Artist preference operations (prefer, block, list, remove)
  • autoplay.ts (250 lines): Thin router with SlashCommandBuilder and dispatcher

Type Safety

  • All handlers maintain full TypeScript type safety with ChatInputCommandInteraction and GuildQueue
  • Error handling preserved in each handler
  • Service layer access (guildSettings, recommendationFeedback, trackHistory) maintained

Testing

  • No TypeScript compilation errors
  • Jest test suite: no tests exist yet (marked --passWithNoTests)
  • Ready for manual validation before merge

Summary by CodeRabbit

  • New Features

    • Enhanced autoplay: manage artist preferences (prefer/block/list), queue controls (skip/clear/status), and settings (mode, genres) with analytics.
  • Documentation

    • README updated with expanded feature descriptions and a new "Why Lucky?" section.
  • UX Improvements

    • More consistent ephemeral replies and richer embedded responses for commands and status displays.
  • Stability

    • Improved streaming/heartbeat handling and test infrastructure for more reliable behavior.

…istHandlers

- Extract queue operations (skip, clear, status) into queueHandlers.ts
- Extract settings operations (mode, genre, analytics) into settingsHandlers.ts
- Extract artist preference operations (prefer, block, list, remove) into artistHandlers.ts
- Refactor main autoplay.ts to thin router pattern with dispatcher
- Maintain all existing functionality and type safety
- Reduce main file from 1074 to ~250 lines
@vercel

vercel Bot commented May 6, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lucky Ready Ready Preview, Comment May 6, 2026 6:25pm

Request Review

@coderabbitai

coderabbitai Bot commented May 6, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@LucasSantana-Dev has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 1 second before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9a5bfff7-84d2-40b6-8363-caf908281fa3

📥 Commits

Reviewing files that changed from the base of the PR and between 13d4b59 and 601235e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (46)
  • package.json
  • packages/backend/jest.config.cjs
  • packages/backend/tests/integration/routes/musicState.test.ts
  • packages/backend/tests/setup.ts
  • packages/backend/tsconfig.test.json
  • packages/bot/src/functions/general/commands/version.spec.ts
  • packages/bot/src/functions/general/commands/version.ts
  • packages/bot/src/functions/management/commands/guildconfig.spec.ts
  • packages/bot/src/functions/management/commands/guildconfig.ts
  • packages/bot/src/functions/management/commands/serversetup.spec.ts
  • packages/bot/src/functions/management/commands/serversetup.ts
  • packages/bot/src/functions/management/commands/settings.spec.ts
  • packages/bot/src/functions/management/commands/settings.ts
  • packages/bot/src/functions/music/commands/album.spec.ts
  • packages/bot/src/functions/music/commands/album.ts
  • packages/bot/src/functions/music/commands/artist.spec.ts
  • packages/bot/src/functions/music/commands/artist.ts
  • packages/bot/src/functions/music/commands/autoplay.spec.ts
  • packages/bot/src/functions/music/commands/autoplay.ts
  • packages/bot/src/functions/music/commands/autoplay/artistHandlers.spec.ts
  • packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts
  • packages/bot/src/functions/music/commands/autoplay/queueHandlers.spec.ts
  • packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts
  • packages/bot/src/functions/music/commands/autoplay/settingsHandlers.spec.ts
  • packages/bot/src/functions/music/commands/autoplay/settingsHandlers.ts
  • packages/bot/src/functions/music/commands/djrole.ts
  • packages/bot/src/functions/music/commands/history.spec.ts
  • packages/bot/src/functions/music/commands/history.ts
  • packages/bot/src/functions/music/commands/lyrics.spec.ts
  • packages/bot/src/functions/music/commands/lyrics.ts
  • packages/bot/src/functions/music/commands/play/handlers/autoplayPreference.ts
  • packages/bot/src/functions/music/commands/play/handlers/playHandler.ts
  • packages/bot/src/functions/music/commands/play/index.spec.ts
  • packages/bot/src/functions/music/commands/play/index.ts
  • packages/bot/src/functions/music/commands/play/queryUtils.ts
  • packages/bot/src/functions/music/commands/playskip.spec.ts
  • packages/bot/src/functions/music/commands/playtop.spec.ts
  • packages/bot/src/functions/music/commands/queue/index.spec.ts
  • packages/bot/src/functions/music/commands/queue/index.ts
  • packages/bot/src/functions/music/commands/recommendation/index.spec.ts
  • packages/bot/src/functions/music/commands/recommendation/index.ts
  • packages/bot/src/handlers/player/playerFactory.ts
  • packages/bot/src/handlers/player/soundcloudMatcher.ts
  • packages/bot/src/handlers/player/streamBridge.ts
  • packages/shared/src/utils/general/interactionReply.spec.ts
  • sonar-project.properties
📝 Walkthrough

Walkthrough

Refactors the autoplay command into three new handler modules (queueHandlers, settingsHandlers, artistHandlers), migrates many command replies to a centralized interactionReply helper and MessageFlags usage, adds tests and mocks, updates backend Jest test config and session-file-store mock, and updates README branding and a package.json override.

Changes

Autoplay Modularization

Layer / File(s) Summary
Data Shape / Helpers
packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts
Adds isAutoplayTrack(track) to detect autoplay-marked tracks.
Core Logic
packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts
Implements handleSkipAutoplayTrack, handleClearAutoplayTracks, handleAutoplayStatus to inspect/remove autoplay tracks, call replenishQueue, and build status embeds (includes Last.fm link usage).
Settings & Configuration
packages/bot/src/functions/music/commands/autoplay/settingsHandlers.ts
Implements handleAutoplayMode, genre handlers (add/remove/list/clear, 5-genre limit/normalization), handleAutoplayAnalytics, and dispatcher handleAutoplayGenre.
Artist Preferences
packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts
Adds handleAutoplayArtist with subcommands prefer, block, remove, list that call recommendationFeedbackService and return embeds.
Wiring / Command Surface
packages/bot/src/functions/music/commands/autoplay.ts
Replaces ~800 lines of inline handlers with imports/delegation to the three new modules; updates import source for SlashCommandBuilder and uses MessageFlags for deferReply.
Tests
packages/bot/src/functions/music/commands/autoplay/queueHandlers.spec.ts, packages/bot/src/functions/music/commands/autoplay.spec.ts
Adds unit tests for queue handlers and updates autoplay status tests to assert new "Blend" embed field behavior.

Interaction Reply Migration

Layer / File(s) Summary
Utility / Flags
packages/bot/src/utils/general/interactionReply (used throughout)
Centralized reply helper is used in many commands; MessageFlags.Ephemeral usages added across commands.
Command Updates
packages/bot/src/functions/*/commands/*.ts
(examples: guildconfig.ts, serversetup.ts, settings.ts, version.ts, album.ts, artist.ts, play/**, playskip.ts, playtop.ts, queue/index.ts, lyrics.ts, history.ts, recommendation/index.ts, djrole.ts)
Replace direct interaction.reply / interaction.editReply flows with interactionReply({ interaction, content: { ... } }) and change deferReply to use flags: MessageFlags.Ephemeral.
Tests Updated
packages/bot/src/functions/*/*.spec.ts
Update tests to mock and assert interactionReply usage and to expect deferReply with { flags: 64 }.

Backend Tests & Jest Config

Layer / File(s) Summary
Jest Config
packages/backend/jest.config.cjs
Moves ts-jest tsconfig from inline object to '<rootDir>/tsconfig.test.json'.
TSConfig for Tests
packages/backend/tsconfig.test.json
Adds new test tsconfig extending base tsconfig with isolatedModules and include/exclude suited for tests.
Test Setup Mocks
packages/backend/tests/setup.ts
Adds mock implementation for session-file-store returning a MockFileStore with get/set/destroy callbacks.
Integration Tests
packages/backend/tests/integration/routes/musicState.test.ts
Hardens SSE streaming tests by adding named heartbeat/global fallback timers and explicit cleanup on heartbeat/close to prevent hangs.

Documentation & Packaging

Layer / File(s) Summary
README / Branding
README.md
Adds an Invite Lucky badge and expands intro to mention moderation suite, session save/restore, React 19 dashboard, Spotify commands, XP leveling, Twitch notifications; inserts a new "Why Lucky?" section listing five advantages.
Package Overrides
package.json
Adds an overrides entry "ip-address": ">=10.1.1" and reorders overrides block.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User as "Discord User"
    participant Interaction as "ChatInputCommandInteraction"
    participant Command as "Autoplay Command"
    participant Queue as "GuildQueue"
    participant Service as "replenishQueue / lastFmLinkService"
    Interaction->>Command: invoke /autoplay status /skip /clear /artist ...
    Command->>Queue: resolve guild queue & metadata
    alt artist subcommand
        Command->>Service: recommendationFeedbackService (set/remove/get)
        Service-->>Command: confirmation / summary
    else queue operation
        Command->>Queue: inspect tracks (isAutoplayTrack)
        Command->>Queue: removeTrack(index)*
        Command->>Service: replenishQueue(queue)
        Service-->>Command: replenishment result
    end
    Command->>Interaction: interactionReply(content: embed/status)
Loading

(Note: rectangles use default styles; interactions emphasize sequential flow among User, Command, Queue, and Services.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main refactoring work: splitting the large autoplay.ts file into three modular handler files (queueHandlers, settingsHandlers, artistHandlers) to improve maintainability.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/split-autoplay-command

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (3)
packages/bot/src/functions/music/commands/autoplay.ts (2)

222-224: 💤 Low value

Dead code: case 'artist' is unreachable.

artist is declared as a addSubcommandGroup (Line 112), not a top-level subcommand, so interaction.options.getSubcommand(false) will return one of prefer|block|list|remove here — never 'artist'. The artist group is already routed at Lines 186-189. Drop this case to avoid misleading future readers.

🔧 Proposed fix
                 case 'mode':
                     await handleAutoplayMode(interaction)
                     break
-                case 'artist':
-                    await handleAutoplayArtist(interaction)
-                    break
                 default:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bot/src/functions/music/commands/autoplay.ts` around lines 222 -
224, The switch contains an unreachable branch: remove the "case 'artist'"
branch (which calls handleAutoplayArtist) because 'artist' is defined as an
addSubcommandGroup and is already handled in the artist-group routing (lines
calling handleAutoplayArtist at the earlier artist-group branch); rely on
interaction.options.getSubcommand(false) returning the actual subcommands
(prefer|block|list|remove) and delete the dead case to avoid confusion.

158-174: 💤 Low value

Use flags: MessageFlags.Ephemeral instead of deprecated ephemeral: true for deferReply.

The ephemeral boolean option for deferReply() is deprecated in discord.js v14.26.4. Replace interaction.deferReply({ ephemeral: true }) with interaction.deferReply({ flags: MessageFlags.Ephemeral }). The same pattern is visible in the internal interactionReply wrapper utility (lines 87–88, 120–121), which correctly uses flags instead of the deprecated option. This also affects other command files like djrole.ts, version.ts, settings.ts, and guildconfig.ts where direct deferReply() calls use the deprecated syntax.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bot/src/functions/music/commands/autoplay.ts` around lines 158 -
174, Replace the deprecated boolean option in the execute function's deferReply
call: in the execute handler (execute: async ({ client, interaction }:
CommandExecuteParams) => { ... }) change interaction.deferReply({ ephemeral:
true }) to use the MessageFlags API (interaction.deferReply({ flags:
MessageFlags.Ephemeral })) so it matches the interactionReply wrapper pattern;
update the import if needed to bring in MessageFlags from discord.js and apply
the same replacement to other command files that call deferReply directly (e.g.,
djrole.ts, version.ts, settings.ts, guildconfig.ts) to remove the deprecated
usage.
packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts (1)

15-17: ⚡ Quick win

Replace any with proper types from discord-player.

The isAutoplayTrack function uses any twice for the parameter and metadata cast. Import Track from discord-player and type the metadata properly to get compile-time guarantees.

🔧 Proposed fix
-import type { GuildQueue } from 'discord-player'
+import type { GuildQueue, Track } from 'discord-player'
 import type { QueueMetadata } from '../../../../types/QueueMetadata'

-function isAutoplayTrack(track: any): boolean {
-    return (track?.metadata as any)?.isAutoplay === true
+function isAutoplayTrack(track: Track | null | undefined): boolean {
+    return (track?.metadata as { isAutoplay?: boolean } | undefined)?.isAutoplay === true
 }

Alternatively, if you prefer using the existing TrackMetadata type from @lucky/shared/types:

+import type { TrackMetadata } from '@lucky/shared/types'
-import type { GuildQueue } from 'discord-player'
+import type { GuildQueue, Track } from 'discord-player'
 import type { QueueMetadata } from '../../../../types/QueueMetadata'

-function isAutoplayTrack(track: any): boolean {
-    return (track?.metadata as any)?.isAutoplay === true
+function isAutoplayTrack(track: Track | null | undefined): boolean {
+    return (track?.metadata as TrackMetadata | undefined)?.isAutoplay === true
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts` around
lines 15 - 17, isAutoplayTrack currently uses `any` for the parameter and
metadata; import `Track` from `discord-player` (or use the existing
`TrackMetadata` from `@lucky/shared/types`) and change the function signature to
accept `track: Track` (or `Track | null | undefined` as needed) and type the
metadata access so `(track.metadata as TrackMetadata).isAutoplay` is
type-checked; update the import statements and adjust the function body to use
the proper types (preserve optional chaining) to remove `any` usages.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts`:
- Around line 159-195: The list output is inconsistent because
getArtistFeedbackSummary(userId) reads global per-user feedback while
setArtistFeedback(...) and removeArtistFeedback(...) accept but ignore guildId
(they store to music:artist_feedback:${userId}); fix by making behavior
consistent: either remove guildId from setArtistFeedback/removeArtistFeedback
signatures and docs to keep global per-user storage, or change persistence and
getArtistFeedbackSummary to include a guildId parameter and store/read under a
per-guild key (e.g., include guildId in the Redis key) so setArtistFeedback,
removeArtistFeedback, and getArtistFeedbackSummary all use the same (user,guild)
scope; update the functions setArtistFeedback, removeArtistFeedback, and
getArtistFeedbackSummary and any callers (including the list subcommand) to the
chosen contract.
- Around line 1-6: The import statements at the top of artistHandlers.ts are
using incorrect module paths; update them to match the project structure the
same way queueHandlers.ts does: replace recommendationFeedbackService import
with "@/services/musicRecommendation/feedbackService", replace createEmbed and
createErrorEmbed import with "@/utils/general/embeds", replace EMBED_COLORS and
EMOJIS import with "@/config/constants", replace errorLog import with
"@lucky/shared/utils" (the shared export), and change the local interactionReply
import to the relative path "../../../../utils/general/interactionReply"; keep
the same named imports (recommendationFeedbackService, createEmbed,
createErrorEmbed, EMBED_COLORS, EMOJIS, errorLog, interactionReply) so all
usages in this file continue to work.

In `@packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts`:
- Around line 23-37: The check on queue.tracks.size currently uses
createErrorEmbed('Queue Empty', 'No autoplay tracks to skip.') which mislabels
an actually-empty queue; change the message in the branch that checks
queue.tracks.size === 0 to clearly indicate the queue is empty (e.g.,
createErrorEmbed('Queue Empty', 'The queue is empty.') or similar) so it differs
from the later !skipped branch that reports "no autoplay tracks among existing
tracks"; update the interactionReply payload in that branch only (the code paths
involving interactionReply, createErrorEmbed, and the queue.tracks.size === 0
condition).

In `@packages/bot/src/functions/music/commands/autoplay/settingsHandlers.ts`:
- Around line 110-117: handleAutoplayGenreAdd (and the corresponding
handleAutoplayGenreRemove) silently return when guildId or tag is missing,
leaving the deferred interaction unresolved; change these guards to send the
same error reply used by handleAutoplayMode/handleAutoplayAnalytics (e.g., call
the shared interaction reply helper with a "Guild Not Found" or similar error)
before returning so the user isn't left on a spinner — update the early-return
paths in handleAutoplayGenreAdd and handleAutoplayGenreRemove to call the
standard interaction reply and then return.
- Around line 418-432: handleAutoplayGenre currently returns without responding
when subcommand is null or unknown (leaving the prior deferReply unresolved);
update handleAutoplayGenre to include a default branch that sends a user-facing
error response (e.g., an embed) and resolves the deferred interaction—use
interaction.editReply (or interaction.followUp if preferred) to send a clear
error message when subcommand is not one of 'add'|'remove'|'list'|'clear';
ensure the default path handles null safely and returns a Promise<void> like the
other branches.
- Around line 1-13: Update the broken import paths in settingsHandlers.ts:
replace the six-level relative imports for utilities and services with the
correct four-level relative imports for local helpers (so imports like
createEmbed, createErrorEmbed, EMBED_COLORS, EMOJIS, errorLog, interactionReply
should come from ../../../../utils/...) and import shared services from the
shared package (replace guildSettingsService and trackHistoryService relative
imports with imports from `@lucky/shared/services`) and any shared utils from
`@lucky/shared/utils`; ensure the imported symbols (createEmbed, createErrorEmbed,
EMBED_COLORS, EMOJIS, guildSettingsService, trackHistoryService, errorLog,
interactionReply) match their original names so existing references in this file
continue to work.

---

Nitpick comments:
In `@packages/bot/src/functions/music/commands/autoplay.ts`:
- Around line 222-224: The switch contains an unreachable branch: remove the
"case 'artist'" branch (which calls handleAutoplayArtist) because 'artist' is
defined as an addSubcommandGroup and is already handled in the artist-group
routing (lines calling handleAutoplayArtist at the earlier artist-group branch);
rely on interaction.options.getSubcommand(false) returning the actual
subcommands (prefer|block|list|remove) and delete the dead case to avoid
confusion.
- Around line 158-174: Replace the deprecated boolean option in the execute
function's deferReply call: in the execute handler (execute: async ({ client,
interaction }: CommandExecuteParams) => { ... }) change interaction.deferReply({
ephemeral: true }) to use the MessageFlags API (interaction.deferReply({ flags:
MessageFlags.Ephemeral })) so it matches the interactionReply wrapper pattern;
update the import if needed to bring in MessageFlags from discord.js and apply
the same replacement to other command files that call deferReply directly (e.g.,
djrole.ts, version.ts, settings.ts, guildconfig.ts) to remove the deprecated
usage.

In `@packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts`:
- Around line 15-17: isAutoplayTrack currently uses `any` for the parameter and
metadata; import `Track` from `discord-player` (or use the existing
`TrackMetadata` from `@lucky/shared/types`) and change the function signature to
accept `track: Track` (or `Track | null | undefined` as needed) and type the
metadata access so `(track.metadata as TrackMetadata).isAutoplay` is
type-checked; update the import statements and adjust the function body to use
the proper types (preserve optional chaining) to remove `any` usages.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55e6424c-2b72-439e-9b19-1c911e4b2094

📥 Commits

Reviewing files that changed from the base of the PR and between 66e6193 and 13d4b59.

📒 Files selected for processing (5)
  • README.md
  • packages/bot/src/functions/music/commands/autoplay.ts
  • packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts
  • packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts
  • packages/bot/src/functions/music/commands/autoplay/settingsHandlers.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: SonarCloud Scan
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use the isPrisma*Error() helper functions to check for specific Prisma error types (e.g., isPrismaForeignKeyError, isPrismaUniqueConstraintError) instead of manually checking error codes
Use Prisma's $transaction() method to ensure database operations are atomic and avoid partial updates when multiple related tables are modified
Always use select or include in Prisma queries to explicitly specify which fields to return, avoiding unnecessary data transfer
For Redis operations, use connection pooling and implement exponential backoff retry logic for transient failures
Always use logAndRethrow() or logAndSwallow() utilities when handling errors to ensure errors are logged with context before propagating or suppressing

Files:

  • packages/bot/src/functions/music/commands/autoplay/settingsHandlers.ts
  • packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts
  • packages/bot/src/functions/music/commands/autoplay.ts
  • packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts
packages/{bot,backend,shared}/src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/{bot,backend,shared}/src/**/*.ts: For feature toggles, check both global and guild-specific toggles using FeatureToggleService.isEnabledForGuild() rather than checking them separately
Use branded types (e.g., GuildId, UserId, ChannelId) for Discord IDs throughout the codebase to prevent type-level ID confusion

Files:

  • packages/bot/src/functions/music/commands/autoplay/settingsHandlers.ts
  • packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts
  • packages/bot/src/functions/music/commands/autoplay.ts
  • packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts
packages/bot/src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

When building Discord embeds, use EmbedBuilderService.createTemplate() or EmbedBuilderService.getTemplate() instead of constructing embeds directly

Files:

  • packages/bot/src/functions/music/commands/autoplay/settingsHandlers.ts
  • packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts
  • packages/bot/src/functions/music/commands/autoplay.ts
  • packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts
🪛 GitHub Actions: CI/CD Pipeline / 1_Quality Gates.txt
packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts

[error] 2-2: Cannot find module '@/services/recommendationFeedbackService' or its corresponding type declarations.

🪛 GitHub Actions: CI/CD Pipeline / Quality Gates
packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts

[error] 2-2: TypeScript error TS2307: Cannot find module '@/services/recommendationFeedbackService' or its corresponding type declarations.

🔇 Additional comments (2)
README.md (1)

23-23: LGTM — Invite badge and "Why Lucky?" section are clear and consistent with the rest of the README.

Also applies to: 52-62

packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts (1)

96-121: LGTM — index-collect-then-reverse-remove is correct.

Iterating forward to collect autoplay indices and then removing from the end avoids index shifting during removal. replenishQueue is called once after the bulk removal, which is efficient.

Comment thread packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts Outdated
Comment on lines +159 to +195
case 'list': {
const summary =
await recommendationFeedbackService.getArtistFeedbackSummary(
userId,
)

const preferredText =
summary.preferred.length > 0
? summary.preferred
.map((a: string) => `⭐ ${a}`)
.join('\n')
: 'No preferred artists.'

const blockedText =
summary.blocked.length > 0
? summary.blocked
.map((a: string) => `🚫 ${a}`)
.join('\n')
: 'No blocked artists.'

const listEmbed = createEmbed({
title: '🎯 Your Artist Preferences',
description: `**Preferred:** (${summary.preferred.length})\n${preferredText}\n\n**Blocked:** (${summary.blocked.length})\n${blockedText}`,
color: EMBED_COLORS.AUTOPLAY as ColorResolvable,
emoji: EMOJIS.AUTOPLAY,
timestamp: true,
})

await interactionReply({
interaction,
content: {
embeds: [listEmbed],
ephemeral: true,
},
})
break
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the recommendationFeedbackService API to confirm expected scoping
ast-grep --pattern $'class $_ {
  $$$
  setArtistFeedback($$$) {
    $$$
  }
  $$$
}'
rg -nP "(setArtistFeedback|removeArtistFeedback|getArtistFeedbackSummary)\s*[(:]" -C3 --type=ts

Repository: LucasSantana-Dev/Lucky

Length of output: 9766


🏁 Script executed:

sed -n '372,410p' packages/bot/src/services/musicRecommendation/feedbackService.ts

Repository: LucasSantana-Dev/Lucky

Length of output: 1370


🏁 Script executed:

sed -n '340,370p' packages/bot/src/services/musicRecommendation/feedbackService.ts

Repository: LucasSantana-Dev/Lucky

Length of output: 1059


🏁 Script executed:

sed -n '320,365p' packages/bot/src/services/musicRecommendation/feedbackService.ts

Repository: LucasSantana-Dev/Lucky

Length of output: 1526


🏁 Script executed:

sed -n '200,265p' packages/bot/src/services/musicRecommendation/feedbackService.ts

Repository: LucasSantana-Dev/Lucky

Length of output: 2159


🏁 Script executed:

sed -n '264,310p' packages/bot/src/services/musicRecommendation/feedbackService.ts

Repository: LucasSantana-Dev/Lucky

Length of output: 1467


🏁 Script executed:

cat -n packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts | sed -n '40,170p'

Repository: LucasSantana-Dev/Lucky

Length of output: 5542


The getArtistFeedbackSummary() API has a guild-scoping inconsistency with the write-side methods.

The setArtistFeedback() and removeArtistFeedback() methods accept a guildId parameter (lines 51–56, 93–98, 135–139), but both methods ignore it entirely—storing feedback in Redis using only the userId key: music:artist_feedback:${userId}. Conversely, getArtistFeedbackSummary(userId) retrieves the same per-user global feedback without any guild context. This means the list subcommand displays preferences across all guilds, while prefer/block/remove appear guild-scoped by accepting guildId. The service contract is misleading: either the write methods should not accept guildId, or the read method should accept it and filter accordingly (or store feedback per-(guild,user) in Redis).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bot/src/functions/music/commands/autoplay/artistHandlers.ts` around
lines 159 - 195, The list output is inconsistent because
getArtistFeedbackSummary(userId) reads global per-user feedback while
setArtistFeedback(...) and removeArtistFeedback(...) accept but ignore guildId
(they store to music:artist_feedback:${userId}); fix by making behavior
consistent: either remove guildId from setArtistFeedback/removeArtistFeedback
signatures and docs to keep global per-user storage, or change persistence and
getArtistFeedbackSummary to include a guildId parameter and store/read under a
per-guild key (e.g., include guildId in the Redis key) so setArtistFeedback,
removeArtistFeedback, and getArtistFeedbackSummary all use the same (user,guild)
scope; update the functions setArtistFeedback, removeArtistFeedback, and
getArtistFeedbackSummary and any callers (including the list subcommand) to the
chosen contract.

Comment on lines +23 to +37
if (queue.tracks.size === 0) {
await interactionReply({
interaction,
content: {
embeds: [
createErrorEmbed(
'Queue Empty',
'No autoplay tracks to skip.',
),
],
ephemeral: true,
},
})
return
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Wrong error message when queue is non-empty but contains no autoplay tracks vs. queue is entirely empty.

The queue.tracks.size === 0 branch shows "No autoplay tracks to skip", but the more accurate "Queue Empty" / "queue is empty" message would help the user. The downstream if (!skipped) branch already handles the "no autoplay tracks among existing tracks" case correctly — these two messages should be distinguishable.

🔧 Proposed tweak
     if (queue.tracks.size === 0) {
         await interactionReply({
             interaction,
             content: {
                 embeds: [
                     createErrorEmbed(
                         'Queue Empty',
-                        'No autoplay tracks to skip.',
+                        'The queue is empty.',
                     ),
                 ],
                 ephemeral: true,
             },
         })
         return
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (queue.tracks.size === 0) {
await interactionReply({
interaction,
content: {
embeds: [
createErrorEmbed(
'Queue Empty',
'No autoplay tracks to skip.',
),
],
ephemeral: true,
},
})
return
}
if (queue.tracks.size === 0) {
await interactionReply({
interaction,
content: {
embeds: [
createErrorEmbed(
'Queue Empty',
'The queue is empty.',
),
],
ephemeral: true,
},
})
return
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bot/src/functions/music/commands/autoplay/queueHandlers.ts` around
lines 23 - 37, The check on queue.tracks.size currently uses
createErrorEmbed('Queue Empty', 'No autoplay tracks to skip.') which mislabels
an actually-empty queue; change the message in the branch that checks
queue.tracks.size === 0 to clearly indicate the queue is empty (e.g.,
createErrorEmbed('Queue Empty', 'The queue is empty.') or similar) so it differs
from the later !skipped branch that reports "no autoplay tracks among existing
tracks"; update the interactionReply payload in that branch only (the code paths
involving interactionReply, createErrorEmbed, and the queue.tracks.size === 0
condition).

Comment thread packages/bot/src/functions/music/commands/autoplay/settingsHandlers.ts Outdated
Comment thread packages/bot/src/functions/music/commands/autoplay/settingsHandlers.ts Outdated
All three handler files (artistHandlers, settingsHandlers, queueHandlers)
were using non-existent @/ aliases and wrong relative depths. Fixed to
use correct 4-level relative paths and @lucky/shared/* workspace imports.

Rewrote handleAutoplayStatus in queueHandlers to remove references to
non-existent QueueMetadata fields (preferences, nowPlayingLastFm) and
non-existent lastFmLinkService.getTrackUrl(). Replaced Array.from(queue.tracks)
with queue.tracks.at(i) for-loop pattern to fix TS2769 overload error.

Also: fix session-file-store mock in backend tests, switch backend jest
to tsconfig.test.json, add ip-address security override.
@github-actions github-actions Bot added dependencies Pull requests that update a dependency file backend labels May 6, 2026
Covers handleAutoplayStatus (status flags, track counting via for-loop,
Blend field with/without Last.fm links, null metadata), handleSkipAutoplayTrack
(empty queue, no autoplay tracks, correct remove+replenish), and
handleClearAutoplayTracks (reverse-order removal, correct count in embed).

These tests would have caught: broken QueueMetadata field references
(metadata.preferences, metadata.nowPlayingLastFm), non-existent
lastFmLinkService.getTrackUrl(), and Array.from(queue.tracks) overload error.
@github-actions

github-actions Bot commented May 6, 2026 •

Copy link
Copy Markdown

Size Change: 0 B

Total Size: 367 kB

ℹ️ View Unchanged
Filename Size
packages/frontend/dist/assets/ActionPanel-dTlZUgqe.js 402 B
packages/frontend/dist/assets/Admin-DnWz1MOA.js 1.97 kB
packages/frontend/dist/assets/AutoMessages-vRB62tJd.js 2.66 kB
packages/frontend/dist/assets/AutoMod-K_viJkXQ.js 4.07 kB
packages/frontend/dist/assets/badge-8qbHQXXg.js 504 B
packages/frontend/dist/assets/Card-PloSFCea.js 506 B
packages/frontend/dist/assets/CommandsConfig-CESed-fz.js 1.44 kB
packages/frontend/dist/assets/Config-By5IL35T.js 1.64 kB
packages/frontend/dist/assets/CustomCommands-CXfqmyJo.js 2.14 kB
packages/frontend/dist/assets/DashboardOverview-CL541_VM.js 3.36 kB
packages/frontend/dist/assets/dialog-BCLK2JMT.js 952 B
packages/frontend/dist/assets/EmbedBuilder-BdtZhzFK.js 3.31 kB
packages/frontend/dist/assets/Features-ByYF18ea.js 756 B
packages/frontend/dist/assets/GuildAutomation-Dc7me2lV.js 2.9 kB
packages/frontend/dist/assets/index-DDLMHTiW.css 17.1 kB
packages/frontend/dist/assets/index-DyCTxeYW.js 56 kB
packages/frontend/dist/assets/input-cOH1PQVi.js 467 B
packages/frontend/dist/assets/label-Dh1uOFi2.js 478 B
packages/frontend/dist/assets/Landing-CD9QzN7X.js 3.71 kB
packages/frontend/dist/assets/LastFm-blvMf2ay.js 1.74 kB
packages/frontend/dist/assets/Levels-B96wzwSM.js 2.61 kB
packages/frontend/dist/assets/Login-tEr-uYLk.js 2.48 kB
packages/frontend/dist/assets/Lyrics-Dvgco87j.js 1.32 kB
packages/frontend/dist/assets/Moderation-Bqlfkaz_.js 3.81 kB
packages/frontend/dist/assets/Music-Dqx9eZ6w.js 7.11 kB
packages/frontend/dist/assets/MusicConfig-C1cDkQXP.js 1.59 kB
packages/frontend/dist/assets/PreferredArtists-CcbRnFlR.js 3.67 kB
packages/frontend/dist/assets/PrivacyPolicy-BmZtXGg8.js 1.38 kB
packages/frontend/dist/assets/ReactionRoles-Dm64nzHC.js 1.86 kB
packages/frontend/dist/assets/rolldown-runtime-BYbx6iT9.js 471 B
packages/frontend/dist/assets/SectionHeader-CtzacEAY.js 895 B
packages/frontend/dist/assets/select-FOgY7B8V.js 1.23 kB
packages/frontend/dist/assets/ServerLogs-QqqQW9cg.js 2.87 kB
packages/frontend/dist/assets/ServerSettings-sBfBpHL0.js 4.19 kB
packages/frontend/dist/assets/ServersPage-CuWU6B6g.js 2.87 kB
packages/frontend/dist/assets/Skeleton-BKxDqjuF.js 237 B
packages/frontend/dist/assets/Spotify-ChhuqXVt.js 1.75 kB
packages/frontend/dist/assets/Starboard-CReFRkuG.js 2.05 kB
packages/frontend/dist/assets/StatTile-B31RaVZ2.js 638 B
packages/frontend/dist/assets/switch-BE_uObSf.js 543 B
packages/frontend/dist/assets/TermsOfService-BolVjdlH.js 1.37 kB
packages/frontend/dist/assets/TrackHistory-Dv_7dEQE.js 2.15 kB
packages/frontend/dist/assets/TwitchNotifications-NRZsAuz4.js 2.43 kB
packages/frontend/dist/assets/useFeatures-BSC8EiID.js 1.99 kB
packages/frontend/dist/assets/usePageMetadata-T1T1Fi3u.js 329 B
packages/frontend/dist/assets/vendor-forms-DNkRbYRs.js 25.6 kB
packages/frontend/dist/assets/vendor-radix-hBYIp2YC.js 38.9 kB
packages/frontend/dist/assets/vendor-react-CFDOOXi7.js 55.7 kB
packages/frontend/dist/assets/vendor-state-BDM_FkZa.js 24.1 kB
packages/frontend/dist/assets/vendor-ui-CbZ_GPii.js 64.7 kB

compressed-size-action

… assertions

case 'artist' in the top-level subcommand switch was unreachable — artist is a
subcommand group and is handled by the early-return group routing above the switch.
Removing it eliminates the dead branch CodeRabbit flagged.

Also fixed two autoplay.spec.ts assertions that checked for embed.description
containing 'Blending taste' — the implementation uses fields (not description)
and the blend copy is 'Mixing taste for N users'. Tests now assert the correct
field structure produced by handleAutoplayStatus.
LucasSantana-Dev and others added 2 commits May 6, 2026 14:15
… adapter (Phase 1)

Replace direct interaction.reply/editReply/deferReply calls with the
interactionReply wrapper and MessageFlags.Ephemeral across all command
handlers. Update all 13 affected spec files to match the new call
signatures, fixing ESM chain failures from uuid v14 by mocking the
interactionReply module.
…ling

- Replace `any` types in isAutoplayTrack with Track and typed metadata cast
- Add error replies in handleAutoplayGenreAdd/Remove when guildId or tag is missing (was silently returning, leaving users on a spinner)
- Add default branch to handleAutoplayGenre switch to handle unknown subcommands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@LucasSantana-Dev

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Extract yt-dlp stream functions to streamBridge.ts and SoundCloud
search/matching logic to soundcloudMatcher.ts. playerFactory.ts retains
only extractor registration and re-exports moved symbols for backward
compatibility. No behavioral changes.
Cover deferReply/editReply/followUp branching, ephemeral flag mapping,
text-to-embed conversion, and error swallowing in the interactionReply
wrapper introduced in Phase 1. 11 tests.
Wrap replenishQueue in handleSkipAutoplayTrack with a try/catch so that
a replenish failure surfaces an ephemeral error embed instead of
silently leaving the queue empty.
… test coverage

- Wrap replenishQueue() calls in try-catch in handleSkipAutoplayTrack and handleClearAutoplayTracks; silent queue replenishment failures now surface an error reply
- Add index re-verification before removal in handleClearAutoplayTracks to prevent removing wrong tracks if queue shifts between collection and removal
- Wrap lastFmLinkService.getByDiscordId() in try-catch in handleAutoplayStatus; Blend field is omitted gracefully on lookup failure
- Add error replies to bare !guildId guards in handleAutoplayGenreList and handleAutoplayGenreClear (was silently returning)
- Add settingsHandlers.spec.ts (40 tests covering all mode/genre/analytics paths and service failure cases)
- Add artistHandlers.spec.ts (22 tests covering all 5 artist subcommands, error paths, and service error handling)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@LucasSantana-Dev

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Add soundcloudMatcher.ts and streamBridge.ts to sonar.cpd.exclusions
(extracted from playerFactory.ts, not new logic). Add NOSONAR comment
for S5852 on bounded regex in normalizeForMatch.
… S4036

Add playHandler.ts and autoplayPreference.ts to sonar.cpd.exclusions
(extracted from play/index.ts). Add NOSONAR S4036 on spawn() in
streamBridge.ts — command is hardcoded, URL validated before call.
Suppress CPD on queueHandlers.ts, settingsHandlers.ts and artistHandlers.ts —
all three were extracted from autoplay.ts in this branch, so duplication against
the original file is expected and not a real concern.
@sonarqubecloud

sonarqubecloud Bot commented May 6, 2026

Copy link
Copy Markdown

@LucasSantana-Dev
LucasSantana-Dev merged commit 82a5d63 into main May 6, 2026
13 checks passed
@LucasSantana-Dev
LucasSantana-Dev deleted the refactor/split-autoplay-command branch May 6, 2026 18:31
LucasSantana-Dev added a commit that referenced this pull request May 13, 2026
…istHandlers (#814)

* docs: add invite badge, Why Lucky section, and expanded feature description

* refactor: split autoplay.ts into queueHandlers, settingsHandlers, artistHandlers

- Extract queue operations (skip, clear, status) into queueHandlers.ts
- Extract settings operations (mode, genre, analytics) into settingsHandlers.ts
- Extract artist preference operations (prefer, block, list, remove) into artistHandlers.ts
- Refactor main autoplay.ts to thin router pattern with dispatcher
- Maintain all existing functionality and type safety
- Reduce main file from 1074 to ~250 lines

* fix: correct broken import paths in split autoplay handlers

All three handler files (artistHandlers, settingsHandlers, queueHandlers)
were using non-existent @/ aliases and wrong relative depths. Fixed to
use correct 4-level relative paths and @lucky/shared/* workspace imports.

Rewrote handleAutoplayStatus in queueHandlers to remove references to
non-existent QueueMetadata fields (preferences, nowPlayingLastFm) and
non-existent lastFmLinkService.getTrackUrl(). Replaced Array.from(queue.tracks)
with queue.tracks.at(i) for-loop pattern to fix TS2769 overload error.

Also: fix session-file-store mock in backend tests, switch backend jest
to tsconfig.test.json, add ip-address security override.

* test(bot): add unit tests for autoplay queueHandlers

Covers handleAutoplayStatus (status flags, track counting via for-loop,
Blend field with/without Last.fm links, null metadata), handleSkipAutoplayTrack
(empty queue, no autoplay tracks, correct remove+replenish), and
handleClearAutoplayTracks (reverse-order removal, correct count in embed).

These tests would have caught: broken QueueMetadata field references
(metadata.preferences, metadata.nowPlayingLastFm), non-existent
lastFmLinkService.getTrackUrl(), and Array.from(queue.tracks) overload error.

* refactor(bot): remove dead artist case from autoplay switch, fix spec assertions

case 'artist' in the top-level subcommand switch was unreachable — artist is a
subcommand group and is handled by the early-return group routing above the switch.
Removing it eliminates the dead branch CodeRabbit flagged.

Also fixed two autoplay.spec.ts assertions that checked for embed.description
containing 'Blending taste' — the implementation uses fields (not description)
and the blend copy is 'Mixing taste for N users'. Tests now assert the correct
field structure produced by handleAutoplayStatus.

* refactor(bot): normalize all Discord replies through interactionReply adapter (Phase 1)

Replace direct interaction.reply/editReply/deferReply calls with the
interactionReply wrapper and MessageFlags.Ephemeral across all command
handlers. Update all 13 affected spec files to match the new call
signatures, fixing ESM chain failures from uuid v14 by mocking the
interactionReply module.

* fix(autoplay): address CodeRabbit review — type safety and error handling

- Replace `any` types in isAutoplayTrack with Track and typed metadata cast
- Add error replies in handleAutoplayGenreAdd/Remove when guildId or tag is missing (was silently returning, leaving users on a spinner)
- Add default branch to handleAutoplayGenre switch to handle unknown subcommands

* refactor(bot): extract play command execute logic into handlers/ (Phase 2)

* refactor(bot): split playerFactory.ts into focused modules

Extract yt-dlp stream functions to streamBridge.ts and SoundCloud
search/matching logic to soundcloudMatcher.ts. playerFactory.ts retains
only extractor registration and re-exports moved symbols for backward
compatibility. No behavioral changes.

* test(shared): add interactionReply unit tests

Cover deferReply/editReply/followUp branching, ephemeral flag mapping,
text-to-embed conversion, and error swallowing in the interactionReply
wrapper introduced in Phase 1. 11 tests.

* fix(bot): notify user when autoplay replenish fails on skip

Wrap replenishQueue in handleSkipAutoplayTrack with a try/catch so that
a replenish failure surfaces an ephemeral error embed instead of
silently leaving the queue empty.

* fix(autoplay): address PR review — error handling, guard replies, and test coverage

- Wrap replenishQueue() calls in try-catch in handleSkipAutoplayTrack and handleClearAutoplayTracks; silent queue replenishment failures now surface an error reply
- Add index re-verification before removal in handleClearAutoplayTracks to prevent removing wrong tracks if queue shifts between collection and removal
- Wrap lastFmLinkService.getByDiscordId() in try-catch in handleAutoplayStatus; Blend field is omitted gracefully on lookup failure
- Add error replies to bare !guildId guards in handleAutoplayGenreList and handleAutoplayGenreClear (was silently returning)
- Add settingsHandlers.spec.ts (40 tests covering all mode/genre/analytics paths and service failure cases)
- Add artistHandlers.spec.ts (22 tests covering all 5 artist subcommands, error paths, and service error handling)

* chore(sonar): suppress CPD and S5852 hotspot on extracted player modules

Add soundcloudMatcher.ts and streamBridge.ts to sonar.cpd.exclusions
(extracted from playerFactory.ts, not new logic). Add NOSONAR comment
for S5852 on bounded regex in normalizeForMatch.

* chore(sonar): add extracted play handlers to CPD exclusions, suppress S4036

Add playHandler.ts and autoplayPreference.ts to sonar.cpd.exclusions
(extracted from play/index.ts). Add NOSONAR S4036 on spawn() in
streamBridge.ts — command is hardcoded, URL validated before call.

* chore: add autoplay split files to sonar cpd.exclusions

Suppress CPD on queueHandlers.ts, settingsHandlers.ts and artistHandlers.ts —
all three were extracted from autoplay.ts in this branch, so duplication against
the original file is expected and not a real concern.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 601235ed Deployed May 6, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend bot dependencies Pull requests that update a dependency file enhancement New feature or request shared size/xl

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant