Réécriture complète en Typescript

This commit is contained in:
Angels-dev
2024-01-14 22:53:06 +01:00
parent 6ffa521888
commit 07d54abdba
108 changed files with 2477 additions and 1943 deletions

16
src/buttons/loop.ts Executable file
View File

@@ -0,0 +1,16 @@
import { ButtonInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
id: 'loop',
async execute(interaction: ButtonInteraction) {
let guild = interaction.guild
if (!guild) return
let queue = useQueue(guild.id)
if (!queue) return
let loop = queue.repeatMode === 0 ? 1 : queue.repeatMode === 1 ? 2 : queue.repeatMode === 2 ? 3 : 0
await queue.setRepeatMode(loop)
await interaction.followUp({ content:`Boucle ${loop === 0 ? 'désactivée' : loop === 1 ? 'en mode Titre' : loop === 2 ? 'en mode File d\'Attente' : 'en autoplay'}.`, ephemeral: true })
}
}

15
src/buttons/pause.ts Executable file
View File

@@ -0,0 +1,15 @@
import { ButtonInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
id: 'pause',
async execute(interaction: ButtonInteraction) {
let guild = interaction.guild
if (!guild) return
let queue = useQueue(guild.id)
if (!queue) return
await queue.node.setPaused(!queue.node.isPaused())
return interaction.followUp({ content: 'Musique mise en pause !', ephemeral: true })
}
}

15
src/buttons/previous.ts Executable file
View File

@@ -0,0 +1,15 @@
import { ButtonInteraction } from 'discord.js'
import { useHistory } from 'discord-player'
module.exports = {
id: 'previous',
async execute(interaction: ButtonInteraction) {
let guild = interaction.guild
if (!guild) return
let history = useHistory(guild.id)
if (!history) return
await history.previous()
return interaction.followUp({ content: 'Musique précédente jouée !', ephemeral: true })
}
}

15
src/buttons/resume.ts Executable file
View File

@@ -0,0 +1,15 @@
import { ButtonInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
id: 'resume',
async execute(interaction: ButtonInteraction) {
let guild = interaction.guild
if (!guild) return
let queue = useQueue(guild.id)
if (!queue) return
await queue.node.setPaused(!queue.node.isPaused())
return interaction.followUp({ content: 'Musique reprise !', ephemeral: true })
}
}

15
src/buttons/shuffle.ts Executable file
View File

@@ -0,0 +1,15 @@
import { ButtonInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
id: 'shuffle',
async execute(interaction: ButtonInteraction) {
let guild = interaction.guild
if (!guild) return
let queue = useQueue(guild.id)
if (!queue) return
await queue.tracks.shuffle()
return interaction.followUp({ content: 'File d\'attente mélangée !', ephemeral: true })
}
}

15
src/buttons/skip.ts Executable file
View File

@@ -0,0 +1,15 @@
import { ButtonInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
id: 'skip',
async execute(interaction: ButtonInteraction) {
let guild = interaction.guild
if (!guild) return
let queue = useQueue(guild.id)
if (!queue) return
await queue.node.skip()
return interaction.followUp({ content: 'Musique passée !', ephemeral: true })
}
}

15
src/buttons/stop.ts Executable file
View File

@@ -0,0 +1,15 @@
import { ButtonInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
id: 'stop',
async execute(interaction: ButtonInteraction) {
let guild = interaction.guild
if (!guild) return
let queue = useQueue(guild.id)
if (!queue) return
await queue.delete()
return interaction.followUp({ content: 'Musique arrêtée !', ephemeral: true })
}
}

16
src/buttons/volume_down.ts Executable file
View File

@@ -0,0 +1,16 @@
import { ButtonInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
id: 'volume_down',
async execute(interaction: ButtonInteraction) {
let guild = interaction.guild
if (!guild) return
let queue = useQueue(guild.id)
if (!queue) return
let volume = queue.node.volume - 10
await queue.node.setVolume(volume)
return interaction.followUp({ content: `🔉 | Volume modifié à ${volume}% !`, ephemeral: true })
}
}

16
src/buttons/volume_up.ts Executable file
View File

@@ -0,0 +1,16 @@
import { ButtonInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
id: 'volume_up',
async execute(interaction: ButtonInteraction) {
let guild = interaction.guild
if (!guild) return
let queue = useQueue(guild.id)
if (!queue) return
let volume = queue.node.volume + 10
await queue.node.setVolume(volume)
return interaction.followUp({ content: `🔊 | Volume modifié à ${volume}% !`, ephemeral: true })
}
}

198
src/commands/global/amp.ts Executable file
View File

@@ -0,0 +1,198 @@
import { SlashCommandBuilder, ChatInputCommandInteraction, AutocompleteInteraction, EmbedBuilder, inlineCode } from 'discord.js'
import * as AMP from '../../utils/amp'
import 'dotenv/config'
interface ListInstancesResult {
status: string
data: {
result: [
Host: {
AvailableInstances: any[]
FriendlyName: string
}
]
}
}
interface InstanceFields {
name: string
value: string
inline: boolean
}
interface failData {
Title: string
Message: string
}
interface errorData {
error_code: string
}
function failMsg(data: any) { return `La commande a échouée !\n${inlineCode(`${data.Title}: ${data.Message}`)}` }
function errorMsg(data: any) { return `Y'a eu une erreur !\n${inlineCode(`${data.error_code}`)}` }
module.exports = {
data: new SlashCommandBuilder().setName('amp').setDescription('Accède à mon panel de jeu AMP !')
.addSubcommand(subcommand => subcommand.setName('login').setDescription("Connectez-vous avant d'effectuer une autre commande !")
.addStringOption(option => option.setName('username').setDescription("Nom d'Utilisateur").setRequired(true))
.addStringOption(option => option.setName('password').setDescription('Mot de Passe').setRequired(true))
.addBooleanOption(option => option.setName('remember').setDescription('Mémoriser les identifiants').setRequired(true))
.addStringOption(option => option.setName('otp').setDescription('Code de double authentification')))
.addSubcommandGroup(subcommandgroup => subcommandgroup.setName('instances').setDescription('Intéragir avec les instances AMP.')
.addSubcommand(subcommand => subcommand.setName('list').setDescription('Liste toutes les instances disponibles.'))
.addSubcommand(subcommand => subcommand.setName('manage').setDescription('Gérer une instance.')
.addStringOption(option => option.setName('instance').setDescription("Nom de l'instance").setRequired(true).setAutocomplete(true)))
/*.addSubcommand(subcommand => subcommand.setName('restart').setDescription('Redémarre une instance.')
.addStringOption(option => option.setName('name').setDescription("Nom de l'instance").setRequired(true)))*/),
async autocompleteRun(interaction: AutocompleteInteraction) {
let query = interaction.options.getString('instance', true)
// Check if the SessionID is still valid
//let sessionID = dotenv.config().parsed.AMP_SESSIONID
let sessionID = process.env.AMP_SESSIONID as string
let session = await AMP.CheckSession(sessionID)
if (session.status === 'fail') {
if (process.env.AMP_REMEMBER_TOKEN) {
// Refresh the SessionID if the RememberMeToken is available
let details = {
username: process.env.AMP_USERNAME,
password: '',
token: process.env.AMP_REMEMBER_TOKEN,
rememberMe: true
}
let result = await AMP.Core.Login(details)
console.log(result)
if (result.status === 'success') sessionID = result.data.sessionID
else if (result.status === 'fail') return interaction.respond([])
else if (result.status === 'error') return interaction.respond([])
}
else {
// If no RememberMeToken is available, ask to login
//return await interaction.followUp(`Tu dois te connecter avant d'effectuer une autre commande !`)
return await interaction.respond([])
}
}
else if (session.status === 'error') return interaction.respond([])
let choices: any = []
let result = await AMP.ADSModule.GetInstances(sessionID)
if (result.status === 'success') {
let hosts = result.data.result as any[]
hosts.forEach(host => {
let instances = host.AvailableInstances as any[]
instances.forEach(instance => {
if (instance.FriendlyName.includes(query)) choices.push({ name: `${host.FriendlyName} - ${instance.FriendlyName}`, value: instance.InstanceID })
})
})
}
else if (result.status === 'fail') return interaction.respond([])
else if (result.status === 'error') return interaction.respond([])
return interaction.respond(choices)
},
async execute(interaction: ChatInputCommandInteraction) {
// Let the user login
if (interaction.options.getSubcommand() == 'login') {
// Get a SessionID and a RememberMeToken if wanted
await interaction.deferReply({ ephemeral: true })
let details = {
username: interaction.options.getString('username'),
password: interaction.options.getString('password'),
token: interaction.options.getString('otp') || '',
rememberMe: interaction.options.getBoolean('remember')
}
let result = await AMP.Core.Login(details)
if (result.status === 'success') return await interaction.followUp(`Tu es connecté au panel sous **${result.data.userInfo.Username}** !`)
else if (result.status === 'fail') return await interaction.followUp(failMsg(result.data))
else if (result.status === 'error') return await interaction.followUp(errorMsg(result.data))
}
await interaction.deferReply()
// Check if the SessionID is still valid
//let sessionID = dotenv.config().parsed.AMP_SESSIONID
let sessionID = process.env.AMP_SESSIONID as string
let session = await AMP.CheckSession(sessionID)
if (session.status === 'fail') {
console.log(session)
if (process.env.AMP_REMEMBER_TOKEN) {
// Refresh the SessionID if the RememberMeToken is available
let details = {
username: process.env.AMP_USERNAME,
password: '',
token: process.env.AMP_REMEMBER_TOKEN,
rememberMe: true
}
let result = await AMP.Core.Login(details)
console.log(result)
if (result.status === 'success') sessionID = result.data.sessionID
else if (result.status === 'fail') return await interaction.followUp(failMsg(result.data))
else if (result.status === 'error') return await interaction.followUp(errorMsg(result.data))
}
else {
// If no RememberMeToken is available, ask to login
return await interaction.followUp(`Tu dois te connecter avant d'effectuer une autre commande !`)
}
}
else if (session.status === 'error') return await interaction.followUp(errorMsg(session.data))
if (interaction.options.getSubcommandGroup() == 'instances') {
if (interaction.options.getSubcommand() == 'list') {
let result = await AMP.ADSModule.GetInstances(sessionID) as ListInstancesResult
if (result.status === 'success') {
await interaction.followUp({ content: `${result.data.result.length} hôtes trouvés !` })
result.data.result.forEach(async host => {
let fields = [] as InstanceFields[]
host.AvailableInstances.forEach(instance => {
fields.push({
name: instance.FriendlyName,
value: `**Running:** ${instance.Running}\n**Port:** ${instance.Port}\n**Module:** ${instance.Module}`,
inline: true
})
})
let embed = new EmbedBuilder()
.setTitle(host.FriendlyName)
.setDescription(`Liste des ${host.AvailableInstances.length} instances :`)
.setColor(interaction.guild?.members.me?.displayColor || '#ffc370')
.setTimestamp()
.setFields(fields)
return await interaction.channel?.send({ embeds: [embed] })
})
}
else if (result.status === 'fail') return await interaction.followUp(failMsg(result.data))
else if (result.status === 'error') return await interaction.followUp(errorMsg(result.data))
}
else if (interaction.options.getSubcommand() == 'manage') {
let instanceID = interaction.options.getString('instance', true)
let result = await AMP.ADSModule.ManageInstance(sessionID, instanceID)
if (result.status === 'success') {
let server = await AMP.ADSModule.Servers(sessionID, instanceID)
console.log(server)
if (server.status === 'success') return await interaction.followUp(`Ok !`)
else if (server.status === 'fail') return await interaction.followUp(failMsg(server.data))
else if (server.status === 'error') return await interaction.followUp(errorMsg(server.data))
}
else if (result.status === 'fail') return await interaction.followUp(failMsg(result.data))
else if (result.status === 'error') return await interaction.followUp(errorMsg(result.data))
}
else if (interaction.options.getSubcommand() == 'restart') {
let query = interaction.options.getString('name')
if (!query) return
let result = await AMP.ADSModule.RestartInstance(sessionID, query)
if (result.status === 'success') return await interaction.followUp(`Ok !`)
else if (result.status === 'fail') return await interaction.followUp(failMsg(result.data))
else if (result.status === 'error') return await interaction.followUp(errorMsg(result.data))
}
}
}
}

58
src/commands/global/crack.ts Executable file
View File

@@ -0,0 +1,58 @@
import { SlashCommandBuilder, EmbedBuilder, ChatInputCommandInteraction, MessageReaction, User }from 'discord.js'
import * as crack from '../../utils/crack'
module.exports = {
data: new SlashCommandBuilder().setName('crack').setDescription('Télécharge un crack sur le site online-fix.me !')
.addStringOption(option => option.setName('jeu').setDescription('Quel jeu tu veux DL ?').setRequired(true)),
async execute(interaction: ChatInputCommandInteraction) {
await interaction.deferReply()
let query = interaction.options.getString('jeu')
if (!query) return
let games = await crack.search(query) as crack.Game[]
if (!Array.isArray(games)) {
//if (games.toString() == "TypeError: Cannot read properties of undefined (reading 'split')") return interaction.followUp({ content: `J'ai rien trouvé pour "${query}" !` })
//else return interaction.followUp({ content: "Une erreur s'est produite ! ```" + games + "```" })
return interaction.followUp({ content: `J'ai rien trouvé pour "${query}" !` })
}
let game = {} as crack.Game
if (games.length > 1) {
games = games.slice(0, 9)
let list = ''
for (let i = 0; i < games.length; i++) list += `\n${i + 1}. ${games[i].name} (${games[i].link})`
let message = await interaction.followUp({ content: `J'ai trouvé plusieurs jeux pour "${query}" ! ${list}` })
let emojis = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣']
for (let i = 0; i < games.length; i++) await message.react(emojis[i])
// Wait for a reaction to be added by the interaction author.
const filter: any = (reaction: MessageReaction, user: User) => { if (reaction.emoji.name) return emojis.includes(reaction.emoji.name) && user.id === interaction.user.id }
await message.awaitReactions({ filter, max: 1, time: 5000, errors: ['time'] }).then(collected => {
console.log(collected)
if (!collected.first) return
let reaction = collected.first()
let index = emojis.indexOf(reaction?.emoji.name ?? '')
game = games[index]
}).catch(() => { return interaction.followUp({ content: 'T\'as mis trop de temps à choisir !' }) })
}
else game = games[0]
let url = await crack.repo(game)
if (!url) return
let file = await crack.torrent(url)
if (!file) return
let filePath = await crack.download(url, file)
if (!filePath) return
let link = await crack.magnet(filePath)
let embed = new EmbedBuilder()
.setColor('#ffc370')
.setTitle(game.name)
.setURL(game.link)
.setDescription(`Voici ce que j'ai trouvé pour "${query}".\nTu peux aussi cliquer sur [ce lien](https://angels-dev.fr/magnet/${link}) pour pouvoir télécharger le jeu direct !`)
await interaction.followUp({ embeds: [embed], files: [filePath] })
}
}

34
src/commands/global/papa.ts Executable file
View File

@@ -0,0 +1,34 @@
import { SlashCommandBuilder, ChatInputCommandInteraction, GuildMember } from 'discord.js'
import { getVoiceConnection, joinVoiceChannel } from '@discordjs/voice'
module.exports = {
data: new SlashCommandBuilder()
.setName('papa')
.setDescription('Si papa m\'appelle, je le rejoins !'),
async execute(interaction: ChatInputCommandInteraction) {
if (interaction.user.id !== '223831938346123275') return interaction.reply({ content: 'T\'es pas mon père, dégage !' })
let guild = interaction.guild
if (!guild) return interaction.reply({ content: 'Je ne peux pas rejoindre ton vocal en message privé, papa !' })
let member = interaction.member as GuildMember
let botChannel = guild.members.me?.voice.channel
let papaChannel = member.voice.channel
if (!papaChannel && botChannel) {
const voiceConnection = getVoiceConnection(guild.id);
if (voiceConnection) voiceConnection.destroy()
return interaction.reply({ content: 'Je quitte le vocal, papa !' })
}
else if (papaChannel && (!botChannel || botChannel.id !== papaChannel.id)) {
joinVoiceChannel({
channelId: papaChannel.id,
guildId: papaChannel.guild.id,
adapterCreator: papaChannel.guild.voiceAdapterCreator,
})
return interaction.reply({ content: 'Je rejoins ton vocal, papa !' })
}
else return interaction.reply({ content: 'Je suis déjà dans ton vocal, papa !' })
}
}

79
src/commands/global/parle.ts Executable file
View File

@@ -0,0 +1,79 @@
import { SlashCommandBuilder, ChatInputCommandInteraction, GuildMember } from 'discord.js'
import { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, EndBehaviorType } from '@discordjs/voice'
module.exports = {
data: new SlashCommandBuilder()
.setName('parle')
.setDescription('Fais moi parler par dessus quelqu\'un de chiant dans le vocal')
.addUserOption(option => option.setName('user').setDescription('La personne en question').setRequired(true)),
async execute(interaction: ChatInputCommandInteraction) {
if (interaction.user.id !== '223831938346123275') return await interaction.reply({ content: 'Tu n\'as pas le droit d\'utiliser cette commande !', ephemeral: true })
let user = interaction.options.getUser('user')
if (!user) return
let guild = interaction.guild
if (!guild) return
let member = guild.members.cache.get(user.id) as GuildMember
if (!member) return
let caller = interaction.member as GuildMember
if (!caller) return
if (!caller.voice.channel) return await interaction.reply({ content: 'You must be in a voice channel to use this command.', ephemeral: true })
if (!member.voice.channel) return await interaction.reply({ content: 'The member must be in a voice channel to use this command.', ephemeral: true })
if (caller.voice.channelId !== member.voice.channelId) return await interaction.reply({ content: 'You must be in the same voice channel than the member to use this command.', ephemeral: true })
await interaction.reply({ content: 'Je vais parler par dessus cette personne !', ephemeral: true })
/*
// Searches for audio files uploaded in the channel
let messages = await interaction.channel.messages.fetch({ limit: 10, cache: false })
messages = messages.filter(m => m.attachments.size > 0)
let files = []
await messages.forEach(m => m.attachments.forEach(a => {
if (a.contentType === 'audio/mpeg') files.push(a)
}))
if (files.size === 0) return await interaction.editReply({ content: 'Aucun fichier audio trouvé dans ce channel.', ephemeral: true })
// Limit the number of files to the last 10
//files = files.sort((a, b) => b.createdTimestamp - a.createdTimestamp).first(10)
// Ask the user to choose a file
let file = await interaction.channel.send({ content: 'Choisissez un fichier audio :', files: files })
let filter = m => m.author.id === interaction.user.id && !isNaN(m.content) && parseInt(m.content) > 0 && parseInt(m.content) <= files.size
let response = await interaction.channel.awaitMessages({ filter, max: 1, time: 30000, errors: ['time'] })
file = files.get(files.keyArray()[response.first().content - 1])
*/
let playing = false
let player = createAudioPlayer()
player.on(AudioPlayerStatus.Idle, () => { playing = false })
let connection = joinVoiceChannel({
channelId: caller.voice.channelId as string,
guildId: interaction.guildId as string,
adapterCreator: guild.voiceAdapterCreator,
selfDeaf: false
})
connection.subscribe(player)
let stream = connection.receiver.subscribe(user.id, { end: { behavior: EndBehaviorType.Manual } })
stream.on('data', (chunk) => {
if (!user) return
if (connection.receiver.speaking.users.has(user.id) && !playing) {
playing = true
let resource = createAudioResource('../../static/parle.mp3', { inlineVolume: true })
//let resource = createAudioResource(file.attachments.first().url, { inlineVolume: true })
if (resource.volume) resource.volume.setVolume(0.2)
player.play(resource)
}
})
interaction.client.on('voiceStateUpdate', (oldState, newState) => {
if (oldState.id === member.id && newState.channelId !== caller.voice.channelId) {
stream.destroy()
connection.disconnect()
}
})
}
}

11
src/commands/global/ping.ts Executable file
View File

@@ -0,0 +1,11 @@
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Check the latency of the bot'),
async execute(interaction: ChatInputCommandInteraction) {
let sent = await interaction.reply({ content: 'Pinging...', fetchReply: true })
interaction.editReply(`Websocket heartbeat: ${interaction.client.ws.ping}ms.\nRoundtrip latency: ${sent.createdTimestamp - interaction.createdTimestamp}ms`)
}
}

29
src/commands/global/spam.ts Executable file
View File

@@ -0,0 +1,29 @@
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'
module.exports = {
data: new SlashCommandBuilder()
.setName('spam')
.setDescription('Spam')
.addUserOption(option => option.setName('user').setDescription('Spam').setRequired(true))
.addStringOption(option => option.setName('string').setDescription('Spam').setRequired(true))
.addIntegerOption(option => option.setName('integer').setDescription('Spam').setRequired(true)),
async execute(interaction: ChatInputCommandInteraction) {
let user = interaction.options.getUser('user')
let string = interaction.options.getString('string')
let integer = interaction.options.getInteger('integer')
await interaction.reply({ content: 'Spam', ephemeral: true })
let i = 0
function myLoop() {
setTimeout(function () {
if (!user) return
if (!string) return
if (!integer) return
user.send(string).catch(error => console.error(error))
i++
if (i < integer) myLoop()
}, 1000)
}
myLoop()
}
}

19
src/commands/global/update.ts Executable file
View File

@@ -0,0 +1,19 @@
import { SlashCommandBuilder, ChatInputCommandInteraction, Guild } from 'discord.js'
module.exports = {
data: new SlashCommandBuilder()
.setName('update')
.setDescription('Update the member count channel.'),
async execute(interaction: ChatInputCommandInteraction) {
let guild = interaction.guild as Guild
guild.members.fetch().then(() => {
var i = 0
guild.members.cache.forEach(async member => { if (!member.user.bot) i++ })
let channel = guild.channels.cache.get('1091140609139560508')
if (!channel) return
channel.setName(`${i} Gens Posés`)
interaction.reply(`${i} Gens Posés !`)
}).catch(console.error)
}
}

21
src/commands/player/loop.ts Executable file
View File

@@ -0,0 +1,21 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js'
import { useQueue } from'discord-player'
module.exports = {
data: new SlashCommandBuilder()
.setName('loop')
.setDescription('Boucler la musique en cours de lecture.')
.addIntegerOption(option => option.setName('loop')
.setDescription('Mode de boucle (0 = Off, 1 = Titre, 2 = File d\'Attente; 3 = Autoplay)')
.setRequired(true)
.setMinValue(0)
.setMaxValue(3)),
async execute(interaction: ChatInputCommandInteraction) {
let loop = interaction.options.getInteger('loop')
let queue = useQueue(interaction.guild?.id ?? '')
if (!queue) return interaction.followUp({ content: 'Aucune file d\'attente en cours, recherche une musique plutôt !' })
queue.setRepeatMode(loop as number)
return await interaction.reply(`Boucle ${loop === 0 ? 'désactivée' : loop === 1 ? 'en mode Titre' : loop === 2 ? 'en mode File d\'Attente' : 'en autoplay'}.`)
}
}

45
src/commands/player/lyrics.ts Executable file
View File

@@ -0,0 +1,45 @@
import { ChatInputCommandInteraction, SlashCommandBuilder, EmbedBuilder } from 'discord.js'
import { useQueue } from 'discord-player'
import { lyricsExtractor } from '@discord-player/extractor'
module.exports = {
data: new SlashCommandBuilder()
.setName('lyrics')
.setDescription('Rechercher les paroles d\'une musique.')
.addStringOption(option => option.setName('recherche').setDescription('Chercher une musique spécifique')),
async execute(interaction: ChatInputCommandInteraction) {
await interaction.deferReply()
let query = interaction.options.getString('recherche', false)
if (!query) {
let queue = useQueue(interaction.guild?.id ?? '')
if (!queue) return interaction.followUp({ content: 'Aucune file d\'attente en cours, recherche une musique plutôt !' })
let track = queue.currentTrack
if (!track) return interaction.followUp({ content: 'Aucune musique en cours, recherche en une plutôt !' })
if (track.raw.source === 'spotify') query = `${track.author} ${track.title}`
else query = track.title
}
let lyricsFinder = lyricsExtractor()
let lyrics = await lyricsFinder.search(query).catch(() => null)
if (!lyrics) return interaction.followUp({ content: 'Pas de paroles trouvées !' })
let trimmedLyrics = lyrics.lyrics.substring(0, 1997)
let embed = new EmbedBuilder()
.setColor('#ffc370')
.setTitle(lyrics.title)
.setURL(lyrics.url)
.setThumbnail(lyrics.thumbnail)
.setAuthor({
name: lyrics.artist.name,
iconURL: lyrics.artist.image,
url: lyrics.artist.url
})
.setDescription(trimmedLyrics.length === 1997 ? `${trimmedLyrics}...` : trimmedLyrics)
return interaction.followUp({ embeds: [embed] })
}
}

15
src/commands/player/pause.ts Executable file
View File

@@ -0,0 +1,15 @@
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
data: new SlashCommandBuilder()
.setName('pause')
.setDescription('Met en pause la musique.'),
async execute(interaction: ChatInputCommandInteraction) {
let queue = useQueue(interaction.guild?.id ?? '')
if (!queue) return interaction.followUp({ content: 'Aucune file d\'attente en cours, recherche une musique plutôt !' })
queue.node.setPaused(!queue.node.isPaused())
return await interaction.reply('Musique mise en pause !')
}
}

91
src/commands/player/play.ts Executable file
View File

@@ -0,0 +1,91 @@
import { SlashCommandBuilder, ChatInputCommandInteraction, AutocompleteInteraction, GuildMember } from 'discord.js'
import { useMainPlayer, useQueue, QueryType } from 'discord-player'
import writeEnv from '../../utils/writeEnv'
export interface TrackSearchResult { name: string, value: string }
module.exports = {
data: new SlashCommandBuilder()
.setName('play')
.setDescription('Jouer une musique.')
.addStringOption(option => option.setName('recherche').setDescription('Titre de la musique à chercher').setRequired(true).setAutocomplete(true)),
async autocompleteRun(interaction: AutocompleteInteraction) {
let query = interaction.options.getString('recherche', true)
if (!query) return interaction.respond([])
let player = useMainPlayer()
const resultsYouTube = await player.search(query, { searchEngine: QueryType.YOUTUBE })
const resultsSpotify = await player.search(query, { searchEngine: QueryType.SPOTIFY_SEARCH })
const tracksYouTube = resultsYouTube.tracks.slice(0, 5).map((t) => ({
name: `YouTube: ${`${t.title} - ${t.author} (${t.duration})`.length > 75 ? `${`${t.title} - ${t.author}`.substring(0, 75)}... (${t.duration})` : `${t.title} - ${t.author} (${t.duration})`}`,
value: t.url
}))
const tracksSpotify = resultsSpotify.tracks.slice(0, 5).map((t) => ({
name: `Spotify: ${`${t.title} - ${t.author} (${t.duration})`.length > 75 ? `${`${t.title} - ${t.author}`.substring(0, 75)}... (${t.duration})` : `${t.title} - ${t.author} (${t.duration})`}`,
value: t.url
}))
const tracks: TrackSearchResult[] = []
tracksYouTube.forEach((t) => tracks.push({ name: t.name, value: t.value }))
tracksSpotify.forEach((t) => tracks.push({ name: t.name, value: t.value }))
return interaction.respond(tracks)
},
async execute(interaction: ChatInputCommandInteraction) {
let member = interaction.member as GuildMember
let voiceChannel = member.voice.channel
if (!voiceChannel) return await interaction.reply({ content: 'T\'es pas dans un vocal, idiot !', ephemeral: true })
let botChannel = interaction.guild?.members.me?.voice.channel
if (botChannel && voiceChannel.id !== botChannel.id) return await interaction.reply({ content: 'T\'es pas dans mon vocal !', ephemeral: true })
await interaction.deferReply()
let query = interaction.options.getString('recherche', true)
let player = useMainPlayer()
let queue = useQueue(interaction.guild?.id ?? '')
if (!queue) {
if (interaction.guild) queue = player.nodes.create(interaction.guild, {
metadata: {
channel: interaction.channel,
client: interaction.guild.members.me,
requestedBy: interaction.user
},
selfDeaf: true,
volume: 20,
leaveOnEmpty: true,
leaveOnEmptyCooldown: 30000,
leaveOnEnd: true,
leaveOnEndCooldown: 300000
})
else return
}
try { if (!queue.connection) await queue.connect(voiceChannel) }
catch (error: any) { console.error(error); return interaction.followUp(`Y'a eu un problème, <@223831938346123275> ! (${error.message})`) }
// Write the values in the .env file to recover the player if the bot restarts
writeEnv('DISCORD_MUSIC_TEXTCHANNEL_ID', interaction.channel?.id ?? '')
writeEnv('DISCORD_MUSIC_VOICECHANNEL_ID', voiceChannel.id)
// Search the song
let result = await player.search(query, { requestedBy: interaction.user })
if (!result.hasTracks()) return interaction.followUp(`Aucune musique trouvée pour **${query}** !`)
let track = result.tracks[0]
console.log(track.duration)
console.log(track.durationMS)
let entry = queue.tasksQueue.acquire()
await entry.getTask()
queue.addTrack(track)
try {
if (!queue.isPlaying()) await queue.node.play()
let track_source = track.source === 'youtube' ? 'Youtube' : track.source === 'spotify' ? 'Spotify' : 'Inconnu'
return interaction.followUp(`Chargement de la musique **${track.title}** de **${track.author}** sur **${track_source}**...`)
} catch (error: any) { console.error(error); return interaction.followUp(`Y'a eu un problème, <@223831938346123275> ! (${error.message})`) }
finally { queue.tasksQueue.release() }
}
}

15
src/commands/player/previous.ts Executable file
View File

@@ -0,0 +1,15 @@
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'
import { useHistory } from 'discord-player'
module.exports = {
data: new SlashCommandBuilder()
.setName('previous')
.setDescription('Joue la musique précédente.'),
async execute(interaction: ChatInputCommandInteraction) {
let history = useHistory(interaction.guild?.id ?? '')
if (!history) return await interaction.reply('Il n\'y a pas d\'historique de musique !')
await history.previous()
return await interaction.reply('Musique précédente jouée !')
}
}

15
src/commands/player/resume.ts Executable file
View File

@@ -0,0 +1,15 @@
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
data: new SlashCommandBuilder()
.setName('resume')
.setDescription('Reprendre la musique.'),
async execute(interaction: ChatInputCommandInteraction) {
let queue = useQueue(interaction.guild?.id ?? '')
if (!queue) return interaction.followUp({ content: 'Aucune file d\'attente en cours, recherche une musique plutôt !' })
queue.node.setPaused(!queue.node.isPaused())
return await interaction.reply('Musique reprise !')
}
}

15
src/commands/player/shuffle.ts Executable file
View File

@@ -0,0 +1,15 @@
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
data: new SlashCommandBuilder()
.setName('shuffle')
.setDescription('Mélange la file d\'attente.'),
async execute(interaction: ChatInputCommandInteraction) {
let queue = useQueue(interaction.guild?.id ?? '')
if (!queue) return interaction.followUp({ content: 'Aucune file d\'attente en cours, recherche une musique plutôt !' })
queue.tracks.shuffle()
return await interaction.reply('File d\'attente mélangée !')
}
}

15
src/commands/player/skip.ts Executable file
View File

@@ -0,0 +1,15 @@
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
data: new SlashCommandBuilder()
.setName('skip')
.setDescription('Passer la musique en cours.'),
async execute(interaction: ChatInputCommandInteraction) {
let queue = useQueue(interaction.guild?.id ?? '')
if (!queue) return interaction.followUp({ content: 'Aucune file d\'attente en cours, recherche une musique plutôt !' })
queue.node.skip()
return await interaction.reply('Musique passée !')
}
}

21
src/commands/player/stop.ts Executable file
View File

@@ -0,0 +1,21 @@
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
import writeEnv from '../../utils/writeEnv'
module.exports = {
data: new SlashCommandBuilder()
.setName('stop')
.setDescription('Arrêter la musique.'),
async execute(interaction: ChatInputCommandInteraction) {
let queue = useQueue(interaction.guild?.id ?? '')
if (!queue) return interaction.followUp({ content: 'Aucune file d\'attente en cours, recherche une musique plutôt !' })
queue.delete()
writeEnv('DISCORD_MUSIC_TEXTCHANNEL_ID', '')
writeEnv('DISCORD_MUSIC_VOICECHANNEL_ID', '')
writeEnv('DISCORD_MUSIC_CURRENT_TRACK', '')
writeEnv('DISCORD_MUSIC_CURRENT_PROGRESS', '')
return await interaction.reply('Musique arrêtée !')
}
}

21
src/commands/player/volume.ts Executable file
View File

@@ -0,0 +1,21 @@
import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js'
import { useQueue } from 'discord-player'
module.exports = {
data: new SlashCommandBuilder()
.setName('volume')
.setDescription('Modifie le volume de la musique.')
.addIntegerOption(option => option.setName('volume')
.setDescription('Le volume à mettre (%)')
.setRequired(true)
.setMinValue(1)
.setMaxValue(100)),
async execute(interaction: ChatInputCommandInteraction) {
let volume = interaction.options.getInteger('volume')
let queue = useQueue(interaction.guild?.id ?? '')
if (!queue) return interaction.followUp({ content: 'Aucune file d\'attente en cours, recherche une musique plutôt !' })
queue.node.setVolume(volume as number)
return await interaction.reply(`Volume modifié à ${volume}% !`)
}
}

8
src/events/error.ts Executable file
View File

@@ -0,0 +1,8 @@
import { Events } from 'discord.js'
module.exports = {
name: Events.Error,
execute(error: any) {
console.error(error)
}
}

19
src/events/guildMemberAdd.ts Executable file
View File

@@ -0,0 +1,19 @@
import { Events, GuildMember } from 'discord.js'
module.exports = {
name: Events.GuildMemberAdd,
async execute(member: GuildMember) {
if (member.guild.id !== '1086577543651524699') return
member.guild.members.fetch().then(() => {
var i = 0
member.guild.members.cache.forEach(async member => { if (!member.user.bot) i++ })
let channel = member.guild.channels.cache.get('1091140609139560508')
if (!channel) return
console.log(channel.name)
console.log(`${i} Gens Posés`)
channel.setName('Changement...')
channel.setName(`${i} Gens Posés`)
}).catch(console.error)
}
}

19
src/events/guildMemberRemove.ts Executable file
View File

@@ -0,0 +1,19 @@
import { Events, GuildMember } from 'discord.js'
module.exports = {
name: Events.GuildMemberRemove,
async execute(member: GuildMember) {
if (member.guild.id !== '1086577543651524699') return
member.guild.members.fetch().then(() => {
var i = 0
member.guild.members.cache.forEach(async member => { if (!member.user.bot) i++ })
let channel = member.guild.channels.cache.get('1091140609139560508')
if (!channel) return
console.log(channel.name)
console.log(`${i} Gens Posés`)
channel.setName('Changement...')
channel.setName(`${i} Gens Posés`)
}).catch(console.error)
}
}

43
src/events/interactionCreate.ts Executable file
View File

@@ -0,0 +1,43 @@
import { Events, Interaction, ChatInputCommandInteraction, AutocompleteInteraction, ButtonInteraction } from 'discord.js'
import editPlayer from '../utilsPlayer/edit'
import playerButtons from '../utilsPlayer/buttons'
module.exports = {
name: Events.InteractionCreate,
async execute(interaction: Interaction) {
//if (!interaction.isAutocomplete() && !interaction.isChatInputCommand() && !interaction.isButton()) return console.error(`Interaction ${interaction.commandName} is not a command.`)
if (interaction.isChatInputCommand()) {
interaction = interaction as ChatInputCommandInteraction
let chatInputCommand = interaction.client.commands.get(interaction.commandName)
if (!chatInputCommand) return console.error(`No chat input command matching ${interaction.commandName} was found.`)
console.log(`Command '${interaction.commandName}' launched by ${interaction.user.tag}`)
try { await chatInputCommand.execute(interaction) }
catch (error) { console.error(`Error executing ${interaction.commandName}:`, error) }
}
else if (interaction.isAutocomplete()) {
interaction = interaction as AutocompleteInteraction
let autoCompleteRun = interaction.client.commands.get(interaction.commandName)
if (!autoCompleteRun) return console.error(`No autoCompleteRun matching ${interaction.commandName} was found.`)
console.log(`AutoCompleteRun '${interaction.commandName}' launched by ${interaction.user.tag}`)
try { await autoCompleteRun.autocompleteRun(interaction) }
catch (error) { console.error(`Error autocompleting ${interaction.commandName}:`, error) }
}
else if (interaction.isButton()) {
interaction = interaction as ButtonInteraction
let button = interaction.client.buttons.get(interaction.customId)
if (!button) return console.error(`No button id matching ${interaction.customId} was found.`)
console.log(`Button '${interaction.customId}' clicked by ${interaction.user.tag}`)
if (playerButtons.includes(interaction.customId)) { await editPlayer(interaction) }
try { await button.execute(interaction) }
catch (error) { console.error(`Error clicking ${interaction.customId}:`, error) }
}
}
}

30
src/events/ready.ts Executable file
View File

@@ -0,0 +1,30 @@
import { Events, Client, ActivityType } from 'discord.js'
import { useMainPlayer } from 'discord-player'
import replay from '../utilsPlayer/replay'
import disco from '../utilsPlayer/disco'
import 'dotenv/config'
declare module "discord.js" {
export interface Client {
disco: { interval: NodeJS.Timeout }
}
}
module.exports = {
name: Events.ClientReady,
once: true,
async execute(client: Client) {
console.log(`Ready! Logged in as ${client.user?.tag ?? 'unknown'}`)
client.user?.setActivity('some bangers...', { type: ActivityType.Listening })
await useMainPlayer().extractors.loadDefault(ext => ext === 'YouTubeExtractor' || ext === 'SpotifyExtractor').then(() => console.log('YouTube and Spotify extractors loaded.')).catch(console.error)
if (process.env.DISCORD_MUSIC_CURRENT_TRACK) await replay(client)
client.disco = { interval: {} as NodeJS.Timeout }
client.disco.interval = setInterval(async () => {
let state = await disco(client)
if (state === 'clear') clearInterval(client.disco.interval)
}, 2000)
}
}

59
src/events/voiceStateUpdate.ts Executable file
View File

@@ -0,0 +1,59 @@
import { Events, VoiceState } from 'discord.js'
module.exports = {
name: Events.VoiceStateUpdate,
async execute(oldState: VoiceState, newState: VoiceState) {
/*
let oldMute = oldState.serverMute
let newMute = newState.serverMute
let oldDeaf = oldState.serverDeaf
let newDeaf = newState.serverDeaf
let oldChannel = oldState.channelId
let newChannel = newState.channelId
console.log(oldChannel)
console.log(newChannel)
let guild = newState.guild
let member = newState.member
let channel = guild.channels.cache.get('1076215868863819848')
let angels = guild.members.cache.get('223831938346123275')
if (oldChannel !== newChannel) {
let executor = await logMoveOrKick('channel_id')
//if (!executor) channel.send(`Impossible de savoir qui a déplacé <@${member.id}> !`)
//else if (member.id === executor.id) channel.send(`<@${member.id}> s'est déplacé lui-même le con...`)
//else {
// channel.send(`<@${member.id}> a été mis en sourdine par <@${executor.id}> !`)
//}
} else if (!oldMute && newMute) {
let executor = await logMuteOrDeaf('mute')
if (!executor) channel.send(`Impossible de savoir qui a muté <@${member.id}> !`)
else if (member.id === executor.id) channel.send(`<@${member.id}> s'est muté lui-même le con...`)
else {
channel.send(`<@${member.id}> a été muté par <@${executor.id}> !`)
}
} else if (!oldDeaf && newDeaf) {
let executor = await logMuteOrDeaf('deaf')
if (!executor) channel.send(`Impossible de savoir qui a mis en sourdine <@${member.id}> !`)
else if (member.id === executor.id) channel.send(`<@${member.id}> s'est mis en sourdine lui-même le con...`)
else {
channel.send(`<@${member.id}> a été mis en sourdine par <@${executor.id}> !`)
}
}
async function logMoveOrKick() {
let auditLogs = await guild.fetchAuditLogs({ limit: 1, type: AuditLogEvent.MemberMove })
console.log(auditLogs.entries.find(entry => { return entry }))
let log = await auditLogs.entries.find(entry => { return entry.extra.channel.id === newChannel })
console.log(log)
if (!log) return undefined
let executor = await guild.members.cache.get(log.executor.id)
return executor
}
async function logMuteOrDeaf(type) {
let auditLogs = await guild.fetchAuditLogs({ limit: 1, type: AuditLogEvent.MemberUpdate })
let log = await auditLogs.entries.find(entry => { return entry.target.id === member.id && entry.changes[0].key === type && entry.changes[0].new === true })
if (!log) return undefined
let executor = await guild.members.cache.get(log.executor.id)
return executor
}
*/
}
}

View File

@@ -0,0 +1,10 @@
import { GuildQueue, Track } from 'discord-player'
import { PlayerMetadata } from '../utilsPlayer/metadata'
export default {
name: 'audioTrackAdd',
async execute(queue: GuildQueue<PlayerMetadata>, track: Track) {
// Emitted when the player adds a single song to its queue
queue.metadata.channel.send(`Musique **${track.title}** de **${track.author}** ajoutée à la file d'attente !`)
}
}

View File

@@ -0,0 +1,10 @@
import { GuildQueue, Track } from 'discord-player'
import { PlayerMetadata } from '../utilsPlayer/metadata'
export default {
name: 'audioTracksAdd',
async execute(queue: GuildQueue<PlayerMetadata>, track: Array<Track>) {
// Emitted when the player adds multiple songs to its queue
queue.metadata.channel.send(`Ajout de ${track.length} musiques à la file d'attente !`)
}
}

10
src/eventsPlayer/debug.ts Executable file
View File

@@ -0,0 +1,10 @@
import { GuildQueue } from 'discord-player'
export default {
name: 'debug',
async execute(queue: GuildQueue, message: string) {
// Emitted when the player queue sends debug info
// Useful for seeing what state the current queue is at
console.log(`Player debug event: ${message}`)
}
}

16
src/eventsPlayer/disconnect.ts Executable file
View File

@@ -0,0 +1,16 @@
import { GuildQueue, Track } from 'discord-player'
import { PlayerMetadata } from '../utilsPlayer/metadata'
import writeEnv from '../utils/writeEnv'
export default {
name: 'disconnect',
async execute(queue: GuildQueue<PlayerMetadata>, track: Track) {
// Emitted when the bot leaves the voice channel
queue.metadata.channel.send("J'ai quitté le vocal !")
writeEnv('DISCORD_MUSIC_TEXTCHANNEL_ID', '')
writeEnv('DISCORD_MUSIC_VOICECHANNEL_ID', '')
writeEnv('DISCORD_MUSIC_CURRENT_TRACK', '')
writeEnv('DISCORD_MUSIC_CURRENT_PROGRESS', '')
}
}

View File

@@ -0,0 +1,11 @@
import { GuildQueue, Track } from 'discord-player'
import { PlayerMetadata } from '../utilsPlayer/metadata'
export default {
name: 'emptyChannel',
async execute(queue: GuildQueue<PlayerMetadata>, track: Track) {
// Emitted when the voice channel has been empty for the set threshold
// Bot will automatically leave the voice channel with this event
queue.metadata.channel.send(`Je quitte le vocal car il est vide depuis trop longtemps.`)
}
}

10
src/eventsPlayer/emptyQueue.ts Executable file
View File

@@ -0,0 +1,10 @@
import { GuildQueue, Track } from 'discord-player'
import { PlayerMetadata } from '../utilsPlayer/metadata'
export default {
name: 'emptyQueue',
async execute(queue: GuildQueue<PlayerMetadata>) {
// Emitted when the player queue has finished
queue.metadata.channel.send("File d'attente vide !")
}
}

10
src/eventsPlayer/error.ts Executable file
View File

@@ -0,0 +1,10 @@
import { GuildQueue } from 'discord-player'
export default {
name: 'error',
async execute(queue: GuildQueue, error: Error) {
// Emitted when the player queue encounters error
console.log(`General player error event: ${error.message}`)
console.error(error)
}
}

10
src/eventsPlayer/playerError.ts Executable file
View File

@@ -0,0 +1,10 @@
import { GuildQueue } from 'discord-player'
export default {
name: 'playerError',
async execute(queue: GuildQueue, error: Error) {
// Emitted when the audio player errors while streaming audio track
console.log(`\u001b[1;31m Player error event: ${error.message}`)
console.error(error)
}
}

10
src/eventsPlayer/playerSkip.ts Executable file
View File

@@ -0,0 +1,10 @@
import { GuildQueue, Track } from 'discord-player'
import { PlayerMetadata } from '../utilsPlayer/metadata'
export default {
name: 'playerSkip',
async execute(queue: GuildQueue<PlayerMetadata>, track: Track) {
// Emitted when the audio player fails to load the stream for a song
queue.metadata.channel.send(`Musique **${track.title}** de **${track.author}** passée !`)
}
}

13
src/eventsPlayer/playerStart.ts Executable file
View File

@@ -0,0 +1,13 @@
import { GuildQueue, Track } from 'discord-player'
import { PlayerMetadata } from '../utilsPlayer/metadata'
import writeEnv from '../utils/writeEnv'
export default {
name: 'playerStart',
async execute(queue: GuildQueue<PlayerMetadata>, track: Track) {
// Emitted when the player starts to play a song
queue.metadata.channel.send(`Lecture de **${track.title}** de **${track.author}** !`)
writeEnv(`DISCORD_MUSIC_CURRENT_TRACK`, track.url)
}
}

89
src/index.ts Executable file
View File

@@ -0,0 +1,89 @@
// PACKAGES
import { Client, Collection, GatewayIntentBits, REST, Routes, ChatInputCommandInteraction, AutocompleteInteraction, ButtonInteraction } from 'discord.js'
import { Player } from 'discord-player'
import path from 'path'
import fs from 'fs'
import 'dotenv/config'
export interface Command {
name: string,
description: string,
data: any,
autocompleteRun: (interaction: AutocompleteInteraction) => any,
execute: (interaction: ChatInputCommandInteraction) => any
}
export interface Button {
name: string,
description: string,
id: string,
execute: (interaction: ButtonInteraction) => any
}
declare module 'discord.js' {
export interface Client {
commands: Collection<unknown, Command>
buttons: Collection<unknown, Button>
}
}
// CLIENT INITIALIZATION
let intents = [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildVoiceStates]
const client = new Client({ intents })
// EVENTS HANDLING
const eventFiles = fs.readdirSync(path.join(__dirname, './events')).filter(file => file.endsWith('.ts'))
eventFiles.forEach(file => {
let event = require(path.join(__dirname, './events', file))
if (event.once) client.once(event.name, (...args) => { event.execute(...args) })
else client.on(event.name, (...args) => { event.execute(...args) })
})
// COMMANDS HANDLING
client.commands = new Collection()
const commandFolders = fs.readdirSync(path.join(__dirname, './commands'))
commandFolders.forEach(folder => {
let folderPath = path.join(__dirname, './commands', folder)
let commandFiles = fs.readdirSync(folderPath).filter(file => file.endsWith('.ts'))
commandFiles.forEach(file => {
let command = require(path.join(folderPath, file))
if ('data' in command && 'execute' in command) {
const commandData = command.data.toJSON()
if (commandData) client.commands.set(commandData.name, command)
} else console.log(`[WARNING] The command at ${`${folderPath}/${file}`} is missing a required "data" or "execute" property.`)
})
})
// COMMANDS REGISTERING
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN as string)
async () => {
try { await rest.put(Routes.applicationCommands(process.env.DISCORD_APP_ID as string), { body: client.commands }) }
catch (error) { console.error(error) }
}
// BUTTONS HANDLING
client.buttons = new Collection()
const buttonFiles = fs.readdirSync(path.join(__dirname, './buttons')).filter(file => file.endsWith('.ts'))
buttonFiles.forEach(file => {
let button = require(path.join(__dirname, './buttons', file))
if ('id' in button && 'execute' in button) client.buttons.set(button.id, button)
else console.log(`[WARNING] The button ${file} is missing a required "id" or "execute" property.`)
})
// PLAYER INITIALIZATION
const player = new Player(client)
// PLAYER EVENTS HANDLING
const eventPlayerFiles = fs.readdirSync(path.join(__dirname, './eventsPlayer')).filter(file => file.endsWith('.ts'))
eventPlayerFiles.forEach(async file => {
let event = await import(path.join(__dirname, './eventsPlayer', file))
if (event.default.name === 'debug') return
player.events.on(event.default.name, (...args: any[]) => event.default.execute(...args))
})
// LAUNCH
client.login()

BIN
src/static/parle.mp3 Executable file

Binary file not shown.

BIN
src/static/stronger_shorter.mp3 Executable file

Binary file not shown.

95
src/utils/amp.ts Normal file
View File

@@ -0,0 +1,95 @@
import axios from 'axios'
import writeEnv from './writeEnv'
export const ADSModule = {
async GetInstances(SESSIONID: string) {
return await axios.post(`${process.env.AMP_HOST}/API/ADSModule/GetInstances`, {
SESSIONID
}).then(response => {
if (!response.data.result) return { status: 'fail', data: response.data }
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
},
async ManageInstance(SESSIONID: string, InstanceId: string) {
return await axios.post(`${process.env.AMP_HOST}/API/ADSModule/ManageInstance`, {
SESSIONID,
InstanceId
}).then(response => {
console.log(response.data)
if (!response.data.result) return { status: 'fail', data: response.data }
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
},
async RestartInstance(SESSIONID: string, InstanceName: string) {
return await axios.post(`${process.env.AMP_HOST}/API/ADSModule/RestartInstance`, {
SESSIONID,
InstanceName
}).then(response => {
console.log(response.data)
//if (!response.data.success) return { status: 'fail', data: response.data }
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
},
async Servers(SESSIONID: string, InstanceId: string) {
return await axios.get(`${process.env.AMP_HOST}/API/ADSModule/Servers`, {
data: {
SESSIONID,
InstanceId
}
}).then(response => {
console.log(response)
console.log(response.data)
if (!response.data.result) return { status: 'fail', data: response.data }
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
}
}
export const Core = {
async Login(details: any) {
return await axios.post(`${process.env.AMP_HOST}/API/Core/Login`,
details
).then(response => {
if (!response.data.success) return { status: 'fail', data: response.data }
writeEnv('AMP_USERNAME', response.data.userInfo.Username)
writeEnv('AMP_SESSIONID', response.data.sessionID)
writeEnv('AMP_REMEMBER_TOKEN', response.data.rememberMeToken)
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
}
}
export async function CheckSession(SESSIONID: string) {
return await axios.post(`${process.env.AMP_HOST}/API/ADSModule/GetInstances`, {
SESSIONID
}).then(response => {
if (!response.data.result) return { status: 'fail', data: response.data }
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
}

98
src/utils/crack.ts Normal file
View File

@@ -0,0 +1,98 @@
import parseTorrent, { toMagnetURI } from 'parse-torrent'
import iconv from 'iconv-lite'
import axios from 'axios'
import path from 'path'
import fs from 'fs'
export declare class Game {
name: string
link: string
}
const headers = {
h1: {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"x-requested-with": "XMLHttpRequest"
},
h2: {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"accept-language": "fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7",
"cache-control": "no-cache",
"pragma": "no-cache",
"sec-ch-ua": "\"Not=A?Brand\";v=\"8\", \"Chromium\";v=\"110\", \"Opera GX\";v=\"96\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"cookie": "online_fix_auth=gAAAAABkKM0s9WNLe_V6euTnJD7UQjppVty9B7OOyHBYOyVcbcejj8F6KveBcLxlf3mlx_vE7JEFPHlrpj-Aq6BFJyKPGzxds_wpcPV2MdXPyDGQLsz4mAvt3qgTgGg25MapWo_fSIOMiAAsF4Gv_uh4kUOiR_jgbHCZWJGPgpNQwU2HFFyvahYR6MzR7nYE9-fCmrev3obkRbro43vIVTTX4UyJMRHadrsY5Q-722TzinCZVmAuJfc=; dle_password=89465c26673e0199e5272e4730772c35; _ym_uid=1670534560361937997; _ym_d=1680394955; _ym_isad=2; dle_user_id=2619796; PHPSESSID=3v8sd281sr0n1n9f1p66q25sa2",
"Referer": "https://online-fix.me/",
"Referrer-Policy": "strict-origin-when-cross-origin"
}
}
export async function search(query: string) {
let body = await fetch("https://online-fix.me/engine/ajax/search.php", { headers: headers.h1, body: `query=${query}`, method: "POST" })
.then(response => response.arrayBuffer())
.then(arrayBuffer => { return iconv.decode(Buffer.from(arrayBuffer), 'win1251') })
.catch(console.error)
try {
if (!body) return
let matches = body.split('</div>')[1].split('<span class="seperator fastfullsearch">')[0].split('</a>')
let games = [] as Game[]
matches.pop()
matches.forEach(async match => {
let name = match.split('"><span class="searchheading">')[1].split('</span>')[0].slice(0, -8)
let link = match.split('<a href="')[1].split('"><span class="searchheading">')[0]
games.push({ name, link })
})
return games
} catch (error) { return error }
}
export async function repo(game: Game) {
let body = await fetch(game.link, { headers: headers.h2, body: null, method: "GET" })
.then(response => response.arrayBuffer())
.then(arrayBuffer => { return iconv.decode(Buffer.from(arrayBuffer), 'win1251') })
.catch(console.error)
try {
if (!body) return
let name = body.split('https://uploads.online-fix.me:2053/torrents/')[1].split('"')[0]
let url = `https://uploads.online-fix.me:2053/torrents/${name}`
return url
} catch (error) { console.error(error) }
}
export async function torrent(url: string) {
let response = await fetch(url, { headers: headers.h2, body: null, method: "GET" }).catch(console.error)
try {
if (!response) return
let body = await response.text()
let file = body.split('<a href="')[2].split('">')[0]
return file
} catch (error) { console.error(error) }
}
export async function download(url: string, file: string) {
let filePath = path.join(__dirname, '../../public/cracks/', file)
let writer = fs.createWriteStream(filePath)
try {
await axios({ url: url + file, method: 'GET', responseType: 'stream', headers: headers.h2 }).then(response => {
return new Promise((resolve, reject) => {
response.data.pipe(writer)
let error = null as unknown as Error
writer.on('error', err => { error = err; writer.close(); reject(err) })
writer.on('close', () => { if (!error) resolve(true) })
})
}).catch(console.error)
return filePath
} catch (error) { console.error(error) }
}
export async function magnet(filePath: string) {
let torrentData = parseTorrent(fs.readFileSync(filePath))
let uri = toMagnetURI(torrentData)
return uri
}

10
src/utils/getUptime.ts Executable file
View File

@@ -0,0 +1,10 @@
import { Client } from 'discord.js'
export default function (uptime: Client["uptime"]) {
if (!uptime) return '0J, 0H, 0M et 0S'
let days = Math.floor(uptime / 86400000)
let hours = Math.floor(uptime / 3600000) % 24
let minutes = Math.floor(uptime / 60000) % 60
let seconds = Math.floor(uptime / 1000) % 60
return `${days}J, ${hours}H, ${minutes}M et ${seconds}S`
}

7
src/utils/writeEnv.ts Executable file
View File

@@ -0,0 +1,7 @@
import fs from 'fs'
export default ((variable: string, value: string) => {
let parsedFile = fs.readFileSync('./.env', 'utf8')
parsedFile = parsedFile.replace(new RegExp(`${variable} = .*`, 'g'), `${variable} = ${value}`)
fs.writeFileSync('./.env', parsedFile)
})

View File

@@ -0,0 +1,13 @@
const axios = require('axios')
module.exports = (async (SESSIONID) => {
return await axios.post(`${process.env.AMP_HOST}/API/${__filename.split('utilsAMP/')[1].split('.js')[0]}`, {
SESSIONID
}).then(response => {
if (!response.data.result) return { status: 'fail', data: response.data }
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
})

View File

@@ -0,0 +1,16 @@
const axios = require('axios')
module.exports = (async (SESSIONID, InstanceId) => {
return await axios.post(`${process.env.AMP_HOST}/API/${__filename.split('utilsAMP/')[1].split('.js')[0]}`, {
SESSIONID,
InstanceId
}).then(response => {
console.log(response.data)
if (!response.data.result) return { status: 'fail', data: response.data }
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
})

View File

@@ -0,0 +1,16 @@
const axios = require('axios')
module.exports = (async (SESSIONID, InstanceName) => {
return await axios.post(`${process.env.AMP_HOST}/API/${__filename.split('utilsAMP/')[1].split('.js')[0]}`, {
SESSIONID,
InstanceName
}).then(response => {
console.log(response.data)
//if (!response.data.success) return { status: 'fail', data: response.data }
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
})

View File

@@ -0,0 +1,17 @@
const axios = require('axios')
module.exports = (async (SESSIONID, InstanceId) => {
return await axios.get(`${process.env.AMP_HOST}/API/${__filename.split('utilsAMP/')[1].split('.js')[0]}`, {
SESSIONID,
InstanceId
}).then(response => {
console.log(response)
console.log(response.data)
if (!response.data.result) return { status: 'fail', data: response.data }
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
})

13
src/utilsAMP/CheckSession.ts Executable file
View File

@@ -0,0 +1,13 @@
const axios = require('axios')
module.exports = (async (SESSIONID) => {
return await axios.post(`${process.env.AMP_HOST}/API/ADSModule/GetInstances`, {
SESSIONID
}).then(response => {
if (!response.data.result) return { status: 'fail', data: response.data }
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
})

21
src/utilsAMP/Core/Login.ts Executable file
View File

@@ -0,0 +1,21 @@
const axios = require('axios')
const appDir = require('path').dirname(require.main.filename)
const writeEnv = require(appDir + '/utils/writeEnv')
module.exports = (async (details) => {
return await axios.post(`${process.env.AMP_HOST}/API/${__filename.split('utilsAMP/')[1].split('.js')[0]}`,
details
).then(response => {
if (!response.data.success) return { status: 'fail', data: response.data }
writeEnv('AMP_USERNAME', response.data.userInfo.Username)
writeEnv('AMP_SESSIONID', response.data.sessionID)
writeEnv('AMP_REMEMBER_TOKEN', response.data.rememberMeToken)
return { status: 'success', data: response.data }
}).catch(error => {
console.error(error)
return { status: 'error', data: error }
})
})

18
src/utilsCrack/download.ts Executable file
View File

@@ -0,0 +1,18 @@
const axios = require('axios')
const fs = require('fs')
module.exports = download = (async (url, file, headers) => {
let path = `./cracks/${file}`
let writer = fs.createWriteStream(path)
try {
await axios({ url: url + file, method: 'GET', responseType: 'stream', headers }).then(response => {
return new Promise((resolve, reject) => {
response.data.pipe(writer)
let error = null
writer.on('error', err => { error = err; writer.close(); reject(err) })
writer.on('close', () => { if (!error) resolve(true) })
})
}).catch(console.error)
return path
} catch (error) { console.error(error) }
})

23
src/utilsCrack/headers.ts Executable file
View File

@@ -0,0 +1,23 @@
const h1 = {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"x-requested-with": "XMLHttpRequest"
}
const h2 = {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"accept-language": "fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7",
"cache-control": "no-cache",
"pragma": "no-cache",
"sec-ch-ua": "\"Not=A?Brand\";v=\"8\", \"Chromium\";v=\"110\", \"Opera GX\";v=\"96\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"cookie": "online_fix_auth=gAAAAABkKM0s9WNLe_V6euTnJD7UQjppVty9B7OOyHBYOyVcbcejj8F6KveBcLxlf3mlx_vE7JEFPHlrpj-Aq6BFJyKPGzxds_wpcPV2MdXPyDGQLsz4mAvt3qgTgGg25MapWo_fSIOMiAAsF4Gv_uh4kUOiR_jgbHCZWJGPgpNQwU2HFFyvahYR6MzR7nYE9-fCmrev3obkRbro43vIVTTX4UyJMRHadrsY5Q-722TzinCZVmAuJfc=; dle_password=89465c26673e0199e5272e4730772c35; _ym_uid=1670534560361937997; _ym_d=1680394955; _ym_isad=2; dle_user_id=2619796; PHPSESSID=3v8sd281sr0n1n9f1p66q25sa2",
"Referer": "https://online-fix.me/",
"Referrer-Policy": "strict-origin-when-cross-origin"
}
module.exports = headers = { h1, h2 }

12
src/utilsCrack/magnet.ts Executable file
View File

@@ -0,0 +1,12 @@
const fs = require('fs')
module.exports = magnet = (async path => {
const parseTorrent = await import('parse-torrent')
const parse = parseTorrent.default
const magnetURI = parseTorrent.toMagnetURI
let data = await parse(fs.readFileSync(path))
let uri = await magnetURI({ infoHash: data.infoHash })
return uri
})

13
src/utilsCrack/repo.ts Executable file
View File

@@ -0,0 +1,13 @@
const iconv = require('iconv-lite')
module.exports = repo = (async (game, headers) => {
let body = await fetch(game.link, { headers, body: null, method: "GET" })
.then(response => response.arrayBuffer())
.then(arrayBuffer => { return iconv.decode(Buffer.from(arrayBuffer), 'win1251') })
.catch(console.error)
try {
let name = body.split('https://uploads.online-fix.me:2053/torrents/')[1].split('"')[0]
let url = `https://uploads.online-fix.me:2053/torrents/${name}`
return url
} catch (error) { console.error(error) }
})

19
src/utilsCrack/search.ts Executable file
View File

@@ -0,0 +1,19 @@
const iconv = require('iconv-lite')
module.exports = search = (async (query, headers) => {
let body = await fetch("https://online-fix.me/engine/ajax/search.php", { headers, body: `query=${query}`, method: "POST" })
.then(response => response.arrayBuffer())
.then(arrayBuffer => { return iconv.decode(Buffer.from(arrayBuffer), 'win1251') })
.catch(console.error)
try {
let matches = body.split('</div>')[1].split('<span class="seperator fastfullsearch">')[0].split('</a>')
let games = []
matches.pop()
matches.forEach(async match => {
let name = match.split('"><span class="searchheading">')[1].split('</span>')[0].slice(0, -8)
let link = match.split('<a href="')[1].split('"><span class="searchheading">')[0]
games.push({ name, link })
})
return games
} catch (error) { return error }
})

8
src/utilsCrack/torrent.ts Executable file
View File

@@ -0,0 +1,8 @@
module.exports = torrent = (async (url, headers) => {
let response = await fetch(url, { headers, body: null, method: "GET" }).catch(console.error)
try {
let body = await response.text()
let file = body.split('<a href="')[2].split('">')[0]
return file
} catch (error) { console.error(error) }
})

1
src/utilsPlayer/buttons.ts Executable file
View File

@@ -0,0 +1 @@
export default ['loop', 'pause', 'previous', 'resume', 'shuffle', 'skip', 'stop', 'volume_down', 'volume_up']

40
src/utilsPlayer/disco.ts Executable file
View File

@@ -0,0 +1,40 @@
import { Client, TextChannel } from 'discord.js'
import { useQueue } from 'discord-player'
import getUptime from '../utils/getUptime'
import writeEnv from '../utils/writeEnv'
import generate from './generate'
export default async (client: Client) => {
let guild = client.guilds.cache.get(process.env.DISCORD_GUILD_ID as string)
if (!guild) {
clearInterval(client.disco.interval)
console.log(`Aucun serveur trouvé avec l'id \`${process.env.DISCORD_GUILD_ID}\`, veuillez utiliser la commande \`/setchannel\` !`)
return 'clear'
}
// Keep track of the music progress if the bot reboots
let queue = useQueue(guild.id)
if (queue) if (queue.isPlaying()) writeEnv('DISCORD_MUSIC_CURRENT_PROGRESS', queue.node.playbackTime.toString())
let channel = client.channels.cache.get(process.env.DISCORD_PLAYERCHANNEL_ID as string) as TextChannel
if (!channel) {
clearInterval(client.disco.interval)
console.log(`Aucun channel trouvé avec l'id \`${process.env.DISCORD_PLAYERCHANNEL_ID}\`, veuillez utiliser la commande \`/setchannel\` !`)
return 'clear'
}
let { embed, components } = await generate(guild)
if (components && embed.data.footer) embed.setFooter({ text: `Uptime: ${getUptime(client.uptime)} \n ${embed.data.footer.text}` })
else embed.setFooter({ text: `Uptime: ${getUptime(client.uptime)}` })
let messages = await channel.messages.fetch()
messages.forEach(msg => { if (msg.member) if (!msg.author.bot && !msg.member.roles.cache.has(process.env.DISCORD_ROLE_ID as string)) msg.delete() })
let botMessage = messages.find(msg => client.user && msg.author.id === client.user.id)
if (botMessage) {
if (!components && botMessage.components.length > 0) {
await botMessage.delete()
return channel.send({ embeds: [embed] })
} else if (components) return botMessage.edit({ embeds: [embed], components })
} else return channel.send({ embeds: [embed] })
}

13
src/utilsPlayer/edit.ts Executable file
View File

@@ -0,0 +1,13 @@
import { ButtonInteraction } from 'discord.js'
import generatePlayer from './generate'
export default async (interaction: ButtonInteraction) => {
let guild = interaction.guild
if (!guild) return await interaction.reply({ content: 'Cette commande n\'est pas disponible en message privé.', ephemeral: true })
let { components } = await generatePlayer(guild)
if (!components) return
components.forEach((actionRow) => actionRow.components.forEach((button) => button.setDisabled(true)))
await interaction.update({ components })
}

76
src/utilsPlayer/generate.ts Executable file
View File

@@ -0,0 +1,76 @@
import { EmbedBuilder, ButtonBuilder, ActionRowBuilder, Guild } from 'discord.js'
import { useQueue } from 'discord-player'
export default async (guild: Guild) => {
let embed = new EmbedBuilder().setColor('#ffc370')
let queue = useQueue(guild.id)
if (!queue) {
embed.setTitle('Aucune session d\'écoute en cours !')
return ({ embed, components: null })
}
let track = queue.currentTrack
if (!track) {
embed.setTitle('Aucune musique en cours de lecture !')
return ({ embed, components: null })
}
embed.setTitle(track.title)
.setAuthor({ name: track.author })
.setURL(track.url)
.setImage(track.thumbnail)
.addFields(
{ name: 'Durée', value: track.duration, inline: true },
{ name: 'Source', value: track.source === 'youtube' ? 'Youtube' : track.source === 'spotify' ? 'Spotify' : 'Inconnu', inline: true },
{ name: 'Volume', value: `${queue.node.volume}%`, inline: true },
{ name: queue.node.isPaused() ? 'Progression (en pause)' : 'Progression', value: queue.node.createProgressBar() || 'Aucune' },
{ name: 'Loop', value: queue.repeatMode === 3 ? 'Autoplay' : queue.repeatMode === 2 ? 'File d\'Attente' : queue.repeatMode === 1 ? 'Titre' : 'Off', inline: true }
)
.setDescription(`**Musique suivante :** ${queue.tracks.data[0] ? queue.tracks.data[0].title : 'Aucune'}`)
.setFooter({ text: `Demandé par ${track.requestedBy ? track.requestedBy.tag : 'Inconnu'}` })
let components = [
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel(queue.node.isPaused() ? '▶️' : '⏸️')
.setStyle(2)
.setCustomId(queue.node.isPaused() ? 'resume' : 'pause'),
new ButtonBuilder()
.setLabel('⏹️')
.setStyle(2)
.setCustomId('stop'),
new ButtonBuilder()
.setLabel('⏭️')
.setStyle(2)
.setCustomId('skip')
.setDisabled(queue.tracks.data.length !== 0),
new ButtonBuilder()
.setLabel('🔉')
.setStyle(2)
.setCustomId('volume_down')
.setDisabled(queue.node.volume === 0),
new ButtonBuilder()
.setLabel('🔊')
.setStyle(2)
.setCustomId('volume_up')
.setDisabled(queue.node.volume === 100)
),
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel('🔀')
.setStyle(2)
.setCustomId('shuffle'),
new ButtonBuilder()
.setLabel('🔁')
.setStyle(2)
.setCustomId('loop'),
new ButtonBuilder()
.setLabel('⏮️')
.setStyle(2)
.setCustomId('previous')
.setDisabled(queue.history.previousTrack ? false : true)
)
]
return ({ embed, components })
}

View File

@@ -0,0 +1,24 @@
import { CommandInteraction, Guild, GuildChannel, TextBasedChannel, VoiceChannel } from 'discord.js'
type ChannelInferrable = {
channel: TextBasedChannel | VoiceChannel
guild?: Guild
}
export class PlayerMetadata {
public constructor(public data: ChannelInferrable) {
if (data.channel.isDMBased()) { throw new Error('PlayerMetadata cannot be created from a DM') }
if (!data.channel) { throw new Error('PlayerMetadata can only be created from a channel') }
}
public get channel() { return this.data.channel! }
public get guild() { return this.data.guild || (this.data.channel as GuildChannel).guild }
public static create(data: ChannelInferrable | CommandInteraction) {
if (data instanceof CommandInteraction) {
if (!data.inGuild()) { throw new Error('PlayerMetadata cannot be created from a DM') }
return new PlayerMetadata({ channel: data.channel!, guild: data.guild! })
}
return new PlayerMetadata(data);
}
}

42
src/utilsPlayer/replay.ts Executable file
View File

@@ -0,0 +1,42 @@
import { Client, TextChannel, VoiceChannel } from 'discord.js'
import { useMainPlayer } from 'discord-player'
export default async (client: Client) => {
let textChannel = client.channels.cache.get(process.env.DISCORD_MUSIC_TEXTCHANNEL_ID as string) as TextChannel
if (!textChannel) return console.log(`Aucun channel trouvé avec l'id \`${process.env.DISCORD_MUSIC_TEXTCHANNEL_ID}\`, veuillez utiliser la commande \`/setchannel\` !`)
let voiceChannel = client.channels.cache.get(process.env.DISCORD_MUSIC_VOICECHANNEL_ID as string) as VoiceChannel
if (!voiceChannel) return console.log(`Aucun channel trouvé avec l'id \`${process.env.DISCORD_MUSIC_VOICECHANNEL_ID}\`, veuillez utiliser la commande \`/setchannel\` !`)
let player = useMainPlayer()
let queue = player.nodes.create(textChannel.guild, {
metadata: {
channel: textChannel,
client: textChannel.guild.members.me,
requestedBy: client.user
},
selfDeaf: true,
volume: 20,
leaveOnEmpty: true,
leaveOnEmptyCooldown: 30000,
leaveOnEnd: true,
leaveOnEndCooldown: 300000
})
try { if (!queue.connection) await queue.connect(voiceChannel) }
catch (error: any) { console.error(error); await textChannel.send(`Y'a eu un problème, <@223831938346123275> ! (${error.message})`) }
let result = await player.search(process.env.DISCORD_MUSIC_CURRENT_TRACK as string, { requestedBy: client.user || undefined })
if (!result.hasTracks()) await textChannel.send(`Aucune musique trouvée pour **${process.env.DISCORD_MUSIC_CURRENT_TRACK}** !`)
let track = result.tracks[0]
let entry = queue.tasksQueue.acquire()
await entry.getTask()
queue.addTrack(track)
try {
await queue.node.play()
await queue.node.seek(Number(process.env.DISCORD_MUSIC_CURRENT_PROGRESS))
await textChannel.send(`Relancement de la musique suite à mon redémarrage...`)
} catch (error: any) { console.error(error); await textChannel.send(`Y'a eu un problème, <@223831938346123275> ! (${error.message})`) }
finally { queue.tasksQueue.release() }
}