feat: voice states handling
This commit is contained in:
parent
da5f4a64ba
commit
a43df93abe
4 changed files with 63 additions and 16 deletions
|
@ -1,4 +1,4 @@
|
|||
import { Client, Collection, Guild, Snowflake, User } from "discord.js";
|
||||
import { Client, Collection, Guild, Snowflake, User, VoiceState } from "discord.js";
|
||||
import { TypedEmitter as EventEmitter } from "tiny-typed-emitter";
|
||||
import { Queue } from "./Structures/Queue";
|
||||
import { VoiceUtils } from "./VoiceInterface/VoiceUtils";
|
||||
|
@ -31,6 +31,39 @@ class DiscordPlayer extends EventEmitter<PlayerEvents> {
|
|||
* @type {Discord.Client}
|
||||
*/
|
||||
this.client = client;
|
||||
|
||||
this.client.on("voiceStateUpdate", this._handleVoiceState.bind(this));
|
||||
}
|
||||
|
||||
private _handleVoiceState(oldState: VoiceState, newState: VoiceState): void {
|
||||
const queue = this.getQueue(oldState.guild.id);
|
||||
if (!queue) return;
|
||||
|
||||
if (oldState.member.id === this.client.user.id && !newState.channelID) {
|
||||
queue.destroy();
|
||||
return void this.emit("botDisconnect", queue);
|
||||
}
|
||||
|
||||
if (!queue.options.leaveOnEmpty || !queue.connection || !queue.connection.channel) return;
|
||||
|
||||
if (!oldState.channelID || newState.channelID) {
|
||||
const emptyTimeout = queue._cooldownsTimeout.get(`empty_${oldState.guild.id}`);
|
||||
const channelEmpty = Util.isVoiceEmpty(queue.connection.channel);
|
||||
|
||||
if (!channelEmpty && emptyTimeout) {
|
||||
clearTimeout(emptyTimeout);
|
||||
queue._cooldownsTimeout.delete(`empty_${oldState.guild.id}`);
|
||||
}
|
||||
} else {
|
||||
if (!Util.isVoiceEmpty(queue.connection.channel)) return;
|
||||
const timeout = setTimeout(() => {
|
||||
if (!Util.isVoiceEmpty(queue.connection.channel)) return;
|
||||
if (!this.queues.has(queue.guild.id)) return;
|
||||
queue.destroy();
|
||||
this.emit("channelEmpty", queue);
|
||||
}, queue.options.leaveOnEmptyCooldown || 0);
|
||||
queue._cooldownsTimeout.set(`empty_${oldState.guild.id}`, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Guild, StageChannel, VoiceChannel } from "discord.js";
|
||||
import { Collection, Guild, Snowflake, StageChannel, VoiceChannel } from "discord.js";
|
||||
import { Player } from "../Player";
|
||||
import { StreamDispatcher } from "../VoiceInterface/BasicStreamDispatcher";
|
||||
import Track from "./Track";
|
||||
|
@ -18,6 +18,7 @@ class Queue<T = unknown> {
|
|||
public playing = false;
|
||||
public metadata?: T = null;
|
||||
public repeatMode: QueueRepeatMode = 0;
|
||||
public _cooldownsTimeout = new Collection<string, NodeJS.Timeout>();
|
||||
|
||||
constructor(player: Player, guild: Guild, options: PlayerOptions = {}) {
|
||||
this.player = player;
|
||||
|
@ -48,6 +49,10 @@ class Queue<T = unknown> {
|
|||
return this.connection.audioResource?.metadata ?? this.tracks[0];
|
||||
}
|
||||
|
||||
nowPlaying() {
|
||||
return this.current;
|
||||
}
|
||||
|
||||
async connect(channel: StageChannel | VoiceChannel) {
|
||||
if (!["stage", "voice"].includes(channel?.type)) throw new TypeError(`Channel type must be voice or stage, got ${channel?.type}!`);
|
||||
const connection = await this.player.voiceUtils.connect(channel);
|
||||
|
@ -125,8 +130,12 @@ class Queue<T = unknown> {
|
|||
if (src && (this.playing || this.tracks.length) && !options.immediate) return this.addTrack(src);
|
||||
const track = options.filtersUpdate ? this.current : src ?? this.tracks.shift();
|
||||
if (!track) return;
|
||||
|
||||
if (!options.filtersUpdate) {
|
||||
this.previousTracks = this.previousTracks.filter((x) => x._trackID !== track._trackID);
|
||||
this.previousTracks.push(track);
|
||||
}
|
||||
|
||||
let stream;
|
||||
if (["youtube", "spotify"].includes(track.raw.source)) {
|
||||
if (track.raw.source === "spotify" && !track.raw.engine) {
|
||||
|
|
|
@ -119,21 +119,21 @@ export enum QueryType {
|
|||
}
|
||||
|
||||
export interface PlayerEvents {
|
||||
botDisconnect: () => any;
|
||||
channelEmpty: () => any;
|
||||
connectionCreate: () => any;
|
||||
botDisconnect: (queue: Queue) => any;
|
||||
channelEmpty: (queue: Queue) => any;
|
||||
connectionCreate: (queue: Queue) => any;
|
||||
debug: (queue: Queue, message: string) => any;
|
||||
error: (queue: Queue, error: Error) => any;
|
||||
musicStop: () => any;
|
||||
noResults: () => any;
|
||||
playlistAdd: () => any;
|
||||
playlistParseEnd: () => any;
|
||||
playlistParseStart: () => any;
|
||||
queueCreate: () => any;
|
||||
musicStop: (queue: Queue) => any;
|
||||
noResults: (queue: Queue) => any;
|
||||
playlistAdd: (queue: Queue) => any;
|
||||
playlistParseEnd: (queue: Queue) => any;
|
||||
playlistParseStart: (queue: Queue) => any;
|
||||
queueCreate: (queue: Queue) => any;
|
||||
queueEnd: (queue: Queue) => any;
|
||||
searchCancel: () => any;
|
||||
searchInvalidResponse: () => any;
|
||||
searchResults: () => any;
|
||||
searchCancel: (queue: Queue) => any;
|
||||
searchInvalidResponse: (queue: Queue) => any;
|
||||
searchResults: (queue: Queue) => any;
|
||||
trackAdd: (queue: Queue, track: Track) => any;
|
||||
tracksAdd: (queue: Queue, track: Track[]) => any;
|
||||
trackStart: (queue: Queue, track: Track) => any;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { StageChannel, VoiceChannel } from "discord.js";
|
||||
import { TimeData } from "../types/types";
|
||||
|
||||
class Util {
|
||||
|
@ -34,6 +35,10 @@ class Util {
|
|||
if (!Array.isArray(arr)) return;
|
||||
return arr[arr.length - 1];
|
||||
}
|
||||
|
||||
static isVoiceEmpty(channel: VoiceChannel | StageChannel) {
|
||||
return channel.members.filter((member) => !member.user.bot).size === 0;
|
||||
}
|
||||
}
|
||||
|
||||
export { Util };
|
||||
|
|
Loading…
Reference in a new issue