feat: base
This commit is contained in:
parent
311df79adc
commit
fac2662025
9 changed files with 116 additions and 18 deletions
|
@ -66,6 +66,7 @@
|
||||||
"@discordjs/opus": "^0.5.0",
|
"@discordjs/opus": "^0.5.0",
|
||||||
"@types/node": "^14.14.41",
|
"@types/node": "^14.14.41",
|
||||||
"@types/ws": "^7.4.1",
|
"@types/ws": "^7.4.1",
|
||||||
|
"discord-api-types": "^0.18.1",
|
||||||
"discord.js": "^13.0.0-dev.dda5ee2e9f0839d3e42d25114ae1b47355cdfd27",
|
"discord.js": "^13.0.0-dev.dda5ee2e9f0839d3e42d25114ae1b47355cdfd27",
|
||||||
"discord.js-docgen": "discordjs/docgen#ts-patch",
|
"discord.js-docgen": "discordjs/docgen#ts-patch",
|
||||||
"jsdoc-babel": "^0.5.0",
|
"jsdoc-babel": "^0.5.0",
|
||||||
|
|
|
@ -30,10 +30,15 @@ export class Player extends EventEmitter {
|
||||||
return new Promise<Queue>((resolve) => {
|
return new Promise<Queue>((resolve) => {
|
||||||
if (this.queues.has(message.guild.id)) return this.queues.get(message.guild.id);
|
if (this.queues.has(message.guild.id)) return this.queues.get(message.guild.id);
|
||||||
const channel = message.member.voice?.channel;
|
const channel = message.member.voice?.channel;
|
||||||
if (!channel) return void this.emit(
|
if (!channel)
|
||||||
PlayerEvents.ERROR,
|
return void this.emit(
|
||||||
new PlayerError('Voice connection is not available in this server!', PlayerErrorEventCodes.NOT_CONNECTED, message)
|
PlayerEvents.ERROR,
|
||||||
);
|
new PlayerError(
|
||||||
|
'Voice connection is not available in this server!',
|
||||||
|
PlayerErrorEventCodes.NOT_CONNECTED,
|
||||||
|
message
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const queue = new Queue(this, message.guild);
|
const queue = new Queue(this, message.guild);
|
||||||
void this.queues.set(message.guild.id, queue);
|
void this.queues.set(message.guild.id, queue);
|
||||||
|
@ -57,7 +62,7 @@ export class Player extends EventEmitter {
|
||||||
});
|
});
|
||||||
|
|
||||||
return queue;
|
return queue;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getQueue(message: Message) {
|
public getQueue(message: Message) {
|
||||||
|
@ -81,7 +86,7 @@ export class Player extends EventEmitter {
|
||||||
if (query instanceof Track) track = query;
|
if (query instanceof Track) track = query;
|
||||||
else {
|
else {
|
||||||
if (ytdl.validateURL(query)) {
|
if (ytdl.validateURL(query)) {
|
||||||
const info = await ytdl.getBasicInfo(query).catch(() => { });
|
const info = await ytdl.getBasicInfo(query).catch(() => {});
|
||||||
if (!info) return void this.emit(PlayerEvents.NO_RESULTS, message, query);
|
if (!info) return void this.emit(PlayerEvents.NO_RESULTS, message, query);
|
||||||
if (info.videoDetails.isLiveContent && !queue.options.enableLive)
|
if (info.videoDetails.isLiveContent && !queue.options.enableLive)
|
||||||
return void this.emit(
|
return void this.emit(
|
||||||
|
@ -194,4 +199,4 @@ export class Player extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Player;
|
export default Player;
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import Player from "../Player";
|
import Player from '../Player';
|
||||||
|
|
||||||
export class Playlist {
|
export class Playlist {
|
||||||
player: Player;
|
player: Player;
|
||||||
|
|
||||||
constructor(player: Player, data: any) {
|
constructor(player: Player, data: any) {
|
||||||
Object.defineProperty(this, "player", { value: player });
|
Object.defineProperty(this, 'player', { value: player });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Playlist;
|
export default Playlist;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Player } from '../Player';
|
||||||
export class Track {
|
export class Track {
|
||||||
readonly player: Player;
|
readonly player: Player;
|
||||||
readonly message: Message;
|
readonly message: Message;
|
||||||
|
|
||||||
constructor(player: Player, data: any) {
|
constructor(player: Player, data: any) {
|
||||||
Object.defineProperty(this, 'player', { value: player, enumerable: false });
|
Object.defineProperty(this, 'player', { value: player, enumerable: false });
|
||||||
}
|
}
|
||||||
|
|
89
src/VoiceNative/VoiceAdapter.ts
Normal file
89
src/VoiceNative/VoiceAdapter.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import { DiscordGatewayAdapterCreator, DiscordGatewayAdapterLibraryMethods } from '@discordjs/voice';
|
||||||
|
import {
|
||||||
|
VoiceChannel,
|
||||||
|
Snowflake,
|
||||||
|
Client,
|
||||||
|
Constants,
|
||||||
|
WebSocketShard,
|
||||||
|
Guild,
|
||||||
|
StageChannel,
|
||||||
|
Collection
|
||||||
|
} from 'discord.js';
|
||||||
|
import { GatewayVoiceServerUpdateDispatchData, GatewayVoiceStateUpdateDispatchData } from 'discord-api-types/v8';
|
||||||
|
|
||||||
|
class VoiceAdapter {
|
||||||
|
public client: Client;
|
||||||
|
public adapters = new Collection<Snowflake, DiscordGatewayAdapterLibraryMethods>();
|
||||||
|
public clients = new Set<Client>();
|
||||||
|
public guilds = new Collection<WebSocketShard, Set<Snowflake>>();
|
||||||
|
|
||||||
|
constructor(client: Client) {
|
||||||
|
this.client = client;
|
||||||
|
|
||||||
|
Object.defineProperty(this, 'client', {
|
||||||
|
enumerable: false,
|
||||||
|
writable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
trackVoiceState() {
|
||||||
|
if (this.clients.has(this.client)) return;
|
||||||
|
this.clients.add(this.client);
|
||||||
|
|
||||||
|
this.client.ws.on('VOICE_STATE_UPDATE', (data: GatewayVoiceServerUpdateDispatchData) => {
|
||||||
|
this.adapters.get(data.guild_id)?.onVoiceServerUpdate(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.client.ws.on(Constants.WSEvents.VOICE_STATE_UPDATE, (payload: GatewayVoiceStateUpdateDispatchData) => {
|
||||||
|
if (payload.guild_id && payload.session_id && payload.user_id === this.client.user?.id) {
|
||||||
|
this.adapters.get(payload.guild_id)?.onVoiceStateUpdate(payload);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupGuilds(shard: WebSocketShard) {
|
||||||
|
const guilds = this.guilds.get(shard);
|
||||||
|
if (guilds) {
|
||||||
|
for (const guildID of guilds.values()) {
|
||||||
|
this.adapters.get(guildID)?.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trackGuild(guild: Guild) {
|
||||||
|
let guilds = this.guilds.get(guild.shard);
|
||||||
|
if (!guilds) {
|
||||||
|
const cleanup = () => this.cleanupGuilds(guild.shard);
|
||||||
|
guild.shard.on('close', cleanup);
|
||||||
|
guild.shard.on('destroyed', cleanup);
|
||||||
|
guilds = new Set();
|
||||||
|
this.guilds.set(guild.shard, guilds);
|
||||||
|
}
|
||||||
|
|
||||||
|
guilds.add(guild.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function createAdapter(channel: VoiceChannel | StageChannel): DiscordGatewayAdapterCreator {
|
||||||
|
return (methods) => {
|
||||||
|
const adapter = new VoiceAdapter(channel.client);
|
||||||
|
adapter.adapters.set(channel.guild.id, methods);
|
||||||
|
adapter.trackVoiceState();
|
||||||
|
adapter.trackGuild(channel.guild);
|
||||||
|
|
||||||
|
return {
|
||||||
|
sendPayload(data) {
|
||||||
|
if (channel.guild.shard.status === Constants.Status.READY) {
|
||||||
|
channel.guild.shard.send(data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
destroy() {
|
||||||
|
return adapter.adapters.delete(channel.guild.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
1
src/VoiceNative/VoiceSubscriptionManager.ts
Normal file
1
src/VoiceNative/VoiceSubscriptionManager.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
class VoiceSubscriptionManager {}
|
|
@ -17,7 +17,7 @@ export enum PlayerEvents {
|
||||||
SEARCH_RESULTS = 'searchResults',
|
SEARCH_RESULTS = 'searchResults',
|
||||||
TRACK_ADD = 'trackAdd',
|
TRACK_ADD = 'trackAdd',
|
||||||
TRACK_START = 'trackStart'
|
TRACK_START = 'trackStart'
|
||||||
};
|
}
|
||||||
|
|
||||||
export enum PlayerErrorEventCodes {
|
export enum PlayerErrorEventCodes {
|
||||||
DEFAULT = 'PlayerError',
|
DEFAULT = 'PlayerError',
|
||||||
|
@ -28,7 +28,7 @@ export enum PlayerErrorEventCodes {
|
||||||
PARSE_ERROR = 'ParseError',
|
PARSE_ERROR = 'ParseError',
|
||||||
VIDEO_UNAVAILABLE = 'VideoUnavailable',
|
VIDEO_UNAVAILABLE = 'VideoUnavailable',
|
||||||
MUSIC_STARTING = 'MusicStarting'
|
MUSIC_STARTING = 'MusicStarting'
|
||||||
};
|
}
|
||||||
|
|
||||||
export const PlayerOptions: DP_OPTIONS = {
|
export const PlayerOptions: DP_OPTIONS = {
|
||||||
leaveOnEnd: true,
|
leaveOnEnd: true,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message } from "discord.js";
|
import { Message } from 'discord.js';
|
||||||
|
|
||||||
export default class PlayerError extends Error {
|
export default class PlayerError extends Error {
|
||||||
discordMessage: Message;
|
discordMessage: Message;
|
||||||
|
|
|
@ -7,12 +7,15 @@ import { validateURL as SoundcloudValidateURL } from 'soundcloud-scraper';
|
||||||
import { VoiceChannel } from 'discord.js';
|
import { VoiceChannel } from 'discord.js';
|
||||||
|
|
||||||
const spotifySongRegex = /https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:track\/|\?uri=spotify:track:)((\w|-){22})/;
|
const spotifySongRegex = /https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:track\/|\?uri=spotify:track:)((\w|-){22})/;
|
||||||
const spotifyPlaylistRegex = /https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:playlist\/|\?uri=spotify:playlist:)((\w|-){22})/;
|
const spotifyPlaylistRegex =
|
||||||
|
/https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:playlist\/|\?uri=spotify:playlist:)((\w|-){22})/;
|
||||||
const spotifyAlbumRegex = /https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:album\/|\?uri=spotify:album:)((\w|-){22})/;
|
const spotifyAlbumRegex = /https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:album\/|\?uri=spotify:album:)((\w|-){22})/;
|
||||||
const vimeoRegex = /(http|https)?:\/\/(www\.|player\.)?vimeo\.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|video\/|)(\d+)(?:|\/\?)/;
|
const vimeoRegex =
|
||||||
|
/(http|https)?:\/\/(www\.|player\.)?vimeo\.com\/(?:channels\/(?:\w+\/)?|groups\/([^/]*)\/videos\/|video\/|)(\d+)(?:|\/\?)/;
|
||||||
const facebookRegex = /(https?:\/\/)(www\.|m\.)?(facebook|fb).com\/.*\/videos\/.*/;
|
const facebookRegex = /(https?:\/\/)(www\.|m\.)?(facebook|fb).com\/.*\/videos\/.*/;
|
||||||
const reverbnationRegex = /https:\/\/(www.)?reverbnation.com\/(.+)\/song\/(.+)/;
|
const reverbnationRegex = /https:\/\/(www.)?reverbnation.com\/(.+)\/song\/(.+)/;
|
||||||
const attachmentRegex = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/;
|
const attachmentRegex =
|
||||||
|
/^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/;
|
||||||
|
|
||||||
export class Util {
|
export class Util {
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue