diff --git a/packages/bot/src/functions/music/commands/autoplay.spec.ts b/packages/bot/src/functions/music/commands/autoplay.spec.ts index 921f3a56d..0be993089 100644 --- a/packages/bot/src/functions/music/commands/autoplay.spec.ts +++ b/packages/bot/src/functions/music/commands/autoplay.spec.ts @@ -178,7 +178,7 @@ describe('autoplay command', () => { expect(interactionReplyMock).toHaveBeenCalled() }) - it('enables autoplay when no queue exists and settings are missing', async () => { + it('disables autoplay when no queue exists and settings are missing (default-on)', async () => { const client = createClient({ directQueue: null }) const interaction = createInteraction() @@ -188,16 +188,12 @@ describe('autoplay command', () => { } as any) expect(setGuildSettingsMock).toHaveBeenCalledWith('guild-1', { - autoPlayEnabled: true, + autoPlayEnabled: false, }) expect(interactionReplyMock).toHaveBeenCalled() - const embedPayload = createEmbedMock.mock.calls[0]?.[0] as { - description: string - } - expect(embedPayload.description).toContain('Next time you use /play') }) - it('shows an error when enabling autoplay without a queue fails to persist', async () => { + it('shows an error when disabling autoplay without a queue fails to persist', async () => { const client = createClient({ directQueue: null }) const interaction = createInteraction() setGuildSettingsMock.mockResolvedValue(false) @@ -209,7 +205,7 @@ describe('autoplay command', () => { expect(warnLogMock).toHaveBeenCalledWith( expect.objectContaining({ - message: 'Failed to persist autoplay enabled preference', + message: 'Failed to persist autoplay disabled preference', }), ) expect(createEmbedMock).toHaveBeenCalledWith( @@ -418,13 +414,11 @@ describe('autoplay command', () => { it('returns silently when deferReply throws unknown interaction error (10062)', async () => { const interaction = createInteraction() - interaction.deferReply = jest - .fn() - .mockRejectedValue( - Object.assign(new Error('Unknown interaction'), { - code: 10062, - }), - ) + interaction.deferReply = jest.fn().mockRejectedValue( + Object.assign(new Error('Unknown interaction'), { + code: 10062, + }), + ) resolveGuildQueueMock.mockReturnValue({ queue: null, source: 'miss', diff --git a/packages/bot/src/functions/music/commands/autoplay.ts b/packages/bot/src/functions/music/commands/autoplay.ts index 69f09d295..1cf082902 100644 --- a/packages/bot/src/functions/music/commands/autoplay.ts +++ b/packages/bot/src/functions/music/commands/autoplay.ts @@ -183,7 +183,7 @@ async function resolveCurrentAutoplayState( return queue.repeatMode === QueueRepeatMode.AUTOPLAY } const settings = await guildSettingsService.getGuildSettings(guildId) - return settings?.autoPlayEnabled ?? false + return settings?.autoPlayEnabled ?? true } export default new Command({ diff --git a/packages/bot/src/utils/music/queueManipulation.spec.ts b/packages/bot/src/utils/music/queueManipulation.spec.ts index 6e14cbd12..e91177871 100644 --- a/packages/bot/src/utils/music/queueManipulation.spec.ts +++ b/packages/bot/src/utils/music/queueManipulation.spec.ts @@ -44,6 +44,14 @@ jest.mock('@lucky/shared/utils', () => ({ errorLog: jest.fn(), })) +const getTrackHistoryMock = jest.fn() + +jest.mock('@lucky/shared/services', () => ({ + trackHistoryService: { + getTrackHistory: (...args: unknown[]) => getTrackHistoryMock(...args), + }, +})) + const getLastFmSeedTracksMock = jest.fn() jest.mock('./autoplay/lastFmSeeds', () => ({ @@ -104,6 +112,7 @@ describe('queueManipulation.replenishQueue', () => { dislikedTrackKeysMock.mockResolvedValue(new Set()) likedTrackKeysMock.mockResolvedValue(new Set()) getLastFmSeedTracksMock.mockResolvedValue([]) + getTrackHistoryMock.mockResolvedValue([]) }) async function replenishWithSingleCandidate(options: { diff --git a/packages/bot/src/utils/music/queueManipulation.ts b/packages/bot/src/utils/music/queueManipulation.ts index 2fba4d972..99847fc83 100644 --- a/packages/bot/src/utils/music/queueManipulation.ts +++ b/packages/bot/src/utils/music/queueManipulation.ts @@ -8,6 +8,7 @@ import { randomInt } from 'node:crypto' import type { User } from 'discord.js' import { debugLog, errorLog } from '@lucky/shared/utils' import { recommendationFeedbackService } from '../../services/musicRecommendation/feedbackService' +import { trackHistoryService } from '@lucky/shared/services' import { getLastFmSeedTracks } from './autoplay/lastFmSeeds' import { cleanSearchQuery, cleanTitle, cleanAuthor } from './searchQueryCleaner' @@ -204,25 +205,29 @@ export async function replenishQueue(queue: GuildQueue): Promise { HISTORY_SEED_LIMIT + 1, ) const requestedBy = getRequestedBy(queue, currentTrack) - const [dislikedTrackKeys, likedTrackKeys] = await Promise.all([ - recommendationFeedbackService.getDislikedTrackKeys( - queue.guild.id, - requestedBy?.id, - ), - recommendationFeedbackService.getLikedTrackKeys( - queue.guild.id, - requestedBy?.id, - ), - ]) + const [dislikedTrackKeys, likedTrackKeys, persistentHistory] = + await Promise.all([ + recommendationFeedbackService.getDislikedTrackKeys( + queue.guild.id, + requestedBy?.id, + ), + recommendationFeedbackService.getLikedTrackKeys( + queue.guild.id, + requestedBy?.id, + ), + trackHistoryService.getTrackHistory(queue.guild.id, 20), + ]) const excludedUrls = buildExcludedUrls( queue, currentTrack, historyTracks, + persistentHistory, ) const excludedKeys = buildExcludedKeys( queue, currentTrack, historyTracks, + persistentHistory, ) const recentArtists = buildRecentArtists(currentTrack, historyTracks) const candidates = await collectRecommendationCandidates( @@ -307,11 +312,13 @@ function buildExcludedUrls( queue: GuildQueue, currentTrack: Track, historyTracks: Track[], + persistentHistory: { url: string }[] = [], ): Set { return new Set([ currentTrack.url, ...historyTracks.map((track) => track.url), ...queue.tracks.toArray().map((track) => track.url), + ...persistentHistory.map((entry) => entry.url).filter(Boolean), ]) } @@ -319,6 +326,7 @@ function buildExcludedKeys( queue: GuildQueue, currentTrack: Track, historyTracks: Track[], + persistentHistory: { title: string; author: string }[] = [], ): Set { return new Set([ normalizeTrackKey(currentTrack.title, currentTrack.author), @@ -328,6 +336,9 @@ function buildExcludedKeys( ...queue.tracks .toArray() .map((track) => normalizeTrackKey(track.title, track.author)), + ...persistentHistory.map((entry) => + normalizeTrackKey(entry.title, entry.author), + ), ]) }