Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 27 additions & 12 deletions packages/bot/src/functions/music/commands/pause.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const requireQueueMock = jest.fn()
const requireVoiceChannelMock = jest.fn()
const interactionReplyMock = jest.fn()
const createSuccessEmbedMock = jest.fn((title: string, desc?: string) => ({ title, description: desc }))
const createWarningEmbedMock = jest.fn((title: string, desc?: string) => ({ title, description: desc }))
const buildCommandTrackEmbedMock = jest.fn(() => ({}))
const resolveGuildQueueMock = jest.fn()

Expand All @@ -20,7 +19,6 @@ jest.mock('../../../utils/general/interactionReply', () => ({

jest.mock('../../../utils/general/embeds', () => ({
createSuccessEmbed: (...args: unknown[]) => createSuccessEmbedMock(...args),
createWarningEmbed: (...args: unknown[]) => createWarningEmbedMock(...args),
}))

jest.mock('../../../utils/general/responseEmbeds', () => ({
Expand All @@ -43,6 +41,7 @@ function createQueue(isPaused = false, currentTrack: unknown = null) {
node: {
isPaused: jest.fn().mockReturnValue(isPaused),
pause: jest.fn(),
resume: jest.fn(),
},
currentTrack,
} as any
Expand All @@ -52,7 +51,7 @@ function createClient() {
return {} as any
}

describe('pause command', () => {
describe('pause command (toggle)', () => {
beforeEach(() => {
jest.clearAllMocks()
requireVoiceChannelMock.mockResolvedValue(true)
Expand All @@ -68,6 +67,7 @@ describe('pause command', () => {
await pauseCommand.execute({ client: createClient(), interaction: createInteraction() } as any)

expect(queue.node.pause).not.toHaveBeenCalled()
expect(queue.node.resume).not.toHaveBeenCalled()
})

it('returns early when queue validation fails', async () => {
Expand All @@ -78,37 +78,52 @@ describe('pause command', () => {
await pauseCommand.execute({ client: createClient(), interaction: createInteraction() } as any)

expect(queue.node.pause).not.toHaveBeenCalled()
expect(queue.node.resume).not.toHaveBeenCalled()
})

it('shows warning when already paused', async () => {
const queue = createQueue(true)
it('resumes when currently paused with no track', async () => {
const queue = createQueue(true, null)
resolveGuildQueueMock.mockReturnValue({ queue })

await pauseCommand.execute({ client: createClient(), interaction: createInteraction() } as any)

expect(queue.node.resume).toHaveBeenCalled()
expect(queue.node.pause).not.toHaveBeenCalled()
expect(createWarningEmbedMock).toHaveBeenCalledWith('Already paused', expect.any(String))
expect(createSuccessEmbedMock).toHaveBeenCalledWith('▶️ Resumed', 'Music has been resumed.')
})

it('shows simple success embed when no current track', async () => {
it('pauses when currently playing with no track', async () => {
const queue = createQueue(false, null)
resolveGuildQueueMock.mockReturnValue({ queue })

await pauseCommand.execute({ client: createClient(), interaction: createInteraction() } as any)

expect(queue.node.pause).toHaveBeenCalled()
expect(queue.node.resume).not.toHaveBeenCalled()
expect(createSuccessEmbedMock).toHaveBeenCalledWith('⏸️ Paused', 'Music has been paused.')
})

it('shows rich track embed when current track exists', async () => {
const track = { title: 'Test Song', author: 'Artist', url: 'http://x', duration: '3:00' }
const queue = createQueue(false, track)
it('resumes when currently paused with current track', async () => {
const currentTrack = { id: 'track-1', title: 'Test Song' }
const queue = createQueue(true, currentTrack)
resolveGuildQueueMock.mockReturnValue({ queue })

await pauseCommand.execute({ client: createClient(), interaction: createInteraction() } as any)

expect(queue.node.resume).toHaveBeenCalled()
expect(queue.node.pause).not.toHaveBeenCalled()
expect(buildCommandTrackEmbedMock).toHaveBeenCalledWith(currentTrack, '▶️ Resumed', expect.any(Object))
})

it('pauses when currently playing with current track', async () => {
const currentTrack = { id: 'track-1', title: 'Test Song' }
const queue = createQueue(false, currentTrack)
resolveGuildQueueMock.mockReturnValue({ queue })

await pauseCommand.execute({ client: createClient(), interaction: createInteraction() } as any)

expect(queue.node.pause).toHaveBeenCalled()
expect(buildCommandTrackEmbedMock).toHaveBeenCalledWith(track, '⏸️ Paused', expect.any(Object))
expect(interactionReplyMock).toHaveBeenCalled()
expect(queue.node.resume).not.toHaveBeenCalled()
expect(buildCommandTrackEmbedMock).toHaveBeenCalledWith(currentTrack, '⏸️ Paused', expect.any(Object))
})
})
34 changes: 13 additions & 21 deletions packages/bot/src/functions/music/commands/pause.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {
requireVoiceChannel,
} from "../../../utils/command/commandValidations"
import { resolveGuildQueue } from '../../../utils/music/queueResolver'
import { createSuccessEmbed, createWarningEmbed } from '../../../utils/general/embeds'
import { createSuccessEmbed } from '../../../utils/general/embeds'
import { buildCommandTrackEmbed } from '../../../utils/general/responseEmbeds'

export default new Command({
data: new SlashCommandBuilder()
.setName('pause')
.setDescription('⏸️ Pause the current music.'),
.setDescription('⏸️ Toggle pause/resume music.'),
category: 'music',
execute: async ({ client, interaction }: CommandExecuteParams) => {
if (!(await requireVoiceChannel(interaction))) return
Expand All @@ -22,41 +22,33 @@ export default new Command({

if (!(await requireQueue(queue, interaction))) return

if (queue?.node.isPaused() === true) {
await interactionReply({
interaction,
content: {
embeds: [
createWarningEmbed(
'Already paused',
'⏸️ Music is already paused.',
),
],
ephemeral: true,
},
})
return
}
const isPaused = queue?.node.isPaused() === true

queue?.node.pause()
if (isPaused) {
queue?.node.resume()
} else {
queue?.node.pause()
}

const currentTrack = queue?.currentTrack
const action = isPaused ? '▶️ Resumed' : '⏸️ Paused'

if (!currentTrack) {
await interactionReply({
interaction,
content: {
embeds: [
createSuccessEmbed(
'⏸️ Paused',
'Music has been paused.',
action,
`Music has been ${isPaused ? 'resumed' : 'paused'}.`,
),
],
},
})
return
}

const trackEmbed = buildCommandTrackEmbed(currentTrack, '⏸️ Paused', interaction.user)
const trackEmbed = buildCommandTrackEmbed(currentTrack, action, interaction.user)
await interactionReply({ interaction, content: { embeds: [trackEmbed] } })
},
})
40 changes: 19 additions & 21 deletions packages/bot/src/functions/music/commands/play/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,12 @@
} from '../../../../utils/music/queueManipulation'
import { buildPlayResponseEmbed } from '../../../../utils/music/nowPlayingEmbed'
import { createMusicControlButtons } from '../../../../utils/music/buttonComponents'

const DISCORD_UNKNOWN_INTERACTION_CODE = 10062

function isUnknownInteractionError(error: unknown): boolean {
return (
typeof error === 'object' &&
error !== null &&
'code' in error &&
(error as { code?: number }).code === DISCORD_UNKNOWN_INTERACTION_CODE
)
}

function isUrl(query: string): boolean {
return query.startsWith('http://') || query.startsWith('https://')
}

function resolveSearchEngine(query: string): QueryType {
if (isUrl(query)) return QueryType.AUTO
return QueryType.SPOTIFY_SEARCH
}
import {
DISCORD_UNKNOWN_INTERACTION_CODE,

Check warning on line 23 in packages/bot/src/functions/music/commands/play/index.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused import of 'DISCORD_UNKNOWN_INTERACTION_CODE'.

See more on https://sonarcloud.io/project/issues?id=LucasSantana-Dev_Lucky&issues=AZ14T2aEFHYCZi2a7lP4&open=AZ14T2aEFHYCZi2a7lP4&pullRequest=522
isUnknownInteractionError,
isUrl,

Check warning on line 25 in packages/bot/src/functions/music/commands/play/index.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused import of 'isUrl'.

See more on https://sonarcloud.io/project/issues?id=LucasSantana-Dev_Lucky&issues=AZ14T2aEFHYCZi2a7lP5&open=AZ14T2aEFHYCZi2a7lP5&pullRequest=522
resolveSearchEngine,
} from './queryUtils'

function isTrackAlreadyQueued(
queue: { tracks: { toArray?: () => Array<{ id?: string; url?: string }> } },
Expand Down Expand Up @@ -66,6 +52,17 @@
'Song name, artist, YouTube URL, or Spotify URL',
)
.setRequired(true),
)
.addStringOption((option) =>
option
.setName('provider')
.setDescription('Music provider to search (default: spotify)')
.addChoices(
{ name: 'Spotify', value: 'spotify' },
{ name: 'YouTube', value: 'youtube' },
{ name: 'SoundCloud', value: 'soundcloud' },
)
.setRequired(false),
),
category: 'music',
execute: async ({
Expand Down Expand Up @@ -98,6 +95,7 @@
}

const query = interaction.options.getString('query', true)
const provider = interaction.options.getString('provider')
const collaborativeCheck = collaborativePlaylistService.canAddTracks(
interaction.guildId,
interaction.user.id,
Expand All @@ -123,7 +121,7 @@
resolveGuildQueue(client, interaction.guildId ?? '').queue,
)

const searchEngine = resolveSearchEngine(query)
const searchEngine = resolveSearchEngine(query, provider)
const playOptions = {
nodeOptions: {
metadata: {
Expand Down
Loading