fix(spotify): per-bot Connect device name to avoid multi-bot device collision/misroute [corner-case R2-5]

config.spotify is a single process-wide object shared by every BotInstance, so
config.spotify.deviceName was identical for all bots. On the Rust (librespot)
backend each bot spawned `librespot --name <deviceName>` with no per-bot
uniqueness, registering two Connect devices with the same name under the one
shared account. findDeviceByName()/waitForDevice() match by name, so bot A's
transfer()+play() could drive bot B's librespot (misroute) and a bot could
report ready on seeing the OTHER bot's same-named device (false readiness).

Fix: derive a per-bot-unique Connect identity from the shared base name.
- controller.ts: new exported pure helper perBotDeviceName(base, instanceId?)
  (`${base}-${instanceId}` when an id is given, else base). Add optional
  instanceId to SpotifyControllerOptions; buildBackend() computes the effective
  name once and passes the SAME value to both the Rust and go backends so
  --name, findDeviceByName, and waitForDevice all key on one identity.
- instance.ts: pass instanceId: this.id into buildController(); add instanceId
  to the spotifyControllerFactory param type (test seam).

The user-configured config.spotify.deviceName base is left untouched; the suffix
applies only to the backend/Connect identity. Behavior-preserving for callers
that pass no instanceId (base name used unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
saopig1andClaude Opus 4.8 committed 2026-07-03 12:32:02 +08:00
1 parent 9a32f3c602
commit 2e05d276bf
4 files changed
+97 -5

No files matched your search

+5
View File
@@ -94,6 +94,7 @@ export interface BotInstanceOptions {
workDir: string;
configDir: string;
logger: Logger;
instanceId: string;
apiPort: number;
callbackPort: number;
oauth?: SpotifyOAuth;
@@ -193,6 +194,10 @@ export class BotInstance extends EventEmitter {
workDir: spotifyWorkDir,
configDir: spotifyConfigDir,
logger: this.logger,
// Per-bot id → unique Spotify Connect device name ("<base>-<id>"), so two
// bots under the one shared account never register the same name and
// misroute Connect commands (corner-case R2-5).
instanceId: this.id,
apiPort: spotifyApiPort,
callbackPort: spotifyCallbackPort,
oauth: options.spotifyOAuth,