update discordeno-audio-plugin, add playlist ability

This commit is contained in:
Lexie Love 2023-03-22 23:30:56 -05:00 committed by A
parent c7c049a097
commit 50e3c4ed21
21 changed files with 341 additions and 168 deletions

5
.gitignore vendored
View File

@ -1 +1,4 @@
/configs.ts
/configs.ts
/errors.txt
/unused_extras.ts
/test

View File

@ -46,6 +46,10 @@ export async function parseCommand(bot: Bot, interaction: Interaction) {
await skip(bot, interaction);
break;
}
case "stop": {
await pause(bot, interaction);
break;
}
case "unloop": {
await unloop(bot, interaction);
break;

View File

@ -7,9 +7,17 @@ import {
type CreateSlashApplicationCommand
} from "../deps.ts";
import { ensureVoiceConnection, formatCallbackData, waitingForResponse } from "../utils.ts";
import { YouTube } from "../discordeno-audio-plugin/deps.ts";
function addedToQueueResponse(interaction: Interaction, title: string) {
import { ensureVoiceConnection, formatCallbackData, isPlaylist, waitingForResponse } from "../utils.ts";
async function addedPlaylistResponse(interaction: Interaction, url: string) {
const playlist = await YouTube.getPlaylist(url);
return formatCallbackData(`${interaction.user.username} added ${playlist.videoCount} videos from [**${playlist.title}**](${interaction!.data!.options![0].value}) to the queue.`,
"Added to queue");
}
function addedSongResponse(interaction: Interaction, title: string) {
return formatCallbackData(`${interaction.user.username} added [**${title}**](${interaction!.data!.options![0].value}) to the queue.`, "Added to queue");
}
@ -74,7 +82,12 @@ export async function play(bot: Bot, interaction: Interaction) {
const result = await player.pushQuery(interaction.guildId, interaction.user.username, href);
if(result && result[0] && parsed_url.href.indexOf("youtube.com") !== -1 || parsed_url.href.indexOf("youtu.be") !== -1 && result[0].title) {
await editOriginalInteractionResponse(bot, interaction.token, addedToQueueResponse(interaction, result[0].title));
if(isPlaylist(parsed_url.href))
{
await editOriginalInteractionResponse(bot, interaction.token, await addedPlaylistResponse(interaction, parsed_url.href));
} else {
await editOriginalInteractionResponse(bot, interaction.token, addedSongResponse(interaction, result[0].title));
}
}
} else {
// restart the player if there's no url

View File

@ -2,5 +2,5 @@ export * from "https://deno.land/x/discordeno@18.0.1/mod.ts";
export * from "https://deno.land/x/discordeno@18.0.1/plugins/cache/mod.ts";
export * as opus from "https://unpkg.com/@evan/wasm@0.0.95/target/opus/deno.js";
export * from "https://unpkg.com/@evan/wasm@0.0.95/target/nacl/deno.js";
export { ytDownload } from "https://deno.land/x/yt_download@1.7/mod.ts";
export { getVideoInfo, ytDownload } from "https://deno.land/x/yt_download@1.7/mod.ts";
export { default as YouTube } from "https://deno.land/x/youtube_sr@v4.1.17/mod.ts";

View File

@ -1,7 +1,23 @@
import { YouTube } from "../../deps.ts";
import { getYoutubeSources } from "./youtube.ts";
import { isPlaylist } from "../../../utils.ts";
export type LoadSource = typeof loadLocalOrYoutube;
export function loadLocalOrYoutube(query: string, guildId: bigint, added_by?: string) {
return getYoutubeSources(guildId, String(added_by), query);
export async function loadLocalOrYoutube(query: string, guildId: bigint, added_by?: string) {
const queries = [];
if(isPlaylist(query))
{
const playlist = await YouTube.getPlaylist(query);
for(const video of playlist.videos) {
const videoId = video.id ? video.id : "";
queries.push(videoId);
}
} else {
queries.push(query);
}
return getYoutubeSources(guildId, String(added_by), queries);
}

View File

@ -1,22 +1,63 @@
import { YouTube, ytDownload } from "../../deps.ts";
import { getVideoInfo, YouTube, ytDownload } from "../../deps.ts";
import { bufferIter, retry } from "../../utils/mod.ts";
import { demux } from "../demux/mod.ts";
import { createAudioSource, empty } from "./audio-source.ts";
import { errorMessageCallback, parseYoutubeId } from "../../../utils.ts";
import { errorMessageCallback, isPlaylist } from "../../../utils.ts";
export async function getYoutubeSources(guildId: bigint, added_by?: string, ...queries: string[]) {
export async function getYoutubeSources(guildId: bigint, added_by?: string, queries: string[]) {
const sources = queries.map((query) => getYoutubeSource(query, guildId, added_by));
const awaitedSources = await Promise.all(sources);
return awaitedSources
.filter((source) => source !== undefined)
.map((source) => source!);
}
export async function getYoutubeSource(query: string, guildId: bigint, added_by?: string) {
/*export async function getYoutubeSource(query: string, guildId: bigint, added_by?: string) {
if(isPlaylist(query)) {
const playlist = await YouTube.getPlaylist(query);
const count = playlist.videoCount;
const sources = [];
for(const video of playlist.videos) {
const videoId = video.id ? video.id : "";
sources.push(getVideo(videoId, guildId, added_by));
}
return sources;
}
return await getVideo(query, guildId, added_by);
}*/
async function getYoutubeSource(query: string, guildId: bigint, added_by?: string) {
try {
query = parseYoutubeId(query);
const results = await YouTube.search(query, { limit: 1, type: "video" });
const result = await getVideoInfo(query);
if(result.videoDetails.videoId) {
const id = result.videoDetails.videoId;
const title = result.videoDetails.title;
return createAudioSource(title, async () => {
const stream = await retry(
async () =>
await ytDownload(id, {
mimeType: `audio/webm; codecs="opus"`,
})
);
if (stream === undefined) {
errorMessageCallback(guildId, `There was an error trying to play **${title}**:\n
The stream couldn't be found`);
console.log(`Failed to play ${title}\n Returning empty stream`);
return empty();
}
return bufferIter(demux(stream));
}, guildId, added_by);
}
//const result = await ytDownload(query, { mimeType: `audio/webm; codecs="opus"`, });
//console.log(result);
/*const results = await YouTube.search(query, { limit: 1, type: "video" });
if (results.length > 0) {
const { id, title } = results[0];
return createAudioSource(title!, async () => {
@ -28,13 +69,13 @@ export async function getYoutubeSource(query: string, guildId: bigint, added_by?
);
if (stream === undefined) {
errorMessageCallback(guildId, `There was an error trying to play **${title}**:\n
something broke in getYoutubeSource`);
The stream couldn't be found`);
console.log(`Failed to play ${title}\n Returning empty stream`);
return empty();
}
return bufferIter(demux(stream));
}, guildId, added_by);
}
}*/
} catch(err) {
console.error(err);
return undefined;

View File

@ -7,17 +7,17 @@ import { sendAudioPacket } from "./udp/packet.ts";
export type BotData = {
bot: Bot;
guildData: Map<bigint, ConnectionData>;
udpSource: EventSource<[UdpArgs]>;
udpSource: EventSource<UdpArgs>;
bufferSize: number;
loadSource: (query: string, guild_id: bigint, added_by?: string) => AudioSource[] | Promise<AudioSource[]>;
};
export type ConnectionData = {
player: QueuePlayer;
audio: EventSource<[Uint8Array]>;
audio: EventSource<Uint8Array>;
guildId: bigint;
udpSocket: Deno.DatagramConn;
udpStream: () => AsyncIterableIterator<Uint8Array>;
udpRaw: EventSource<Uint8Array>;
ssrcToUser: Map<number, bigint>;
usersToSsrc: Map<bigint, number>;
context: {
@ -56,7 +56,7 @@ function randomNBit(n: number) {
export function createBotData(
bot: Bot,
udpSource: EventSource<[UdpArgs]>,
udpSource: EventSource<UdpArgs>,
loadSource: LoadSource,
bufferSize = 10
) {
@ -97,12 +97,12 @@ export function getConnectionData(botId: bigint, guildId: bigint) {
}
}
udpSocket = udpSocket as Deno.DatagramConn;
const udpReceive = new EventSource<[Uint8Array]>();
const udpRaw = new EventSource<Uint8Array>();
data = {
player: undefined as unknown as QueuePlayer,
guildId,
udpSocket,
udpStream: () => udpReceive.iter().map(([packet]) => packet),
udpRaw,
context: {
ssrc: 1,
ready: false,
@ -113,7 +113,7 @@ export function getConnectionData(botId: bigint, guildId: bigint) {
reconnect: 0,
},
connectInfo: {},
audio: new EventSource<[Uint8Array]>(),
audio: new EventSource<Uint8Array>(),
ssrcToUser: new Map<number, bigint>(),
usersToSsrc: new Map<bigint, number>(),
stopHeart: () => {},
@ -125,7 +125,7 @@ export function getConnectionData(botId: bigint, guildId: bigint) {
guildId,
udpSocket,
botData.udpSource,
udpReceive
udpRaw
);
connectAudioIterable(data);
}
@ -133,7 +133,7 @@ export function getConnectionData(botId: bigint, guildId: bigint) {
}
async function connectAudioIterable(conn: ConnectionData) {
for await (const [chunk] of conn.audio.iter()) {
for await (const chunk of conn.audio.iter()) {
await sendAudioPacket(conn, chunk);
}
}
@ -155,8 +155,8 @@ async function connectSocketToSource(
bot: Bot,
guildId: bigint,
socket: Deno.DatagramConn,
source: EventSource<[UdpArgs]>,
localSource: EventSource<[Uint8Array]>
source: EventSource<UdpArgs>,
localSource: EventSource<Uint8Array>
) {
for await (const [data, _address] of socket) {
source.trigger({ bot, guildId, data });

View File

@ -26,15 +26,11 @@ type ReceivedAudio = {
};
export function createOnAudio(
source: EventSource<
[
{
bot: Bot;
guildId: bigint;
data: Uint8Array;
}
]
>
source: EventSource<{
bot: Bot;
guildId: bigint;
data: Uint8Array;
}>
) {
return (
guild: bigint | bigint[],
@ -44,7 +40,6 @@ export function createOnAudio(
const users = asArray(user);
return source
.iter()
.map(([udp]) => udp)
.filter(({ data }) => {
return data[1] === 120;
})

View File

@ -33,7 +33,7 @@ export function enableAudioPlugin<T extends Bot>(
}
function createAudioHelpers(bot: Bot, loadSource: LoadSource) {
const udpSource = new EventSource<[UdpArgs]>();
const udpSource = new EventSource<UdpArgs>();
createBotData(bot, udpSource, loadSource);
const resetPlayer = (guildId: bigint) => {
const conn = getConnectionData(bot.id, guildId);

View File

@ -0,0 +1,35 @@
import { BasicSource } from "../../utils/event-source.ts";
export type AllEventTypes = RawEventTypes | QueueEventTypes;
export type RawEventTypes = "next" | "done";
export type QueueEventTypes = "loop";
export type PlayerListener<T, J extends AllEventTypes> = (
data: EventData<T>[J]
) => void;
type PlayerEvent<T, J extends AllEventTypes> = {
type: J;
data: EventData<T>[J];
};
type EventData<T> = {
next: T;
done: T;
loop: T;
};
export class PlayerEventSource<T, K extends AllEventTypes> {
#source = new BasicSource<PlayerEvent<T, K>>();
on<J extends K>(event: J, listener: PlayerListener<T, J>) {
return this.#source.listen((value) => {
if (value.type === event) {
listener((value as PlayerEvent<T, J>).data);
}
});
}
trigger<J extends K>(event: J, data: EventData<T>[J]) {
this.#source.trigger({ type: event, data });
}
}

View File

@ -1,50 +1,64 @@
import { Queue } from "../../utils/mod.ts";
import { AudioSource, LoadSource } from "../audio-source/mod.ts";
import { ConnectionData } from "../connection-data.ts";
import { PlayerEventSource, AllEventTypes, PlayerListener } from "./events.ts";
import { RawPlayer } from "./raw-player.ts";
import { Player } from "./types.ts";
import { nowPlayingCallback } from "../../../commands/np.ts";
export class QueuePlayer extends Queue<AudioSource> implements Player {
playing = false;
export class QueuePlayer
extends Queue<AudioSource>
implements Player<AudioSource>
{
playing = true;
looping = false;
playingSince?: number;
nowPlaying?: AudioSource;
#rawPlayer: RawPlayer;
#loadSource: LoadSource;
#events = new PlayerEventSource<AudioSource, AllEventTypes>();
constructor(conn: ConnectionData, loadSource: LoadSource) {
super();
this.#loadSource = loadSource;
this.#rawPlayer = new RawPlayer(conn);
this.#startQueue();
this.playing = true;
super.waiting = true;
this.playNext();
this.#rawPlayer.on("done", async () => {
const current = this.current();
if (current) {
this.#events.trigger("done", current);
}
await this.playNext();
if (!this.playing) {
this.pause();
}
});
}
async #setSong(song: AudioSource) {
async playNext() {
let song;
const current = this.current();
if (this.looping && current !== undefined) {
song = current;
this.#events.trigger("loop", song);
} else {
song = await super.next();
this.#events.trigger("next", song);
}
this.playingSince = Date.now();
this.nowPlaying = song;
this.#rawPlayer.setAudio(await song.data());
await nowPlayingCallback(this.#rawPlayer.conn);
await this.#rawPlayer.onDone();
if (this.looping) {
this.#setSong(song);
} else {
this.triggerNext();
}
}
async #startQueue() {
for await (const [song] of this.stream()) {
await this.#setSong(song);
}
clear() {
return super.clear();
}
play() {
this.#rawPlayer.play();
this.playing = true;
this.#rawPlayer.play();
return Promise.resolve();
}
@ -54,12 +68,12 @@ export class QueuePlayer extends Queue<AudioSource> implements Player {
}
stop() {
this.playingSince = undefined;
this.#rawPlayer.clear();
this.skip();
this.pause();
}
skip() {
this.looping = false;
this.#rawPlayer.clear();
}
@ -71,8 +85,23 @@ export class QueuePlayer extends Queue<AudioSource> implements Player {
this.#rawPlayer.interrupt(undefined);
}
interrupt(audio: AsyncIterableIterator<Uint8Array>) {
this.#rawPlayer.interrupt(audio);
/**
* Listen to events:
*
* `next`: New sound started playing
*
* `done`: Last sound is done playing
*
* `loop`: New loop iteration was started
* @param event Event to listen to
* @param listener Triggered on event
* @returns Function that disconnects the listener
*/
on<J extends AllEventTypes>(
event: J,
listener: PlayerListener<AudioSource, J>
) {
return this.#events.on(event, listener);
}
/**

View File

@ -1,33 +1,23 @@
import { EventSource } from "../../utils/mod.ts";
import { ConnectionData } from "../connection-data.ts";
import { FRAME_DURATION } from "../sample-consts.ts";
import { Player } from "./types.ts";
import { setDriftlessInterval, clearDriftless } from "npm:driftless";
import { PlayerEventSource, RawEventTypes, PlayerListener } from "./events.ts";
export class RawPlayer implements Player {
import { errorMessageCallback } from "../../../utils.ts";
export class RawPlayer implements Player<AsyncIterableIterator<Uint8Array>> {
#audio?: AsyncIterableIterator<Uint8Array>;
#interrupt?: AsyncIterableIterator<Uint8Array>;
playing = false;
conn: ConnectionData;
#doneSource = new EventSource<[]>();
#nextSource = new EventSource<[]>();
#onNext = () => this.#nextSource.iter().nextValue();
#events = new PlayerEventSource<
AsyncIterableIterator<Uint8Array>,
RawEventTypes
>();
constructor(conn: ConnectionData) {
this.conn = conn;
this.play();
}
setAudio(audio: AsyncIterableIterator<Uint8Array>) {
this.#audio = audio;
this.#nextSource.trigger();
}
interrupt(audio?: AsyncIterableIterator<Uint8Array>) {
this.#interrupt = audio;
if (this.#audio === undefined) {
this.#nextSource.trigger();
}
}
play() {
@ -40,28 +30,16 @@ export class RawPlayer implements Player {
const inter = setDriftlessInterval(async () => {
if (this.playing === false) {
clearDriftless(inter);
}
if (this.#interrupt) {
const { done, value } = await this.#interrupt.next();
if (done) {
this.#interrupt = undefined;
} else {
this.conn.audio.trigger(value);
return;
}
}
const nextAudioIter = await this.#audio?.next();
if (nextAudioIter === undefined || nextAudioIter.done) {
this.#audio = undefined;
this.#doneSource.trigger();
this.playing = false;
await this.#onNext();
this.play();
return;
}
this.conn.audio.trigger(nextAudioIter.value);
const frame = await this.#getFrame();
if (frame === undefined) {
return;
}
this.conn.audio.trigger(frame);
}, FRAME_DURATION);
} catch(err) {
errorMessageCallback(this.conn.guildId, `The player broke for some reason: ${err}`);
console.log("error while playing!!");
console.error(err);
}
@ -72,16 +50,61 @@ export class RawPlayer implements Player {
}
stop() {
this.pause();
this.clear();
this.pause();
}
clear() {
this.#audio = undefined;
if (this.#audio) {
this.#events.trigger("done", this.#audio);
}
this.#interrupt = undefined;
}
async onDone() {
await this.#doneSource.iter().nextValue();
setAudio(audio: AsyncIterableIterator<Uint8Array>) {
this.#audio = audio;
this.#events.trigger("next", audio);
this.play();
}
interrupt(audio?: AsyncIterableIterator<Uint8Array>) {
this.#interrupt = audio;
if (!this.playing) {
this.play();
}
}
on<J extends RawEventTypes>(
event: J,
listener: PlayerListener<AsyncIterableIterator<Uint8Array>, J>
) {
return this.#events.on(event, listener);
}
async #getFrame() {
const interrupt = await this.#getNextFrame(this.#interrupt);
if (interrupt !== undefined) {
return interrupt;
}
const audio = await this.#getNextFrame(this.#audio);
if (audio === undefined) {
this.#handleAudioStopped();
}
return audio;
}
async #getNextFrame(source: AsyncIterableIterator<Uint8Array> | undefined) {
const nextResult = await source?.next();
return nextResult !== undefined && !nextResult?.done
? nextResult.value
: undefined;
}
#handleAudioStopped() {
if (this.#audio !== undefined) {
this.#events.trigger("done", this.#audio);
}
this.#audio = undefined;
this.playing = false;
}
}

View File

@ -1,8 +1,13 @@
export type Player = {
import { PlayerListener, RawEventTypes } from "./events.ts";
export type Player<T> = {
playing: boolean;
play(): void;
pause(): void;
stop(): void;
clear(): void;
interrupt(audio: AsyncIterableIterator<Uint8Array>): void;
on<J extends RawEventTypes>(
event: J,
listener: PlayerListener<T, J>
): () => void;
};

View File

@ -12,7 +12,7 @@ export async function discoverIP(
port,
transport: "udp",
});
const { value } = await conn.udpStream().next();
const value = await conn.udpRaw.next();
const decoder = new TextDecoder();
const localIp = decoder.decode(value.slice(8, value.indexOf(0, 8)));
const localPort = new DataView(value.buffer).getUint16(72, false);

View File

@ -1,28 +1,17 @@
import { IterSource, fromCallback } from "./iterator/mod.ts";
import { Arr } from "./types.ts";
type Listener<T extends Arr> = (...arg: T) => void;
type Listener<T> = (arg: T) => void;
export class EventSource<T extends Arr> {
export class BasicSource<T> {
listeners: Listener<T>[] = [];
iter: IterSource<T>["iterator"];
disconnect: IterSource<T>["disconnect"];
constructor() {
const { iterator, disconnect } = fromCallback<T>((listener) =>
this.addListener(listener)
);
this.iter = iterator;
this.disconnect = disconnect;
}
trigger(...arg: T) {
trigger(value: T) {
for (const listener of this.listeners) {
listener(...arg);
listener(value);
}
}
addListener(listener: Listener<T>) {
listen(listener: Listener<T>) {
this.listeners.push(listener);
return () => {
this.removeListener(listener);
@ -33,4 +22,31 @@ export class EventSource<T extends Arr> {
const index = this.listeners.indexOf(listener);
this.listeners.splice(index, 1);
}
listenOnce(listener: Listener<T>) {
const disconnect = this.listen((value) => {
disconnect();
listener(value);
});
}
next() {
return new Promise<T>((resolve) => {
this.listenOnce(resolve);
});
}
}
export class EventSource<T> extends BasicSource<T> {
iter: IterSource<T>["iterator"];
disconnect: IterSource<T>["disconnect"];
constructor() {
super();
const { iterator, disconnect } = fromCallback<T>((listener) =>
this.listen(listener)
);
this.iter = iterator;
this.disconnect = disconnect;
}
}

View File

@ -1,22 +1,21 @@
import { Arr } from "../types.ts";
import { pushIter, addIterUtils } from "./util/mod.ts";
type Listener<T> = { push: (arg: T) => void; done: () => void };
export type IterSource<T extends Arr> = ReturnType<typeof fromCallback<T>>;
export type IterSource<T> = ReturnType<typeof fromCallback<T>>;
export function fromCallback<T extends Arr>(
source: (listener: (...values: T) => void) => void,
export function fromCallback<T>(
source: (listener: (value: T) => void) => void,
disconnect?: () => void
) {
let isDone = false;
let listeners: Listener<T>[] = [];
function trigger(...values: T) {
function trigger(value: T) {
if (isDone) {
return;
}
for (const listener of listeners) {
listener.push(values);
listener.push(value);
}
}
source(trigger);
@ -53,4 +52,4 @@ export function fromCallback<T extends Arr>(
},
disconnect: done,
};
}
}

View File

@ -1,15 +1,15 @@
import { assertEquals } from "https://deno.land/std@0.104.0/testing/asserts.ts";
import { arrayMove, arrayShuffle } from "./array.ts";
import { EventSource } from "./event-source.ts";
export class Queue<T> {
#current: T | undefined;
#queue: T[] = [];
#source = new EventSource<[T]>();
waiting = false;
#waiting: ((value: T) => void)[] = [];
clear() {
const cleared = this.#queue;
this.#queue = [];
return cleared;
}
current() {
@ -22,10 +22,15 @@ export class Queue<T> {
push(...values: T[]) {
this.#queue.push(...values);
if (this.waiting) {
this.triggerNext();
this.waiting = false;
for (const waiting of this.#waiting) {
const value = this.#queue.shift();
this.#current = value;
if (value == undefined) {
break;
}
waiting(value);
}
this.#waiting = [];
}
unshift(...values: T[]) {
@ -52,18 +57,15 @@ export class Queue<T> {
return false;
}
triggerNext() {
const value = this.#queue.shift();
async next() {
let value = this.#queue.shift();
this.#current = value;
if (value === undefined) {
this.waiting = true;
} else {
this.#source.trigger(value);
value = await new Promise<T>((resolve) => {
this.#waiting.push(resolve);
});
}
}
stream() {
return this.#source.iter();
return value;
}
}
@ -71,18 +73,15 @@ Deno.test({
name: "Test",
fn: async () => {
const queue = new Queue<string>();
const promise0 = queue.next();
queue.push("Hello");
queue.push("World!");
const messages = queue.stream();
queue.triggerNext();
queue.triggerNext();
queue.triggerNext();
queue.triggerNext();
queue.triggerNext();
assertEquals("Hello", await messages.nextValue());
assertEquals("World!", await messages.nextValue());
assertEquals(undefined, await messages.nextValue());
assertEquals(undefined, await messages.nextValue());
assertEquals(undefined, await messages.nextValue());
assertEquals("Hello", await promise0);
assertEquals("World!", await queue.next());
const promise1 = queue.next();
const promise2 = queue.next();
queue.push("Multiple", "Words!");
assertEquals("Multiple", await promise1);
assertEquals("Words!", await promise2);
},
});
});

View File

@ -1 +0,0 @@
export type Arr = readonly unknown[];

11
main.ts
View File

@ -7,20 +7,18 @@ import {
upsertGlobalApplicationCommands
} from "./deps.ts";
import { parseCommand } from "./commands.ts";
import { cyan, yellow } from "https://deno.land/std@0.161.0/fmt/colors.ts";
import { cyan } from "https://deno.land/std@0.161.0/fmt/colors.ts";
import { helpCommand } from "./commands/help.ts";
import { leaveCommand } from "./commands/leave.ts";
import { loopCommand } from "./commands/loop.ts";
import { npCommand } from "./commands/np.ts";
import { pauseCommand } from "./commands/pause.ts";
import { pauseCommand, stopCommand } from "./commands/pause.ts";
import { playCommand } from "./commands/play.ts";
import { skipCommand } from "./commands/skip.ts";
import { unloopCommand } from "./commands/unloop.ts";
import { enableAudioPlugin } from "./discordeno-audio-plugin/mod.ts";
let sessionId = "";
const baseBot = createBot({
token: configs.discord_token,
intents: Intents.Guilds | Intents.GuildMessages | Intents.GuildVoiceStates,
@ -28,10 +26,9 @@ const baseBot = createBot({
export const bot = enableAudioPlugin(baseBot);
bot.events.ready = async function (bot, payload) {
bot.events.ready = async function () {
//await registerCommands(bot);
console.log(`${cyan("permanent waves")} is ready to go`);
sessionId = payload.sessionId;
}
// Another way to do events
@ -42,7 +39,7 @@ bot.events.interactionCreate = async function (bot, interaction) {
await startBot(bot);
async function registerCommands(bot: Bot) {
console.log(await upsertGlobalApplicationCommands(bot, [helpCommand, leaveCommand, loopCommand, npCommand, pauseCommand, playCommand, skipCommand, unloopCommand]));
console.log(await upsertGlobalApplicationCommands(bot, [helpCommand, leaveCommand, loopCommand, npCommand, pauseCommand, playCommand, skipCommand, stopCommand, unloopCommand]));
}
/*

View File

@ -1,9 +0,0 @@
import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts";
import { parseYoutubeId } from "../utils.ts";
Deno.test("parseYoutubeId", () => {
const url = parseYoutubeId("https://www.youtube.com/watch?v=ylAF0WNvLx0");
assertEquals(url, "ylAF0WNvLx0");
});

View File

@ -95,6 +95,14 @@ export async function errorMessageCallback(guildId: bigint, message: string) {
await sendMessage(bot, channel.id, errorMessage(message));
}
export function isPlaylist(query: string) {
if(query.includes("playlist")){
return true;
}
return false;
}
export function parseYoutubeId(url: string) {
return url.substring(url.indexOf("?")+3);
}