Documentation of Music & Core (almost done)
This commit is contained in:
22 files changed
+886
-459
No files matched your search
@@ -1,3 +1,13 @@
|
||||
/**
|
||||
* /modules
|
||||
*
|
||||
* Replies module listing as well as description of each.
|
||||
*
|
||||
* Just for the information I guess...
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "$lib/client.js";
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/**
|
||||
* /ping
|
||||
*
|
||||
* Replies websocket ping (ping between bot and discord) and real ping (ping to fetch + process).
|
||||
*
|
||||
* @remarks Probably should double their value as two ways, but nvm.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "$lib/client.js";
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"CR1001": {
|
||||
"id": 652627557,
|
||||
"title": "Imported Modules",
|
||||
"color": 6412972,
|
||||
"fields": [],
|
||||
"footer": {
|
||||
"text": "If there is a bug - submit it"
|
||||
},
|
||||
"timestamp": "2024-01-18T21:00:00.000Z"
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
/**
|
||||
* InteractionCreate
|
||||
*
|
||||
* Resolves creation of an interaction by users.
|
||||
*
|
||||
* @remark Only {@link ChatInputCommand} currently being resolved.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
import { Events, Interaction } from "discord.js";
|
||||
|
||||
import Client from "$lib/client.js";
|
||||
import logger from "$lib/logger.js";
|
||||
|
||||
/**
|
||||
* Resolves creation of an interaction by users.
|
||||
*
|
||||
* @remark Only {@link ChatInputCommand} currently being resolved.
|
||||
*
|
||||
* @param client - Quetza client.
|
||||
* @param eventee - List containing invoked interaction.
|
||||
*/
|
||||
async function execute(client: Client, eventee: [Interaction]): Promise<void> {
|
||||
const [interaction] = eventee;
|
||||
|
||||
@@ -36,7 +37,6 @@ async function execute(client: Client, eventee: [Interaction]): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/** Event name. */
|
||||
const name = Events.InteractionCreate;
|
||||
|
||||
export { execute, name };
|
||||
@@ -1,3 +1,14 @@
|
||||
/**
|
||||
* Ready
|
||||
*
|
||||
* Will execute upon emitting Client's Ready event.
|
||||
*
|
||||
* @remarks Will push slash commands, set activity status,
|
||||
* and log bot's initials.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
import { generateDependencyReport } from "@discordjs/voice";
|
||||
import { Events } from "discord.js";
|
||||
|
||||
@@ -5,14 +16,6 @@ import config from "$config.js";
|
||||
import Client from "$lib/client.js";
|
||||
import logger from "$lib/logger.js";
|
||||
|
||||
/**
|
||||
* Function that will execute upon emitting Client's Ready event.
|
||||
*
|
||||
* @remarks Will push slash commands, set activity status,
|
||||
* and log bot's initials.
|
||||
*
|
||||
* @param client - Quetza client.
|
||||
*/
|
||||
async function execute(client: Client<true>): Promise<void> {
|
||||
const commandData = client.commands.map((value) => value.data);
|
||||
|
||||
@@ -42,7 +45,6 @@ async function execute(client: Client<true>): Promise<void> {
|
||||
client.user.setActivity(config.application.activity);
|
||||
}
|
||||
|
||||
/** Event name. */
|
||||
const name = Events.ClientReady;
|
||||
|
||||
export { execute, name };
|
||||
+16
-2
@@ -1,6 +1,20 @@
|
||||
/** Module's name. */
|
||||
/**
|
||||
* Core Quetza Module definition.
|
||||
*
|
||||
* This module provides default capabilities.
|
||||
* - Comands are being resolved here at InteractionCreated event.
|
||||
* - Aplication status being generated as well as commands pushed to Discord at Ready event.
|
||||
*
|
||||
* Also this module provides basic commands:
|
||||
* - ping
|
||||
* - modules
|
||||
*
|
||||
* There a lot of speaking because it's as important as $lib.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
const name = "core";
|
||||
/** Module's description */
|
||||
const description = "Quetza's core functionality";
|
||||
|
||||
export { description, name };
|
||||
@@ -1,3 +1,12 @@
|
||||
/**
|
||||
* voiceStateUpdate
|
||||
*
|
||||
* Monitors voice states of attended guilds.
|
||||
* If Quetza happens to be alone in the Voice Channel - destroys guilds player and disconnects.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
import { BaseGuildVoiceChannel, User, VoiceState } from "discord.js";
|
||||
|
||||
import Client from "$lib/client.js";
|
||||
@@ -16,6 +25,8 @@ const TIME_TILL_DISCONNECT = 30_000;
|
||||
*
|
||||
* @param user - User that might be in one of a channels.
|
||||
* @param channels - List of channels that might have user as it member.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function resolveVoiceChannel(
|
||||
user: User,
|
||||
@@ -26,15 +37,6 @@ function resolveVoiceChannel(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks voice channel Quetza is connected to.
|
||||
*
|
||||
* @remark Destroys player if Quetza appears to be alone.
|
||||
*
|
||||
* @param client - Quetza client.
|
||||
* @param eventee - Old and new {@link VoiceState}.
|
||||
* @param controller - Module's controller.
|
||||
*/
|
||||
async function execute(
|
||||
client: Client<true>,
|
||||
eventee: [VoiceState, VoiceState],
|
||||
@@ -59,7 +61,6 @@ async function execute(
|
||||
}
|
||||
}
|
||||
|
||||
/** Event name. */
|
||||
const name = "voiceStateUpdate";
|
||||
|
||||
export { execute, name };
|
||||
@@ -1,3 +1,9 @@
|
||||
/**
|
||||
* @see ../../../src/lib/error.ts
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
import QuetzaError from "$lib/error.js";
|
||||
|
||||
export default class MusicError extends QuetzaError {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { DlpDump, dlpExtractorKey, DlpInfo } from "$mlib/types.js";
|
||||
|
||||
/**
|
||||
* Avaliable extractors.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const dlpExtractorsValues = ["Youtube", "Soundcloud"] as const;
|
||||
|
||||
/**
|
||||
@@ -8,6 +13,8 @@ export const dlpExtractorsValues = ["Youtube", "Soundcloud"] as const;
|
||||
* For example:
|
||||
*
|
||||
* @see the {@link youtubeExtractor | youtubeExtractor(...) } function
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type dlpExtractor = (arg0: DlpDump, arg1: (arg0: string) => Promise<DlpInfo>) => Promise<DlpInfo>;
|
||||
|
||||
@@ -19,6 +26,8 @@ type dlpExtractor = (arg0: DlpDump, arg1: (arg0: string) => Promise<DlpInfo>) =>
|
||||
* @param dump - Parsed dlp output
|
||||
* @param dlpInfo - Function to call if any other information needed
|
||||
* (will recurcively call next extractor after its run)
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
const youtubeExtractor: dlpExtractor = async (dump, dlpInfo) => {
|
||||
if (dump.id && dump.title && dump.duration) {
|
||||
@@ -55,6 +64,8 @@ const soundcloudExtractor: dlpExtractor = async (dump, dlpInfo) => {
|
||||
|
||||
/**
|
||||
* DlpDump extractor_key values mapped to corresponding extractor.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
const extractors: Record<dlpExtractorKey, dlpExtractor> = {
|
||||
Youtube: youtubeExtractor,
|
||||
|
||||
@@ -16,16 +16,22 @@ const execFileAsync = promisify(execFile);
|
||||
|
||||
/**
|
||||
* Maximum length of dlp search stack.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const DLPINFO_DEPTH = 3;
|
||||
|
||||
/**
|
||||
* Path to the Dlp binary.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const DLP_BINARY = path.join(config.path.binaries, "/yt-dlp");
|
||||
|
||||
/**
|
||||
* Base arguments for Ffmpeg transcoder.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const FFMPEG_BASE_ARGS = [
|
||||
"-analyzeduration",
|
||||
@@ -48,6 +54,8 @@ const FFMPEG_BASE_ARGS = [
|
||||
* @returns true if given string is a valid URL, false otherwise
|
||||
*
|
||||
* @param str - Any string
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function isURL(str: string): boolean {
|
||||
return /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)$/.test(
|
||||
@@ -64,6 +72,8 @@ function isURL(str: string): boolean {
|
||||
*
|
||||
* @param query - Dlp search request or compatible URL
|
||||
* @param depth - Recursion depth (force stops at 0)
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
async function dlpInfoRecursive(query: string, depth = DLPINFO_DEPTH): Promise<DlpInfo> {
|
||||
const error = (message: string, reason: number, ...meta: unknown[]): DlpInfo => {
|
||||
@@ -119,6 +129,8 @@ async function dlpInfoRecursive(query: string, depth = DLPINFO_DEPTH): Promise<D
|
||||
* @returns Metadata to a youtube video that related to query by title or URL of findings
|
||||
*
|
||||
* @param query - Dlp search request or compatible URL
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export async function dlpInfo(query: string): Promise<DlpInfo> {
|
||||
return await dlpInfoRecursive(isURL(query) ? query : "ytsearch1:" + query);
|
||||
@@ -131,6 +143,8 @@ export async function dlpInfo(query: string): Promise<DlpInfo> {
|
||||
*
|
||||
* @param url - Dlp compatible URL to the requested resource
|
||||
* @param options - Transcoders and dlp options
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function dlpStream(url: string, options?: dlpStreamOptions): Promise<Readable> {
|
||||
let ffmpegArgs = FFMPEG_BASE_ARGS;
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
/**
|
||||
* Filter entry.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type filterValue = {
|
||||
/** Public well formatted name of the filter. */
|
||||
name: string;
|
||||
|
||||
/** FFmpeg argument of this filter. */
|
||||
value: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Record mapping id of filter with its {@link filterValue}.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const FILTERS: Record<string, filterValue> = {
|
||||
bassboost1x: { name: "Bassboost 1x", value: "bass=g=15:f=110:w=0.3" },
|
||||
bassboost2x: { name: "Bassboost 2x", value: "bass=g=30:f=110:w=0.3" },
|
||||
@@ -14,17 +27,47 @@ const FILTERS: Record<string, filterValue> = {
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Type build of {@link FILTERS} keys.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
type filterPreset = keyof typeof FILTERS;
|
||||
|
||||
/**
|
||||
* {@link ./player#Player | Player's } filters options.
|
||||
*
|
||||
* Resolves FFMpeg transcoder arguments that tweak playback stream.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export default class Filter {
|
||||
/**
|
||||
* Filter list
|
||||
*/
|
||||
public static readonly FILTERS = FILTERS;
|
||||
|
||||
/**
|
||||
* Active filters. Mock of {@link FILTERS} object.
|
||||
*/
|
||||
private switches_ = new Map<filterPreset, filterValue>();
|
||||
|
||||
/**
|
||||
* Converts active {@link filterValue | filterValues} to array.
|
||||
*
|
||||
* @returns Array of active filter values.
|
||||
*/
|
||||
public toggled(): filterValue[] {
|
||||
return [...this.switches_.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles filter specified. Clears them all if no arguments provided.
|
||||
*
|
||||
* @returns True if filter was toggled, False otherwise.
|
||||
*
|
||||
* @param query - filter id to toggle. Undefined to clear all.
|
||||
*/
|
||||
public toggle(query?: filterPreset): boolean {
|
||||
if (!query) {
|
||||
this.switches_.clear();
|
||||
@@ -39,14 +82,29 @@ export default class Filter {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates of active filter map.
|
||||
*
|
||||
* @returns Size of active filter map.
|
||||
*/
|
||||
public size(): number {
|
||||
return this.switches_.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are any active filter.
|
||||
*
|
||||
* @returns size === 0 ? True : False.
|
||||
*/
|
||||
public empty(): boolean {
|
||||
return this.size() === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts active filters to FFmpeg compatable argument.
|
||||
*
|
||||
* @returns FFmpeg compatable argument representing active filters.
|
||||
*/
|
||||
public toString(): string {
|
||||
return this.toggled()
|
||||
.map((value) => value.value)
|
||||
|
||||
@@ -2,8 +2,26 @@ import { AudioResource } from "@discordjs/voice";
|
||||
|
||||
import { LoopOption, Track } from "./types.js";
|
||||
|
||||
/**
|
||||
* Length of playback status bar in emoji symbols.
|
||||
*
|
||||
* @intenal
|
||||
*/
|
||||
const STATUSBAR_LENGTH = 15;
|
||||
|
||||
/**
|
||||
* Generates pretty playback status bar out of {@link AudioResource}.
|
||||
*
|
||||
* @remarks Takes duration and current playback duration to 'draw' bar.
|
||||
*
|
||||
* @returns String representing generated status bar.
|
||||
*
|
||||
* @param resource - AudioResource with {@link Track} metadata, so duration and playback duration is available.
|
||||
*
|
||||
* @privateRemarks I don't know why resource can be undefined - I forgor.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function statusBarGenerator(resource?: AudioResource<Track>): string {
|
||||
const duration = resource?.metadata.duration || 1;
|
||||
const playback = Math.min((resource?.playbackDuration || 0) / 1000, duration);
|
||||
@@ -15,6 +33,20 @@ export function statusBarGenerator(resource?: AudioResource<Track>): string {
|
||||
return `[${filled.join("")}${empty.join("")}] ${toISOTime(playback)}/${toISOTime(duration)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts seconds to ISO time string.
|
||||
*
|
||||
* @returns String representing given seconds in ISO format.
|
||||
*
|
||||
* @param amount - Seconds to convert.
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* toISOTime(1337228); // "371:27:08"
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function toISOTime(amount: number): string {
|
||||
const h = Math.floor(amount / 3600);
|
||||
const m = Math.floor((amount % 3600) / 60);
|
||||
@@ -27,19 +59,60 @@ export function toISOTime(amount: number): string {
|
||||
return hDisplay + mDisplay + sDisplay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronous timeout.
|
||||
*
|
||||
* @returns Promise that will be settled after timeout pass.
|
||||
*
|
||||
* @param amount - Milliseconds to wait before settle.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export async function asleep(amount: number): Promise<unknown> {
|
||||
return new Promise((r) => setTimeout(r, amount));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts volume value to emoji representation.
|
||||
*
|
||||
* @remarks In span of 0 ... 100 - emoji from muted to loud.
|
||||
*
|
||||
* @returns String with single emoji representing specified volume.
|
||||
*
|
||||
* @param amount - volume to convert.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function volumeMoji(amount: number): string {
|
||||
return amount > 100 ? "🔊" : amount > 50 ? "🔉" : amount > 0 ? "🔈" : "🔇";
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@link LoopOption} to emoji representation..
|
||||
*
|
||||
* @returns String with single emoji representing specified loop option.
|
||||
*
|
||||
* @param loop - Loop option that will be converted.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function loopMoji(loop: LoopOption): string {
|
||||
return loop === "AUTO" ? "🔀" : loop === "LOOP" ? "🔁" : loop === "NONE" ? "⤵️" : "🔂";
|
||||
}
|
||||
|
||||
export function largestCommonSequence(s1: string, s2: string) {
|
||||
/**
|
||||
* Finds larges common sequence (LCS) of characters withing provided strings.
|
||||
*
|
||||
* @remarks Used to fuzzy search query title of Track.
|
||||
*
|
||||
* @returns Length of the LCS found.
|
||||
*
|
||||
* @param s1 - First string.
|
||||
* @param s2 - Second string.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function largestCommonSequence(s1: string, s2: string): number {
|
||||
s1 = s1.toLowerCase().slice(0, 100);
|
||||
s2 = s2.toLowerCase().slice(0, 100);
|
||||
|
||||
|
||||
@@ -4,9 +4,26 @@ import logger from "$lib/logger.js";
|
||||
|
||||
import Player from "./player.js";
|
||||
|
||||
/**
|
||||
* Module's controller.
|
||||
*
|
||||
* Binds guilds to player.
|
||||
* Creates and destroys them.
|
||||
*/
|
||||
export default class Music {
|
||||
/**
|
||||
* GuildId to {@link Player} mapping.
|
||||
*/
|
||||
private players_ = new Collection<string, Player>();
|
||||
|
||||
/**
|
||||
* Retrieves {@link Player} by {@link Guild}.
|
||||
*
|
||||
* @returns Player if such mapping exists or undefined otherwise.
|
||||
*
|
||||
* @param guild - Discord guild that might has a Player binded.
|
||||
* @param channel - Text channel that should replace Player's one.
|
||||
*/
|
||||
public get(guild: Guild, channel?: GuildTextBasedChannel): Player | undefined {
|
||||
const player = this.players_.get(guild.id);
|
||||
|
||||
@@ -19,6 +36,14 @@ export default class Music {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link Player} and maps it to GuildId.
|
||||
*
|
||||
* @returns Newly created Player.
|
||||
*
|
||||
* @param guild - {@link Guild} that created Player will be binded to.
|
||||
* @param channel - Text channel that used by Player constructor.
|
||||
*/
|
||||
public set(guild: Guild, channel: GuildTextBasedChannel): Player {
|
||||
const player = new Player(guild, this, channel);
|
||||
|
||||
@@ -29,6 +54,11 @@ export default class Music {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deleted {@link Player} and its mapping by GuildId.
|
||||
*
|
||||
* @param guildId - Id of {@link Guild} that must be removed from mapping.
|
||||
*/
|
||||
public delete(guildId: string): void {
|
||||
logger.info("Player was deleted.", { guildId });
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ export default class Player {
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays current {@link queue} track from position specified.
|
||||
* Plays current {@link Queue} track from position specified.
|
||||
*
|
||||
* @param seek - Amount of seconds to start playback from
|
||||
*/
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
import { LoopOption, Track } from "./types.js";
|
||||
|
||||
/**
|
||||
* Class of {@link ./player#Player | Player's} queue.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export default class Queue {
|
||||
/**
|
||||
* Array of {@link Track | Tracks} - content of queue.
|
||||
*/
|
||||
private tracks_: Track[] = [];
|
||||
public get tracks(): Track[] {
|
||||
return this.tracks_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Position of current playing track.
|
||||
*/
|
||||
private position_ = 0;
|
||||
public get position(): number {
|
||||
return this.position_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines behavior of queue.
|
||||
*
|
||||
* @see ./types.ts for more info about values.
|
||||
*/
|
||||
private loop_: LoopOption = "NONE";
|
||||
public get loop(): LoopOption {
|
||||
return this.loop_;
|
||||
@@ -19,11 +35,21 @@ export default class Queue {
|
||||
this.loop_ = x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue overall duration in seconds.
|
||||
*/
|
||||
private duration_ = 0;
|
||||
public get duration(): number {
|
||||
return this.duration_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips current track.
|
||||
*
|
||||
* @remarks Behavior depends on loop option.
|
||||
*
|
||||
* @returns next {@link Track} or undefined if end was reached.
|
||||
*/
|
||||
public next(): Track | undefined {
|
||||
if (this.loop_ === "SONG") {
|
||||
return this.current();
|
||||
@@ -46,6 +72,9 @@ export default class Queue {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffles queue by using simple Math.random.
|
||||
*/
|
||||
public shuffle(): void {
|
||||
let currentIndex = this.tracks_.length;
|
||||
|
||||
@@ -62,15 +91,23 @@ export default class Queue {
|
||||
this.position_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts tracks from provided array at specific index.
|
||||
*
|
||||
* @returns first {@link Track} of inserted Tracks.
|
||||
*
|
||||
* @param tracks - List of Tracks to insert to the queue.
|
||||
* @param index - Position at which insertion will be occurred.
|
||||
*/
|
||||
public push(tracks: Track[], index?: number): Track {
|
||||
for (const track of tracks) {
|
||||
this.duration_ += track.duration;
|
||||
}
|
||||
|
||||
if (index === undefined || index >= this.tracks_.length) {
|
||||
if (!index || index >= this.tracks_.length) {
|
||||
this.tracks_ = this.tracks_.concat(tracks);
|
||||
|
||||
return this.tracks_[this.tracks_.length - 1];
|
||||
return this.tracks_[this.tracks_.length - tracks.length];
|
||||
}
|
||||
|
||||
this.tracks_.splice(Math.max(0, index), 0, ...tracks);
|
||||
@@ -81,6 +118,13 @@ export default class Queue {
|
||||
return this.tracks_[Math.max(0, index)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@link Track} from the queue at position specified.
|
||||
*
|
||||
* @returns Track that was deleted or undefined if not.
|
||||
*
|
||||
* @param query - Index of element that must be deleted.
|
||||
*/
|
||||
public pop(query: number): Track | undefined {
|
||||
if (query >= 0 && query < this.tracks_.length) {
|
||||
const track = this.tracks_[query];
|
||||
@@ -94,6 +138,11 @@ export default class Queue {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns position new value.
|
||||
*
|
||||
* @returns {@link Track} at position specified or undefined if index out of bounds.
|
||||
*/
|
||||
public jump(query: number): Track | undefined {
|
||||
if (query >= 0 && query < this.tracks_.length) {
|
||||
const track = this.tracks_[query];
|
||||
@@ -104,14 +153,27 @@ export default class Queue {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current {@link Track} (at position).
|
||||
*
|
||||
* @returns Track at internal position.
|
||||
*/
|
||||
public current(): Track {
|
||||
return this.tracks_[this.position_];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if queue is empty.
|
||||
*
|
||||
* @returns True is queue is empty, False otherwise.
|
||||
*/
|
||||
public empty(): boolean {
|
||||
return this.tracks_.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears entire queue and sets position to 0.
|
||||
*/
|
||||
public clear(): void {
|
||||
this.tracks_ = [];
|
||||
this.position_ = 0;
|
||||
|
||||
@@ -4,6 +4,8 @@ import { dlpExtractorsValues } from "./fetch/extractors.js";
|
||||
|
||||
/**
|
||||
* Track information fetched by Dlp.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface TrackBase {
|
||||
/** URL of track. */
|
||||
@@ -21,6 +23,8 @@ export interface TrackBase {
|
||||
|
||||
/**
|
||||
* Track information with {@link User} that requested it.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface Track extends TrackBase {
|
||||
/** User requested this track. */
|
||||
@@ -31,21 +35,33 @@ export interface Track extends TrackBase {
|
||||
* Player loop options.
|
||||
*
|
||||
* @remarks LOOP - Looping queue;
|
||||
* SONG - Looping single song;
|
||||
* NONE - Stops after queue end; (default)
|
||||
* AUTO - Random order;
|
||||
* SONG - Looping single song;
|
||||
* NONE - Stops after queue end; (default)
|
||||
* AUTO - Random order;
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type LoopOption = "LOOP" | "SONG" | "NONE" | "AUTO";
|
||||
|
||||
/** Dlp fetched track list. */
|
||||
/**
|
||||
* Dlp fetched track list.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DlpInfo = TrackBase[];
|
||||
|
||||
/** Dlp supported extractors as types. */
|
||||
/**
|
||||
* Dlp supported extractors as types.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type dlpExtractorKey = (typeof dlpExtractorsValues)[number];
|
||||
|
||||
/**
|
||||
* Actual Dlp dump that later translates to {@link DlpInfo}.
|
||||
* */
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface DlpDump {
|
||||
/** Id of resource. */
|
||||
id: string;
|
||||
@@ -71,6 +87,8 @@ export interface DlpDump {
|
||||
|
||||
/**
|
||||
* Options that shall be passed to DlpStream function.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface dlpStreamOptions {
|
||||
/** Amount of seconds to start playback from. */
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
/**
|
||||
* Music module definition.
|
||||
*
|
||||
* Module to play media content from various sites.
|
||||
* @see ./lib/fetch/extractors.ts for site extractors.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
import Music from "./lib/music.js";
|
||||
|
||||
/** Module's name. */
|
||||
const name = "music";
|
||||
/** Module's description. */
|
||||
const description = "Music module";
|
||||
|
||||
/** Module's controller. */
|
||||
const controller = new Music();
|
||||
|
||||
export { controller, description, name };
|
||||
+9
-10
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "quetza",
|
||||
"version": "2023.1.1-pre",
|
||||
"version": "2024.3.0",
|
||||
"description": "Awesome Discord bot that performs various duties",
|
||||
"type": "module",
|
||||
"main": "./dist/src/index.js",
|
||||
@@ -33,22 +33,21 @@
|
||||
"dependencies": {
|
||||
"@discordjs/opus": "^0.9.0",
|
||||
"@discordjs/voice": "^0.16.1",
|
||||
"@prisma/client": "5.7.0",
|
||||
"discord.js": "^14.14.1",
|
||||
"prism-media": "^1.3.5",
|
||||
"tweetnacl": "^1.0.3",
|
||||
"winston": "^3.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.4",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
||||
"@typescript-eslint/parser": "^6.13.2",
|
||||
"eslint": "^8.55.0",
|
||||
"@types/node": "^20.11.24",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||
"@typescript-eslint/parser": "^7.1.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"mocha": "^10.2.0",
|
||||
"prettier": "^3.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||
"mocha": "^10.3.0",
|
||||
"prettier": "^3.2.5",
|
||||
"tsc-alias": "^1.8.8",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
|
||||
Generated
+493
-384
File diff suppressed because it is too large.
Load diff
+2
-5
@@ -26,13 +26,10 @@ export default class QuetzaError extends Error {
|
||||
*
|
||||
* @param msg - Error message (that shall not be displayed to end user)
|
||||
* @param code - {@link code | Code} unique to error in module
|
||||
* @param prefix - Indentifier of module where error has occured
|
||||
* @param prefix - Identifier of module where error has occurred
|
||||
*/
|
||||
public constructor(msg: string, code: number, prefix = QuetzaError.DEFAULT_PREFIX) {
|
||||
super(msg);
|
||||
|
||||
Object.setPrototypeOf(this, QuetzaError.prototype);
|
||||
|
||||
this.code = code;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
@@ -40,7 +37,7 @@ export default class QuetzaError extends Error {
|
||||
/**
|
||||
* Must create a unique error ID if no collision bugs were written.
|
||||
*
|
||||
* @returns error ID as concatination of {@link prefix} and {@link code}
|
||||
* @returns error ID as concatenation of {@link prefix} and {@link code}
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
|
||||
+8
-2
@@ -3,10 +3,16 @@ import winston from "winston";
|
||||
|
||||
import config from "$config.js";
|
||||
|
||||
/** Error logging file path. */
|
||||
/** Error logging file path.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const errorLog = path.join(config.path.log, "error.log");
|
||||
|
||||
/** Combined logging file path. */
|
||||
/** Combined logging file path.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const combinedLog = path.join(config.path.log, "combined.log");
|
||||
|
||||
/**
|
||||
|
||||
+17
-3
@@ -9,6 +9,8 @@ import Client from "$lib/client.js";
|
||||
|
||||
/**
|
||||
* Quetza's module declaration.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ModuleBase {
|
||||
/** Name of a module. */
|
||||
@@ -22,7 +24,9 @@ export interface ModuleBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete {@link ModuleBase | Module} declaration with commands and events.
|
||||
* Complete {@link ModuleBase} declaration with commands and events.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface Module extends ModuleBase {
|
||||
/** Module's commands. */
|
||||
@@ -34,6 +38,8 @@ export interface Module extends ModuleBase {
|
||||
|
||||
/**
|
||||
* Quetza's command interaction declaration.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface CommandBase {
|
||||
/** Command metadata that will be passed to the Discord API. */
|
||||
@@ -48,7 +54,9 @@ export interface CommandBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Command with module included.
|
||||
* {@link ModuleBase} with {@link Module} included.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface Command extends CommandBase {
|
||||
/** Module containing this Command. */
|
||||
@@ -57,6 +65,8 @@ export interface Command extends CommandBase {
|
||||
|
||||
/**
|
||||
* Quetza's event declaration.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface EventBase {
|
||||
/** Name of an event to listen. */
|
||||
@@ -67,7 +77,9 @@ export interface EventBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link EventBase | Event} with {@link Module} included.
|
||||
* {@link EventBase} with {@link Module} included.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface Event extends EventBase {
|
||||
/** Module containing this Event. */
|
||||
@@ -76,6 +88,8 @@ export interface Event extends EventBase {
|
||||
|
||||
/**
|
||||
* Application status provides information that may be useful at initialization.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ApplicationStatus<Ready extends boolean = boolean> {
|
||||
/** Discord's ID of an application. */
|
||||
|
||||
Reference in new issue
Block a user