✏️ Discord.js v14 port

This commit is contained in:
DevAndromeda 2022-06-15 18:55:26 +05:45
parent c7a6bf2ec1
commit 6571e2a3c1
19 changed files with 559 additions and 3856 deletions

1
.gitattributes vendored
View file

@ -1 +1,2 @@
* text=auto eol=lf * text=auto eol=lf
*.js,*.mjs linguist-language=TypeScript

2
.gitignore vendored
View file

@ -14,3 +14,5 @@ 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/

View file

@ -1,2 +0,0 @@
# Discord Player Examples
This section contains example bot(s) made with Discord Player.

View file

@ -1,2 +0,0 @@
# Music Bot
Slash commands music bot backed by **[Discord Player](https://discord-player.js.org)**.

View file

@ -1,3 +0,0 @@
module.exports = {
token: process.env.DISCORD_TOKEN
};

View file

@ -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);

View file

@ -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"
}
}

View file

@ -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"
]
}
}

View file

@ -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
View 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`);

View file

@ -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, {

View file

@ -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));

View file

@ -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 ?? "";

View file

@ -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);

View file

@ -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 };

View file

@ -1,8 +0,0 @@
{
"extends": "./tsconfig.json",
"include": [
"**/*.ts",
"**/*.js"
],
"exclude": []
}

View file

@ -1,6 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES6", "target": "ES2020",
"module": "commonjs", "module": "commonjs",
"declaration": true, "declaration": true,
"outDir": "./dist", "outDir": "./dist",

3897
yarn.lock

File diff suppressed because it is too large Load diff