add error callback to allow errors to be shown within discord

This commit is contained in:
Lexie Love 2023-02-10 12:56:14 -06:00
parent 891b081791
commit 83ede78a1d
9 changed files with 61 additions and 117 deletions

View File

@ -54,7 +54,7 @@ function nowPlayingResponse(bot: Bot, interaction: Interaction) {
} }
} }
function nowPlayingMessage(bot: Bot, guildId: number) { function nowPlayingMessage(bot: Bot, guildId: BigInt) {
const player = bot.helpers.getPlayer(guildId); const player = bot.helpers.getPlayer(guildId);
return <CreateMessage>{ return <CreateMessage>{
embeds: [<Embed>{ embeds: [<Embed>{

View File

@ -60,13 +60,14 @@ export async function play(bot: Bot, interaction: Interaction, _args?) {
} }
let href; let href;
// remove the timestamp from the query
if(parsed_url.href.indexOf("?t=") !== -1) { if(parsed_url.href.indexOf("?t=") !== -1) {
href = parsed_url.href.substring(0, parsed_url.href.indexOf("?")) href = parsed_url.href.substring(0, parsed_url.href.indexOf("?"))
} else { } else {
href = parsed_url.href; href = parsed_url.href;
} }
const result = await player.pushQuery(interaction.user.username, href); 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) { 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)); await editOriginalInteractionResponse(bot, interaction.token, addedToQueueResponse(interaction, result[0].title));
} }
@ -84,72 +85,3 @@ export async function play(bot: Bot, interaction: Interaction, _args?) {
} }
} }
} }
/*import { exists } from "https://deno.land/std@0.161.0/fs/mod.ts";
import { configs } from "../configs.ts";
import { Bot } from "../deps.ts";
import { download, ensureVoiceConnection } from "../utils.ts";
import { type Command } from "../types.ts";
export async function play(bot: Bot, command: Command) {
await ensureVoiceConnection(bot, command.guildId);
const player = bot.helpers.getPlayer(command.guildId);
await player.pushQuery(command.params);
await player.play();
}*/
/*const parsed_url = new URL(url);
let video_id = "";
if(parsed_url.href.indexOf("youtube.com") !== -1) {
video_id = parsed_url.search.substring(3);
} else if(parsed_url.href.indexOf("youtu.be") === -1) {
video_id = parsed_url.pathname.substring(1);
} else {
return {
status: false,
message: "This URL is invalid"
};
}
if (!(await exists(`${configs.project_root}/music/`))) {
await download(video_id);
}*/
// single video
/*
URL {
href: "https://www.youtube.com/watch?v=DhobsmmyGFs",
origin: "https://www.youtube.com",
protocol: "https:",
username: "",
password: "",
host: "www.youtube.com",
hostname: "www.youtube.com",
port: "",
pathname: "/watch",
hash: "",
search: "?v=DhobsmmyGFs"
}
{
endpoint: "stockholm3048.discord.media:443",
sessionId: "74d4d31a8f5c507f9852278867d42c05",
token: "08b9f3bc65a233d5"
}*/
// playlist
/*URL {
href: "https://www.youtube.com/playlist?list=PLvNazUnle2rTZO7OVYhhRdzFb9W4xSpNk",
origin: "https://www.youtube.com",
protocol: "https:",
username: "",
password: "",
host: "www.youtube.com",
hostname: "www.youtube.com",
port: "",
pathname: "/playlist",
hash: "",
search: "?list=PLvNazUnle2rTZO7OVYhhRdzFb9W4xSpNk"
}*/

View File

@ -6,6 +6,7 @@ export type AudioSource = {
data: () => data: () =>
| Promise<AsyncIterableIterator<Uint8Array>> | Promise<AsyncIterableIterator<Uint8Array>>
| AsyncIterableIterator<Uint8Array>; | AsyncIterableIterator<Uint8Array>;
guildId: bigint;
added_by?: string; added_by?: string;
}; };
@ -16,6 +17,7 @@ export function createAudioSource(
data: () => data: () =>
| Promise<AsyncIterableIterator<Uint8Array>> | Promise<AsyncIterableIterator<Uint8Array>>
| AsyncIterableIterator<Uint8Array>, | AsyncIterableIterator<Uint8Array>,
guildId: bigint,
added_by?: string added_by?: string
): AudioSource { ): AudioSource {
lastId++; lastId++;
@ -31,6 +33,7 @@ export function createAudioSource(
return empty(); return empty();
} }
}, },
guildId,
added_by added_by
}; };
} }

View File

@ -2,6 +2,6 @@ import { getYoutubeSources } from "./youtube.ts";
export type LoadSource = typeof loadLocalOrYoutube; export type LoadSource = typeof loadLocalOrYoutube;
export function loadLocalOrYoutube(query: string, added_by?: string) { export function loadLocalOrYoutube(query: string, guildId: bigint, added_by?: string) {
return getYoutubeSources(query, String(added_by)); return getYoutubeSources(guildId, String(added_by), query);
} }

View File

@ -3,31 +3,35 @@ import { bufferIter } from "../../utils/mod.ts";
import { demux } from "../demux/mod.ts"; import { demux } from "../demux/mod.ts";
import { createAudioSource, empty } from "./audio-source.ts"; import { createAudioSource, empty } from "./audio-source.ts";
export async function getYoutubeSources(added_by?: string, ...queries: string[]) { import { errorMessageCallback, parseYoutubeId } from "../../../utils.ts";
const sources = queries.map((query) => getYoutubeSource(query, added_by));
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); const awaitedSources = await Promise.all(sources);
return awaitedSources return awaitedSources
.filter((source) => source !== undefined) .filter((source) => source !== undefined)
.map((source) => source!); .map((source) => source!);
} }
export async function getYoutubeSource(query: string, added_by?: string) { export async function getYoutubeSource(query: string, guildId: bigint, added_by?: string) {
try { try {
query = parseYoutubeId(query);
const results = await YouTube.search(query, { limit: 1, type: "video" }); const results = await YouTube.search(query, { limit: 1, type: "video" });
if (results.length > 0) { if (results.length > 0) {
const { id, title } = results[0]; const { id, title } = results[0];
return createAudioSource(title!, async () => { return createAudioSource(title!, async () => {
try { try {
const stream = await ytDownload(id!, { const stream = await ytDownload(query, {
mimeType: `audio/webm; codecs="opus"`, mimeType: `audio/webm; codecs="opus"`,
}); });
return bufferIter(demux(stream)); return bufferIter(demux(stream));
} catch { } catch {
console.error("error"); errorMessageCallback(guildId, `There was an error trying to play **${title}**:\n
something broke in getYoutubeSource`);
console.log(`Failed to play ${title}\n Returning empty stream`); console.log(`Failed to play ${title}\n Returning empty stream`);
return empty(); return empty();
} }
}, added_by); }, guildId, added_by);
} }
} catch(err) { } catch(err) {
console.error(err); console.error(err);

View File

@ -9,7 +9,7 @@ export type BotData = {
guildData: Map<bigint, ConnectionData>; guildData: Map<bigint, ConnectionData>;
udpSource: EventSource<[UdpArgs]>; udpSource: EventSource<[UdpArgs]>;
bufferSize: number; bufferSize: number;
loadSource: (query: string, added_by?: string) => AudioSource[] | Promise<AudioSource[]>; loadSource: (query: string, guild_id: bigint, added_by?: string) => AudioSource[] | Promise<AudioSource[]>;
}; };
export type ConnectionData = { export type ConnectionData = {

View File

@ -79,24 +79,24 @@ export class QueuePlayer extends Queue<AudioSource> implements Player {
* Interrupts the current song, resumes when finished * Interrupts the current song, resumes when finished
* @param query Loads a universal song (local file or youtube search) * @param query Loads a universal song (local file or youtube search)
*/ */
async interruptQuery(query: string) { async interruptQuery(guildId: bigint, query: string) {
const sources = await this.#loadSource(query as string); const sources = await this.#loadSource(query as string, guildId);
this.#rawPlayer.interrupt(await sources[0].data()); this.#rawPlayer.interrupt(await sources[0].data());
} }
async pushQuery(added_by?: string, ...queries: string[]) { async pushQuery(guildId: bigint, added_by?: string, ...queries: string[]) {
const sources = []; const sources = [];
for (const query of queries) { for (const query of queries) {
sources.push(...(await this.#loadSource(String(added_by), query as string))); sources.push(...(await this.#loadSource(query as string, guildId, String(added_by))));
this.push(...sources); this.push(...sources);
} }
return sources; return sources;
} }
async unshiftQuery(...queries: string[]) { async unshiftQuery(guildId: bigint, ...queries: string[]) {
const sources = []; const sources = [];
for (const query of queries) { for (const query of queries) {
sources.push(...(await this.#loadSource(query as string))); sources.push(...(await this.#loadSource(query as string, guildId)));
this.unshift(...sources); this.unshift(...sources);
} }
return sources; return sources;

View File

@ -30,7 +30,7 @@ export const bot = enableAudioPlugin(baseBot);
bot.events.ready = async function (bot, payload) { bot.events.ready = async function (bot, payload) {
//await registerCommands(bot); //await registerCommands(bot);
console.log(`${cyan("permanent waves")} is ready to go with session id ${yellow(payload.sessionId)}`); console.log(`${cyan("permanent waves")} is ready to go`);
sessionId = payload.sessionId; sessionId = payload.sessionId;
} }

View File

@ -1,9 +1,19 @@
import { ytdl } from "https://deno.land/x/ytdl_core@v0.1.1/mod.ts";
import { Bot } from "https://deno.land/x/discordeno@18.0.1/bot.ts"; import { Bot } from "https://deno.land/x/discordeno@18.0.1/bot.ts";
import { connectToVoiceChannel } from "https://deno.land/x/discordeno@18.0.1/helpers/guilds/mod.ts";
import { configs } from "./configs.ts"; import { configs } from "./configs.ts";
import { getChannel, getChannels, getGuild, type BigString, type Embed, type InteractionCallbackData, type InteractionResponse } from "./deps.ts"; import {
getChannel,
getChannels,
getGuild,
sendMessage,
type BigString,
type CreateMessage,
type Embed,
type InteractionCallbackData,
type InteractionResponse
} from "./deps.ts";
import { bot } from "./main.ts";
import { ConnectionData } from "./discordeno-audio-plugin/mod.ts";
export function channelIsAllowed(guild: string, channel: string) { export function channelIsAllowed(guild: string, channel: string) {
if(`${guild}:${channel}` in configs.allowed_text_channels) { if(`${guild}:${channel}` in configs.allowed_text_channels) {
@ -13,30 +23,6 @@ export function channelIsAllowed(guild: string, channel: string) {
return false; return false;
} }
export async function download(url: string) {
try {
const stream = await ytdl(url, { filter: "audio" });
const chunks: Uint8Array[] = [];
for await (const chunk of stream) {
chunks.push(chunk);
}
const videoDetails = stream.videoInfo.videoDetails;
const blob = new Blob(chunks);
const result = await Deno.writeFile(`${configs.project_root}/music/${videoDetails.videoId}.mp3`, new Uint8Array(await blob.arrayBuffer()));
return {
result: true,
message: `Now playing **${videoDetails.title}**`
};
} catch(err) {
console.error(err);
}
return false;
}
export async function ensureVoiceConnection(bot: Bot, guildId: BigString) { export async function ensureVoiceConnection(bot: Bot, guildId: BigString) {
const channels = await getChannels(bot, guildId); const channels = await getChannels(bot, guildId);
const guild = await getGuild(bot, <BigString>guildId); const guild = await getGuild(bot, <BigString>guildId);
@ -47,9 +33,9 @@ export async function ensureVoiceConnection(bot: Bot, guildId: BigString) {
} }
} }
const channel = await getChannel(bot, channelId); await getChannel(bot, channelId);
try { try {
const connection = await bot.helpers.connectToVoiceChannel(guildId, channelId); await bot.helpers.connectToVoiceChannel(guildId, channelId);
} catch(err) { } catch(err) {
console.error(err); console.error(err);
} }
@ -93,4 +79,23 @@ export const waitingForResponse = <InteractionResponse>{
data: { data: {
content: "waiting for response..." content: "waiting for response..."
} }
}; };
function errorMessage(bot: Bot, guildId: bigint, message: string) {
const player = bot.helpers.getPlayer(guildId);
return <CreateMessage>{
embeds: [<Embed>{
color: configs.embed_color,
description: message
}]
}
}
export async function errorMessageCallback(guildId: bigint, message: string) {
const channel = await getAllowedTextChannel(bot, guildId);
await sendMessage(bot, channel.id, errorMessage(bot, guildId, message));
}
export function parseYoutubeId(url: string) {
return url.substring(url.indexOf("?")+3);
}