UNPKG

damonjs

Version:

A modified Shoukaku wrapper with enhanced queue support.

231 lines (230 loc) 10.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DamonJs = void 0; const events_1 = require("events"); const Interfaces_1 = require("./Modules/Interfaces"); const shoukaku_1 = require("shoukaku"); const DamonJsPlayer_1 = require("./Managers/DamonJsPlayer"); const DamonJsTrack_1 = require("./Managers/Supports/DamonJsTrack"); // Add other methods related to your base class class DamonJs extends events_1.EventEmitter { /** * Initialize a DamonJs instance. * @param DamonJsOptions DamonJsOptions * @param connector Connector * @param nodes NodeOption[] * @param options ShoukakuOptions */ constructor(DamonJsOptions, shoukaku) { super(); this.DamonJsOptions = DamonJsOptions; this.shoukaku = shoukaku; this.skipSpam = this.DamonJsOptions.skipSpam ? this.DamonJsOptions.skipSpam : { rule: { maxhits: 3, timeFrame: 30 * 1000, cooldown: 5 * 1000 }, destroy: { maxhits: 4, timeFrame: 30 * 1000 }, }; this.trackEnd = this.DamonJsOptions.trackEnd ? this.DamonJsOptions.trackEnd : { skip: true }; this.trackException = this.DamonJsOptions.trackException ? this.DamonJsOptions.trackException : { skip: true }; this.trackStuck = this.DamonJsOptions.trackStuck ? this.DamonJsOptions.trackStuck : { skip: true }; this.trackResolveError = this.DamonJsOptions.trackResolveError ? this.DamonJsOptions.trackResolveError : { skip: true }; if (this.DamonJsOptions.plugins) { for (const [, plugin] of this.DamonJsOptions.plugins.entries()) { if (plugin.constructor.name !== 'DamonJsPlugin') throw new Interfaces_1.DamonJsError(1, 'Plugin must be an instance of DamonJsPlugin'); plugin.load(this); } } this.shoukaku.joinVoiceChannel = this.joinVoiceChannel.bind(this); this.players = new Map(); } on(event, listener) { super.on(event, (...args) => listener(...args)); return this; } once(event, listener) { super.once(event, (...args) => listener(...args)); return this; } off(event, listener) { super.off(event, (...args) => listener(...args)); return this; } emit(event, ...data) { return super.emit(event, ...data); } /** * Create a player. * @param options CreatePlayerOptions * @returns Promise<DamonJsPlayer> */ createPlayer(options) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { const exist = this.players.get(options.guildId); if (exist) return exist; if (!options.deaf) options.deaf = false; if (!options.mute) options.mute = false; const shoukakuPlayer = yield this.shoukaku.joinVoiceChannel({ guildId: options.guildId, channelId: options.voiceId, deaf: options.deaf, mute: options.mute, shardId: options.shardId && !isNaN(options.shardId) ? options.shardId : 0, }); const shoukakuConnection = this.shoukaku.connections.get(options.guildId); if (!shoukakuConnection) throw new Interfaces_1.DamonJsError(1, 'Cannot find the shoukaku connection'); const damonjsPlayer = new ((_b = (_a = this.DamonJsOptions.extends) === null || _a === void 0 ? void 0 : _a.player) !== null && _b !== void 0 ? _b : DamonJsPlayer_1.DamonJsPlayer)(this, this.shoukaku, shoukakuPlayer, shoukakuConnection, { data: options.data, textId: options.textId, volume: isNaN(Number(options.volume)) ? 100 : options.volume, }); yield damonjsPlayer.init(); this.players.set(options.guildId, damonjsPlayer); this.emit(Interfaces_1.Events.PlayerCreate, damonjsPlayer); return damonjsPlayer; }); } /** * Get a player by guildId. * @param guildId Guild ID * @returns DamonJsPlayer | undefined */ getPlayer(guildId) { return this.players.get(guildId); } /** * Destroy a player. * @param guildId Guild ID * @returns void */ destroyPlayer(guildId) { const player = this.getPlayer(guildId); if (!player) return; player.destroy(); } /** * Search a track by query or uri. * @param player DamonJs Player * @param query Query * @param options DamonJsOptions * @returns Promise<DamonJsSearchResult> */ search(query, options, player) { return __awaiter(this, void 0, void 0, function* () { const source = Interfaces_1.SourceIDs[((options === null || options === void 0 ? void 0 : options.engine) && ['youtube', 'youtube_music', 'soundcloud'].includes(options.engine) ? options.engine : null) || (!!this.DamonJsOptions.defaultSearchEngine && ['youtube', 'youtube_music', 'soundcloud'].includes(this.DamonJsOptions.defaultSearchEngine) ? this.DamonJsOptions.defaultSearchEngine : null) || 'youtube']; const isUrl = /^https?:\/\/.*/.test(query); const node = player ? player.node : yield this.getLeastUsedNode().catch((_) => null); if (!node) throw new Interfaces_1.DamonJsError(2, 'No nodes are online'); const result = yield node.rest.resolve(!isUrl ? `${source}search:${query}` : query).catch((_) => null); if ((result === null || result === void 0 ? void 0 : result.loadType) === shoukaku_1.LoadType.TRACK && result.data) { return this.buildSearch(undefined, [new DamonJsTrack_1.DamonJsTrack(result.data, options.requester)], Interfaces_1.SearchResultTypes.Track); } else if ((result === null || result === void 0 ? void 0 : result.loadType) === shoukaku_1.LoadType.PLAYLIST && result.data.tracks.length) { return this.buildSearch(result.data, result.data.tracks.map((track) => new DamonJsTrack_1.DamonJsTrack(track, options.requester)), Interfaces_1.SearchResultTypes.Playlist); } else if ((result === null || result === void 0 ? void 0 : result.loadType) === shoukaku_1.LoadType.SEARCH && result.data.length) { return this.buildSearch(undefined, result.data.map((track) => new DamonJsTrack_1.DamonJsTrack(track, options.requester)), Interfaces_1.SearchResultTypes.Search); } else if ((result === null || result === void 0 ? void 0 : result.loadType) === shoukaku_1.LoadType.EMPTY) { return this.buildSearch(undefined, [], Interfaces_1.SearchResultTypes.Empty); } else { return this.buildSearch(undefined, undefined, Interfaces_1.SearchResultTypes.Error); } }); } /** * Retrieves the least used node from the list of nodes. * inspired from kazagumo * @return {Promise<Node>} The least used node */ getLeastUsedNode() { return __awaiter(this, void 0, void 0, function* () { const nodes = [...this.shoukaku.nodes.values()]; const onlineNodes = nodes.filter((node) => node.state === shoukaku_1.Constants.State.CONNECTED); if (!onlineNodes.length) throw new Interfaces_1.DamonJsError(2, 'No nodes are online'); const temp = yield Promise.all(onlineNodes.map((node) => __awaiter(this, void 0, void 0, function* () { return ({ node, players: (yield node.rest.getPlayers()) .filter((x) => this.players.get(x.guildId)) .map((x) => this.players.get(x.guildId)) .filter((x) => x.node.name === node.name).length, }); }))); return temp.reduce((a, b) => (a.players + a.node.penalties < b.players + b.node.penalties ? a : b)).node; }); } buildSearch(playlistInfo, tracks = [], type) { return { playlistInfo, tracks, type: type !== null && type !== void 0 ? type : Interfaces_1.SearchResultTypes.Search, }; } joinVoiceChannel(options) { return __awaiter(this, void 0, void 0, function* () { if (this.shoukaku.connections.has(options.guildId)) throw new Error('This guild already have an existing connection'); const connection = new shoukaku_1.Connection(this.shoukaku, options); this.shoukaku.connections.set(connection.guildId, connection); try { yield connection.connect(); } catch (error) { this.shoukaku.connections.delete(options.guildId); throw error; } try { const node = yield this.getLeastUsedNode().catch((_) => null); if (!node) throw new Error("Can't find any nodes to connect on"); const player = this.shoukaku.options.structures.player ? new this.shoukaku.options.structures.player(connection.guildId, node) : new shoukaku_1.Player(connection.guildId, node); const onUpdate = (state) => { if (state !== shoukaku_1.Constants.VoiceState.SESSION_READY) return; player.sendServerUpdate(connection); }; yield player.sendServerUpdate(connection); connection.on('connectionUpdate', onUpdate); this.shoukaku.players.set(player.guildId, player); return player; } catch (error) { connection.disconnect(); this.shoukaku.connections.delete(options.guildId); throw error; } }); } } exports.DamonJs = DamonJs;