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);
return <CreateMessage>{
embeds: [<Embed>{

View File

@ -60,13 +60,14 @@ export async function play(bot: Bot, interaction: Interaction, _args?) {
}
let href;
// remove the timestamp from the query
if(parsed_url.href.indexOf("?t=") !== -1) {
href = parsed_url.href.substring(0, parsed_url.href.indexOf("?"))
} else {
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) {
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: () =>
| Promise<AsyncIterableIterator<Uint8Array>>
| AsyncIterableIterator<Uint8Array>;
guildId: bigint;
added_by?: string;
};
@ -16,6 +17,7 @@ export function createAudioSource(
data: () =>
| Promise<AsyncIterableIterator<Uint8Array>>
| AsyncIterableIterator<Uint8Array>,
guildId: bigint,
added_by?: string
): AudioSource {
lastId++;
@ -31,6 +33,7 @@ export function createAudioSource(
return empty();
}
},
guildId,
added_by
};
}

View File

@ -2,6 +2,6 @@ import { getYoutubeSources } from "./youtube.ts";
export type LoadSource = typeof loadLocalOrYoutube;
export function loadLocalOrYoutube(query: string, added_by?: string) {
return getYoutubeSources(query, String(added_by));
export function loadLocalOrYoutube(query: string, guildId: bigint, added_by?: string) {
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 { createAudioSource, empty } from "./audio-source.ts";
export async function getYoutubeSources(added_by?: string, ...queries: string[]) {
const sources = queries.map((query) => getYoutubeSource(query, added_by));
import { errorMessageCallback, parseYoutubeId } from "../../../utils.ts";
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, added_by?: string) {
export async function getYoutubeSource(query: string, guildId: bigint, added_by?: string) {
try {
query = parseYoutubeId(query);
const results = await YouTube.search(query, { limit: 1, type: "video" });
if (results.length > 0) {
const { id, title } = results[0];
return createAudioSource(title!, async () => {
try {
const stream = await ytDownload(id!, {
const stream = await ytDownload(query, {
mimeType: `audio/webm; codecs="opus"`,
});
return bufferIter(demux(stream));
} 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`);
return empty();
}
}, added_by);
}, guildId, added_by);
}
} catch(err) {
console.error(err);

View File

@ -9,7 +9,7 @@ export type BotData = {
guildData: Map<bigint, ConnectionData>;
udpSource: EventSource<[UdpArgs]>;
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 = {

View File

@ -79,24 +79,24 @@ export class QueuePlayer extends Queue<AudioSource> implements Player {
* Interrupts the current song, resumes when finished
* @param query Loads a universal song (local file or youtube search)
*/
async interruptQuery(query: string) {
const sources = await this.#loadSource(query as string);
async interruptQuery(guildId: bigint, query: string) {
const sources = await this.#loadSource(query as string, guildId);
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 = [];
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);
}
return sources;
}
async unshiftQuery(...queries: string[]) {
async unshiftQuery(guildId: bigint, ...queries: string[]) {
const sources = [];
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);
}
return sources;

View File

@ -30,7 +30,7 @@ export const bot = enableAudioPlugin(baseBot);
bot.events.ready = async function (bot, payload) {
//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;
}

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 { connectToVoiceChannel } from "https://deno.land/x/discordeno@18.0.1/helpers/guilds/mod.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) {
if(`${guild}:${channel}` in configs.allowed_text_channels) {
@ -13,30 +23,6 @@ export function channelIsAllowed(guild: string, channel: string) {
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) {
const channels = await getChannels(bot, 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 {
const connection = await bot.helpers.connectToVoiceChannel(guildId, channelId);
await bot.helpers.connectToVoiceChannel(guildId, channelId);
} catch(err) {
console.error(err);
}
@ -93,4 +79,23 @@ export const waitingForResponse = <InteractionResponse>{
data: {
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);
}