✏️ Discord.js v14 port
This commit is contained in:
parent
c7a6bf2ec1
commit
6571e2a3c1
19 changed files with 559 additions and 3856 deletions
3
.gitattributes
vendored
3
.gitattributes
vendored
|
@ -1 +1,2 @@
|
||||||
* text=auto eol=lf
|
* text=auto eol=lf
|
||||||
|
*.js,*.mjs linguist-language=TypeScript
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -13,4 +13,6 @@ yarn*.log
|
||||||
example/test
|
example/test
|
||||||
example/music-bot/node_modules
|
example/music-bot/node_modules
|
||||||
example/music-bot/package-lock.json
|
example/music-bot/package-lock.json
|
||||||
example/music-bot/.env
|
example/music-bot/.env
|
||||||
|
|
||||||
|
dev/
|
|
@ -1,2 +0,0 @@
|
||||||
# Discord Player Examples
|
|
||||||
This section contains example bot(s) made with Discord Player.
|
|
|
@ -1,2 +0,0 @@
|
||||||
# Music Bot
|
|
||||||
Slash commands music bot backed by **[Discord Player](https://discord-player.js.org)**.
|
|
|
@ -1,3 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
token: process.env.DISCORD_TOKEN
|
|
||||||
};
|
|
|
@ -1,331 +0,0 @@
|
||||||
require("dotenv").config({
|
|
||||||
path: __dirname+"/.env"
|
|
||||||
});
|
|
||||||
const { Client, GuildMember, Intents } = require("discord.js");
|
|
||||||
const config = require("./config");
|
|
||||||
const { Player, QueryType, QueueRepeatMode } = require("discord-player");
|
|
||||||
const client = new Client({
|
|
||||||
intents: [Intents.FLAGS.GUILD_VOICE_STATES, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILDS]
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on("ready", () => {
|
|
||||||
console.log("Bot is online!");
|
|
||||||
client.user.setActivity({
|
|
||||||
name: "🎶 | Music Time",
|
|
||||||
type: "LISTENING"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
client.on("error", console.error);
|
|
||||||
client.on("warn", console.warn);
|
|
||||||
|
|
||||||
// instantiate the player
|
|
||||||
const player = new Player(client, {
|
|
||||||
ytdlOptions: {
|
|
||||||
headers: {
|
|
||||||
cookie: process.env.YT_COOKIE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
player.on("error", (queue, error) => {
|
|
||||||
console.log(`[${queue.guild.name}] Error emitted from the queue: ${error.message}`);
|
|
||||||
});
|
|
||||||
player.on("connectionError", (queue, error) => {
|
|
||||||
console.log(`[${queue.guild.name}] Error emitted from the connection: ${error.message}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
player.on("trackStart", (queue, track) => {
|
|
||||||
queue.metadata.send(`🎶 | Started playing: **${track.title}** in **${queue.connection.channel.name}**!`);
|
|
||||||
});
|
|
||||||
|
|
||||||
player.on("trackAdd", (queue, track) => {
|
|
||||||
queue.metadata.send(`🎶 | Track **${track.title}** queued!`);
|
|
||||||
});
|
|
||||||
|
|
||||||
player.on("botDisconnect", (queue) => {
|
|
||||||
queue.metadata.send("❌ | I was manually disconnected from the voice channel, clearing queue!");
|
|
||||||
});
|
|
||||||
|
|
||||||
player.on("channelEmpty", (queue) => {
|
|
||||||
queue.metadata.send("❌ | Nobody is in the voice channel, leaving...");
|
|
||||||
});
|
|
||||||
|
|
||||||
player.on("queueEnd", (queue) => {
|
|
||||||
queue.metadata.send("✅ | Queue finished!");
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on("messageCreate", async (message) => {
|
|
||||||
if (message.author.bot || !message.guild) return;
|
|
||||||
if (!client.application?.owner) await client.application?.fetch();
|
|
||||||
|
|
||||||
if (message.content === "!deploy" && message.author.id === client.application?.owner?.id) {
|
|
||||||
await message.guild.commands.set([
|
|
||||||
{
|
|
||||||
name: "play",
|
|
||||||
description: "Plays a song from youtube",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: "query",
|
|
||||||
type: "STRING",
|
|
||||||
description: "The song you want to play",
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "soundcloud",
|
|
||||||
description: "Plays a song from soundcloud",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: "query",
|
|
||||||
type: "STRING",
|
|
||||||
description: "The song you want to play",
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "volume",
|
|
||||||
description: "Sets music volume",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: "amount",
|
|
||||||
type: "INTEGER",
|
|
||||||
description: "The volume amount to set (0-100)",
|
|
||||||
required: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "loop",
|
|
||||||
description: "Sets loop mode",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: "mode",
|
|
||||||
type: "INTEGER",
|
|
||||||
description: "Loop type",
|
|
||||||
required: true,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
name: "Off",
|
|
||||||
value: QueueRepeatMode.OFF
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Track",
|
|
||||||
value: QueueRepeatMode.TRACK
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Queue",
|
|
||||||
value: QueueRepeatMode.QUEUE
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Autoplay",
|
|
||||||
value: QueueRepeatMode.AUTOPLAY
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "skip",
|
|
||||||
description: "Skip to the current song"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "queue",
|
|
||||||
description: "See the queue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "pause",
|
|
||||||
description: "Pause the current song"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "resume",
|
|
||||||
description: "Resume the current song"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "stop",
|
|
||||||
description: "Stop the player"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "np",
|
|
||||||
description: "Now Playing"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "bassboost",
|
|
||||||
description: "Toggles bassboost filter"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ping",
|
|
||||||
description: "Shows bot latency"
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
await message.reply("Deployed!");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on("interactionCreate", async (interaction) => {
|
|
||||||
if (!interaction.isCommand() || !interaction.guildId) return;
|
|
||||||
|
|
||||||
if (interaction.commandName === "ping") {
|
|
||||||
await interaction.deferReply();
|
|
||||||
const queue = player.getQueue(interaction.guild);
|
|
||||||
|
|
||||||
return void interaction.followUp({
|
|
||||||
embeds: [
|
|
||||||
{
|
|
||||||
title: "⏱️ | Latency",
|
|
||||||
fields: [
|
|
||||||
{ name: "Bot Latency", value: `\`${Math.round(client.ws.ping)}ms\`` },
|
|
||||||
{ name: "Voice Latency", value: !queue ? "N/A" : `UDP: \`${queue.connection.voiceConnection.ping.udp ?? "N/A"}\`ms\nWebSocket: \`${queue.connection.voiceConnection.ping.ws ?? "N/A"}\`ms` }
|
|
||||||
],
|
|
||||||
color: 0xFFFFFF
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(interaction.member instanceof GuildMember) || !interaction.member.voice.channel) {
|
|
||||||
return void interaction.reply({ content: "You are not in a voice channel!", ephemeral: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interaction.guild.me.voice.channelId && interaction.member.voice.channelId !== interaction.guild.me.voice.channelId) {
|
|
||||||
return void interaction.reply({ content: "You are not in my voice channel!", ephemeral: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interaction.commandName === "play" || interaction.commandName === "soundcloud") {
|
|
||||||
await interaction.deferReply();
|
|
||||||
|
|
||||||
const query = interaction.options.get("query").value;
|
|
||||||
const searchResult = await player
|
|
||||||
.search(query, {
|
|
||||||
requestedBy: interaction.user,
|
|
||||||
searchEngine: interaction.commandName === "soundcloud" ? QueryType.SOUNDCLOUD_SEARCH : QueryType.AUTO
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
if (!searchResult || !searchResult.tracks.length) return void interaction.followUp({ content: "No results were found!" });
|
|
||||||
|
|
||||||
const queue = await player.createQueue(interaction.guild, {
|
|
||||||
metadata: interaction.channel
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!queue.connection) await queue.connect(interaction.member.voice.channel);
|
|
||||||
} catch {
|
|
||||||
void player.deleteQueue(interaction.guildId);
|
|
||||||
return void interaction.followUp({ content: "Could not join your voice channel!" });
|
|
||||||
}
|
|
||||||
|
|
||||||
await interaction.followUp({ content: `⏱ | Loading your ${searchResult.playlist ? "playlist" : "track"}...` });
|
|
||||||
searchResult.playlist ? queue.addTracks(searchResult.tracks) : queue.addTrack(searchResult.tracks[0]);
|
|
||||||
if (!queue.playing) await queue.play();
|
|
||||||
} else if (interaction.commandName === "volume") {
|
|
||||||
await interaction.deferReply();
|
|
||||||
const queue = player.getQueue(interaction.guildId);
|
|
||||||
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
|
|
||||||
const vol = interaction.options.get("amount");
|
|
||||||
if (!vol) return void interaction.followUp({ content: `🎧 | Current volume is **${queue.volume}**%!` });
|
|
||||||
if ((vol.value) < 0 || (vol.value) > 100) return void interaction.followUp({ content: "❌ | Volume range must be 0-100" });
|
|
||||||
const success = queue.setVolume(vol.value);
|
|
||||||
return void interaction.followUp({
|
|
||||||
content: success ? `✅ | Volume set to **${vol.value}%**!` : "❌ | Something went wrong!"
|
|
||||||
});
|
|
||||||
} else if (interaction.commandName === "skip") {
|
|
||||||
await interaction.deferReply();
|
|
||||||
const queue = player.getQueue(interaction.guildId);
|
|
||||||
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
|
|
||||||
const currentTrack = queue.current;
|
|
||||||
const success = queue.skip();
|
|
||||||
return void interaction.followUp({
|
|
||||||
content: success ? `✅ | Skipped **${currentTrack}**!` : "❌ | Something went wrong!"
|
|
||||||
});
|
|
||||||
} else if (interaction.commandName === "queue") {
|
|
||||||
await interaction.deferReply();
|
|
||||||
const queue = player.getQueue(interaction.guildId);
|
|
||||||
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
|
|
||||||
const currentTrack = queue.current;
|
|
||||||
const tracks = queue.tracks.slice(0, 10).map((m, i) => {
|
|
||||||
return `${i + 1}. **${m.title}** ([link](${m.url}))`;
|
|
||||||
});
|
|
||||||
|
|
||||||
return void interaction.followUp({
|
|
||||||
embeds: [
|
|
||||||
{
|
|
||||||
title: "Server Queue",
|
|
||||||
description: `${tracks.join("\n")}${
|
|
||||||
queue.tracks.length > tracks.length
|
|
||||||
? `\n...${queue.tracks.length - tracks.length === 1 ? `${queue.tracks.length - tracks.length} more track` : `${queue.tracks.length - tracks.length} more tracks`}`
|
|
||||||
: ""
|
|
||||||
}`,
|
|
||||||
color: 0xff0000,
|
|
||||||
fields: [{ name: "Now Playing", value: `🎶 | **${currentTrack.title}** ([link](${currentTrack.url}))` }]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
} else if (interaction.commandName === "pause") {
|
|
||||||
await interaction.deferReply();
|
|
||||||
const queue = player.getQueue(interaction.guildId);
|
|
||||||
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
|
|
||||||
const paused = queue.setPaused(true);
|
|
||||||
return void interaction.followUp({ content: paused ? "⏸ | Paused!" : "❌ | Something went wrong!" });
|
|
||||||
} else if (interaction.commandName === "resume") {
|
|
||||||
await interaction.deferReply();
|
|
||||||
const queue = player.getQueue(interaction.guildId);
|
|
||||||
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
|
|
||||||
const paused = queue.setPaused(false);
|
|
||||||
return void interaction.followUp({ content: !paused ? "❌ | Something went wrong!" : "▶ | Resumed!" });
|
|
||||||
} else if (interaction.commandName === "stop") {
|
|
||||||
await interaction.deferReply();
|
|
||||||
const queue = player.getQueue(interaction.guildId);
|
|
||||||
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
|
|
||||||
queue.destroy();
|
|
||||||
return void interaction.followUp({ content: "🛑 | Stopped the player!" });
|
|
||||||
} else if (interaction.commandName === "np") {
|
|
||||||
await interaction.deferReply();
|
|
||||||
const queue = player.getQueue(interaction.guildId);
|
|
||||||
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
|
|
||||||
const progress = queue.createProgressBar();
|
|
||||||
const perc = queue.getPlayerTimestamp();
|
|
||||||
|
|
||||||
return void interaction.followUp({
|
|
||||||
embeds: [
|
|
||||||
{
|
|
||||||
title: "Now Playing",
|
|
||||||
description: `🎶 | **${queue.current.title}**! (\`${perc.progress}%\`)`,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: "\u200b",
|
|
||||||
value: progress
|
|
||||||
}
|
|
||||||
],
|
|
||||||
color: 0xffffff
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
} else if (interaction.commandName === "loop") {
|
|
||||||
await interaction.deferReply();
|
|
||||||
const queue = player.getQueue(interaction.guildId);
|
|
||||||
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
|
|
||||||
const loopMode = interaction.options.get("mode").value;
|
|
||||||
const success = queue.setRepeatMode(loopMode);
|
|
||||||
const mode = loopMode === QueueRepeatMode.TRACK ? "🔂" : loopMode === QueueRepeatMode.QUEUE ? "🔁" : "▶";
|
|
||||||
return void interaction.followUp({ content: success ? `${mode} | Updated loop mode!` : "❌ | Could not update loop mode!" });
|
|
||||||
} else if (interaction.commandName === "bassboost") {
|
|
||||||
await interaction.deferReply();
|
|
||||||
const queue = player.getQueue(interaction.guildId);
|
|
||||||
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
|
|
||||||
await queue.setFilters({
|
|
||||||
bassboost: !queue.getFiltersEnabled().includes("bassboost"),
|
|
||||||
normalizer2: !queue.getFiltersEnabled().includes("bassboost") // because we need to toggle it with bass
|
|
||||||
});
|
|
||||||
|
|
||||||
return void interaction.followUp({ content: `🎵 | Bassboost ${queue.getFiltersEnabled().includes("bassboost") ? "Enabled" : "Disabled"}!` });
|
|
||||||
} else {
|
|
||||||
interaction.reply({
|
|
||||||
content: "Unknown command!",
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
client.login(config.token);
|
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"name": "music-bot",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Simple music bot created with discord-player",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"start": "node index.js"
|
|
||||||
},
|
|
||||||
"author": "Snowflake107",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@discordjs/opus": "^0.5.3",
|
|
||||||
"discord-player": "^5.2.1",
|
|
||||||
"discord.js": "^13.0.1",
|
|
||||||
"dotenv": "^10.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
17
jsdoc.json
17
jsdoc.json
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"source": {
|
|
||||||
"includePattern": ".+\\.ts(doc|x)?$"
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"plugins/markdown",
|
|
||||||
"node_modules/jsdoc-babel"
|
|
||||||
],
|
|
||||||
"babel": {
|
|
||||||
"extensions": ["ts"],
|
|
||||||
"babelrc": false,
|
|
||||||
"presets": [
|
|
||||||
["@babel/preset-env", { "targets": { "node": true } }],
|
|
||||||
"@babel/preset-typescript"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
55
package.json
55
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "discord-player",
|
"name": "discord-player",
|
||||||
"version": "5.2.2",
|
"version": "6.0.0-dev",
|
||||||
"description": "Complete framework to facilitate music commands using discord.js",
|
"description": "Complete framework to facilitate music commands using discord.js",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
@ -23,9 +23,9 @@
|
||||||
"build:check": "tsc --noEmit --incremental false",
|
"build:check": "tsc --noEmit --incremental false",
|
||||||
"prepublishOnly": "rollup-type-bundler -e stream",
|
"prepublishOnly": "rollup-type-bundler -e stream",
|
||||||
"build:esm": "gen-esm-wrapper ./dist/index.js ./dist/index.mjs",
|
"build:esm": "gen-esm-wrapper ./dist/index.js ./dist/index.mjs",
|
||||||
"format": "prettier --write \"src/**/*.ts\" \"example/**/*.ts\"",
|
"format": "prettier --write \"src/**/*.ts\"",
|
||||||
"docs": "docgen --jsdoc jsdoc.json --source src/*.ts src/**/*.ts --custom docs/index.yml --output docs/docs.json",
|
"docs": "typedoc --json docs/typedoc.json src/index.ts",
|
||||||
"docs:test": "docgen --jsdoc jsdoc.json --source src/*.ts src/**/*.ts --custom docs/index.yml",
|
"postdocs": "node scripts/docgen.js",
|
||||||
"lint": "eslint src --ext .ts",
|
"lint": "eslint src --ext .ts",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"lint:fix": "eslint src --ext .ts --fix"
|
"lint:fix": "eslint src --ext .ts --fix"
|
||||||
|
@ -65,37 +65,32 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://discord-player.js.org",
|
"homepage": "https://discord-player.js.org",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/voice": "^0.8.0",
|
"@discordjs/voice": "^0.10.0",
|
||||||
"discord-ytdl-core": "^5.0.4",
|
"discord-ytdl-core": "^5.0.4",
|
||||||
"libsodium-wrappers": "^0.7.9",
|
"libsodium-wrappers": "^0.7.10",
|
||||||
"soundcloud-scraper": "^5.0.2",
|
"soundcloud-scraper": "^5.0.2",
|
||||||
"spotify-url-info": "^2.2.5",
|
"spotify-url-info": "^3.1.2",
|
||||||
"tiny-typed-emitter": "^2.1.0",
|
"tslib": "^2.4.0",
|
||||||
"youtube-sr": "^4.1.13",
|
"youtube-sr": "^4.1.17",
|
||||||
"ytdl-core": "^4.10.0"
|
"ytdl-core": "^4.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "^7.16.0",
|
"@discordjs/ts-docgen": "^0.4.1",
|
||||||
"@babel/core": "^7.16.0",
|
"@favware/rollup-type-bundler": "^1.0.7",
|
||||||
"@babel/preset-env": "^7.16.4",
|
"@types/node": "^17.0.43",
|
||||||
"@babel/preset-typescript": "^7.16.0",
|
"@types/ws": "^8.5.3",
|
||||||
"@devsnowflake/docgen": "devsnowflake/docgen#ts-patch",
|
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||||
"@discord-player/extractor": "^3.0.2",
|
"@typescript-eslint/parser": "^5.28.0",
|
||||||
"@discordjs/opus": "github:discordjs/opus",
|
"discord-api-types": "^0.34.0",
|
||||||
"@favware/rollup-type-bundler": "^1.0.6",
|
"discord.js": "^14.0.0-dev.1655165434-b4e28a8",
|
||||||
"@types/node": "^16.11.10",
|
"eslint": "^8.17.0",
|
||||||
"@types/ws": "^8.2.0",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
|
||||||
"@typescript-eslint/parser": "^5.4.0",
|
|
||||||
"discord-api-types": "^0.24.0",
|
|
||||||
"discord.js": "^13.6.0",
|
|
||||||
"eslint": "^8.3.0",
|
|
||||||
"gen-esm-wrapper": "^1.1.3",
|
"gen-esm-wrapper": "^1.1.3",
|
||||||
"husky": "^7.0.4",
|
"husky": "^8.0.1",
|
||||||
"jsdoc-babel": "^0.5.0",
|
"opusscript": "^0.0.8",
|
||||||
"prettier": "^2.5.0",
|
"prettier": "^2.7.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"ts-node": "^10.4.0",
|
"ts-node": "^10.8.1",
|
||||||
"typescript": "^4.5.2"
|
"typedoc": "^0.22.17",
|
||||||
|
"typescript": "^4.7.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
scripts/docgen.js
Normal file
11
scripts/docgen.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
const { runGenerator } = require("@discordjs/ts-docgen");
|
||||||
|
const fs = require("node:fs");
|
||||||
|
|
||||||
|
runGenerator({
|
||||||
|
existingOutput: "docs/typedoc.json",
|
||||||
|
custom: "docs/config.yml",
|
||||||
|
output: "docs/docs.json"
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.unlinkSync(`${__dirname}/../docs/typedoc.json`);
|
|
@ -1,4 +1,4 @@
|
||||||
import { Client, Collection, GuildResolvable, Snowflake, User, VoiceState, Intents } from "discord.js";
|
import { Client, Collection, GuildResolvable, Snowflake, User, VoiceState, IntentsBitField } from "discord.js";
|
||||||
import { TypedEmitter as EventEmitter } from "tiny-typed-emitter";
|
import { TypedEmitter as EventEmitter } from "tiny-typed-emitter";
|
||||||
import { Queue } from "./Structures/Queue";
|
import { Queue } from "./Structures/Queue";
|
||||||
import { VoiceUtils } from "./VoiceInterface/VoiceUtils";
|
import { VoiceUtils } from "./VoiceInterface/VoiceUtils";
|
||||||
|
@ -45,8 +45,8 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
*/
|
*/
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
|
||||||
if (this.client?.options?.intents && !new Intents(this.client?.options?.intents).has(Intents.FLAGS.GUILD_VOICE_STATES)) {
|
if (this.client?.options?.intents && !new IntentsBitField(this.client?.options?.intents).has(IntentsBitField.Flags.GuildVoiceStates)) {
|
||||||
throw new PlayerError('client is missing "GUILD_VOICE_STATES" intent');
|
throw new PlayerError('client is missing "GuildVoiceStates" intent');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,8 +78,8 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
if (!queue) return;
|
if (!queue) return;
|
||||||
|
|
||||||
if (oldState.channelId && newState.channelId && oldState.channelId !== newState.channelId) {
|
if (oldState.channelId && newState.channelId && oldState.channelId !== newState.channelId) {
|
||||||
if (queue?.connection && newState.member.id === newState.guild.me.id) queue.connection.channel = newState.channel;
|
if (queue?.connection && newState.member.id === newState.guild.members.me.id) queue.connection.channel = newState.channel;
|
||||||
if (newState.member.id === newState.guild.me.id || (newState.member.id !== newState.guild.me.id && oldState.channelId === queue.connection.channel.id)) {
|
if (newState.member.id === newState.guild.members.me.id || (newState.member.id !== newState.guild.members.me.id && oldState.channelId === queue.connection.channel.id)) {
|
||||||
if (!Util.isVoiceEmpty(queue.connection.channel)) return;
|
if (!Util.isVoiceEmpty(queue.connection.channel)) return;
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
if (!Util.isVoiceEmpty(queue.connection.channel)) return;
|
if (!Util.isVoiceEmpty(queue.connection.channel)) return;
|
||||||
|
@ -90,20 +90,20 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
queue._cooldownsTimeout.set(`empty_${oldState.guild.id}`, timeout);
|
queue._cooldownsTimeout.set(`empty_${oldState.guild.id}`, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!oldState.channelId && newState.channelId && newState.member.id === newState.guild.me.id) {
|
if (!oldState.channelId && newState.channelId && newState.member.id === newState.guild.members.me.id) {
|
||||||
if (newState.serverMute || !newState.serverMute) {
|
if (newState.serverMute || !newState.serverMute) {
|
||||||
queue.setPaused(newState.serverMute);
|
queue.setPaused(newState.serverMute);
|
||||||
} else if (newState.suppress || !newState.suppress) {
|
} else if (newState.suppress || !newState.suppress) {
|
||||||
if (newState.suppress) newState.guild.me.voice.setRequestToSpeak(true).catch(Util.noop);
|
if (newState.suppress) newState.guild.members.me.voice.setRequestToSpeak(true).catch(Util.noop);
|
||||||
queue.setPaused(newState.suppress);
|
queue.setPaused(newState.suppress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldState.channelId === newState.channelId && oldState.member.id === newState.guild.me.id) {
|
if (oldState.channelId === newState.channelId && oldState.member.id === newState.guild.members.me.id) {
|
||||||
if (oldState.serverMute !== newState.serverMute) {
|
if (oldState.serverMute !== newState.serverMute) {
|
||||||
queue.setPaused(newState.serverMute);
|
queue.setPaused(newState.serverMute);
|
||||||
} else if (oldState.suppress !== newState.suppress) {
|
} else if (oldState.suppress !== newState.suppress) {
|
||||||
if (newState.suppress) newState.guild.me.voice.setRequestToSpeak(true).catch(Util.noop);
|
if (newState.suppress) newState.guild.members.me.voice.setRequestToSpeak(true).catch(Util.noop);
|
||||||
queue.setPaused(newState.suppress);
|
queue.setPaused(newState.suppress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,7 +338,7 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
return { playlist: null, tracks: res };
|
return { playlist: null, tracks: res };
|
||||||
}
|
}
|
||||||
case QueryType.SPOTIFY_SONG: {
|
case QueryType.SPOTIFY_SONG: {
|
||||||
const spotifyData = await Spotify.getData(query).catch(Util.noop);
|
const spotifyData = await Spotify(Util.getFetch()).getData(query).catch(Util.noop);
|
||||||
if (!spotifyData) return { playlist: null, tracks: [] };
|
if (!spotifyData) return { playlist: null, tracks: [] };
|
||||||
const spotifyTrack = new Track(this, {
|
const spotifyTrack = new Track(this, {
|
||||||
title: spotifyData.name,
|
title: spotifyData.name,
|
||||||
|
@ -359,7 +359,9 @@ class Player extends EventEmitter<PlayerEvents> {
|
||||||
}
|
}
|
||||||
case QueryType.SPOTIFY_PLAYLIST:
|
case QueryType.SPOTIFY_PLAYLIST:
|
||||||
case QueryType.SPOTIFY_ALBUM: {
|
case QueryType.SPOTIFY_ALBUM: {
|
||||||
const spotifyPlaylist = await Spotify.getData(query).catch(Util.noop);
|
const spotifyPlaylist = await Spotify(await Util.getFetch())
|
||||||
|
.getData(query)
|
||||||
|
.catch(Util.noop);
|
||||||
if (!spotifyPlaylist) return { playlist: null, tracks: [] };
|
if (!spotifyPlaylist) return { playlist: null, tracks: [] };
|
||||||
|
|
||||||
const playlist = new Playlist(this, {
|
const playlist = new Playlist(this, {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Collection, Guild, StageChannel, VoiceChannel, Snowflake, SnowflakeUtil, GuildChannelResolvable } from "discord.js";
|
import { Collection, Guild, StageChannel, VoiceChannel, SnowflakeUtil, GuildChannelResolvable, ChannelType } from "discord.js";
|
||||||
import { Player } from "../Player";
|
import { Player } from "../Player";
|
||||||
import { StreamDispatcher } from "../VoiceInterface/StreamDispatcher";
|
import { StreamDispatcher } from "../VoiceInterface/StreamDispatcher";
|
||||||
import Track from "./Track";
|
import Track from "./Track";
|
||||||
|
@ -22,7 +22,7 @@ class Queue<T = unknown> {
|
||||||
public playing = false;
|
public playing = false;
|
||||||
public metadata?: T = null;
|
public metadata?: T = null;
|
||||||
public repeatMode: QueueRepeatMode = 0;
|
public repeatMode: QueueRepeatMode = 0;
|
||||||
public readonly id: Snowflake = SnowflakeUtil.generate();
|
public readonly id = SnowflakeUtil.generate().toString();
|
||||||
private _streamTime = 0;
|
private _streamTime = 0;
|
||||||
public _cooldownsTimeout = new Collection<string, NodeJS.Timeout>();
|
public _cooldownsTimeout = new Collection<string, NodeJS.Timeout>();
|
||||||
private _activeFilters: any[] = []; // eslint-disable-line @typescript-eslint/no-explicit-any
|
private _activeFilters: any[] = []; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
|
@ -152,16 +152,16 @@ class Queue<T = unknown> {
|
||||||
async connect(channel: GuildChannelResolvable) {
|
async connect(channel: GuildChannelResolvable) {
|
||||||
if (this.#watchDestroyed()) return;
|
if (this.#watchDestroyed()) return;
|
||||||
const _channel = this.guild.channels.resolve(channel) as StageChannel | VoiceChannel;
|
const _channel = this.guild.channels.resolve(channel) as StageChannel | VoiceChannel;
|
||||||
if (!["GUILD_STAGE_VOICE", "GUILD_VOICE"].includes(_channel?.type))
|
if (![ChannelType.GuildStageVoice, ChannelType.GuildVoice].includes(_channel?.type))
|
||||||
throw new PlayerError(`Channel type must be GUILD_VOICE or GUILD_STAGE_VOICE, got ${_channel?.type}!`, ErrorStatusCode.INVALID_ARG_TYPE);
|
throw new PlayerError(`Channel type must be GuildVoice or GuildStageVoice, got ${_channel?.type}!`, ErrorStatusCode.INVALID_ARG_TYPE);
|
||||||
const connection = await this.player.voiceUtils.connect(_channel, {
|
const connection = await this.player.voiceUtils.connect(_channel, {
|
||||||
deaf: this.options.autoSelfDeaf
|
deaf: this.options.autoSelfDeaf
|
||||||
});
|
});
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
|
||||||
if (_channel.type === "GUILD_STAGE_VOICE") {
|
if (_channel.type === ChannelType.GuildStageVoice) {
|
||||||
await _channel.guild.me.voice.setSuppressed(false).catch(async () => {
|
await _channel.guild.members.me.voice.setSuppressed(false).catch(async () => {
|
||||||
return await _channel.guild.me.voice.setRequestToSpeak(true).catch(Util.noop);
|
return await _channel.guild.members.me.voice.setRequestToSpeak(true).catch(Util.noop);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,10 +473,10 @@ class Queue<T = unknown> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a track from the queue
|
* Removes a track from the queue
|
||||||
* @param {Track|Snowflake|number} track The track to remove
|
* @param {Track|string|number} track The track to remove
|
||||||
* @returns {Track}
|
* @returns {Track}
|
||||||
*/
|
*/
|
||||||
remove(track: Track | Snowflake | number) {
|
remove(track: Track | string | number) {
|
||||||
if (this.#watchDestroyed()) return;
|
if (this.#watchDestroyed()) return;
|
||||||
let trackFound: Track = null;
|
let trackFound: Track = null;
|
||||||
if (typeof track === "number") {
|
if (typeof track === "number") {
|
||||||
|
@ -496,10 +496,10 @@ class Queue<T = unknown> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the index of the specified track. If found, returns the track index else returns -1.
|
* Returns the index of the specified track. If found, returns the track index else returns -1.
|
||||||
* @param {number|Track|Snowflake} track The track
|
* @param {number|Track|string} track The track
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
getTrackPosition(track: number | Track | Snowflake) {
|
getTrackPosition(track: number | Track | string) {
|
||||||
if (this.#watchDestroyed()) return;
|
if (this.#watchDestroyed()) return;
|
||||||
if (typeof track === "number") return this.tracks[track] != null ? track : -1;
|
if (typeof track === "number") return this.tracks[track] != null ? track : -1;
|
||||||
return this.tracks.findIndex((pred) => pred.id === (track instanceof Track ? track.id : track));
|
return this.tracks.findIndex((pred) => pred.id === (track instanceof Track ? track.id : track));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { User, Util, SnowflakeUtil, Snowflake } from "discord.js";
|
import { User, escapeMarkdown, SnowflakeUtil } from "discord.js";
|
||||||
import { Player } from "../Player";
|
import { Player } from "../Player";
|
||||||
import { RawTrackData, TrackJSON } from "../types/types";
|
import { RawTrackData, TrackJSON } from "../types/types";
|
||||||
import { Playlist } from "./Playlist";
|
import { Playlist } from "./Playlist";
|
||||||
|
@ -16,7 +16,7 @@ class Track {
|
||||||
public requestedBy!: User;
|
public requestedBy!: User;
|
||||||
public playlist?: Playlist;
|
public playlist?: Playlist;
|
||||||
public readonly raw: RawTrackData = {} as RawTrackData;
|
public readonly raw: RawTrackData = {} as RawTrackData;
|
||||||
public readonly id: Snowflake = SnowflakeUtil.generate();
|
public readonly id = SnowflakeUtil.generate().toString();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track constructor
|
* Track constructor
|
||||||
|
@ -109,7 +109,7 @@ class Track {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _patch(data: RawTrackData) {
|
private _patch(data: RawTrackData) {
|
||||||
this.title = Util.escapeMarkdown(data.title ?? "");
|
this.title = escapeMarkdown(data.title ?? "");
|
||||||
this.author = data.author ?? "";
|
this.author = data.author ?? "";
|
||||||
this.url = data.url ?? "";
|
this.url = data.url ?? "";
|
||||||
this.thumbnail = data.thumbnail ?? "";
|
this.thumbnail = data.thumbnail ?? "";
|
||||||
|
|
|
@ -68,6 +68,7 @@ class StreamDispatcher extends EventEmitter<VoiceEvents> {
|
||||||
*/
|
*/
|
||||||
this.paused = false;
|
this.paused = false;
|
||||||
|
|
||||||
|
// @ts-expect-error ???
|
||||||
this.voiceConnection.on("stateChange", async (_, newState) => {
|
this.voiceConnection.on("stateChange", async (_, newState) => {
|
||||||
if (newState.status === VoiceConnectionStatus.Disconnected) {
|
if (newState.status === VoiceConnectionStatus.Disconnected) {
|
||||||
if (newState.reason === VoiceConnectionDisconnectReason.WebSocketClose && newState.closeCode === 4014) {
|
if (newState.reason === VoiceConnectionDisconnectReason.WebSocketClose && newState.closeCode === 4014) {
|
||||||
|
@ -110,6 +111,7 @@ class StreamDispatcher extends EventEmitter<VoiceEvents> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// @ts-expect-error ???
|
||||||
this.audioPlayer.on("stateChange", (oldState, newState) => {
|
this.audioPlayer.on("stateChange", (oldState, newState) => {
|
||||||
if (newState.status === AudioPlayerStatus.Playing) {
|
if (newState.status === AudioPlayerStatus.Playing) {
|
||||||
if (!this.paused) return void this.emit("start", this.audioResource);
|
if (!this.paused) return void this.emit("start", this.audioResource);
|
||||||
|
|
|
@ -95,6 +95,15 @@ class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
static noop() {} // eslint-disable-line @typescript-eslint/no-empty-function
|
static noop() {} // eslint-disable-line @typescript-eslint/no-empty-function
|
||||||
|
|
||||||
|
static async getFetch() {
|
||||||
|
if ("fetch" in globalThis) return globalThis.fetch;
|
||||||
|
try {
|
||||||
|
return await import("undici").then((res) => res.default);
|
||||||
|
} catch {
|
||||||
|
// uh?
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Util };
|
export { Util };
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"include": [
|
|
||||||
"**/*.ts",
|
|
||||||
"**/*.js"
|
|
||||||
],
|
|
||||||
"exclude": []
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES6",
|
"target": "ES2020",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
|
|
Loading…
Reference in a new issue