Skip to content

Commit

Permalink
finish: base feature for radio
Browse files Browse the repository at this point in the history
  • Loading branch information
RainyXeon committed Sep 14, 2024
1 parent 8ef39ae commit 002d55f
Show file tree
Hide file tree
Showing 5 changed files with 394 additions and 267 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"plsargs": "^0.1.6",
"pm2": "^5.4.2",
"pretty-ms": "^9.1.0",
"rainlink": "^1.0.7",
"rainlink": "^1.1.0",
"rainlink-apple": "^1.0.5",
"rainlink-deezer": "^1.0.8",
"rainlink-nico": "^1.0.7",
Expand Down
3 changes: 1 addition & 2 deletions src/commands/Music/Play.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Manager } from '../../manager.js'
import { Accessableby, Command } from '../../structures/Command.js'
import { AutocompleteInteractionChoices, GlobalInteraction } from '../../@types/Interaction.js'
import { CommandHandler } from '../../structures/CommandHandler.js'
import { RainlinkPlayer, RainlinkSearchResultType, RainlinkTrack } from 'rainlink'
import { RainlinkSearchResultType, RainlinkTrack } from 'rainlink'

export default class implements Command {
public name = ['play']
Expand Down Expand Up @@ -176,7 +176,6 @@ export default class implements Command {
return `[${tracks[0].title}](${value})`
} else {
return `[${tracks[0].title}](${tracks[0].uri})`
return `[${tracks[0].title}](${tracks[0].uri})`
}
}
}
Expand Down
177 changes: 143 additions & 34 deletions src/commands/Music/Radio.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { ActionRowBuilder, ApplicationCommandOptionType, ComponentType, EmbedBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder } from 'discord.js'
import {
ActionRowBuilder,
ApplicationCommandOptionType,
ComponentType,
EmbedBuilder,
StringSelectMenuBuilder,
StringSelectMenuOptionBuilder,
} from 'discord.js'
import { Manager } from '../../manager.js'
import { Accessableby, Command } from '../../structures/Command.js'
import { CommandHandler } from '../../structures/CommandHandler.js'
import { RadioStationNewInterface } from '../../utilities/RadioStations.js'
import { RadioStationNewInterface, RadioStationArray } from '../../utilities/RadioStations.js'
import { RainlinkSearchResultType, RainlinkTrack } from 'rainlink'
import { convertTime } from '../../utilities/ConvertTime.js'

// Main code
export default class implements Command {
Expand All @@ -19,21 +28,92 @@ export default class implements Command {
public permissions = []
public options = [
{
name: "number",
description: "The number of radio to choose the radio station",
name: 'number',
description: 'The number of radio to choose the radio station',
type: ApplicationCommandOptionType.Number,
required: false,
},
];
]

public async execute(client: Manager, handler: CommandHandler) {
await handler.deferReply()
let player = client.rainlink.players.get(handler.guild!.id)
const radioList = RadioStationNewInterface()
const radioArrayList = RadioStationArray()
const radioListKeys = Object.keys(radioList)

await handler.deferReply()

const getNum = handler.args[0] ? Number(handler.args[0]) : undefined
if (!getNum) return this.sendHelp(client, handler, radioList, radioListKeys)

const radioData = radioArrayList[getNum - 1]
if (!radioData) return this.sendHelp(client, handler, radioList, radioListKeys)


const { channel } = handler.member!.voice
if (!channel)
return handler.editReply({
embeds: [
new EmbedBuilder()
.setDescription(`${client.i18n.get(handler.language, 'error', 'no_in_voice')}`)
.setColor(client.color),
],
})

if (!player)
player = await client.rainlink.create({
guildId: handler.guild!.id,
voiceId: handler.member!.voice.channel!.id,
textId: handler.channel!.id,
shardId: handler.guild?.shardId ?? 0,
deaf: true,
volume: client.config.player.DEFAULT_VOLUME,
})
else if (player && !this.checkSameVoice(client, handler, handler.language)) {
return
}

player.textId = handler.channel!.id

const result = await player.search(radioData.link, { requester: handler.user })

if (!result.tracks.length)
return handler.editReply({
embeds: [
new EmbedBuilder()
.setDescription(`${client.i18n.get(handler.language, 'command.music', 'play_match')}`)
.setColor(client.color),
],
})
if (result.type === 'PLAYLIST') for (let track of result.tracks) player.queue.add(track)
else if (player.playing && result.type === 'SEARCH') player.queue.add(result.tracks[0])
else if (player.playing && result.type !== 'SEARCH')
for (let track of result.tracks) player.queue.add(track)
else player.queue.add(result.tracks[0])

if (handler.message) await handler.message.delete().catch(() => null)

if (!player.playing) player.play()
const embed = new EmbedBuilder().setColor(client.color).setDescription(
`${client.i18n.get(handler.language, 'command.music', 'play_result', {
title: this.getTitle(client, result.type, result.tracks),
duration: convertTime(result.tracks[0].duration as number),
request: String(result.tracks[0].requester),
})}`
)

handler.editReply({ content: ' ', embeds: [embed] })
}

protected async sendHelp(
client: Manager,
handler: CommandHandler,
radioList: Record<string, { no: number; name: string; link: string }[]>,
radioListKeys: string[]
) {
const pages: EmbedBuilder[] = []
for (let i = 0; i < radioListKeys.length; i++) {
const radioListKey = radioListKeys[i];
const radioListKey = radioListKeys[i]
const stringArray = radioList[radioListKey]
const converted = this.stringConverter(stringArray)

Expand All @@ -43,27 +123,28 @@ export default class implements Command {
iconURL: handler.user?.displayAvatarURL(),
})
.setColor(client.color)
.addFields(converted)
.addFields(converted)

pages.push(embed)
}

const providerSelector = (disable: boolean) => new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
new StringSelectMenuBuilder()
.setCustomId('provider')
.setPlaceholder('Choose a provider / country to get radio id list')
.addOptions(this.getOptionBuilder(radioListKeys))
.setDisabled(disable)
)
const providerSelector = (disable: boolean) =>
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
new StringSelectMenuBuilder()
.setCustomId('provider')
.setPlaceholder('Choose a provider / country to get radio id list')
.addOptions(this.getOptionBuilder(radioListKeys))
.setDisabled(disable)
)

const msg = await handler.editReply({
embeds: [pages[0]],
components: [providerSelector(false)]
components: [providerSelector(false)],
})

const collector = msg.createMessageComponentCollector({
componentType: ComponentType.StringSelect,
time: 60000
time: 45000,
})

collector.on('collect', async (message): Promise<void> => {
Expand All @@ -79,47 +160,75 @@ export default class implements Command {
const msgReply = await message
.reply({
embeds: [replyEmbed],
ephemeral: true
ephemeral: true,
})
.catch(() => {})
if (msgReply)
setTimeout(() => msgReply.delete().catch(() => {}), client.config.utilities.DELETE_MSG_TIMEOUT)
setTimeout(
() => msgReply.delete().catch(() => {}),
client.config.utilities.DELETE_MSG_TIMEOUT
)
})

collector.on('end', async () => {
// @ts-ignore
collector.removeAllListeners()
await msg.edit({
components: [providerSelector(false)]
components: [providerSelector(true)],
})
})
}

protected getOptionBuilder(radioListKeys: string[]) {
const result = []
for (let i = 0; i < radioListKeys.length; i++) {
const key = radioListKeys[i];
result.push(
new StringSelectMenuOptionBuilder()
.setLabel(key)
.setValue(String(i))
)
const key = radioListKeys[i]
result.push(new StringSelectMenuOptionBuilder().setLabel(key).setValue(String(i)))
}
return result
}

protected stringConverter(array: { no: number, name: string, link: string}[]) {
protected stringConverter(array: { no: number; name: string; link: string }[]) {
const radioStrings = []
for (let i = 0; i < array.length; i++) {
const radio = array[i]
radioStrings.push(
{
name: `**${String(radio.no).padEnd(3)}** ${radio.name}`,
value: " ",
inline: true
}
)
radioStrings.push({
name: `**${String(radio.no).padEnd(3)}** ${radio.name}`,
value: ' ',
inline: true,
})
}
return radioStrings
}

checkSameVoice(client: Manager, handler: CommandHandler, language: string) {
if (handler.member!.voice.channel !== handler.guild!.members.me!.voice.channel) {
handler.editReply({
embeds: [
new EmbedBuilder()
.setDescription(`${client.i18n.get(handler.language, 'error', 'no_same_voice')}`)
.setColor(client.color),
],
})
return false
}

return true
}

getTitle(
client: Manager,
type: RainlinkSearchResultType,
tracks: RainlinkTrack[],
value?: string
): string {
if (client.config.player.AVOID_SUSPEND) return tracks[0].title
else {
if (type === 'PLAYLIST') {
return `[${tracks[0].title}](${value})`
} else {
return `[${tracks[0].title}](${tracks[0].uri})`
}
}
}
}
2 changes: 0 additions & 2 deletions src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import {
GatewayIntentBits,
ColorResolvable,
Message,
ActionRowBuilder,
ButtonBuilder,
Collection as DJSCollection,
InteractionCollector,
ButtonInteraction,
Expand Down
Loading

0 comments on commit 002d55f

Please sign in to comment.