From 0f6adf77a049b4aa771137a615b205c8bc8cccf1 Mon Sep 17 00:00:00 2001 From: appujet Date: Sat, 14 Sep 2024 18:06:05 +0530 Subject: [PATCH 01/32] beta v5 --- .env.example | 7 +- README.md | 4 +- Translation.md | 61 ++-- biome.json | 7 +- locales/ChineseCN.json | 3 + locales/ChineseTW.json | 3 + locales/French.json | 3 + locales/German.json | 3 + locales/Hindi.json | 3 + locales/Indonesian.json | 3 + locales/Japanese.json | 3 + locales/Norwegian.json | 3 + locales/Polish.json | 3 + locales/Russian.json | 3 + locales/SpanishES.json | 3 + locales/Vietnamese.json | 3 + package.json | 5 +- prisma/schema.prisma | 17 +- scripts/clean.js | 12 +- scripts/{restart.ts => restart.js} | 8 +- src/LavaClient.ts | 6 +- src/commands/config/247.ts | 20 +- src/commands/config/Dj.ts | 2 +- src/commands/config/Language.ts | 4 +- src/commands/config/Prefix.ts | 8 +- src/commands/config/Setup.ts | 12 +- src/commands/dev/CreateInvite.ts | 2 +- src/commands/dev/DeleteInvites.ts | 2 +- src/commands/dev/Deploy.ts | 2 +- src/commands/dev/Eval.ts | 2 +- src/commands/dev/GuildLeave.ts | 2 +- src/commands/dev/GuildList.ts | 2 +- src/commands/dev/Restart.ts | 2 +- src/commands/filters/8d.ts | 73 ---- src/commands/filters/BassBoost.ts | 77 ----- src/commands/filters/Distorsion.ts | 81 ----- src/commands/filters/Karaoke.ts | 77 ----- src/commands/filters/LowPass.ts | 72 ---- src/commands/filters/NightCore.ts | 72 ---- src/commands/filters/Pitch.ts | 81 ----- src/commands/filters/Rate.ts | 81 ----- src/commands/filters/Reset.ts | 57 ---- src/commands/filters/Rotation.ts | 70 ---- src/commands/filters/Speed.ts | 81 ----- src/commands/filters/Tremolo.ts | 72 ---- src/commands/filters/Vibrato.ts | 72 ---- src/commands/info/About.ts | 4 +- src/commands/info/Botinfo.ts | 2 +- src/commands/info/Help.ts | 2 +- src/commands/info/Invite.ts | 4 +- src/commands/info/LavaLink.ts | 4 +- src/commands/info/Ping.ts | 2 +- src/commands/music/Autoplay.ts | 9 +- src/commands/music/ClearQueue.ts | 8 +- src/commands/music/Grab.ts | 13 +- src/commands/music/Join.ts | 30 +- src/commands/music/Leave.ts | 6 +- src/commands/music/Loop.ts | 14 +- src/commands/music/Lyrics.ts | 8 +- src/commands/music/Nowplaying.ts | 12 +- src/commands/music/Pause.ts | 4 +- src/commands/music/Play.ts | 177 +++------- src/commands/music/PlayNext.ts | 176 +++------- src/commands/music/Queue.ts | 18 +- src/commands/music/Remove.ts | 10 +- src/commands/music/Replay.ts | 6 +- src/commands/music/Resume.ts | 6 +- src/commands/music/Search.ts | 75 ++--- src/commands/music/Seek.ts | 10 +- src/commands/music/Shuffle.ts | 8 +- src/commands/music/Skip.ts | 8 +- src/commands/music/Skipto.ts | 6 +- src/commands/music/Stop.ts | 7 +- src/commands/music/Volume.ts | 8 +- src/commands/playlist/AddSong.ts | 35 +- src/commands/playlist/Create.ts | 2 +- src/commands/playlist/Delete.ts | 2 +- src/commands/playlist/List.ts | 2 +- src/commands/playlist/Load.ts | 48 ++- src/commands/playlist/RemoveSong.ts | 7 +- src/commands/playlist/Steal.ts | 6 +- src/config.ts | 42 +-- src/database/server.ts | 151 ++++++--- src/env.ts | 101 ++++++ src/events/client/ChannelDelete.ts | 13 +- src/events/client/GuildCreate.ts | 4 +- src/events/client/GuildDelete.ts | 4 +- src/events/client/InteractionCreate.ts | 18 +- src/events/client/MessageCreate.ts | 18 +- .../{player/NodeRaw.ts => client/Raw.ts} | 9 +- src/events/client/Ready.ts | 13 +- src/events/client/SetupButtons.ts | 246 -------------- src/events/client/SetupSystem.ts | 73 ---- src/events/client/VoiceStateUpdate.ts | 17 +- .../NodeConnect.ts => node/Connect.ts} | 23 +- src/events/node/Destroy.ts | 16 + src/events/player/NodeDestroy.ts | 38 --- src/events/player/NodeDisconnect.ts | 27 -- src/events/player/NodeError.ts | 28 -- src/events/player/NodeReconnect.ts | 27 -- src/events/player/QueueEnd.ts | 56 +--- src/events/player/TrackEnd.ts | 49 +-- src/events/player/TrackStart.ts | 99 +++--- src/index.ts | 51 +-- src/plugin/index.ts | 6 +- src/plugin/plugins/antiCrash.ts | 4 +- src/plugin/plugins/keepAlive.ts | 7 +- src/plugin/plugins/updateStatus.ts | 4 +- src/shard.ts | 22 ++ src/structures/Command.ts | 2 +- src/structures/Context.ts | 4 +- src/structures/Dispatcher.ts | 311 ------------------ src/structures/Event.ts | 12 +- src/structures/I18n.ts | 6 +- src/structures/LavalinkClient.ts | 40 +++ src/structures/Lavamusic.ts | 61 ++-- src/structures/Queue.ts | 92 ------ src/structures/Shoukaku.ts | 50 --- src/structures/index.ts | 13 +- src/types.ts | 7 + src/utils/BotLog.ts | 6 +- src/utils/Buttons.ts | 5 +- src/utils/SetupSystem.ts | 114 ++++--- src/utils/Utils.ts | 15 +- src/utils/functions/player.ts | 24 ++ tsconfig.json | 11 +- 126 files changed, 1049 insertions(+), 2818 deletions(-) rename scripts/{restart.ts => restart.js} (50%) delete mode 100644 src/commands/filters/8d.ts delete mode 100644 src/commands/filters/BassBoost.ts delete mode 100644 src/commands/filters/Distorsion.ts delete mode 100644 src/commands/filters/Karaoke.ts delete mode 100644 src/commands/filters/LowPass.ts delete mode 100644 src/commands/filters/NightCore.ts delete mode 100644 src/commands/filters/Pitch.ts delete mode 100644 src/commands/filters/Rate.ts delete mode 100644 src/commands/filters/Reset.ts delete mode 100644 src/commands/filters/Rotation.ts delete mode 100644 src/commands/filters/Speed.ts delete mode 100644 src/commands/filters/Tremolo.ts delete mode 100644 src/commands/filters/Vibrato.ts create mode 100644 src/env.ts rename src/events/{player/NodeRaw.ts => client/Raw.ts} (63%) delete mode 100644 src/events/client/SetupButtons.ts delete mode 100644 src/events/client/SetupSystem.ts rename src/events/{player/NodeConnect.ts => node/Connect.ts} (58%) create mode 100644 src/events/node/Destroy.ts delete mode 100644 src/events/player/NodeDestroy.ts delete mode 100644 src/events/player/NodeDisconnect.ts delete mode 100644 src/events/player/NodeError.ts delete mode 100644 src/events/player/NodeReconnect.ts create mode 100644 src/shard.ts delete mode 100644 src/structures/Dispatcher.ts create mode 100644 src/structures/LavalinkClient.ts delete mode 100644 src/structures/Queue.ts delete mode 100644 src/structures/Shoukaku.ts create mode 100644 src/utils/functions/player.ts diff --git a/.env.example b/.env.example index 307dd77ef..f1f299634 100644 --- a/.env.example +++ b/.env.example @@ -17,9 +17,4 @@ SEARCH_ENGINE= "YouTubeMusic" # Search engine to be used when playing the song. MAX_PLAYLIST_SIZE= "100" # Max playlist size. MAX_QUEUE_SIZE= "100" # Max queue size. GENIUS_API= "" # Sign up and get your own api at (https://genius.com/) to fetch your lyrics (CLIENT TOKEN) - -# Configuration for multiple Lavalink servers -LAVALINK_SERVERS = '[ - {"url":"localhost:2333","auth":"youshallnotpass","name":"Local Node","secure":false}, - {"url":"localhost:2333","auth":"youshallnotpass2","name":"Another Node","secure":false} -]' +NODES=[{"id":"main","host":"localhost","port":2333,"authorization":"youshallnotpass"}] diff --git a/README.md b/README.md index 2a5ed466f..3399659f9 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ - User-friendly and Easy to Use - Highly Configurable - Customizable Prefix -- Multilingual support +- Multilingual support [Here](/Translation.md) - Hybrid Command Handling (Slash and Normal Commands) - Developed using TypeScript and Discord.js v14 - Advanced Music System @@ -242,4 +242,4 @@ Thanks go to these wonderful people: [license-shield]: https://img.shields.io/github/license/appujet/lavamusic.svg?style=for-the-badge [license-url]: https://github.com/appujet/lavamusic/blob/master/LICENSE [support-server]: https://discord.gg/PMpJnJaHmy -[support-shield]: https://img.shields.io/discord/942117923001098260.svg?style=for-the-badge&logo=discord&colorB=7289DA \ No newline at end of file +[support-shield]: https://img.shields.io/discord/942117923001098260.svg?style=for-the-badge&logo=discord&colorB=7289DA diff --git a/Translation.md b/Translation.md index 6655ed71a..0a2cbdeb3 100644 --- a/Translation.md +++ b/Translation.md @@ -6,7 +6,6 @@ 2. 📋 Copy the contents of the `EnglishUS.json` file into the new file. 3. 🌐 Translate the strings in the new file to the desired language. - ### 📚 Available Translations - [x] English (US) - `EnglishUS.json` (Default) @@ -42,7 +41,6 @@ - [ ] Ukrainian - `Ukrainian.json` (Not Started) - [x] Vietnamese - `Vietnamese.json` [by @nhutlamm](https://github.com/nhutlamm) (Ai Translation - Not Accurate) - ## 📚 How to Use the Translations 1. 📁 Create a new file in the `locales` directory with the name of the language in the format `language_code.json`. For example, `EnglishUS.json` for English, `SpanishES.json` for Spanish, etc. @@ -51,8 +49,8 @@ 3. 🌐 Translate the strings in the new file to the desired language. - ## Have a language to contribute? 🎉 + - Fork the repository. - Add the translation file in the `locales` directory. - Create a pull request with the changes. @@ -65,60 +63,61 @@ - **Do not** add any new keys to the translation JSON file. - **Do not** add any new directories to the repository. - - ## 📝 Translation JSON Structure The translation JSON file should be structured as follows: ```json { - "category": { - "command": { - "description": "Description of the command.", - "content": "Command content.", - "key": "value" - } - } + "category": { + "command": { + "description": "Description of the command.", + "content": "Command content.", + "key": "value" + } + } } ``` ### Example Translation JSON **EnglishUS:** + ```json { - "cmd": { - "ping": { - "description": "Shows the bot's ping.", - "content": "Pinging...", - "bot_latency": "Bot Latency", - "api_latency": "API Latency", - "requested_by": "Requested by {author}" - } - } + "cmd": { + "ping": { + "description": "Shows the bot's ping.", + "content": "Pinging...", + "bot_latency": "Bot Latency", + "api_latency": "API Latency", + "requested_by": "Requested by {author}" + } + } } ``` **Hindi:** + ```json { - "cmd": { - "ping": { - "description": "बॉट का पिंग दिखाता है।", - "content": "पिंगिंग...", - "bot_latency": "पिंगिंग...", - "api_latency": "एपीआई लेटेंसी", - "requested_by": "{author} द्वारा अनुरोधित" - } - } + "cmd": { + "ping": { + "description": "बॉट का पिंग दिखाता है।", + "content": "पिंगिंग...", + "bot_latency": "पिंगिंग...", + "api_latency": "एपीआई लेटेंसी", + "requested_by": "{author} द्वारा अनुरोधित" + } + } } ``` ### Formatting Tags for i18n NPM -To ensure `{}` are not removed during translations, use the format tags: `["{", "}"]`. +To ensure `{}` are not removed during translations, use the format tags: `["{", "}"]`. ## 📚 Resources + - [i18n NPM](https://www.npmjs.com/package/i18n) - [Discord Developer Portal - Locales](https://discord.com/developers/docs/reference#locales) diff --git a/biome.json b/biome.json index 0411b661d..0c2f79ce8 100644 --- a/biome.json +++ b/biome.json @@ -29,11 +29,13 @@ "useFilenamingConvention": "off", "useNamingConvention": "off", "useNumberNamespace": "off", - "useSingleCaseStatement": "off" + "useSingleCaseStatement": "off", + "useImportType": "warn" }, "complexity": { "noBannedTypes": "off", "noForEach": "off", + "useOptionalChain": "off", "noStaticOnlyClass": "off", "noExcessiveCognitiveComplexity": { "level": "warn", @@ -47,7 +49,8 @@ }, "correctness": { "noNodejsModules": "off", - "noVoidTypeReturn": "off" + "noVoidTypeReturn": "off", + "useImportExtensions": "off" }, "performance": { "noBarrelFile": "off", diff --git a/locales/ChineseCN.json b/locales/ChineseCN.json index 83ef7e43f..322be9fe5 100644 --- a/locales/ChineseCN.json +++ b/locales/ChineseCN.json @@ -494,6 +494,9 @@ "playlist_stolen": "已成功从 {user} 窃取播放列表 `{playlist}`。", "error_occurred": "窃取播放列表时出错。" } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/locales/ChineseTW.json b/locales/ChineseTW.json index 39e6fb2bb..e9c3d3c33 100644 --- a/locales/ChineseTW.json +++ b/locales/ChineseTW.json @@ -494,6 +494,9 @@ "playlist_stolen": "已成功從 {user} 竊取播放清單 `{playlist}`。", "error_occurred": "竊取播放清單時出錯。" } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/locales/French.json b/locales/French.json index cee09e0bc..c9c70a1d2 100644 --- a/locales/French.json +++ b/locales/French.json @@ -494,6 +494,9 @@ "language": "La langue que vous souhaitez définir", "reset": "Change la langue par défaut" } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/locales/German.json b/locales/German.json index 9d423d5bb..f8e495061 100644 --- a/locales/German.json +++ b/locales/German.json @@ -494,6 +494,9 @@ "language": "Die Sprache, die du einstellen möchtest", "reset": "Ändere die Sprache zurück zur Standardsprache" } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/locales/Hindi.json b/locales/Hindi.json index 6585ccb86..21760c3ad 100644 --- a/locales/Hindi.json +++ b/locales/Hindi.json @@ -494,6 +494,9 @@ "playlist_stolen": "{user} se playlist `{playlist}` ko safaltapoorvak chura लिया गया है.", "error_occurred": "Playlist ko churaate समय एक error aaya." } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/locales/Indonesian.json b/locales/Indonesian.json index 313551799..4cd3616fe 100644 --- a/locales/Indonesian.json +++ b/locales/Indonesian.json @@ -494,6 +494,9 @@ "playlist_stolen": "Berhasil mencuri playlist `{playlist}` dari {user}.", "error_occurred": "Terjadi kesalahan saat mencuri Playlist." } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/locales/Japanese.json b/locales/Japanese.json index 07de728d6..dc8899c8f 100644 --- a/locales/Japanese.json +++ b/locales/Japanese.json @@ -494,6 +494,9 @@ "playlist_stolen": "{user} からプレイリスト `{playlist}` を正常に盗みました。", "error_occurred": "プレイリストの盗難中にエラーが発生しました。" } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/locales/Norwegian.json b/locales/Norwegian.json index dcbf8baf3..65112849e 100644 --- a/locales/Norwegian.json +++ b/locales/Norwegian.json @@ -494,6 +494,9 @@ "playlist_stolen": "Stjal spillelisten `{playlist}` fra {user}.", "error_occurred": "En feil oppstod under stjeling av spillelisten." } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/locales/Polish.json b/locales/Polish.json index afc79c502..4014481c7 100644 --- a/locales/Polish.json +++ b/locales/Polish.json @@ -494,6 +494,9 @@ "playlist_stolen": "Pomyślnie skradziono playlistę `{playlist}` od {user}.", "error_occurred": "Wystąpił błąd podczas kradzieży playlisty." } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/locales/Russian.json b/locales/Russian.json index 6ed4e962f..4311e12a3 100644 --- a/locales/Russian.json +++ b/locales/Russian.json @@ -494,6 +494,9 @@ "playlist_stolen": "Успешно украден плейлист `{playlist}` у {user}.", "error_occurred": "Произошла ошибка при краже плейлиста." } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/locales/SpanishES.json b/locales/SpanishES.json index c27a668e1..9df924bff 100644 --- a/locales/SpanishES.json +++ b/locales/SpanishES.json @@ -494,6 +494,9 @@ "language": "El idioma que quieres establecer", "reset": "Cambia el idioma de nuevo al idioma predeterminado" } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/locales/Vietnamese.json b/locales/Vietnamese.json index 92f7d1b08..ab5b6b6ea 100644 --- a/locales/Vietnamese.json +++ b/locales/Vietnamese.json @@ -494,6 +494,9 @@ "playlist_stolen": "Đã đánh cắp danh sách phát `{playlist}` từ {user} thành công.", "error_occurred": "Đã xảy ra lỗi khi đánh cắp danh sách phát." } + }, + "lyrics": { + "description": "Get's the lyrics of the currently playing track" } }, "buttons": { diff --git a/package.json b/package.json index 55f74b00c..a5365d550 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,6 @@ "version": "4.6.7", "description": "LavaMusic is a music bot for Discord, written in JavaScript using the Discord.js, Typescript, Shoukaku (Lavalink) library.", "main": "dist/index.js", - "type": "module", "scripts": { "start": "npm run clean && node .", "db:push": "npx prisma db push", @@ -48,12 +47,14 @@ "dotenv": "^16.4.5", "genius-lyrics-api": "^3.2.1", "i18n": "^0.15.1", + "lavalink-client": "https://pkg.pr.new/moe-music/lavalink-client@f95457c", "node-system-stats": "^1.3.0", "shoukaku": "^4.1.1", "signale": "^1.4.0", "topgg-autoposter": "^2.0.2", "tslib": "^2.7.0", - "undici": "^6.19.8" + "undici": "^6.19.8", + "zod": "^3.23.8" }, "signale": { "displayScope": true, diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ad4099269..2187ca2dd 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -51,23 +51,14 @@ model Role { } model Playlist { - id String @id @default(uuid()) - userId String - name String - songs Song[] + id String @id @default(uuid()) + userId String + name String + tracks String? // Store the array of encoded tracks as a JSON string @@unique([userId, name]) } -model Song { - id String @id @default(uuid()) - track String - playlistId String - playlist Playlist @relation(fields: [playlistId], references: [id]) - - @@unique([track, playlistId]) -} - model Setup { guildId String @id textId String diff --git a/scripts/clean.js b/scripts/clean.js index 4ce4104c4..671a864e4 100644 --- a/scripts/clean.js +++ b/scripts/clean.js @@ -1,12 +1,12 @@ -import { existsSync } from "node:fs"; -import { rm } from "node:fs/promises"; -import { resolve } from "node:path"; +const fs = require("node:fs"); +const { rm } = require("node:fs").promises; +const path = require("node:path"); async function clean() { try { - const path = resolve("dist"); - if (existsSync(path)) { - await rm(path, { recursive: true, force: true }); + const distPath = path.resolve("dist"); + if (fs.existsSync(distPath)) { + await rm(distPath, { recursive: true, force: true }); } } catch (error) { console.error("Error while cleaning dist folder:", error); diff --git a/scripts/restart.ts b/scripts/restart.js similarity index 50% rename from scripts/restart.ts rename to scripts/restart.js index f2ba2a50f..d377f4397 100644 --- a/scripts/restart.ts +++ b/scripts/restart.js @@ -1,13 +1,13 @@ -import { exec } from "node:child_process"; +const { exec } = require("node:child_process"); -async function startLavamusic(): Promise { - exec("npm start", (error, stderr) => { +async function startLavamusic() { + exec("npm start", (error, stdout, stderr) => { if (error) { console.error(`Error starting Lavamusic: ${error.message}`); return; } if (stderr) { - console.error(`Error output: ${stderr}`); + console.error(`Error starting Lavamusic: ${stderr}`); } }); } diff --git a/src/LavaClient.ts b/src/LavaClient.ts index 2c1663f04..b173f4665 100644 --- a/src/LavaClient.ts +++ b/src/LavaClient.ts @@ -1,6 +1,6 @@ import { type ClientOptions, GatewayIntentBits } from "discord.js"; -import config from "./config.js"; -import Lavamusic from "./structures/Lavamusic.js"; +import Lavamusic from "./structures/Lavamusic"; +import { env } from "./env"; const { GuildMembers, MessageContent, GuildVoiceStates, GuildMessages, Guilds, GuildMessageTyping } = GatewayIntentBits; @@ -10,7 +10,7 @@ const clientOptions: ClientOptions = { }; const client = new Lavamusic(clientOptions); -client.start(config.token); +client.start(env.TOKEN); /** * Project: lavamusic diff --git a/src/commands/config/247.ts b/src/commands/config/247.ts index 25e8d5f31..7e531c3df 100644 --- a/src/commands/config/247.ts +++ b/src/commands/config/247.ts @@ -1,5 +1,5 @@ import type { GuildMember } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class _247 extends Command { constructor(client: Lavamusic) { @@ -33,7 +33,7 @@ export default class _247 extends Command { public async run(client: Lavamusic, ctx: Context): Promise { const embed = this.client.embed(); - let player = client.shoukaku.players.get(ctx.guild!.id) as any; + let player = client.manager.getPlayer(ctx.guild.id) try { const data = await client.db.get_247(ctx.guild!.id); const member = ctx.member as GuildMember; @@ -50,13 +50,17 @@ export default class _247 extends Command { } await client.db.set_247(ctx.guild!.id, ctx.channel.id, member.voice.channel.id); if (!player) { - player = await client.queue.create( - ctx.guild, - member.voice.channel, - ctx.channel, - client.shoukaku.options.nodeResolver(client.shoukaku.nodes), - ); + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: member.voice.channel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + instaUpdateFiltersFix: true, + vcRegion: member.voice.channel.rtcRegion, + }) } + if (!player.connected) await player.connect(); return await ctx.sendMessage({ embeds: [embed.setDescription(ctx.locale("cmd.247.messages.enabled")).setColor(this.client.color.main)], }); diff --git a/src/commands/config/Dj.ts b/src/commands/config/Dj.ts index 2cafdaf6e..99b92922d 100644 --- a/src/commands/config/Dj.ts +++ b/src/commands/config/Dj.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Dj extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/config/Language.ts b/src/commands/config/Language.ts index 050160751..e90de4ea9 100644 --- a/src/commands/config/Language.ts +++ b/src/commands/config/Language.ts @@ -1,6 +1,6 @@ import type { AutocompleteInteraction } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; -import { Language, LocaleFlags } from "../../types.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Language, LocaleFlags } from "../../types"; export default class LanguageCommand extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/config/Prefix.ts b/src/commands/config/Prefix.ts index b745b03bd..a4dd58624 100644 --- a/src/commands/config/Prefix.ts +++ b/src/commands/config/Prefix.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Prefix extends Command { constructor(client: Lavamusic) { @@ -68,7 +68,7 @@ export default class Prefix extends Command { switch (subCommand) { case "set": { if (!prefix) { - const currentPrefix = guildData ? guildData.prefix : client.config.prefix; + const currentPrefix = guildData ? guildData.prefix : client.env.PREFIX; embed.setDescription( ctx.locale("cmd.prefix.messages.current_prefix", { prefix: currentPrefix, @@ -85,7 +85,7 @@ export default class Prefix extends Command { return await ctx.sendMessage({ embeds: [embed] }); } case "reset": { - const defaultPrefix = client.config.prefix; + const defaultPrefix = client.env.PREFIX; await client.db.setPrefix(guildId, defaultPrefix); embed.setDescription( ctx.locale("cmd.prefix.messages.prefix_reset", { @@ -95,7 +95,7 @@ export default class Prefix extends Command { return await ctx.sendMessage({ embeds: [embed] }); } default: { - const currentPrefix = guildData ? guildData.prefix : client.config.prefix; + const currentPrefix = guildData ? guildData.prefix : client.env.PREFIX; embed.setDescription( ctx.locale("cmd.prefix.messages.current_prefix", { prefix: currentPrefix, diff --git a/src/commands/config/Setup.ts b/src/commands/config/Setup.ts index f261efd97..b2fe14649 100644 --- a/src/commands/config/Setup.ts +++ b/src/commands/config/Setup.ts @@ -1,6 +1,6 @@ import { ChannelType, OverwriteType, PermissionFlagsBits } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; -import { getButtons } from "../../utils/Buttons.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { getButtons } from "../../utils/Buttons"; export default class Setup extends Command { constructor(client: Lavamusic) { @@ -90,11 +90,11 @@ export default class Setup extends Command { }, ], }); - const player = this.client.queue.get(ctx.guild!.id); + const player = this.client.manager.getPlayer(ctx.guild!.id); const image = this.client.config.links.img; const desc = - player?.queue && player.current - ? `[${player.current.info.title}](${player.current.info.uri})` + player.queue.current + ? `[${player.queue.current.info.title}](${player.queue.current.info.uri})` : ctx.locale("player.setupStart.nothing_playing"); embed.setDescription(desc).setImage(image); await textChannel @@ -129,7 +129,7 @@ export default class Setup extends Command { } client.db.deleteSetup(ctx.guild!.id); const textChannel = ctx.guild.channels.cache.get(data2.textId); - if (textChannel) await textChannel.delete().catch(() => {}); + if (textChannel) await textChannel.delete().catch(() => { }); await ctx.sendMessage({ embeds: [ { diff --git a/src/commands/dev/CreateInvite.ts b/src/commands/dev/CreateInvite.ts index fc0bcdfd1..df994bdfc 100644 --- a/src/commands/dev/CreateInvite.ts +++ b/src/commands/dev/CreateInvite.ts @@ -1,5 +1,5 @@ import { ChannelType, PermissionFlagsBits, type TextChannel } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class CreateInvite extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/dev/DeleteInvites.ts b/src/commands/dev/DeleteInvites.ts index c63e6f72d..31eeb71a0 100644 --- a/src/commands/dev/DeleteInvites.ts +++ b/src/commands/dev/DeleteInvites.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class DestroyInvites extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/dev/Deploy.ts b/src/commands/dev/Deploy.ts index 9203a3397..2d011ae57 100644 --- a/src/commands/dev/Deploy.ts +++ b/src/commands/dev/Deploy.ts @@ -1,5 +1,5 @@ import { ActionRowBuilder, ButtonBuilder, type ButtonInteraction, ButtonStyle, ComponentType, type Message } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Deploy extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/dev/Eval.ts b/src/commands/dev/Eval.ts index 6f0e890fc..810128993 100644 --- a/src/commands/dev/Eval.ts +++ b/src/commands/dev/Eval.ts @@ -1,7 +1,7 @@ import util from "node:util"; import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; import { fetch } from "undici"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Eval extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/dev/GuildLeave.ts b/src/commands/dev/GuildLeave.ts index e08c6b65b..d8f5ff80c 100644 --- a/src/commands/dev/GuildLeave.ts +++ b/src/commands/dev/GuildLeave.ts @@ -1,5 +1,5 @@ import { ChannelType, type TextChannel } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class GuildLeave extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/dev/GuildList.ts b/src/commands/dev/GuildList.ts index 7d7373aa3..cb92967b7 100644 --- a/src/commands/dev/GuildList.ts +++ b/src/commands/dev/GuildList.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class GuildList extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/dev/Restart.ts b/src/commands/dev/Restart.ts index b947d7981..0467cde82 100644 --- a/src/commands/dev/Restart.ts +++ b/src/commands/dev/Restart.ts @@ -1,6 +1,6 @@ import { exec } from "node:child_process"; import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Restart extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/filters/8d.ts b/src/commands/filters/8d.ts deleted file mode 100644 index cf10b89d1..000000000 --- a/src/commands/filters/8d.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class _8d extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "8d", - description: { - content: "cmd.8d.description", - examples: ["8d"], - usage: "8d", - }, - category: "filters", - aliases: ["3d"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); - const filterEnabled = player.filters.includes("8D"); - const rotationConfig = filterEnabled ? {} : { rotationHz: 0.2 }; - - await player.player.setRotation(rotationConfig); - - if (filterEnabled) { - player.filters = player.filters.filter((filter) => filter !== "8D"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.8d.messages.filter_disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - player.filters.push("8D"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.8d.messages.filter_enabled"), - color: this.client.color.main, - }, - ], - }); - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/BassBoost.ts b/src/commands/filters/BassBoost.ts deleted file mode 100644 index 75188421f..000000000 --- a/src/commands/filters/BassBoost.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class BassBoost extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "bassboost", - description: { - content: "cmd.bassboost.description", - examples: ["bassboost"], - usage: "bassboost", - }, - category: "filters", - aliases: ["bb"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); - const filterEnabled = player.filters.includes("bassboost"); - - if (filterEnabled) { - await player.player.setEqualizer([]); - player.filters = player.filters.filter((filter) => filter !== "bassboost"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.bassboost.messages.filter_disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - await player.player.setEqualizer([ - { band: 0, gain: 0.34 }, - { band: 1, gain: 0.34 }, - { band: 2, gain: 0.34 }, - { band: 3, gain: 0.34 }, - ]); - player.filters.push("bassboost"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.bassboost.messages.filter_enabled"), - color: this.client.color.main, - }, - ], - }); - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/Distorsion.ts b/src/commands/filters/Distorsion.ts deleted file mode 100644 index 289588984..000000000 --- a/src/commands/filters/Distorsion.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class Distorsion extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "distorsion", - description: { - content: "cmd.distorsion.description", - examples: ["distorsion"], - usage: "distorsion", - }, - category: "filters", - aliases: ["dt"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); - const filterEnabled = player.filters.includes("distorsion"); - - if (filterEnabled) { - await player.player.setDistortion({}); - player.filters = player.filters.filter((filter) => filter !== "distorsion"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.distorsion.messages.filter_disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - await player.player.setDistortion({ - sinOffset: 0, - sinScale: 1, - cosOffset: 0, - cosScale: 1, - tanOffset: 0, - tanScale: 1, - offset: 0, - scale: 1, - }); - player.filters.push("distorsion"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.distorsion.messages.filter_enabled"), - color: this.client.color.main, - }, - ], - }); - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/Karaoke.ts b/src/commands/filters/Karaoke.ts deleted file mode 100644 index c41e3d358..000000000 --- a/src/commands/filters/Karaoke.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class Karaoke extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "karaoke", - description: { - content: "cmd.karaoke.description", - examples: ["karaoke"], - usage: "karaoke", - }, - category: "filters", - aliases: ["kk"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); - const filterEnabled = player.filters.includes("karaoke"); - - if (filterEnabled) { - await player.player.setKaraoke(); - player.filters = player.filters.filter((filter) => filter !== "karaoke"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.karaoke.messages.filter_disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - await player.player.setKaraoke({ - level: 1, - monoLevel: 1, - filterBand: 220, - filterWidth: 100, - }); - player.filters.push("karaoke"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.karaoke.messages.filter_enabled"), - color: this.client.color.main, - }, - ], - }); - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/LowPass.ts b/src/commands/filters/LowPass.ts deleted file mode 100644 index da33a65e9..000000000 --- a/src/commands/filters/LowPass.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class LowPass extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "lowpass", - description: { - content: "cmd.lowpass.description", - examples: ["lowpass"], - usage: "lowpass ", - }, - category: "filters", - aliases: ["lp"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); - const filterEnabled = player.filters.includes("lowpass"); - - if (filterEnabled) { - await player.player.setLowPass({ smoothing: 0 }); - player.filters = player.filters.filter((filter) => filter !== "lowpass"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.lowpass.messages.filter_disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - await player.player.setLowPass({ smoothing: 20 }); - player.filters.push("lowpass"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.lowpass.messages.filter_enabled"), - color: this.client.color.main, - }, - ], - }); - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/NightCore.ts b/src/commands/filters/NightCore.ts deleted file mode 100644 index 57cbae9d9..000000000 --- a/src/commands/filters/NightCore.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class NightCore extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "nightcore", - description: { - content: "cmd.nightcore.description", - examples: ["nightcore"], - usage: "nightcore", - }, - category: "filters", - aliases: ["nc"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); - const filterEnabled = player.filters.includes("nightcore"); - - if (filterEnabled) { - await player.player.setTimescale({}); - player.filters = player.filters.filter((filter) => filter !== "nightcore"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.nightcore.messages.filter_disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - await player.player.setTimescale({ rate: 1.2 }); - player.filters.push("nightcore"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.nightcore.messages.filter_enabled"), - color: this.client.color.main, - }, - ], - }); - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/Pitch.ts b/src/commands/filters/Pitch.ts deleted file mode 100644 index 5cb2105d4..000000000 --- a/src/commands/filters/Pitch.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class Pitch extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "pitch", - description: { - content: "cmd.pitch.description", - examples: ["pitch 1", "pitch 1.5", "pitch 1,5"], - usage: "pitch ", - }, - category: "filters", - aliases: ["ph"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "pitch", - description: "cmd.pitch.options.pitch", - type: 3, - required: true, - }, - ], - }); - } - - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.queue.get(ctx.guild!.id); - const pitchString = args[0].replace(",", "."); - const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(pitchString); - const pitch = parseFloat(pitchString); - - if (!isValidNumber || isNaN(pitch) || pitch < 0.5 || pitch > 5) { - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.pitch.errors.invalid_number"), - color: this.client.color.red, - }, - ], - }); - return; - } - - await player.player.setTimescale({ pitch }); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.pitch.messages.pitch_set", { - pitch, - }), - color: this.client.color.main, - }, - ], - }); - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/Rate.ts b/src/commands/filters/Rate.ts deleted file mode 100644 index f9e1733d3..000000000 --- a/src/commands/filters/Rate.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class Rate extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "rate", - description: { - content: "cmd.rate.description", - examples: ["rate 1", "rate 1.5", "rate 1,5"], - usage: "rate ", - }, - category: "filters", - aliases: ["rt"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "rate", - description: "cmd.rate.options.rate", - type: 3, - required: true, - }, - ], - }); - } - - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.queue.get(ctx.guild!.id); - const rateString = args[0].replace(",", "."); - const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(rateString); - const rate = parseFloat(rateString); - - if (!isValidNumber || isNaN(rate) || rate < 0.5 || rate > 5) { - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.rate.errors.invalid_number"), - color: this.client.color.red, - }, - ], - }); - return; - } - - await player.player.setTimescale({ rate }); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.rate.messages.rate_set", { - rate, - }), - color: this.client.color.main, - }, - ], - }); - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/Reset.ts b/src/commands/filters/Reset.ts deleted file mode 100644 index cced01703..000000000 --- a/src/commands/filters/Reset.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class Reset extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "reset", - description: { - content: "cmd.reset.description", - examples: ["reset"], - usage: "reset", - }, - category: "filters", - aliases: ["rs"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); - player.player.clearFilters(); - player.filters = []; - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.reset.messages.filters_reset"), - color: this.client.color.main, - }, - ], - }); - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/Rotation.ts b/src/commands/filters/Rotation.ts deleted file mode 100644 index 6519a80d1..000000000 --- a/src/commands/filters/Rotation.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class Rotation extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "rotation", - description: { - content: "cmd.rotation.description", - examples: ["rotation"], - usage: "rotation", - }, - category: "filters", - aliases: ["rt"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); - if (player.filters.includes("rotation")) { - player.player.setRotation(); - player.filters = player.filters.filter((filter) => filter !== "rotation"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.rotation.messages.disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - player.player.setRotation({ rotationHz: 0 }); - player.filters.push("rotation"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.rotation.messages.enabled"), - color: this.client.color.main, - }, - ], - }); - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/Speed.ts b/src/commands/filters/Speed.ts deleted file mode 100644 index cc467c539..000000000 --- a/src/commands/filters/Speed.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class Speed extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "speed", - description: { - content: "cmd.speed.description", - examples: ["speed 1.5", "speed 1,5"], - usage: "speed ", - }, - category: "filters", - aliases: ["spd"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "speed", - description: "cmd.speed.options.speed", - type: 3, - required: true, - }, - ], - }); - } - - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.queue.get(ctx.guild!.id); - const speedString = args[0].replace(",", "."); - const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(speedString); - const speed = parseFloat(speedString); - - if (!isValidNumber || isNaN(speed) || speed < 0.5 || speed > 5) { - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.speed.messages.invalid_number"), - color: this.client.color.red, - }, - ], - }); - return; - } - - player.player.setTimescale({ speed }); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.speed.messages.set_speed", { - speed, - }), - color: this.client.color.main, - }, - ], - }); - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/Tremolo.ts b/src/commands/filters/Tremolo.ts deleted file mode 100644 index 0d31189a8..000000000 --- a/src/commands/filters/Tremolo.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class Tremolo extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "tremolo", - description: { - content: "cmd.tremolo.description", - examples: ["tremolo"], - usage: "tremolo", - }, - category: "filters", - aliases: ["tr"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); - const tremoloEnabled = player.filters.includes("tremolo"); - - if (tremoloEnabled) { - player.player.setTremolo(); - player.filters.splice(player.filters.indexOf("tremolo"), 1); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.tremolo.messages.disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - player.player.setTremolo({ depth: 0.75, frequency: 4 }); - player.filters.push("tremolo"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.tremolo.messages.enabled"), - color: this.client.color.main, - }, - ], - }); - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/filters/Vibrato.ts b/src/commands/filters/Vibrato.ts deleted file mode 100644 index 4dad08598..000000000 --- a/src/commands/filters/Vibrato.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; - -export default class Vibrato extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "vibrato", - description: { - content: "cmd.vibrato.description", - examples: ["vibrato"], - usage: "vibrato", - }, - category: "filters", - aliases: ["vb"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); - const vibratoEnabled = player.filters.includes("vibrato"); - - if (vibratoEnabled) { - player.player.setVibrato(); - player.filters.splice(player.filters.indexOf("vibrato"), 1); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.vibrato.messages.disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - player.player.setVibrato({ depth: 0.75, frequency: 4 }); - player.filters.push("vibrato"); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.vibrato.messages.enabled"), - color: this.client.color.main, - }, - ], - }); - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/commands/info/About.ts b/src/commands/info/About.ts index 7865d9c01..5893d3c28 100644 --- a/src/commands/info/About.ts +++ b/src/commands/info/About.ts @@ -1,5 +1,5 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class About extends Command { constructor(client: Lavamusic) { @@ -36,7 +36,7 @@ export default class About extends Command { .setLabel(ctx.locale("buttons.invite")) .setStyle(ButtonStyle.Link) .setURL( - `https://discord.com/api/oauth2/authorize?client_id=${client.config.clientId}&permissions=8&scope=bot%20applications.commands`, + `https://discord.com/api/oauth2/authorize?client_id=${client.env.CLIENT_ID}&permissions=8&scope=bot%20applications.commands`, ); const supportButton = new ButtonBuilder() .setLabel(ctx.locale("buttons.support")) diff --git a/src/commands/info/Botinfo.ts b/src/commands/info/Botinfo.ts index c4038bfea..d09dd89c8 100644 --- a/src/commands/info/Botinfo.ts +++ b/src/commands/info/Botinfo.ts @@ -1,7 +1,7 @@ import os from "node:os"; import { version } from "discord.js"; import { showTotalMemory, usagePercent } from "node-system-stats"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Botinfo extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/info/Help.ts b/src/commands/info/Help.ts index eddcc5617..404b6d3ae 100644 --- a/src/commands/info/Help.ts +++ b/src/commands/info/Help.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Help extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/info/Invite.ts b/src/commands/info/Invite.ts index 3e222457f..aa1bc6b05 100644 --- a/src/commands/info/Invite.ts +++ b/src/commands/info/Invite.ts @@ -1,5 +1,5 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Invite extends Command { constructor(client: Lavamusic) { @@ -38,7 +38,7 @@ export default class Invite extends Command { .setLabel(ctx.locale("buttons.invite")) .setStyle(ButtonStyle.Link) .setURL( - `https://discord.com/api/oauth2/authorize?client_id=${client.config.clientId}&permissions=8&scope=bot%20applications.commands`, + `https://discord.com/api/oauth2/authorize?client_id=${client.env.CLIENT_ID}&permissions=8&scope=bot%20applications.commands`, ), new ButtonBuilder().setLabel(ctx.locale("buttons.support")).setStyle(ButtonStyle.Link).setURL("https://discord.gg/STXurwnZD5"), ); diff --git a/src/commands/info/LavaLink.ts b/src/commands/info/LavaLink.ts index feccfe55c..ca1f8ff9b 100644 --- a/src/commands/info/LavaLink.ts +++ b/src/commands/info/LavaLink.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class LavaLink extends Command { constructor(client: Lavamusic) { @@ -31,7 +31,7 @@ export default class LavaLink extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const nodes = client.shoukaku.nodes; + const nodes = client.manager.nodeManager.nodes; const nodesPerPage = 2; const nodeArray = Array.from(nodes.values()); diff --git a/src/commands/info/Ping.ts b/src/commands/info/Ping.ts index 298795cef..55422a28e 100644 --- a/src/commands/info/Ping.ts +++ b/src/commands/info/Ping.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Ping extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/music/Autoplay.ts b/src/commands/music/Autoplay.ts index d3266b486..1a568df5d 100644 --- a/src/commands/music/Autoplay.ts +++ b/src/commands/music/Autoplay.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Autoplay extends Command { constructor(client: Lavamusic) { @@ -31,7 +31,7 @@ export default class Autoplay extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); if (!player) { return await ctx.sendMessage({ embeds: [ @@ -44,8 +44,9 @@ export default class Autoplay extends Command { } const embed = this.client.embed(); - const autoplay = player.autoplay; - player.setAutoplay(!autoplay); + const autoplay = player.get("autoplay"); + + player.set("autoplay", !autoplay); if (autoplay) { embed.setDescription(ctx.locale("cmd.autoplay.messages.disabled")).setColor(this.client.color.main); diff --git a/src/commands/music/ClearQueue.ts b/src/commands/music/ClearQueue.ts index 3087c0f82..289690d34 100644 --- a/src/commands/music/ClearQueue.ts +++ b/src/commands/music/ClearQueue.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class ClearQueue extends Command { constructor(client: Lavamusic) { @@ -31,7 +31,7 @@ export default class ClearQueue extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); if (!player) { @@ -40,13 +40,13 @@ export default class ClearQueue extends Command { }); } - if (player.queue.length === 0) { + if (player.queue.tracks.length === 0) { return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("player.errors.no_song"))], }); } - player.queue = []; + player.queue.tracks.splice(0, player.queue.tracks.length); return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.clearqueue.messages.cleared"))], }); diff --git a/src/commands/music/Grab.ts b/src/commands/music/Grab.ts index ee91ab13a..cb421a390 100644 --- a/src/commands/music/Grab.ts +++ b/src/commands/music/Grab.ts @@ -1,4 +1,5 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { Requester } from "../../types"; export default class Grab extends Command { constructor(client: Lavamusic) { @@ -31,24 +32,24 @@ export default class Grab extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); await ctx.sendDeferMessage(ctx.locale("cmd.grab.loading")); - if (!player?.current) { + if (!player?.queue.current) { return await ctx.sendMessage({ embeds: [this.client.embed().setColor(this.client.color.red).setDescription(ctx.locale("player.errors.no_song"))], }); } - const song = player.current; + const song = player.queue.current; const songInfo = ctx.locale("cmd.grab.content", { title: song.info.title, uri: song.info.uri, artworkUrl: song.info.artworkUrl, - length: song.info.isStream ? "LIVE" : client.utils.formatTime(song.info.length), - requester: song.info.requester, + length: song.info.isStream ? "LIVE" : client.utils.formatTime(song.info.duration), + requester: `<@${(song.requester as Requester).id}>`, }); try { diff --git a/src/commands/music/Join.ts b/src/commands/music/Join.ts index 7f2eab587..5b744ae81 100644 --- a/src/commands/music/Join.ts +++ b/src/commands/music/Join.ts @@ -1,4 +1,5 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import type { VoiceChannel } from "discord.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Join extends Command { constructor(client: Lavamusic) { @@ -32,41 +33,42 @@ export default class Join extends Command { public async run(client: Lavamusic, ctx: Context): Promise { const embed = this.client.embed(); - let player = client.queue.get(ctx.guild!.id); + let player = client.manager.getPlayer(ctx.guild!.id); if (player) { - const channelId = player.node.manager.connections.get(ctx.guild!.id)!.channelId; return await ctx.sendMessage({ embeds: [ embed.setColor(this.client.color.main).setDescription( ctx.locale("cmd.join.already_connected", { - channelId, + channelId: player.voiceChannelId, }), ), ], }); } - const memberVoiceChannel = (ctx.member as any).voice.channel; + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel if (!memberVoiceChannel) { return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.join.no_voice_channel"))], }); } - player = await client.queue.create( - ctx.guild!, - memberVoiceChannel, - ctx.channel, - client.shoukaku.options.nodeResolver(client.shoukaku.nodes), - ); - - const joinedChannelId = player.node.manager.connections.get(ctx.guild!.id)!.channelId; + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: memberVoiceChannel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + instaUpdateFiltersFix: true, + vcRegion: memberVoiceChannel.rtcRegion, + }) + if (!player.connected) await player.connect(); return await ctx.sendMessage({ embeds: [ embed.setColor(this.client.color.main).setDescription( ctx.locale("cmd.join.joined", { - channelId: joinedChannelId, + channelId: player.voiceChannelId, }), ), ], diff --git a/src/commands/music/Leave.ts b/src/commands/music/Leave.ts index 8673a86a8..75797e27b 100644 --- a/src/commands/music/Leave.ts +++ b/src/commands/music/Leave.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Leave extends Command { constructor(client: Lavamusic) { @@ -31,11 +31,11 @@ export default class Leave extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); if (player) { - const channelId = player.node.manager.connections.get(ctx.guild!.id)!.channelId; + const channelId = player.voiceChannelId; player.destroy(); return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.leave.left", { channelId }))], diff --git a/src/commands/music/Loop.ts b/src/commands/music/Loop.ts index f4b090424..817f74cd5 100644 --- a/src/commands/music/Loop.ts +++ b/src/commands/music/Loop.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Loop extends Command { constructor(client: Lavamusic) { @@ -32,20 +32,20 @@ export default class Loop extends Command { public async run(client: Lavamusic, ctx: Context): Promise { const embed = this.client.embed().setColor(this.client.color.main); - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); let loopMessage = ""; - switch (player?.loop) { + switch (player?.repeatMode) { case "off": - player.loop = "repeat"; + player.setRepeatMode("track") loopMessage = ctx.locale("cmd.loop.looping_song"); break; - case "repeat": - player.loop = "queue"; + case "track": + player.setRepeatMode("queue"); loopMessage = ctx.locale("cmd.loop.looping_queue"); break; case "queue": - player.loop = "off"; + player.setRepeatMode("off"); loopMessage = ctx.locale("cmd.loop.looping_off"); break; } diff --git a/src/commands/music/Lyrics.ts b/src/commands/music/Lyrics.ts index c6d9dc4f2..64c8d1ff6 100644 --- a/src/commands/music/Lyrics.ts +++ b/src/commands/music/Lyrics.ts @@ -1,6 +1,6 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType } from "discord.js"; import { getLyrics } from "genius-lyrics-api"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Lyrics extends Command { constructor(client: Lavamusic) { @@ -33,10 +33,10 @@ export default class Lyrics extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); - const currentTrack = player.current; + const currentTrack = player.queue.current; const trackTitle = currentTrack.info.title.replace(/\[.*?\]/g, "").trim(); const artistName = currentTrack.info.author.replace(/\[.*?\]/g, "").trim(); const trackUrl = currentTrack.info.uri; @@ -45,7 +45,7 @@ export default class Lyrics extends Command { await ctx.sendDeferMessage(ctx.locale("cmd.lyrics.searching", { trackTitle })); const options = { - apiKey: client.config.lyricsApi, + apiKey: "client.config.lyricsApi", title: trackTitle, artist: artistName, optimizeQuery: true, diff --git a/src/commands/music/Nowplaying.ts b/src/commands/music/Nowplaying.ts index 55766cceb..f9ba021c5 100644 --- a/src/commands/music/Nowplaying.ts +++ b/src/commands/music/Nowplaying.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Nowplaying extends Command { constructor(client: Lavamusic) { @@ -31,10 +31,10 @@ export default class Nowplaying extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id)!; - const track = player.current!; - const position = player.player.position; - const duration = track.info.length; + const player = client.manager.getPlayer(ctx.guild!.id); + const track = player.queue.current!; + const position = player.position; + const duration = track.info.duration; const bar = client.utils.progressBar(position, duration, 20); const embed = this.client @@ -49,7 +49,7 @@ export default class Nowplaying extends Command { ctx.locale("cmd.nowplaying.track_info", { title: track.info.title, uri: track.info.uri, - requester: track.info.requester, + requester: `<@${(track.requester as any).id}>`, bar: bar, }), ) diff --git a/src/commands/music/Pause.ts b/src/commands/music/Pause.ts index 48923906b..e55783ce7 100644 --- a/src/commands/music/Pause.ts +++ b/src/commands/music/Pause.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Pause extends Command { constructor(client: Lavamusic) { @@ -31,7 +31,7 @@ export default class Pause extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); if (player?.paused) { diff --git a/src/commands/music/Play.ts b/src/commands/music/Play.ts index 9ca06e567..00e29a91a 100644 --- a/src/commands/music/Play.ts +++ b/src/commands/music/Play.ts @@ -1,6 +1,6 @@ -import type { AutocompleteInteraction } from "discord.js"; -import { LoadType } from "shoukaku"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import type { AutocompleteInteraction, VoiceChannel } from "discord.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { SearchResult } from "lavalink-client/dist/types"; export default class Play extends Command { constructor(client: Lavamusic) { @@ -48,138 +48,67 @@ export default class Play extends Command { public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { const query = args.join(" "); await ctx.sendDeferMessage(ctx.locale("cmd.play.loading")); - let player = client.queue.get(ctx.guild!.id); - const vc = ctx.member as any; - if (!player) player = await client.queue.create(ctx.guild, vc.voice.channel, ctx.channel); + let player = client.manager.getPlayer(ctx.guild!.id); + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel - const res = await this.client.queue.search(query); + if (!player) player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: memberVoiceChannel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + instaUpdateFiltersFix: true, + vcRegion: memberVoiceChannel.rtcRegion, + }) + if (!player.connected) await player.connect(); + + const response = await player.search({ query: query }, ctx.author) as SearchResult; const embed = this.client.embed(); - switch (res.loadType) { - case LoadType.ERROR: - await ctx.editMessage({ - content: "", - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.play.errors.search_error"))], - }); - break; - case LoadType.EMPTY: - await ctx.editMessage({ - content: "", - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.play.errors.no_results"))], - }); - break; - case LoadType.TRACK: { - const track = player.buildTrack(res.data, ctx.author); - if (player.queue.length > client.config.maxQueueSize) - return await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.red) - .setDescription(ctx.locale("cmd.play.errors.queue_too_long", { maxQueueSize: client.config.maxQueueSize })), - ], - }); - player.queue.push(track); - await player.isPlaying(); - await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.main) - .setDescription(ctx.locale("cmd.play.added_to_queue", { title: res.data.info.title, uri: res.data.info.uri })), - ], - }); - break; - } - case LoadType.PLAYLIST: { - if (res.data.tracks.length > client.config.maxPlaylistSize) - return await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.red) - .setDescription( - ctx.locale("cmd.play.errors.playlist_too_long", { maxPlaylistSize: client.config.maxPlaylistSize }), - ), - ], - }); - for (const track of res.data.tracks) { - const pl = player.buildTrack(track, ctx.author); - if (player.queue.length > client.config.maxQueueSize) - return await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.red) - .setDescription( - ctx.locale("cmd.play.errors.queue_too_long", { maxQueueSize: client.config.maxQueueSize }), - ), - ], - }); - player.queue.push(pl); - } - await player.isPlaying(); - await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.main) - .setDescription(ctx.locale("cmd.play.added_playlist_to_queue", { length: res.data.tracks.length })), - ], - }); - break; - } - case LoadType.SEARCH: { - const track1 = player.buildTrack(res.data[0], ctx.author); - if (player.queue.length > client.config.maxQueueSize) - return await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.red) - .setDescription(ctx.locale("cmd.play.errors.queue_too_long", { maxQueueSize: client.config.maxQueueSize })), - ], - }); - player.queue.push(track1); - await player.isPlaying(); - await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.main) - .setDescription( - ctx.locale("cmd.play.added_to_queue", { title: res.data[0].info.title, uri: res.data[0].info.uri }), - ), - ], - }); - break; - } + if (!response || response.tracks?.length === 0) { + return await ctx.editMessage({ + content: "", + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.play.errors.search_error"))], + }); } + + await player.queue.add(response.loadType === "playlist" ? response.tracks : response.tracks[0]); + + if (response.loadType === "playlist") { + await ctx.editMessage({ + content: "", + embeds: [ + embed + .setColor(this.client.color.main) + .setDescription(ctx.locale("cmd.play.added_playlist_to_queue", { length: response.tracks.length })), + ], + }); + } else { + await ctx.editMessage({ + content: "", + embeds: [ + embed + .setColor(this.client.color.main) + .setDescription(ctx.locale("cmd.play.added_to_queue", { title: response.tracks[0].info.title, uri: response.tracks[0].info.uri })), + ], + }); + } + if (!player.playing) await player.play({ paused: false }); } public async autocomplete(interaction: AutocompleteInteraction): Promise { const focusedValue = interaction.options.getFocused(); - const res = await this.client.queue.search(focusedValue); + const res = await this.client.manager.search(focusedValue, interaction.user); const songs = []; - if (res?.loadType) { - if (res.loadType === LoadType.SEARCH && res.data.length > 0) { - res.data.slice(0, 10).forEach((track) => { - const name = `${track.info.title} by ${track.info.author}`; - songs.push({ - name: name.length > 100 ? `${name.substring(0, 97)}...` : name, - value: track.info.uri, - }); - }); - } else if (res.loadType === LoadType.PLAYLIST && res.data.tracks.length > 0) { - res.data.tracks.slice(0, 10).forEach((track) => { - const name = `${track.info.title} by ${track.info.author}`; - songs.push({ - name: name.length > 100 ? `${name.substring(0, 97)}...` : name, - value: track.info.uri, - }); + if (res.loadType === "search") { + res.tracks.slice(0, 10).forEach((track) => { + const name = `${track.info.title} by ${track.info.author}`; + songs.push({ + name: name.length > 100 ? `${name.substring(0, 97)}...` : name, + value: track.info.uri, }); - } + }) } return await interaction.respond(songs).catch(console.error); diff --git a/src/commands/music/PlayNext.ts b/src/commands/music/PlayNext.ts index 6ef71ee4c..3760db687 100644 --- a/src/commands/music/PlayNext.ts +++ b/src/commands/music/PlayNext.ts @@ -1,6 +1,6 @@ -import type { AutocompleteInteraction } from "discord.js"; -import { LoadType } from "shoukaku"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import type { AutocompleteInteraction, VoiceChannel } from "discord.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { SearchResult } from "lavalink-client/dist/types"; export default class PlayNext extends Command { constructor(client: Lavamusic) { @@ -47,137 +47,69 @@ export default class PlayNext extends Command { public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { const query = args.join(" "); - let player = client.queue.get(ctx.guild!.id); - const vc = ctx.member as any; - if (!player) player = await client.queue.create(ctx.guild, vc.voice.channel, ctx.channel); + let player = client.manager.getPlayer(ctx.guild!.id); + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel + + if (!player) player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: memberVoiceChannel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + instaUpdateFiltersFix: true, + vcRegion: memberVoiceChannel.rtcRegion, + }) + if (!player.connected) await player.connect(); await ctx.sendDeferMessage(ctx.locale("cmd.playnext.loading")); - const res = await this.client.queue.search(query); + + const response = await player.search({ query: query }, ctx.author) as SearchResult; const embed = this.client.embed(); - switch (res.loadType) { - case LoadType.ERROR: - await ctx.editMessage({ - content: "", - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.playnext.errors.search_error"))], - }); - break; - case LoadType.EMPTY: - await ctx.editMessage({ - content: "", - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.playnext.errors.no_results"))], - }); - break; - case LoadType.TRACK: { - const track = player.buildTrack(res.data, ctx.author); - if (player.queue.length > client.config.maxQueueSize) - return await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.red) - .setDescription( - ctx.locale("cmd.playnext.errors.queue_too_long", { maxQueueSize: client.config.maxQueueSize }), - ), - ], - }); - player.queue.splice(0, 0, track); - await player.isPlaying(); - await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.main) - .setDescription( - ctx.locale("cmd.playnext.added_to_play_next", { title: res.data.info.title, uri: res.data.info.uri }), - ), - ], - }); - break; - } - case LoadType.PLAYLIST: { - if (res.data.tracks.length > client.config.maxPlaylistSize) - return await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.red) - .setDescription( - ctx.locale("cmd.playnext.errors.playlist_too_long", { maxPlaylistSize: client.config.maxPlaylistSize }), - ), - ], - }); - for (const track of res.data.tracks) { - const pl = player.buildTrack(track, ctx.author); - if (player.queue.length > client.config.maxQueueSize) - return await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.red) - .setDescription( - ctx.locale("cmd.playnext.errors.queue_too_long", { maxQueueSize: client.config.maxQueueSize }), - ), - ], - }); - player.queue.splice(0, 0, pl); - } - await player.isPlaying(); - await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.main) - .setDescription(ctx.locale("cmd.playnext.added_playlist_to_play_next", { length: res.data.tracks.length })), - ], - }); - break; - } - case LoadType.SEARCH: { - const track1 = player.buildTrack(res.data[0], ctx.author); - if (player.queue.length > client.config.maxQueueSize) - return await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.red) - .setDescription( - ctx.locale("cmd.playnext.errors.queue_too_long", { maxQueueSize: client.config.maxQueueSize }), - ), - ], - }); - player.queue.splice(0, 0, track1); - await player.isPlaying(); - await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.main) - .setDescription( - ctx.locale("cmd.playnext.added_to_play_next", { title: res.data[0].info.title, uri: res.data[0].info.uri }), - ), - ], - }); - break; - } + + if (!response || response.tracks?.length === 0) { + return await ctx.editMessage({ + content: "", + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.play.errors.search_error"))], + }); } + await player.queue.splice(0, 0, response.loadType === "playlist" ? response.tracks : response.tracks[0]); + + if (response.loadType === "playlist") { + await ctx.editMessage({ + content: "", + embeds: [ + embed + .setColor(this.client.color.main) + .setDescription(ctx.locale("cmd.playnext.added_playlist_to_play_next", { length: response.tracks.length })), + ], + }); + } else { + await ctx.editMessage({ + content: "", + embeds: [ + embed + .setColor(this.client.color.main) + .setDescription(ctx.locale("cmd.playnext.added_to_play_next", { title: response.tracks[0].info.title, uri: response.tracks[0].info.uri })), + ], + }); + } + if (!player.playing) await player.play({ paused: false }); + } public async autocomplete(interaction: AutocompleteInteraction): Promise { const focusedValue = interaction.options.getFocused(); - const res = await this.client.queue.search(focusedValue); + const res = await this.client.manager.search(focusedValue, interaction.user); const songs = []; - if (res.loadType === LoadType.SEARCH && res.data.length > 0) { - res.data.slice(0, 10).forEach((x) => { - let name = `${x.info.title} by ${x.info.author}`; - if (name.length > 100) { - name = `${name.substring(0, 97)}...`; - } + if (res.loadType === "search") { + res.tracks.slice(0, 10).forEach((track) => { + const name = `${track.info.title} by ${track.info.author}`; songs.push({ - name: name, - value: x.info.uri, + name: name.length > 100 ? `${name.substring(0, 97)}...` : name, + value: track.info.uri, }); - }); + }) } return await interaction.respond(songs).catch(console.error); diff --git a/src/commands/music/Queue.ts b/src/commands/music/Queue.ts index a6707fcc7..72ebaf10d 100644 --- a/src/commands/music/Queue.ts +++ b/src/commands/music/Queue.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Queue extends Command { constructor(client: Lavamusic) { @@ -31,26 +31,26 @@ export default class Queue extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); - if (player.queue.length === 0) { + if (player.queue.tracks.length === 0) { return await ctx.sendMessage({ embeds: [ embed.setColor(this.client.color.main).setDescription( ctx.locale("cmd.queue.now_playing", { - title: player.current.info.title, - uri: player.current.info.uri, - requester: player.current?.info.requester, - duration: player.current.info.isStream + title: player.queue.current.info.title, + uri: player.queue.current.info.uri, + requester: `<@${(player.queue.current.requester as any).id}>`, + duration: player.queue.current.info.isStream ? ctx.locale("cmd.queue.live") - : client.utils.formatTime(player.current.info.length), + : client.utils.formatTime(player.queue.current.info.duration), }), ), ], }); } const songStrings = []; - for (let i = 0; i < player.queue.length; i++) { + for (let i = 0; i < player.queue.tracks.length; i++) { const track = player.queue[i]; songStrings.push( ctx.locale("cmd.queue.track_info", { diff --git a/src/commands/music/Remove.ts b/src/commands/music/Remove.ts index 83445236a..62784febf 100644 --- a/src/commands/music/Remove.ts +++ b/src/commands/music/Remove.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Remove extends Command { constructor(client: Lavamusic) { @@ -38,21 +38,21 @@ export default class Remove extends Command { } public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); - if (player.queue.length === 0) + if (player.queue.tracks.length === 0) return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.remove.errors.no_songs"))], }); const songNumber = Number(args[0]); - if (isNaN(songNumber) || songNumber <= 0 || songNumber > player.queue.length) + if (isNaN(songNumber) || songNumber <= 0 || songNumber > player.queue.tracks.length) return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.remove.errors.invalid_number"))], }); - player.remove(songNumber - 1); + player.queue.remove(songNumber - 1); return await ctx.sendMessage({ embeds: [ embed.setColor(this.client.color.main).setDescription( diff --git a/src/commands/music/Replay.ts b/src/commands/music/Replay.ts index d1a32907d..de61b40ad 100644 --- a/src/commands/music/Replay.ts +++ b/src/commands/music/Replay.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Replay extends Command { constructor(client: Lavamusic) { @@ -31,10 +31,10 @@ export default class Replay extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); - if (!player.current?.info.isSeekable) { + if (!player.queue.current?.info.isSeekable) { return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.replay.errors.not_seekable"))], }); diff --git a/src/commands/music/Resume.ts b/src/commands/music/Resume.ts index d55eddfa5..fab194b11 100644 --- a/src/commands/music/Resume.ts +++ b/src/commands/music/Resume.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Resume extends Command { constructor(client: Lavamusic) { @@ -31,7 +31,7 @@ export default class Resume extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); if (!player.paused) { @@ -40,7 +40,7 @@ export default class Resume extends Command { }); } - player.pause(); + player.resume(); return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.resume.messages.resumed"))], }); diff --git a/src/commands/music/Search.ts b/src/commands/music/Search.ts index a26412b72..58c8cd133 100644 --- a/src/commands/music/Search.ts +++ b/src/commands/music/Search.ts @@ -1,7 +1,6 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; -import { LoadType } from "shoukaku"; -import type { Song } from "../../structures/Dispatcher.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type VoiceChannel } from "discord.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { SearchResult, Track } from "lavalink-client/dist/types"; export default class Search extends Command { constructor(client: Lavamusic) { @@ -42,19 +41,22 @@ export default class Search extends Command { public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { const embed = this.client.embed().setColor(this.client.color.main); - let player = client.queue.get(ctx.guild!.id); + let player = client.manager.getPlayer(ctx.guild!.id); const query = args.join(" "); - if (!player) { - const vc = ctx.member as any; - player = await client.queue.create( - ctx.guild, - vc.voice.channel, - ctx.channel, - client.shoukaku.options.nodeResolver(client.shoukaku.nodes), - ); - } - const res = await this.client.queue.search(query); - if (!res) { + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel + + if (!player) player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: memberVoiceChannel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + instaUpdateFiltersFix: true, + vcRegion: memberVoiceChannel.rtcRegion, + }) + if (!player.connected) await player.connect(); + const response = await player.search({ query: query }, ctx.author) as SearchResult; + if (!response || response.tracks?.length === 0) { return await ctx.sendMessage({ embeds: [embed.setDescription(ctx.locale("cmd.search.errors.no_results")).setColor(this.client.color.red)], }); @@ -66,28 +68,14 @@ export default class Search extends Command { new ButtonBuilder().setCustomId("4").setLabel("4").setStyle(ButtonStyle.Primary), new ButtonBuilder().setCustomId("5").setLabel("5").setStyle(ButtonStyle.Primary), ); - switch (res.loadType) { - case LoadType.ERROR: - ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.search.errors.search_error"))], - }); - break; - case LoadType.EMPTY: - ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.search.errors.no_results"))], - }); - break; - case LoadType.SEARCH: { - const tracks = res.data.slice(0, 5); - const embeds = tracks.map( - (track: Song, index: number) => `${index + 1}. [${track.info.title}](${track.info.uri}) - \`${track.info.author}\``, - ); - await ctx.sendMessage({ - embeds: [embed.setDescription(embeds.join("\n"))], - components: [row], - }); - break; - } + if (response.loadType === "search" && response.tracks.length > 5) { + const embeds = response.tracks.map( + (track: Track, index: number) => `${index + 1}. [${track.info.title}](${track.info.uri}) - \`${track.info.author}\``, + ); + await ctx.sendMessage({ + embeds: [embed.setDescription(embeds.join("\n"))], + components: [row], + }); } const collector = ctx.channel.createMessageComponentCollector({ filter: (f: any) => f.user.id === ctx.author.id, @@ -96,18 +84,17 @@ export default class Search extends Command { idle: 60000 / 2, }); collector.on("collect", async (int: any) => { - const track = res.data[parseInt(int.customId) - 1]; + const track = response.tracks[parseInt(int.customId) - 1]; await int.deferUpdate(); if (!track) return; - const song = player.buildTrack(track, ctx.author); - player.queue.push(song); - player.isPlaying(); + player.queue.add(track); + if (!player.playing) await player.play({ paused: false }); await ctx.editMessage({ embeds: [ embed.setDescription( ctx.locale("cmd.search.messages.added_to_queue", { - title: song.info.title, - uri: song.info.uri, + title: track.info.title, + uri: track.info.uri, }), ), ], diff --git a/src/commands/music/Seek.ts b/src/commands/music/Seek.ts index f17c21489..3192eedaf 100644 --- a/src/commands/music/Seek.ts +++ b/src/commands/music/Seek.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Seek extends Command { constructor(client: Lavamusic) { @@ -38,8 +38,8 @@ export default class Seek extends Command { } public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.queue.get(ctx.guild!.id); - const current = player.current.info; + const player = client.manager.getPlayer(ctx.guild!.id); + const current = player.queue.current.info; const embed = this.client.embed(); const duration = client.utils.parseTime(args.join(" ")); if (!duration) { @@ -52,12 +52,12 @@ export default class Seek extends Command { embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.seek.errors.not_seekable"))], }); } - if (duration > current.length) { + if (duration > current.duration) { return await ctx.sendMessage({ embeds: [ embed.setColor(this.client.color.red).setDescription( ctx.locale("cmd.seek.errors.beyond_duration", { - length: client.utils.formatTime(current.length), + length: client.utils.formatTime(current.duration), }), ), ], diff --git a/src/commands/music/Shuffle.ts b/src/commands/music/Shuffle.ts index 0fb4d908b..17322314d 100644 --- a/src/commands/music/Shuffle.ts +++ b/src/commands/music/Shuffle.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Shuffle extends Command { constructor(client: Lavamusic) { @@ -31,14 +31,14 @@ export default class Shuffle extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); - if (player.queue.length === 0) { + if (player.queue.tracks.length === 0) { return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("player.errors.no_song"))], }); } - player.setShuffle(); + player.queue.shuffle() return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.shuffle.messages.shuffled"))], }); diff --git a/src/commands/music/Skip.ts b/src/commands/music/Skip.ts index 057b415a5..fa54dd522 100644 --- a/src/commands/music/Skip.ts +++ b/src/commands/music/Skip.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Skip extends Command { constructor(client: Lavamusic) { @@ -31,14 +31,14 @@ export default class Skip extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); - if (player.queue.length === 0) { + if (player.queue.tracks.length === 0) { return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("player.errors.no_song"))], }); } - const currentTrack = player.current.info; + const currentTrack = player.queue.current.info; player.skip(); if (ctx.isInteraction) { return await ctx.sendMessage({ diff --git a/src/commands/music/Skipto.ts b/src/commands/music/Skipto.ts index e0bad9876..7b60c6508 100644 --- a/src/commands/music/Skipto.ts +++ b/src/commands/music/Skipto.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Skipto extends Command { constructor(client: Lavamusic) { @@ -38,11 +38,11 @@ export default class Skipto extends Command { } public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); const num = Number(args[0]); - if (player.queue.length === 0 || isNaN(num) || num > player.queue.length || num < 1) { + if (player.queue.tracks.length === 0 || isNaN(num) || num > player.queue.tracks.length || num < 1) { return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.skipto.errors.invalid_number"))], }); diff --git a/src/commands/music/Stop.ts b/src/commands/music/Stop.ts index e8f97a802..136eee07d 100644 --- a/src/commands/music/Stop.ts +++ b/src/commands/music/Stop.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Stop extends Command { constructor(client: Lavamusic) { @@ -31,11 +31,10 @@ export default class Stop extends Command { } public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); - player.queue = []; - player.stop(); + player.stopPlaying(true, false) return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.stop.messages.stopped"))], diff --git a/src/commands/music/Volume.ts b/src/commands/music/Volume.ts index 36ef52acb..c63568870 100644 --- a/src/commands/music/Volume.ts +++ b/src/commands/music/Volume.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Volume extends Command { constructor(client: Lavamusic) { @@ -38,7 +38,7 @@ export default class Volume extends Command { } public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.queue.get(ctx.guild!.id); + const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); const number = Number(args[0]); @@ -53,8 +53,8 @@ export default class Volume extends Command { }); } - await player.player.setGlobalVolume(number); - const currentVolume = player.player.volume; + await player.setVolume(number); + const currentVolume = player.volume; return await ctx.sendMessage({ embeds: [ diff --git a/src/commands/playlist/AddSong.ts b/src/commands/playlist/AddSong.ts index c4ef21ff3..9d14b86f9 100644 --- a/src/commands/playlist/AddSong.ts +++ b/src/commands/playlist/AddSong.ts @@ -1,6 +1,5 @@ import type { AutocompleteInteraction } from "discord.js"; -import { LoadType } from "shoukaku"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class AddSong extends Command { constructor(client: Lavamusic) { @@ -71,9 +70,8 @@ export default class AddSong extends Command { ], }); } - const res = await client.queue.search(song); - - if (!res || res.loadType === LoadType.EMPTY) { + const res = await client.manager.search(song, ctx.author); + if (!res) { return await ctx.sendMessage({ embeds: [ { @@ -85,7 +83,6 @@ export default class AddSong extends Command { } const playlistData = await client.db.getPlaylist(ctx.author.id, playlist); - if (!playlistData) { return await ctx.sendMessage({ embeds: [ @@ -99,28 +96,24 @@ export default class AddSong extends Command { let trackStrings: any; let count: number; - - if (res.loadType === LoadType.PLAYLIST) { - trackStrings = res.data.tracks; - count = res.data.tracks.length; - } else if (res.loadType === LoadType.TRACK) { - trackStrings = [res.data]; + if (res.loadType === "playlist") { + trackStrings = res.tracks.map((track) => track.encoded); + count = res.tracks.length; + } else if (res.loadType === "track") { + trackStrings = [res.tracks[0].encoded]; count = 1; - } else if (res.loadType === LoadType.SEARCH) { - trackStrings = [res.data[0]]; + } else if (res.loadType === "search") { + trackStrings = [res.tracks[0].encoded]; count = 1; } - await client.db.addSong(ctx.author.id, playlist, trackStrings); + await client.db.addTracksToPlaylist(ctx.author.id, playlist, trackStrings); - await ctx.sendMessage({ + return await ctx.sendMessage({ embeds: [ { - description: ctx.locale("cmd.addsong.messages.added", { - count, - playlist: playlistData.name, - }), - color: this.client.color.main, + description: ctx.locale("cmd.addsong.messages.added", { playlist: playlistData.name, count }), + color: this.client.color.green, }, ], }); diff --git a/src/commands/playlist/Create.ts b/src/commands/playlist/Create.ts index c6b80a7ea..252268df2 100644 --- a/src/commands/playlist/Create.ts +++ b/src/commands/playlist/Create.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class CreatePlaylist extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/playlist/Delete.ts b/src/commands/playlist/Delete.ts index 663bd4518..ab06f0e78 100644 --- a/src/commands/playlist/Delete.ts +++ b/src/commands/playlist/Delete.ts @@ -1,5 +1,5 @@ import type { AutocompleteInteraction } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class DeletePlaylist extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/playlist/List.ts b/src/commands/playlist/List.ts index 73e136c91..56d170cac 100644 --- a/src/commands/playlist/List.ts +++ b/src/commands/playlist/List.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class GetPlaylists extends Command { constructor(client: Lavamusic) { diff --git a/src/commands/playlist/Load.ts b/src/commands/playlist/Load.ts index 618e7c10c..f273f007e 100644 --- a/src/commands/playlist/Load.ts +++ b/src/commands/playlist/Load.ts @@ -1,5 +1,5 @@ -import type { AutocompleteInteraction } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import type { AutocompleteInteraction, GuildMember } from "discord.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class LoadPlaylist extends Command { constructor(client: Lavamusic) { @@ -40,7 +40,7 @@ export default class LoadPlaylist extends Command { } public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - let player = client.queue.get(ctx.guild!.id); + let player = client.manager.getPlayer(ctx.guild!.id); const playlistName = args.join(" ").trim(); const playlistData = await client.db.getPlaylist(ctx.author.id, playlistName); if (!playlistData) { @@ -54,7 +54,7 @@ export default class LoadPlaylist extends Command { }); } - const songs = await client.db.getSongs(ctx.author.id, playlistName); + const songs = await client.db.getTracksFromPlaylist(ctx.author.id, playlistName); if (songs.length === 0) { return await ctx.sendMessage({ embeds: [ @@ -66,25 +66,37 @@ export default class LoadPlaylist extends Command { }); } - const vc = ctx.member as any; + const member = ctx.member as GuildMember; if (!player) { - player = await client.queue.create( - ctx.guild, - vc.voice.channel, - ctx.channel, - client.shoukaku.options.nodeResolver(client.shoukaku.nodes), - ); + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: member.voice.channelId, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + instaUpdateFiltersFix: true, + vcRegion: member.voice.channel.rtcRegion, + }) + if (!player.connected) await player.connect(); } - for (const song of songs) { - const trackData = JSON.parse(song.track); - for (const track of trackData) { - const builtTrack = player.buildTrack(track, ctx.author as any); - player.queue.push(builtTrack); - } + const nodes = client.manager.nodeManager.leastUsedNodes(); + const node = nodes[Math.floor(Math.random() * nodes.length)]; + const tracks = await node.decode.multipleTracks(songs as any, ctx.author); + if (tracks.length === 0) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.load.messages.playlist_empty"), + color: client.color.red, + }, + ], + }); } + player.queue.add(tracks); + + if (!player.playing) await player.play({ paused: false }); - await player.isPlaying(); return await ctx.sendMessage({ embeds: [ { diff --git a/src/commands/playlist/RemoveSong.ts b/src/commands/playlist/RemoveSong.ts index 04c2e7201..900b95301 100644 --- a/src/commands/playlist/RemoveSong.ts +++ b/src/commands/playlist/RemoveSong.ts @@ -1,6 +1,5 @@ import type { AutocompleteInteraction } from "discord.js"; -import { LoadType } from "shoukaku"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class RemoveSong extends Command { constructor(client: Lavamusic) { @@ -76,7 +75,7 @@ export default class RemoveSong extends Command { return await ctx.sendMessage({ embeds: [playlistNotFoundError] }); } - const res = await client.queue.search(song); + /* const res = await client.queue.search(song); if (!res || res.loadType !== LoadType.TRACK) { const noSongsFoundError = this.client @@ -108,7 +107,7 @@ export default class RemoveSong extends Command { .setDescription(ctx.locale("cmd.removesong.messages.error_occurred")) .setColor(this.client.color.red); return await ctx.sendMessage({ embeds: [genericError] }); - } + } */ } public async autocomplete(interaction: AutocompleteInteraction): Promise { const focusedValue = interaction.options.getFocused(); diff --git a/src/commands/playlist/Steal.ts b/src/commands/playlist/Steal.ts index c780c7a21..6cb52bab3 100644 --- a/src/commands/playlist/Steal.ts +++ b/src/commands/playlist/Steal.ts @@ -1,4 +1,4 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class StealPlaylist extends Command { constructor(client: Lavamusic) { @@ -115,7 +115,7 @@ export default class StealPlaylist extends Command { }); } - const targetSongs = await client.db.getSongs(targetUserId, playlistName); + const targetSongs = await client.db.getTracksFromPlaylist(targetUserId, playlistName); const existingPlaylist = await client.db.getPlaylist(ctx.author.id, playlistName); if (existingPlaylist) { @@ -129,7 +129,7 @@ export default class StealPlaylist extends Command { }); } - await client.db.createPlaylistWithSongs(ctx.author.id, playlistName, targetSongs); + await client.db.createPlaylistWithTracks(ctx.author.id, playlistName, targetSongs); return await ctx.sendMessage({ embeds: [ diff --git a/src/config.ts b/src/config.ts index 7368abb93..b0456632b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,14 +1,4 @@ -import "dotenv/config"; -import { SearchEngine } from "./types.js"; - -const parseBoolean = (value) => { - if (typeof value !== "string") return false; - return value.trim().toLowerCase() === "true"; -}; - export default { - token: process.env.TOKEN, - prefix: process.env.PREFIX, color: { red: 0xff0000, green: 0x00ff00, @@ -40,25 +30,6 @@ export default { cancel: "⏹️", }, }, - defaultLanguage: process.env.DEFAULT_LANGUAGE, - topGG: process.env.TOPGG, - keepAlive: parseBoolean(process.env.KEEP_ALIVE), - autoNode: parseBoolean(process.env.AUTO_NODE), - searchEngine: SearchEngine[process.env.SEARCH_ENGINE] || SearchEngine.YouTubeMusic, - maxPlaylistSize: parseInt(process.env.MAX_PLAYLIST_SIZE || "100"), - botStatus: process.env.BOT_STATUS || "online", - botActivity: process.env.BOT_ACTIVITY || "Lavamusic", - botActivityType: parseInt(process.env.BOT_ACTIVITY_TYPE || "2"), - maxQueueSize: parseInt(process.env.MAX_QUEUE_SIZE || "100"), - owners: process.env.OWNER_IDS ? JSON.parse(process.env.OWNER_IDS) : [], - clientId: process.env.CLIENT_ID, - guildId: process.env.GUILD_ID, - logChannelId: process.env.LOG_CHANNEL_ID, - commandLogs: process.env.LOG_COMMANDS_ID, - lyricsApi: process.env.GENIUS_API, - links: { - img: process.env.IMG_LINK || "https://i.imgur.com/ud3EWNh.jpg", - }, icons: { youtube: "https://i.imgur.com/xzVHhFY.png", spotify: "https://i.imgur.com/qvdqtsc.png", @@ -67,16 +38,9 @@ export default { deezer: "https://i.imgur.com/xyZ43FG.png", jiosaavn: "https://i.imgur.com/N9Nt80h.png", }, - lavalink: process.env.LAVALINK_SERVERS - ? JSON.parse(process.env.LAVALINK_SERVERS).map((server) => { - return { - url: server.url, - auth: server.auth, - name: server.name, - secure: parseBoolean(server.secure), - }; - }) - : [], + links: { + img: "https://i.imgur.com/ud3EWNh.jpg", + }, }; /** diff --git a/src/database/server.ts b/src/database/server.ts index 1710a8321..a04241fcc 100644 --- a/src/database/server.ts +++ b/src/database/server.ts @@ -1,5 +1,5 @@ -import { type Dj, type Guild, type Playlist, PrismaClient, type Role, type Setup, type Song, type Stay } from "@prisma/client"; -import config from "../config.js"; +import { type Dj, type Guild, type Playlist, PrismaClient, type Role, type Setup, type Stay } from "@prisma/client"; +import { env } from "../env"; export default class ServerData { private prisma: PrismaClient; @@ -16,7 +16,7 @@ export default class ServerData { return await this.prisma.guild.create({ data: { guildId, - prefix: config.prefix, + prefix: env.PREFIX, }, }); } @@ -31,7 +31,7 @@ export default class ServerData { public async getPrefix(guildId: string): Promise { const guild = await this.get(guildId); - return guild?.prefix ?? config.prefix; + return guild?.prefix ?? env.PREFIX; } public async updateLanguage(guildId: string, language: string): Promise { @@ -43,7 +43,7 @@ export default class ServerData { public async getLanguage(guildId: string): Promise { const guild = await this.get(guildId); - return guild?.language ?? config.defaultLanguage; + return guild?.language ?? env.DEFAULT_LANGUAGE; } public async getSetup(guildId: string): Promise { @@ -125,18 +125,22 @@ export default class ServerData { await this.prisma.playlist.create({ data: { userId, name } }); } - public async createPlaylistWithSongs(userId: string, name: string, songs: any[]): Promise { + // createPlaylist with tracks + public async createPlaylistWithTracks(userId: string, name: string, tracks: string[]): Promise { await this.prisma.playlist.create({ data: { userId, name, - songs: { - create: songs.map((song) => ({ track: song.track })), - }, + tracks: JSON.stringify(tracks), }, }); } - + /** + * Deletes a playlist from the database + * + * @param userId The ID of the user that owns the playlist + * @param name The name of the playlist to delete + */ public async deletePlaylist(userId: string, name: string): Promise { await this.prisma.playlist.delete({ where: { userId_name: { userId, name } }, @@ -144,68 +148,119 @@ export default class ServerData { } public async deleteSongsFromPlaylist(userId: string, playlistName: string): Promise { + // Fetch the playlist const playlist = await this.getPlaylist(userId, playlistName); + if (playlist) { - await this.prisma.song.deleteMany({ + // Update the playlist and reset the tracks to an empty array + await this.prisma.playlist.update({ where: { - playlistId: playlist.id, + userId_name: { + userId, + name: playlistName, + }, + }, + data: { + tracks: JSON.stringify([]), // Set tracks to an empty array }, }); } } - public async addSong(userId: string, name: string, song: string): Promise { - const playlist = await this.getPlaylist(userId, name); + public async addTracksToPlaylist(userId: string, playlistName: string, tracks: string[]) { + // Serialize the tracks array into a JSON string + const tracksJson = JSON.stringify(tracks); + + // Check if the playlist already exists for the user + const playlist = await this.prisma.playlist.findUnique({ + where: { + userId_name: { + userId, + name: playlistName, + }, + }, + }); + if (playlist) { - await this.prisma.song.create({ + // If the playlist exists, handle existing tracks + const existingTracks = playlist.tracks ? JSON.parse(playlist.tracks) : []; // Initialize as an empty array if null + + if (Array.isArray(existingTracks)) { + // Merge new and existing tracks + const updatedTracks = [...existingTracks, ...tracks]; + + // Update the playlist with the new tracks + await this.prisma.playlist.update({ + where: { + userId_name: { + userId, + name: playlistName, + }, + }, + data: { + tracks: JSON.stringify(updatedTracks), // Store the updated tracks as a serialized JSON string + }, + }); + } else { + throw new Error('Existing tracks are not in an array format.'); + } + } else { + // If no playlist exists, create a new one with the provided tracks + await this.prisma.playlist.create({ data: { - track: JSON.stringify(song), - playlistId: playlist.id, + userId, + name: playlistName, + tracks: tracksJson, // Store the serialized JSON string }, }); - } else { - await this.createPlaylist(userId, name); - await this.addSong(userId, name, song); } } public async removeSong(userId: string, playlistName: string, encodedSong: string): Promise { const playlist = await this.getPlaylist(userId, playlistName); if (playlist) { - await this.prisma.song.deleteMany({ - where: { - playlistId: playlist.id, - track: { contains: encodedSong }, - }, - }); + const tracks: string[] = JSON.parse(playlist.tracks); + + // Find the index of the song to remove + const songIndex = tracks.indexOf(encodedSong); + + if (songIndex !== -1) { + // Remove the song from the array + tracks.splice(songIndex, 1); + + // Update the playlist with the new list of tracks + await this.prisma.playlist.update({ + where: { + userId_name: { + userId, + name: playlistName, + }, + }, + data: { + tracks: JSON.stringify(tracks), // Re-serialize the updated array back to a string + }, + }); + } } } - public async getSongs(userId: string, name: string): Promise { - const playlist = await this.getPlaylist(userId, name); - if (playlist) { - return this.prisma.song.findMany({ where: { playlistId: playlist.id } }); - } - return []; - } + public async getTracksFromPlaylist(userId: string, playlistName: string) { + const playlist = await this.prisma.playlist.findUnique({ + where: { + userId_name: { + userId, + name: playlistName, + }, + }, + }); - public async clearPlaylist(userId: string, name: string): Promise { - const playlist = await this.getPlaylist(userId, name); - if (playlist) { - await this.prisma.song.deleteMany({ where: { playlistId: playlist.id } }); + if (!playlist) { + return null; } - } - - public async clearPlaylists(userId: string): Promise { - await this.prisma.playlist.deleteMany({ where: { userId } }); - } - - public async clearAllPlaylists(): Promise { - await this.prisma.playlist.deleteMany(); - } - public async clearAllSongs(): Promise { - await this.prisma.song.deleteMany(); + // Deserialize the tracks JSON string back into an array + const tracks = JSON.parse(playlist.tracks); + return tracks; } } diff --git a/src/env.ts b/src/env.ts new file mode 100644 index 000000000..5c3a42c4a --- /dev/null +++ b/src/env.ts @@ -0,0 +1,101 @@ +import path from "node:path"; +import { config } from "dotenv"; +import { z } from "zod"; + +config({ + path: path.join(__dirname, "../.env"), +}); + +const envSchema = z.object({ + /** + * The discord app token + */ + TOKEN: z.string(), + + /** + * The client id + */ + CLIENT_ID: z.string(), + + /** + * The default language + */ + DEFAULT_LANGUAGE: z.string().default("EnglishUS"), + + /** + * The bot prefix + */ + PREFIX: z.string().default("!"), + + /** + * The owner ids + */ + OWNER_IDS: z.string().array().optional(), + + /** + * The guild id for devlopment purposes + */ + GUILD_ID: z.string().optional(), + + /** + * The Top.gg api key + */ + TOPGG: z.string().optional(), + + /** + * The keep alive boolean + */ + KEEP_ALIVE: z.boolean().default(false), + + /** + * The log channel id + */ + LOG_CHANNEL_ID: z.string().optional(), + + /** + * The log command id + */ + LOG_COMMANDS_ID: z.string().optional(), + + /** + * The bot status + */ + BOT_STATUS: z.string().default("online"), + + /** + * The bot activity + */ + BOT_ACTIVITY: z.string().default("Lavamusic"), + + /** + * The bot activity type + */ + BOT_ACTIVITY_TYPE: z.number().default(0), + /** + * The database url + */ + DATABASE_URL: z.string().optional(), + + /** + * Search engine + */ + SEARCH_ENGINE: z.enum(["youtube", "youtubemusic", "soundcloud", "spotify", "apple", "deezer", "yandex", "jiosaavn"]).default("youtube"), + + /** + * Node in json + */ + NODES: z.string(), +}); + +type Env = z.infer; + +/** + * The environment variables + */ +export const env: Env = envSchema.parse(process.env); + +for (const key in env) { + if (!(key in env)) { + throw new Error(`Missing env variable: ${key}`); + } +} diff --git a/src/events/client/ChannelDelete.ts b/src/events/client/ChannelDelete.ts index 1fc1ceeaf..5363b007b 100644 --- a/src/events/client/ChannelDelete.ts +++ b/src/events/client/ChannelDelete.ts @@ -1,4 +1,4 @@ -import { Event, type Lavamusic } from "../../structures/index.js"; +import { Event, type Lavamusic } from "../../structures/index"; export default class ChannelDelete extends Event { constructor(client: Lavamusic, file: string) { @@ -29,14 +29,9 @@ export default class ChannelDelete extends Event { await this.client.db.deleteSetup(guild.id); } - const queue = this.client.queue.get(guild.id); - if (queue) { - if ( - queue.channelId === channel.id || - (queue.player && queue.node.manager.connections.get(guild.id)!.channelId === channel.id) - ) { - queue.stop(); - } + const player = this.client.manager.getPlayer(guild.id); + if (player && player.voiceChannelId === channel.id) { + player.destroy(); } } } diff --git a/src/events/client/GuildCreate.ts b/src/events/client/GuildCreate.ts index e7b2a27e4..5fc15e198 100644 --- a/src/events/client/GuildCreate.ts +++ b/src/events/client/GuildCreate.ts @@ -1,5 +1,5 @@ import { EmbedBuilder, type Guild, type GuildMember, type TextChannel } from "discord.js"; -import { Event, type Lavamusic } from "../../structures/index.js"; +import { Event, type Lavamusic } from "../../structures/index"; export default class GuildCreate extends Event { constructor(client: Lavamusic, file: string) { @@ -49,7 +49,7 @@ export default class GuildCreate extends Event { ) .setTimestamp(); - const logChannelId = this.client.config.logChannelId; + const logChannelId = this.client.env.LOG_CHANNEL_ID; if (!logChannelId) { this.client.logger.error("Log channel ID not found in configuration."); return; diff --git a/src/events/client/GuildDelete.ts b/src/events/client/GuildDelete.ts index 9df21f6f8..e0e14b426 100644 --- a/src/events/client/GuildDelete.ts +++ b/src/events/client/GuildDelete.ts @@ -1,5 +1,5 @@ import { EmbedBuilder, type Guild, type GuildMember, type TextChannel } from "discord.js"; -import { Event, type Lavamusic } from "../../structures/index.js"; +import { Event, type Lavamusic } from "../../structures/index"; export default class GuildDelete extends Event { constructor(client: Lavamusic, file: string) { @@ -49,7 +49,7 @@ export default class GuildDelete extends Event { ) .setTimestamp(); - const logChannelId = this.client.config.logChannelId; + const logChannelId = this.client.env.LOG_CHANNEL_ID; if (!logChannelId) { this.client.logger.error("Log channel ID not found in configuration."); return; diff --git a/src/events/client/InteractionCreate.ts b/src/events/client/InteractionCreate.ts index 800f563ac..4e64c68c2 100644 --- a/src/events/client/InteractionCreate.ts +++ b/src/events/client/InteractionCreate.ts @@ -12,8 +12,8 @@ import { PermissionFlagsBits, type TextChannel, } from "discord.js"; -import { T } from "../../structures/I18n.js"; -import { Context, Event, type Lavamusic } from "../../structures/index.js"; +import { T } from "../../structures/I18n"; +import { Context, Event, type Lavamusic } from "../../structures/index"; export default class InteractionCreate extends Event { constructor(client: Lavamusic, file: string) { @@ -67,7 +67,7 @@ export default class InteractionCreate extends Event { .catch(() => {}); } - const logs = this.client.channels.cache.get(this.client.config.commandLogs); + const logs = this.client.channels.cache.get(this.client.env.LOG_COMMANDS_ID); if (command.permissions) { if (command.permissions?.client) { @@ -91,18 +91,18 @@ export default class InteractionCreate extends Event { return; } - if (command.permissions?.dev && this.client.config.owners) { - const isDev = this.client.config.owners.includes(interaction.user.id); + if (command.permissions?.dev && this.client.env.OWNER_IDS) { + const isDev = this.client.env.OWNER_IDS.includes(interaction.user.id); if (!isDev) return; } } - if (command.vote && this.client.config.topGG) { + if (command.vote && this.client.env.TOPGG) { const voted = await this.client.topGG.hasVoted(interaction.user.id); if (!voted) { const voteBtn = new ActionRowBuilder().addComponents( new ButtonBuilder() .setLabel(T(locale, "event.interaction.vote_button")) - .setURL(`https://top.gg/bot/${this.client.config.clientId}/vote`) + .setURL(`https://top.gg/bot/${this.client.user.id}/vote`) .setStyle(ButtonStyle.Link), ); @@ -156,8 +156,8 @@ export default class InteractionCreate extends Event { } if (command.player.active) { - const queue = this.client.queue.get(interaction.guildId); - if (!(queue?.queue && queue.current)) { + const queue = this.client.manager.getPlayer(interaction.guildId); + if (!queue.queue.current) { return await interaction.reply({ content: T(locale, "event.interaction.no_music_playing"), }); diff --git a/src/events/client/MessageCreate.ts b/src/events/client/MessageCreate.ts index bdb31688c..faa336011 100644 --- a/src/events/client/MessageCreate.ts +++ b/src/events/client/MessageCreate.ts @@ -10,8 +10,8 @@ import { PermissionFlagsBits, type TextChannel, } from "discord.js"; -import { T } from "../../structures/I18n.js"; -import { Context, Event, type Lavamusic } from "../../structures/index.js"; +import { T } from "../../structures/I18n"; +import { Context, Event, type Lavamusic } from "../../structures/index"; export default class MessageCreate extends Event { constructor(client: Lavamusic, file: string) { @@ -92,19 +92,19 @@ export default class MessageCreate extends Event { }); } - if (command.permissions?.dev && this.client.config.owners) { - const isDev = this.client.config.owners.includes(message.author.id); + if (command.permissions?.dev && this.client.env.OWNER_IDS) { + const isDev = this.client.env.OWNER_IDS.includes(message.author.id); if (!isDev) return; } } - if (command.vote && this.client.config.topGG) { + if (command.vote && this.client.env.TOPGG) { const voted = await this.client.topGG.hasVoted(message.author.id); if (!voted) { const voteBtn = new ActionRowBuilder().addComponents( new ButtonBuilder() .setLabel(T(locale, "event.message.vote_button")) - .setURL(`https://top.gg/bot/${this.client.config.clientId}/vote`) + .setURL(`https://top.gg/bot/${this.client.user.id}/vote`) .setStyle(ButtonStyle.Link), ); @@ -155,8 +155,8 @@ export default class MessageCreate extends Event { } if (command.player.active) { - const queue = this.client.queue.get(message.guildId); - if (!(queue?.queue && queue.current)) { + const queue = this.client.manager.getPlayer(message.guildId); + if (!queue?.queue.current) { return await message.reply({ content: T(locale, "event.message.no_music_playing"), }); @@ -237,7 +237,7 @@ export default class MessageCreate extends Event { content: T(locale, "event.message.error", { error: error.message || "Unknown error" }), }); } finally { - const logs = this.client.channels.cache.get(this.client.config.commandLogs); + const logs = this.client.channels.cache.get(this.client.env.LOG_COMMANDS_ID); if (logs) { const embed = new EmbedBuilder() .setAuthor({ diff --git a/src/events/player/NodeRaw.ts b/src/events/client/Raw.ts similarity index 63% rename from src/events/player/NodeRaw.ts rename to src/events/client/Raw.ts index b4d6bd967..41794bf43 100644 --- a/src/events/player/NodeRaw.ts +++ b/src/events/client/Raw.ts @@ -1,15 +1,14 @@ import { Event, type Lavamusic } from "../../structures/index.js"; -export default class NodeRaw extends Event { +export default class Raw extends Event { constructor(client: Lavamusic, file: string) { super(client, file, { - name: "nodeRaw", + name: "raw", }); } - public async run(_payload: any): Promise { - // Uncomment the following line for debugging purposes - // this.client.logger.debug(`Node raw event: ${JSON.stringify(payload)}`); + public async run(d): Promise { + this.client.manager.sendRawData(d); } } diff --git a/src/events/client/Ready.ts b/src/events/client/Ready.ts index ac45ae9d1..9e42ab8dd 100644 --- a/src/events/client/Ready.ts +++ b/src/events/client/Ready.ts @@ -1,6 +1,6 @@ import { AutoPoster } from "topgg-autoposter"; -import config from "../../config.js"; import { Event, type Lavamusic } from "../../structures/index.js"; +import { env } from "../../env.js"; export default class Ready extends Event { constructor(client: Lavamusic, file: string) { @@ -15,21 +15,22 @@ export default class Ready extends Event { this.client.user?.setPresence({ activities: [ { - name: config.botActivity, - type: config.botActivityType, + name: env.BOT_ACTIVITY, + type: env.BOT_ACTIVITY_TYPE, }, ], - status: config.botStatus as any, + status: env.BOT_STATUS as any, }); - if (config.topGG) { - const autoPoster = AutoPoster(config.topGG, this.client); + if (env.TOPGG) { + const autoPoster = AutoPoster(env.TOPGG, this.client); setInterval(() => { autoPoster.on("posted", (_stats) => {}); }, 86400000); // 24 hours in milliseconds } else { this.client.logger.warn("Top.gg token not found. Skipping auto poster."); } + await this.client.manager.init({ ...this.client.user!, shards: "auto" }); } } diff --git a/src/events/client/SetupButtons.ts b/src/events/client/SetupButtons.ts deleted file mode 100644 index 267e56512..000000000 --- a/src/events/client/SetupButtons.ts +++ /dev/null @@ -1,246 +0,0 @@ -import type { Message } from "discord.js"; -import { T } from "../../structures/I18n.js"; -import { Event, type Lavamusic } from "../../structures/index.js"; -import { getButtons } from "../../utils/Buttons.js"; -import { buttonReply } from "../../utils/SetupSystem.js"; -import { checkDj } from "../player/TrackStart.js"; - -export default class SetupButtons extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "setupButtons", - }); - } - - public async run(interaction: any): Promise { - const locale = await this.client.db.getLanguage(interaction.guildId); - - if (!interaction.replied) await interaction.deferReply().catch(() => {}); - if (!interaction.member.voice.channel) { - return await buttonReply(interaction, T(locale, "event.setupButton.no_voice_channel_button"), this.client.color.red); - } - const clientMember = interaction.guild.members.cache.get(this.client.user.id); - if (clientMember.voice.channel && clientMember.voice.channelId !== interaction.member.voice.channelId) { - return await buttonReply( - interaction, - T(locale, "event.setupButton.different_voice_channel_button", { - channel: clientMember.voice.channel, - }), - this.client.color.red, - ); - } - const player = this.client.queue.get(interaction.guildId); - if (!player) return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); - if (!player.queue) return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); - if (!player.current) return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); - const data = await this.client.db.getSetup(interaction.guildId); - const { title, uri, length, artworkUrl, sourceName, isStream, requester } = player.current.info; - let message: Message; - try { - message = await interaction.channel.messages.fetch(data.messageId, { - cache: true, - }); - } catch (_e) { - /* empty */ - } - - const iconUrl = this.client.config.icons[sourceName] || this.client.user.displayAvatarURL({ extension: "png" }); - const embed = this.client - .embed() - .setAuthor({ - name: T(locale, "event.setupButton.now_playing"), - iconURL: iconUrl, - }) - .setColor(this.client.color.main) - .setDescription( - `[${title}](${uri}) - ${isStream ? T(locale, "event.setupButton.live") : this.client.utils.formatTime(length)} - ${T(locale, "event.setupButton.requested_by", { requester })}`, - ) - .setImage(artworkUrl || this.client.user.displayAvatarURL({ extension: "png" })); - - if (!interaction.isButton()) return; - if (!(await checkDj(this.client, interaction))) { - return await buttonReply(interaction, T(locale, "event.setupButton.no_dj_permission"), this.client.color.red); - } - if (message) { - const handleVolumeChange = async (change: number) => { - const vol = player.player.volume + change; - player.player.setGlobalVolume(vol); - await buttonReply(interaction, T(locale, "event.setupButton.volume_set", { vol }), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.volume_footer", { - vol, - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - }; - switch (interaction.customId) { - case "LOW_VOL_BUT": - await handleVolumeChange(-10); - break; - case "HIGH_VOL_BUT": - await handleVolumeChange(10); - break; - case "PAUSE_BUT": { - const name = player.player.paused ? T(locale, "event.setupButton.resumed") : T(locale, "event.setupButton.paused"); - player.pause(); - await buttonReply(interaction, T(locale, "event.setupButton.pause_resume", { name }), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.pause_resume_footer", { - name, - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - components: getButtons(player, this.client), - }); - break; - } - case "SKIP_BUT": - if (player.queue.length === 0) { - return await buttonReply(interaction, T(locale, "event.setupButton.no_music_to_skip"), this.client.color.main); - } - player.skip(); - await buttonReply(interaction, T(locale, "event.setupButton.skipped"), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.skipped_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - break; - case "STOP_BUT": - player.stop(); - await buttonReply(interaction, T(locale, "event.setupButton.stopped"), this.client.color.main); - await message.edit({ - embeds: [ - embed - .setFooter({ - text: T(locale, "event.setupButton.stopped_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }) - .setDescription(T(locale, "event.setupButton.nothing_playing")) - .setImage(this.client.config.links.img) - .setAuthor({ - name: this.client.user.username, - iconURL: this.client.user.displayAvatarURL({ - extension: "png", - }), - }), - ], - }); - break; - case "LOOP_BUT": { - const loopOptions: Array<"off" | "queue" | "repeat"> = ["off", "queue", "repeat"]; - const newLoop = loopOptions[(loopOptions.indexOf(player.loop) + 1) % loopOptions.length]; - player.setLoop(newLoop); - await buttonReply( - interaction, - T(locale, "event.setupButton.loop_set", { - loop: newLoop, - }), - this.client.color.main, - ); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.loop_footer", { - loop: newLoop, - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - break; - } - case "SHUFFLE_BUT": - player.setShuffle(); - await buttonReply(interaction, T(locale, "event.setupButton.shuffled"), this.client.color.main); - break; - case "PREV_BUT": - if (!player.previous) { - return await buttonReply(interaction, T(locale, "event.setupButton.no_previous_track"), this.client.color.main); - } - player.previousTrack(); - await buttonReply(interaction, T(locale, "event.setupButton.playing_previous"), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.previous_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - break; - case "REWIND_BUT": { - const time = player.player.position - 10000; - if (time < 0) { - return await buttonReply(interaction, T(locale, "event.setupButton.rewind_limit"), this.client.color.main); - } - player.seek(time); - await buttonReply(interaction, T(locale, "event.setupButton.rewinded"), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.rewind_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - break; - } - case "FORWARD_BUT": { - const time = player.player.position + 10000; - if (time > player.current.info.length) { - return await buttonReply(interaction, T(locale, "event.setupButton.forward_limit"), this.client.color.main); - } - player.seek(time); - await buttonReply(interaction, T(locale, "event.setupButton.forwarded"), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.forward_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - break; - } - default: - await buttonReply(interaction, T(locale, "event.setupButton.button_not_available"), this.client.color.main); - break; - } - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/events/client/SetupSystem.ts b/src/events/client/SetupSystem.ts deleted file mode 100644 index 72acb39fb..000000000 --- a/src/events/client/SetupSystem.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { type Message, PermissionsBitField, TextChannel } from "discord.js"; -import { T } from "../../structures/I18n.js"; -import { Event, type Lavamusic } from "../../structures/index.js"; -import { oops, setupStart } from "../../utils/SetupSystem.js"; - -export default class SetupSystem extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "setupSystem", - }); - } - - public async run(message: Message): Promise { - const locale = await this.client.db.getLanguage(message.guildId); - const channel = message.channel as TextChannel; - if (!(channel instanceof TextChannel)) return; - if (!message.member?.voice.channel) { - await oops(channel, T(locale, "event.message.no_voice_channel_queue")); - await message.delete().catch(() => {}); - return; - } - - const voiceChannel = message.member.voice.channel; - const clientUser = this.client.user; - const clientMember = message.guild.members.cache.get(clientUser.id); - - if (!voiceChannel.permissionsFor(clientUser).has(PermissionsBitField.Flags.Connect | PermissionsBitField.Flags.Speak)) { - await oops( - channel, - T(locale, "event.message.no_permission_connect_speak", { - channel: voiceChannel.id, - }), - ); - await message.delete().catch(() => {}); - return; - } - - if (clientMember?.voice.channel && clientMember.voice.channelId !== voiceChannel.id) { - await oops( - channel, - T(locale, "event.message.different_voice_channel_queue", { - channel: clientMember.voice.channelId, - }), - ); - await message.delete().catch(() => {}); - return; - } - - let player = this.client.queue.get(message.guildId); - if (!player) { - player = await this.client.queue.create( - message.guild, - voiceChannel, - message.channel, - this.client.shoukaku.options.nodeResolver(this.client.shoukaku.nodes), - ); - } - - await setupStart(this.client, message.content, player, message); - await message.delete().catch(() => {}); - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/events/client/VoiceStateUpdate.ts b/src/events/client/VoiceStateUpdate.ts index 80c2029d2..b5af8c3d7 100644 --- a/src/events/client/VoiceStateUpdate.ts +++ b/src/events/client/VoiceStateUpdate.ts @@ -1,5 +1,5 @@ import { ChannelType, type GuildMember, type VoiceState } from "discord.js"; -import { Event, type Lavamusic } from "../../structures/index.js"; +import { Event, type Lavamusic } from "../../structures/index"; export default class VoiceStateUpdate extends Event { constructor(client: Lavamusic, file: string) { @@ -8,17 +8,16 @@ export default class VoiceStateUpdate extends Event { }); } - public async run(_oldState: VoiceState, newState: VoiceState): Promise { + public async run(_oldState: VoiceState, newState: VoiceState): Promise { const guildId = newState.guild.id; if (!guildId) return; - const player = this.client.queue.get(guildId); + const player = this.client.manager.getPlayer(guildId); if (!player) return; - const vcConnection = player.node.manager.connections.get(guildId); - if (!vcConnection?.channelId) return; + if (!player?.voiceChannelId) return; - const vc = newState.guild.channels.cache.get(vcConnection.channelId); + const vc = newState.guild.channels.cache.get(player.voiceChannelId); if (!(vc && vc.members instanceof Map)) return; const is247 = await this.client.db.get_247(guildId); @@ -58,10 +57,10 @@ export default class VoiceStateUpdate extends Event { if (vc.members instanceof Map && [...vc.members.values()].filter((x: GuildMember) => !x.user.bot).length <= 0) { setTimeout(async () => { - const vcConnection = player.node.manager.connections.get(guildId); - if (!vcConnection?.channelId) return; + + if (!player?.voiceChannelId) return; - const playerVoiceChannel = newState.guild.channels.cache.get(vcConnection.channelId); + const playerVoiceChannel = newState.guild.channels.cache.get(player?.voiceChannelId); if ( player && playerVoiceChannel && diff --git a/src/events/player/NodeConnect.ts b/src/events/node/Connect.ts similarity index 58% rename from src/events/player/NodeConnect.ts rename to src/events/node/Connect.ts index 4a186ce96..d26d19b3a 100644 --- a/src/events/player/NodeConnect.ts +++ b/src/events/node/Connect.ts @@ -1,15 +1,16 @@ -import { Event, type Lavamusic } from "../../structures/index.js"; -import BotLog from "../../utils/BotLog.js"; +import type { LavalinkNode } from "lavalink-client"; +import { Event, type Lavamusic } from "../../structures/index"; +import BotLog from "../../utils/BotLog"; -export default class NodeConnect extends Event { +export default class Connect extends Event { constructor(client: Lavamusic, file: string) { super(client, file, { - name: "nodeConnect", + name: "connect", }); } - public async run(node: string): Promise { - this.client.logger.success(`Node ${node} is ready!`); + public async run(node: LavalinkNode): Promise { + this.client.logger.success(`Node ${node.id} is ready!`); let data = await this.client.db.get_247(); if (!data) return; @@ -28,7 +29,15 @@ export default class NodeConnect extends Event { if (channel && vc) { try { - await this.client.queue.create(guild, vc, channel); + const player = this.client.manager.createPlayer({ + guildId: guild.id, + voiceChannelId: vc.id, + textChannelId: channel.id, + selfDeaf: true, + selfMute: false, + instaUpdateFiltersFix: true, + }); + if (!player.connected) await player.connect(); } catch (error) { this.client.logger.error(`Failed to create queue for guild ${guild.id}: ${error.message}`); } diff --git a/src/events/node/Destroy.ts b/src/events/node/Destroy.ts new file mode 100644 index 000000000..2b0147c0e --- /dev/null +++ b/src/events/node/Destroy.ts @@ -0,0 +1,16 @@ +import type { DestroyReasonsType, LavalinkNode } from "lavalink-client"; +import { Event, type Lavamusic } from "../../structures/index"; +import BotLog from "../../utils/BotLog"; + +export default class Destroy extends Event { + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: "destroy", + }); + } + + public async run(node: LavalinkNode, destroyReason?: DestroyReasonsType): Promise { + this.client.logger.success(`Node ${node.id} is destroyed!`); + BotLog.send(this.client, `Node ${node} is destroyed: ${destroyReason}`, "warn"); + } +} diff --git a/src/events/player/NodeDestroy.ts b/src/events/player/NodeDestroy.ts deleted file mode 100644 index 924f7c125..000000000 --- a/src/events/player/NodeDestroy.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Event, type Lavamusic } from "../../structures/index.js"; -import BotLog from "../../utils/BotLog.js"; -let destroyCount = 0; - -export default class NodeDestroy extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "nodeDestroy", - }); - } - - public async run(node: string, code: number, reason: string): Promise { - const message = `Node ${node} destroyed with code ${code} and reason ${reason}.`; - this.client.logger.error(message); - BotLog.send(this.client, message, "error"); - - destroyCount++; - - if (destroyCount >= 5) { - this.client.shoukaku.removeNode(node); - destroyCount = 0; - const warnMessage = `Node ${node} removed from nodes list due to excessive disconnects.`; - this.client.logger.warn(warnMessage); - BotLog.send(this.client, warnMessage, "warn"); - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/events/player/NodeDisconnect.ts b/src/events/player/NodeDisconnect.ts deleted file mode 100644 index fb8c92e2a..000000000 --- a/src/events/player/NodeDisconnect.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Event, type Lavamusic } from "../../structures/index.js"; -import BotLog from "../../utils/BotLog.js"; - -export default class NodeDisconnect extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "nodeDisconnect", - }); - } - - public async run(node: string, count: number): Promise { - const message = `Node ${node} disconnected ${count} times`; - this.client.logger.warn(message); - BotLog.send(this.client, message, "warn"); - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/events/player/NodeError.ts b/src/events/player/NodeError.ts deleted file mode 100644 index 8f3888dba..000000000 --- a/src/events/player/NodeError.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Event, type Lavamusic } from "../../structures/index.js"; -import BotLog from "../../utils/BotLog.js"; - -export default class NodeError extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "nodeError", - }); - } - - public async run(node: string, error: any): Promise { - const errorMessage = JSON.stringify(error, null, 2); - const message = `Node ${node} Error: ${errorMessage}`; - this.client.logger.error(message); - BotLog.send(this.client, message, "error"); - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/events/player/NodeReconnect.ts b/src/events/player/NodeReconnect.ts deleted file mode 100644 index 9a5111c32..000000000 --- a/src/events/player/NodeReconnect.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Event, type Lavamusic } from "../../structures/index.js"; -import BotLog from "../../utils/BotLog.js"; - -export default class NodeReconnect extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "nodeReconnect", - }); - } - - public async run(node: string): Promise { - const message = `Node ${node} reconnected`; - this.client.logger.warn(message); - BotLog.send(this.client, message, "warn"); - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/events/player/QueueEnd.ts b/src/events/player/QueueEnd.ts index 202964ae5..6e009098f 100644 --- a/src/events/player/QueueEnd.ts +++ b/src/events/player/QueueEnd.ts @@ -1,8 +1,8 @@ -import type { Player } from "shoukaku"; -import type { Song } from "../../structures/Dispatcher.js"; -import { type Dispatcher, Event, type Lavamusic } from "../../structures/index.js"; -import { updateSetup } from "../../utils/SetupSystem.js"; +import type { Player, Track, TrackStartEvent } from "lavalink-client"; +import { Event, type Lavamusic } from "../../structures/index"; +import type { TextChannel } from "discord.js"; +// queueEnd export default class QueueEnd extends Event { constructor(client: Lavamusic, file: string) { super(client, file, { @@ -10,41 +10,21 @@ export default class QueueEnd extends Event { }); } - public async run(_player: Player, track: Song, dispatcher: Dispatcher): Promise { - const guild = this.client.guilds.cache.get(dispatcher.guildId); + public async run(player: Player, _track: Track | null, _payload: TrackStartEvent): Promise { + const guild = this.client.guilds.cache.get(player.guildId); if (!guild) return; - const locale = await this.client.db.getLanguage(guild.id); - switch (dispatcher.loop) { - case "repeat": - dispatcher.queue.unshift(track); - break; - case "queue": - dispatcher.queue.push(track); - break; - case "off": - dispatcher.previous = dispatcher.current; - dispatcher.current = null; - break; - } - if (dispatcher.autoplay) { - await dispatcher.Autoplay(track); - } else { - dispatcher.autoplay = false; - } + const messageId = player.get("messageId"); + if (!messageId) return; - await updateSetup(this.client, guild, locale); - this.client.utils.updateStatus(this.client, guild.id); - } -} + const channel = guild.channels.cache.get(player.textChannelId) as TextChannel; + if (!channel) return; -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ + const message = await channel.messages.fetch(messageId).catch(() => {}); + if (!message) return; + + if (message.editable) { + await message.edit({components: []}).catch(() => {}); + } + } +} \ No newline at end of file diff --git a/src/events/player/TrackEnd.ts b/src/events/player/TrackEnd.ts index ccd09a034..7462c2d5b 100644 --- a/src/events/player/TrackEnd.ts +++ b/src/events/player/TrackEnd.ts @@ -1,6 +1,6 @@ -import type { Player } from "shoukaku"; -import type { Song } from "../../structures/Dispatcher.js"; -import { type Dispatcher, Event, type Lavamusic } from "../../structures/index.js"; +import type { Player, Track, TrackStartEvent } from "lavalink-client"; +import { Event, type Lavamusic } from "../../structures/index"; +import type { TextChannel } from "discord.js"; export default class TrackEnd extends Event { constructor(client: Lavamusic, file: string) { @@ -9,40 +9,19 @@ export default class TrackEnd extends Event { }); } - public async run(_player: Player, track: Song, dispatcher: Dispatcher): Promise { - dispatcher.previous = dispatcher.current; - dispatcher.current = null; + public async run(player: Player, _track: Track | null, _payload: TrackStartEvent): Promise { + const guild = this.client.guilds.cache.get(player.guildId); + if (!guild) return; - const nowPlayingMessage = await dispatcher.nowPlayingMessage?.fetch().catch(() => null); + const messageId = player.get("messageId"); + if (!messageId) return; - switch (dispatcher.loop) { - case "repeat": - dispatcher.queue.unshift(track); - break; - case "queue": - dispatcher.queue.push(track); - break; - } + const channel = guild.channels.cache.get(player.textChannelId) as TextChannel; + if (!channel) return; - await dispatcher.play(); + const message = await channel.messages.fetch(messageId).catch(() => { }); + if (!message) return; - if (dispatcher.autoplay) { - await dispatcher.Autoplay(track); - } - - if (nowPlayingMessage?.deletable) { - await nowPlayingMessage.delete().catch(() => {}); - } + message.delete().catch(() => { }); } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ +} \ No newline at end of file diff --git a/src/events/player/TrackStart.ts b/src/events/player/TrackStart.ts index 85f97715a..77550d8ee 100644 --- a/src/events/player/TrackStart.ts +++ b/src/events/player/TrackStart.ts @@ -1,3 +1,6 @@ +import type { Player, Track, TrackStartEvent } from "lavalink-client"; +import { Event, type Lavamusic } from "../../structures/index"; +import { T } from "../../structures/I18n"; import { ActionRowBuilder, ButtonBuilder, @@ -12,11 +15,8 @@ import { type TextChannel, type UserSelectMenuInteraction, } from "discord.js"; -import type { Player } from "shoukaku"; -import type { Song } from "../../structures/Dispatcher.js"; -import { T } from "../../structures/I18n.js"; -import { type Dispatcher, Event, type Lavamusic } from "../../structures/index.js"; -import { trackStart } from "../../utils/SetupSystem.js"; +import type { Requester } from "../../types"; +import { trackStart } from "../../utils/SetupSystem"; export default class TrackStart extends Event { constructor(client: Lavamusic, file: string) { @@ -25,13 +25,11 @@ export default class TrackStart extends Event { }); } - public async run(player: Player, track: Song, dispatcher: Dispatcher): Promise { - if (!track?.info) return; - + public async run(player: Player, track: Track | null, _payload: TrackStartEvent): Promise { const guild = this.client.guilds.cache.get(player.guildId); if (!guild) return; - const channel = guild.channels.cache.get(dispatcher.channelId) as TextChannel; + const channel = guild.channels.cache.get(player.textChannelId) as TextChannel; if (!channel) return; this.client.utils.updateStatus(this.client, guild.id); @@ -48,15 +46,15 @@ export default class TrackStart extends Event { .setDescription(`**[${track.info.title}](${track.info.uri})**`) .setFooter({ text: T(locale, "player.trackStart.requested_by", { - user: track.info.requester.tag, + user: (track.requester as Requester).username, }), - iconURL: track.info.requester.avatarURL({}), + iconURL: (track.requester as Requester).avatarURL, }) .setThumbnail(track.info.artworkUrl) .addFields( { name: T(locale, "player.trackStart.duration"), - value: track.info.isStream ? "LIVE" : this.client.utils.formatTime(track.info.length), + value: track.info.isStream ? "LIVE" : this.client.utils.formatTime(track.info.duration), inline: true, }, { @@ -71,35 +69,33 @@ export default class TrackStart extends Event { if (setup?.textId) { const textChannel = guild.channels.cache.get(setup.textId) as TextChannel; - const id = setup.messageId; - if (textChannel) { - await trackStart(id, textChannel, dispatcher, track, this.client, locale); + await trackStart(setup.messageId, textChannel, player, track, this.client, locale); } } else { const message = await channel.send({ embeds: [embed], - components: [createButtonRow(dispatcher, this.client)], + components: [createButtonRow(player, this.client)], }); - dispatcher.nowPlayingMessage = message; - createCollector(message, dispatcher, track, embed, this.client, locale); + player.set("messageId", message.id); + createCollector(message, player, track, embed, this.client, locale); } } } -function createButtonRow(dispatcher: Dispatcher, client: Lavamusic): ActionRowBuilder { +function createButtonRow(player: Player, client: Lavamusic): ActionRowBuilder { const previousButton = new ButtonBuilder() .setCustomId("previous") .setEmoji(client.emoji.previous) .setStyle(ButtonStyle.Secondary) - .setDisabled(!dispatcher.previous); + .setDisabled(!player.queue.previous); const resumeButton = new ButtonBuilder() .setCustomId("resume") - .setEmoji(dispatcher.paused ? client.emoji.resume : client.emoji.pause) - .setStyle(dispatcher.paused ? ButtonStyle.Success : ButtonStyle.Secondary); + .setEmoji(player.paused ? client.emoji.resume : client.emoji.pause) + .setStyle(player.paused ? ButtonStyle.Success : ButtonStyle.Secondary); const stopButton = new ButtonBuilder().setCustomId("stop").setEmoji(client.emoji.stop).setStyle(ButtonStyle.Danger); @@ -107,13 +103,13 @@ function createButtonRow(dispatcher: Dispatcher, client: Lavamusic): ActionRowBu const loopButton = new ButtonBuilder() .setCustomId("loop") - .setEmoji(dispatcher.loop === "repeat" ? client.emoji.loop.track : client.emoji.loop.none) - .setStyle(dispatcher.loop !== "off" ? ButtonStyle.Success : ButtonStyle.Secondary); + .setEmoji(player.repeatMode === "track" ? client.emoji.loop.track : client.emoji.loop.none) + .setStyle(player.repeatMode !== "off" ? ButtonStyle.Success : ButtonStyle.Secondary); return new ActionRowBuilder().addComponents(resumeButton, previousButton, stopButton, skipButton, loopButton); } -function createCollector(message: any, dispatcher: Dispatcher, _track: Song, embed: any, client: Lavamusic, locale: string): void { +function createCollector(message: any, player: Player, _track: Track, embed: any, client: Lavamusic, locale: string): void { const collector = message.createMessageComponentCollector({ filter: async (b: ButtonInteraction) => { if (b.member instanceof GuildMember) { @@ -148,15 +144,18 @@ function createCollector(message: any, dispatcher: Dispatcher, _track: Song, emb iconURL: interaction.user.avatarURL({}), }), ], - components: [createButtonRow(dispatcher, client)], + components: [createButtonRow(player, client)], }); } }; switch (interaction.customId) { case "previous": - if (dispatcher.previous) { + if (player.queue.previous) { await interaction.deferUpdate(); - dispatcher.previousTrack(); + const previousTrack = player.queue.previous[0]; + player.play({ + track: previousTrack, + }); await editMessage( T(locale, "player.trackStart.previous_by", { user: interaction.user.tag, @@ -170,26 +169,32 @@ function createCollector(message: any, dispatcher: Dispatcher, _track: Song, emb } break; case "resume": - dispatcher.pause(); - await interaction.deferUpdate(); - await editMessage( - dispatcher.paused - ? T(locale, "player.trackStart.paused_by", { - user: interaction.user.tag, - }) - : T(locale, "player.trackStart.resumed_by", { - user: interaction.user.tag, - }), - ); + if (player.paused) { + player.resume(); + await interaction.deferUpdate(); + await editMessage( + T(locale, "player.trackStart.resumed_by", { + user: interaction.user.tag, + }), + ); + } else { + player.pause(); + await interaction.deferUpdate(); + await editMessage( + T(locale, "player.trackStart.paused_by", { + user: interaction.user.tag, + }), + ); + } break; case "stop": - dispatcher.stop(); + player.stopPlaying(true, false); await interaction.deferUpdate(); break; case "skip": - if (dispatcher.queue.length > 0) { + if (player.queue.tracks.length > 0) { await interaction.deferUpdate(); - dispatcher.skip(); + player.skip(); await editMessage( T(locale, "player.trackStart.skipped_by", { user: interaction.user.tag, @@ -204,17 +209,17 @@ function createCollector(message: any, dispatcher: Dispatcher, _track: Song, emb break; case "loop": await interaction.deferUpdate(); - switch (dispatcher.loop) { + switch (player.repeatMode) { case "off": - dispatcher.loop = "repeat"; + player.setRepeatMode("track"); await editMessage( T(locale, "player.trackStart.looping_by", { user: interaction.user.tag, }), ); break; - case "repeat": - dispatcher.loop = "queue"; + case "track": + player.setRepeatMode("queue"); await editMessage( T(locale, "player.trackStart.looping_queue_by", { user: interaction.user.tag, @@ -222,7 +227,7 @@ function createCollector(message: any, dispatcher: Dispatcher, _track: Song, emb ); break; case "queue": - dispatcher.loop = "off"; + player.setRepeatMode("off"); await editMessage( T(locale, "player.trackStart.looping_off_by", { user: interaction.user.tag, diff --git a/src/index.ts b/src/index.ts index 2fc907477..6b3e189fd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,7 @@ import * as fs from "node:fs"; -import { ShardingManager } from "discord.js"; - -import config from "./config.js"; -import Logger from "./structures/Logger.js"; -import { ThemeSelector } from "./utils/ThemeSelector.js"; +import Logger from "./structures/Logger"; +import { ThemeSelector } from "./utils/ThemeSelector"; +import { shardStart } from "./shard"; const logger = new Logger(); @@ -18,40 +16,21 @@ function setConsoleTitle(title: string): void { process.stdout.write(`\x1b]0;${title}\x07`); } -async function main(): Promise { - try { - if (!fs.existsSync("./src/utils/LavaLogo.txt")) { - logger.error("LavaLogo.txt file is missing"); - process.exit(1); - } - console.clear(); - // Set a custom title for the console window - setConsoleTitle("Lavamusic"); - const logFile = fs.readFileSync("./src/utils/LavaLogo.txt", "utf-8"); - console.log(theme.purpleNeon(logFile)); - const manager = new ShardingManager("./dist/LavaClient.js", { - respawn: true, - token: config.token, - totalShards: "auto", - shardList: "auto", - }); - - manager.on("shardCreate", (shard) => { - shard.on("ready", () => { - logger.start(`[CLIENT] Shard ${shard.id} connected to Discord's Gateway.`); - }); - }); - - await manager.spawn(); - - logger.start(`[CLIENT] ${manager.totalShards} shard(s) spawned.`); - } catch (err) { - logger.error("[CLIENT] An error has occurred:", err); +try { + if (!fs.existsSync("./src/utils/LavaLogo.txt")) { + logger.error("LavaLogo.txt file is missing"); + process.exit(1); } + console.clear(); + // Set a custom title for the console window + setConsoleTitle("Lavamusic"); + const logFile = fs.readFileSync("./src/utils/LavaLogo.txt", "utf-8"); + console.log(theme.purpleNeon(logFile)); + shardStart(logger); +} catch (err) { + logger.error("[CLIENT] An error has occurred:", err); } -main(); - /** * Project: lavamusic * Author: Appu diff --git a/src/plugin/index.ts b/src/plugin/index.ts index dd455b10f..b4adc6f9c 100644 --- a/src/plugin/index.ts +++ b/src/plugin/index.ts @@ -1,9 +1,7 @@ import fs from "node:fs"; import path from "node:path"; -import { fileURLToPath } from "node:url"; -import type { Lavamusic } from "../structures/index.js"; +import type { Lavamusic } from "../structures/index"; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); const pluginsFolder = path.join(__dirname, "plugins"); export default async function loadPlugins(client: Lavamusic): Promise { @@ -11,7 +9,7 @@ export default async function loadPlugins(client: Lavamusic): Promise { const pluginFiles = fs.readdirSync(pluginsFolder).filter((file) => file.endsWith(".js")); for (const file of pluginFiles) { const pluginPath = path.join(pluginsFolder, file); - const { default: plugin } = await import(`file://${pluginPath}`); + const { default: plugin } = require(pluginPath); if (plugin.initialize) plugin.initialize(client); client.logger.info(`Loaded plugin: ${plugin.name} v${plugin.version}`); } diff --git a/src/plugin/plugins/antiCrash.ts b/src/plugin/plugins/antiCrash.ts index bd84eb4ff..1f5bed3da 100644 --- a/src/plugin/plugins/antiCrash.ts +++ b/src/plugin/plugins/antiCrash.ts @@ -1,5 +1,5 @@ -import type { Lavamusic } from "../../structures/index.js"; -import type { BotPlugin } from "../index.js"; +import type { Lavamusic } from "../../structures/index"; +import type { BotPlugin } from "../index"; const antiCrash: BotPlugin = { name: "AntiCrash Plugin", diff --git a/src/plugin/plugins/keepAlive.ts b/src/plugin/plugins/keepAlive.ts index adf545818..4087c0ef2 100644 --- a/src/plugin/plugins/keepAlive.ts +++ b/src/plugin/plugins/keepAlive.ts @@ -1,13 +1,14 @@ import http from "node:http"; -import type { Lavamusic } from "../../structures/index.js"; -import type { BotPlugin } from "../index.js"; +import type { Lavamusic } from "../../structures/index"; +import type { BotPlugin } from "../index"; +import { env } from "../../env"; const keepAlive: BotPlugin = { name: "KeepAlive Plugin", version: "1.0.0", author: "Appu", initialize: (client: Lavamusic) => { - if (client.config.keepAlive) { + if (env.KEEP_ALIVE) { const server = http.createServer((_req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end(`I'm alive! Currently serving ${client.guilds.cache.size} guilds.`); diff --git a/src/plugin/plugins/updateStatus.ts b/src/plugin/plugins/updateStatus.ts index e8487f547..20eeb41a9 100644 --- a/src/plugin/plugins/updateStatus.ts +++ b/src/plugin/plugins/updateStatus.ts @@ -1,5 +1,5 @@ -import type { Lavamusic } from "../../structures/index.js"; -import type { BotPlugin } from "../index.js"; +import type { Lavamusic } from "../../structures/index"; +import type { BotPlugin } from "../index"; const updateStatusPlugin: BotPlugin = { name: "Update Status Plugin", diff --git a/src/shard.ts b/src/shard.ts new file mode 100644 index 000000000..3b8a44d48 --- /dev/null +++ b/src/shard.ts @@ -0,0 +1,22 @@ +import { ShardingManager } from "discord.js"; +import { env } from "./env"; +import type Logger from "./structures/Logger"; + +export async function shardStart(logger: Logger) { + const manager = new ShardingManager("./dist/LavaClient.js", { + respawn: true, + token: env.TOKEN, + totalShards: "auto", + shardList: "auto", + }); + + manager.on("shardCreate", (shard) => { + shard.on("ready", () => { + logger.start(`[CLIENT] Shard ${shard.id} connected to Discord's Gateway.`); + }); + }); + + await manager.spawn(); + + logger.start(`[CLIENT] ${manager.totalShards} shard(s) spawned.`); +} diff --git a/src/structures/Command.ts b/src/structures/Command.ts index 2e2f5b990..69d25af82 100644 --- a/src/structures/Command.ts +++ b/src/structures/Command.ts @@ -1,5 +1,5 @@ import type { APIApplicationCommandOption, PermissionResolvable } from "discord.js"; -import type Lavamusic from "./Lavamusic.js"; +import type Lavamusic from "./Lavamusic"; interface CommandDescription { content: string; diff --git a/src/structures/Context.ts b/src/structures/Context.ts index da14bb27b..26eb2b26a 100644 --- a/src/structures/Context.ts +++ b/src/structures/Context.ts @@ -18,8 +18,8 @@ import { type TextChannel, type User, } from "discord.js"; -import { T } from "./I18n.js"; -import type { Lavamusic } from "./index.js"; +import { T } from "./I18n"; +import type { Lavamusic } from "./index"; export default class Context { public ctx: CommandInteraction | Message; diff --git a/src/structures/Dispatcher.ts b/src/structures/Dispatcher.ts deleted file mode 100644 index b2b7676c8..000000000 --- a/src/structures/Dispatcher.ts +++ /dev/null @@ -1,311 +0,0 @@ -import type { Message, User } from "discord.js"; -import { LoadType, type Node, type Player, type Track } from "shoukaku"; -import { SearchEngine } from "../types.js"; -import type { Lavamusic } from "./index.js"; - -export class Song implements Track { - encoded: string; - info: { - identifier: string; - isSeekable: boolean; - author: string; - length: number; - isStream: boolean; - position: number; - title: string; - uri?: string; - artworkUrl?: string; - isrc?: string; - sourceName: string; - requester: User; - }; - pluginInfo: unknown; - - constructor(track: Song | Track, user: User) { - if (!track) throw new Error("Track is not provided"); - this.encoded = track.encoded; - this.info = { - ...track.info, - requester: user, - }; - } -} - -interface DispatcherOptions { - client: Lavamusic; - guildId: string; - channelId: string; - player: Player; - node: Node; -} - -export default class Dispatcher { - private client: Lavamusic; - public guildId: string; - public channelId: string; - public player: Player; - public queue: Song[]; - public stopped: boolean; - public previous: Song | null; - public current: Song | null; - public loop: "off" | "repeat" | "queue"; - public requester: User; - public repeat: number; - public node: Node; - public paused: boolean; - public filters: string[]; - public autoplay: boolean; - public nowPlayingMessage: Message | null; - public history: Song[]; - - constructor(options: DispatcherOptions) { - this.client = options.client; - this.guildId = options.guildId; - this.channelId = options.channelId; - this.player = options.player; - this.queue = []; - this.stopped = false; - this.previous = null; - this.current = null; - this.loop = "off"; - this.repeat = 0; - this.node = options.node; - this.paused = false; - this.filters = []; - this.autoplay = false; - this.nowPlayingMessage = null; - this.history = []; - this.player - .on("start", () => this.client.shoukaku.emit("trackStart" as any, this.player, this.current, this)) - .on("end", () => { - if (this.queue.length === 0) { - this.client.shoukaku.emit("queueEnd" as any, this.player, this.current, this); - } - this.client.shoukaku.emit("trackEnd" as any, this.player, this.current, this); - }) - .on("stuck", () => this.client.shoukaku.emit("trackStuck" as any, this.player, this.current)) - .on("closed", (...args) => this.client.shoukaku.emit("socketClosed" as any, this.player, ...args)); - } - - get exists(): boolean { - return this.client.queue.has(this.guildId); - } - - get volume(): number { - return this.player.volume; - } - - public play(): Promise { - if (!(this.exists && (this.queue.length > 0 || this.current))) return; - this.current = this.queue.length > 0 ? this.queue.shift() : this.queue[0]; - if (this.current) { - this.player.playTrack({ track: { encoded: this.current.encoded } }); - this.history.push(this.current); - if (this.history.length > 100) this.history.shift(); - } - } - - public pause(): void { - if (this.player) { - this.paused = !this.paused; - this.player.setPaused(this.paused); - } - } - - public remove(index: number): void { - if (this.player && index <= this.queue.length) { - this.queue.splice(index, 1); - } - } - - public previousTrack(): void { - if (this.player && this.previous) { - this.queue.unshift(this.previous); - this.player.stopTrack(); - } - } - - public destroy(): void { - this.queue.length = 0; - this.history = []; - this.client.shoukaku.leaveVoiceChannel(this.guildId); - this.player.destroy(); - this.client.queue.delete(this.guildId); - if (!this.stopped) { - this.client.shoukaku.emit("playerDestroy" as any, this.player); - } - } - - public setShuffle(): void { - if (this.player) { - for (let i = this.queue.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [this.queue[i], this.queue[j]] = [this.queue[j], this.queue[i]]; - } - } - } - - public skip(skipto = 1): void { - if (this.player) { - if (skipto > this.queue.length) { - this.queue.length = 0; - } else { - this.queue.splice(0, skipto - 1); - } - this.repeat = this.repeat === 1 ? 0 : this.repeat; - this.player.stopTrack(); - } - } - - public seek(time: number): void { - if (this.player) { - this.player.seekTo(time); - } - } - - public stop(): void { - if (this.player) { - this.queue.length = 0; - this.history = []; - this.loop = "off"; - this.autoplay = false; - this.repeat = 0; - this.stopped = true; - this.player.stopTrack(); - } - } - - public setLoop(loop: "off" | "repeat" | "queue"): void { - this.loop = loop; - } - - public buildTrack(track: Song | Track, user: User): Song { - return new Song(track, user); - } - - public async isPlaying(): Promise { - if (this.queue.length > 0 && !this.current && !this.player.paused) { - await this.play(); - } - } - - public async Autoplay(song: Song): Promise { - if (!song?.info) return; - - try { - const node = this.client.shoukaku.options.nodeResolver(this.client.shoukaku.nodes); - if (!node) return; - switch (song.info.sourceName) { - case "youtube": { - const resolve = await node.rest.resolve(`${SearchEngine.YouTubeMusic}:${song.info.author}`); - this.addAutoplayTrack(resolve); - break; - } - case "soundcloud": - await node.rest.resolve(`${SearchEngine.SoundCloud}:${song.info.author}`); - break; - case "spotify": { - // need lavaSrc plugin in lavalink - const data = await node.rest.resolve(`sprec:seed_tracks=${song.info.identifier}`); - if (!data) return; - if (data.loadType === LoadType.PLAYLIST) { - const tracks = data.data.tracks; - const trackUrl = tracks[Math.floor(Math.random() * tracks.length)]?.info?.uri; - if (!trackUrl) return; - const resolve = await node.rest.resolve(trackUrl); - if (!resolve) return; - if (resolve.loadType === LoadType.TRACK) { - const song = new Song(resolve.data, this.client.user!); - this.queue.push(song); - return this.isPlaying(); - } - } - break; - } - // need jiosaavn plugin in lavalink (https://github.com/appujet/jiosaavn-plugin) - case "jiosaavn": { - const data = await node.rest.resolve(`jsrec:${song.info.identifier}`); - if (!data) return; - if (data.loadType === LoadType.PLAYLIST) { - const tracks = data.data.tracks; - const trackUrl = tracks[Math.floor(Math.random() * tracks.length)]?.info?.uri; - if (!trackUrl) return; - const resolve = await node.rest.resolve(trackUrl); - if (!resolve) return; - if (resolve.loadType === LoadType.TRACK) { - const song = new Song(resolve.data, this.client.user!); - this.queue.push(song); - return this.isPlaying(); - } - } - break; - } - case "deezer": { - const resolve = await node.rest.resolve(`${SearchEngine.Deezer}:${song.info.author}`); - this.addAutoplayTrack(resolve); - break; - } - case "applemusic": { - const resolve = await node.rest.resolve(`${SearchEngine.Apple}:${song.info.author}`); - this.addAutoplayTrack(resolve); - break; - } - default: { - const resolve = await node.rest.resolve(`${SearchEngine.YouTubeMusic}:${song.info.author}`); - this.addAutoplayTrack(resolve); - break; - } - } - } catch (_error) { - return this.destroy(); - } - } - private addAutoplayTrack(resolve: any) { - if (!(resolve?.data && Array.isArray(resolve.data))) { - console.error("Failed to fetch node resolve data."); - return this.destroy(); - } - - let choosed: Song | null = null; - const maxAttempts = 10; - let attempts = 0; - - const metadata = resolve.data as any[] as any; - - while (attempts < maxAttempts) { - const potentialChoice = new Song(metadata[Math.floor(Math.random() * metadata.length)], this.client.user!); - if ( - !( - this.queue.some((s) => s.encoded === potentialChoice.encoded) || - this.history.some((s) => s.encoded === potentialChoice.encoded) - ) - ) { - choosed = potentialChoice; - break; - } - attempts++; - } - - if (choosed) { - this.queue.push(choosed); - return this.isPlaying(); - } - } - public async setAutoplay(autoplay: boolean): Promise { - this.autoplay = autoplay; - if (autoplay) { - await this.Autoplay(this.current || this.queue[0]); - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/structures/Event.ts b/src/structures/Event.ts index d5e8edcf3..bb3ca41b4 100644 --- a/src/structures/Event.ts +++ b/src/structures/Event.ts @@ -1,7 +1,11 @@ -import type Lavamusic from "./Lavamusic.js"; +import type { ClientEvents } from "discord.js"; +import type Lavamusic from "./Lavamusic"; +import type { LavalinkManagerEvents, NodeManagerEvents } from "lavalink-client"; + +export type AllEvents = LavalinkManagerEvents & NodeManagerEvents & ClientEvents; interface EventOptions { - name: string; + name: keyof AllEvents; one?: boolean; } @@ -9,7 +13,7 @@ export default class Event { public client: Lavamusic; public one: boolean; public file: string; - public name: string; + public name: keyof AllEvents; public fileName: string; constructor(client: Lavamusic, file: string, options: EventOptions) { @@ -20,7 +24,7 @@ export default class Event { this.fileName = file.split(".")[0]; } - public async run(..._args: any[]): Promise { + public async run(..._args: any): Promise { return await Promise.resolve(); } } diff --git a/src/structures/I18n.ts b/src/structures/I18n.ts index d9d286689..b54d6597a 100644 --- a/src/structures/I18n.ts +++ b/src/structures/I18n.ts @@ -1,9 +1,9 @@ import i18n from "i18n"; import { Locale } from "discord.js"; -import defaultLanguage from "../config.js"; -import { Language } from "../types.js"; -import Logger from "./Logger.js"; +import defaultLanguage from "../config"; +import { Language } from "../types"; +import Logger from "./Logger"; const logger = new Logger(); diff --git a/src/structures/LavalinkClient.ts b/src/structures/LavalinkClient.ts new file mode 100644 index 000000000..a98fafc82 --- /dev/null +++ b/src/structures/LavalinkClient.ts @@ -0,0 +1,40 @@ +import { LavalinkManager, type SearchResult, type SearchPlatform } from "lavalink-client"; +import type Lavamusic from "./Lavamusic"; +import { requesterTransformer } from "../utils/functions/player"; + +export default class LavalinkClient extends LavalinkManager { + public client: Lavamusic; + constructor(client: Lavamusic) { + super({ + nodes: JSON.parse(client.env.NODES), + sendToShard: (guildId, payload) => client.guilds.cache.get(guildId)?.shard?.send(payload), + queueOptions: { + maxPreviousTracks: 25, + }, + playerOptions: { + defaultSearchPlatform: client.env.SEARCH_ENGINE, + onDisconnect: { + destroyPlayer: true, + }, + requesterTransformer: requesterTransformer, + onEmptyQueue: { + //autoPlayFunction, + }, + }, + }); + this.client = client; + } + /** + * Searches for a song and returns the tracks. + * @param query The query to search for. + * @param user The user who requested the search. + * @param source The source to search in. Defaults to youtube. + * @returns An array of tracks that match the query. + */ + public async search(query: string, user: unknown, source?: SearchPlatform): Promise { + const nodes = this.nodeManager.leastUsedNodes(); + const node = nodes[Math.floor(Math.random() * nodes.length)]; + const result = await node.search({ query, source }, user, false); + return result; + } +} diff --git a/src/structures/Lavamusic.ts b/src/structures/Lavamusic.ts index 96a58dd27..d10b6e382 100644 --- a/src/structures/Lavamusic.ts +++ b/src/structures/Lavamusic.ts @@ -1,6 +1,5 @@ import fs from "node:fs"; import path from "node:path"; -import { fileURLToPath } from "node:url"; import { Api } from "@top-gg/sdk"; import { ApplicationCommandType, @@ -15,15 +14,15 @@ import { Routes, } from "discord.js"; import { Locale } from "discord.js"; -import config from "../config.js"; -import ServerData from "../database/server.js"; -import loadPlugins from "../plugin/index.js"; -import { Utils } from "../utils/Utils.js"; -import { T, i18n, initI18n, localization } from "./I18n.js"; -import Logger from "./Logger.js"; -import { type Command, Queue, ShoukakuClient } from "./index.js"; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); +import config from "../config"; +import ServerData from "../database/server"; +import loadPlugins from "../plugin/index"; +import { Utils } from "../utils/Utils"; +import { T, i18n, initI18n, localization } from "./I18n"; +import Logger from "./Logger"; +import type { Command } from "./index"; +import { env } from "../env"; +import LavalinkClient from "./LavalinkClient"; export default class Lavamusic extends Client { public commands: Collection = new Collection(); @@ -35,20 +34,22 @@ export default class Lavamusic extends Client { public readonly emoji = config.emoji; public readonly color = config.color; private body: RESTPostAPIChatInputApplicationCommandsJSONBody[] = []; - public shoukaku!: ShoukakuClient; public topGG!: Api; - public utils = Utils; - public queue = new Queue(this); - + public utils = Utils;; + public env: typeof env = env; + public manager: LavalinkClient; public embed(): EmbedBuilder { return new EmbedBuilder(); } public async start(token: string): Promise { initI18n(); - const nodes = this.config.autoNode ? await this.getNodes() : this.config.lavalink; - this.shoukaku = new ShoukakuClient(this, nodes); - this.topGG = new Api(this.config.topGG); + if (env.TOPGG) { + this.topGG = new Api(env.TOPGG); + } else { + this.logger.warn("Top.gg token not found!"); + } + this.manager = new LavalinkClient(this); await this.loadCommands(); this.logger.info("Successfully loaded commands!"); await this.loadEvents(); @@ -67,15 +68,14 @@ export default class Lavamusic extends Client { } private async loadCommands(): Promise { - const commandsPath = path.join(__dirname, "../commands"); - const commandDirs = fs.readdirSync(commandsPath); + const commandsPath = fs.readdirSync(path.join(__dirname, "../commands")); - for (const dir of commandDirs) { - const commandFiles = fs.readdirSync(path.join(commandsPath, dir)).filter((file) => file.endsWith(".js")); + for (const dir of commandsPath) { + const commandFiles = fs.readdirSync(path.join(__dirname, "../commands", dir)).filter((file) => file.endsWith(".js")); for (const file of commandFiles) { - const cmdModule = await import(`../commands/${dir}/${file}`); - const command: Command = new cmdModule.default(this); + const cmdModule = require(`../commands/${dir}/${file}`); + const command: Command = new cmdModule.default(this, file); command.category = dir; this.commands.set(command.name, command); @@ -174,7 +174,7 @@ export default class Lavamusic extends Client { : Routes.applicationCommands(this.user?.id ?? ""); try { - const rest = new REST({ version: "10" }).setToken(this.config.token ?? ""); + const rest = new REST({ version: "10" }).setToken(env.TOKEN ?? ""); await rest.put(route, { body: this.body }); this.logger.info("Successfully deployed slash commands!"); } catch (error) { @@ -197,18 +197,19 @@ export default class Lavamusic extends Client { } private async loadEvents(): Promise { - const eventsPath = path.join(__dirname, "../events"); - const eventDirs = fs.readdirSync(eventsPath); + const eventsPath = fs.readdirSync(path.join(__dirname, "..", "events")); - for (const dir of eventDirs) { - const eventFiles = fs.readdirSync(path.join(eventsPath, dir)).filter((file) => file.endsWith(".js")); + for (const dir of eventsPath) { + const eventFiles = fs.readdirSync(path.join(__dirname, "..", "events", dir)).filter((file) => file.endsWith(".js")); for (const file of eventFiles) { - const eventModule = await import(`../events/${dir}/${file}`); + const eventModule = require(`../events/${dir}/${file}`); const event = new eventModule.default(this, file); if (dir === "player") { - this.shoukaku.on(event.name, (...args) => event.run(...args)); + this.manager.on(event.name, (...args) => event.run(...args)); + } else if (dir === "node") { + this.manager.nodeManager.on(event.name, (...args) => event.run(...args)); } else { this.on(event.name, (...args) => event.run(...args)); } diff --git a/src/structures/Queue.ts b/src/structures/Queue.ts deleted file mode 100644 index 5223677b3..000000000 --- a/src/structures/Queue.ts +++ /dev/null @@ -1,92 +0,0 @@ -import type { Guild } from "discord.js"; -import type { LavalinkResponse, Node } from "shoukaku"; -import { Dispatcher, type Lavamusic } from "./index.js"; - -export class Queue extends Map { - public client: Lavamusic; - - constructor(client: Lavamusic) { - super(); - this.client = client; - } - - public override get(guildId: string): Dispatcher | undefined { - return super.get(guildId); - } - - public override set(guildId: string, dispatcher: Dispatcher): this { - return super.set(guildId, dispatcher); - } - - public override delete(guildId: string): boolean { - return super.delete(guildId); - } - - public override clear(): void { - super.clear(); - } - - public async create(guild: Guild, voice: any, channel: any, givenNode?: Node): Promise { - if (!voice) throw new Error("No voice channel was provided"); - if (!channel) throw new Error("No text channel was provided"); - if (!guild) throw new Error("No guild was provided"); - - let dispatcher = this.get(guild.id); - const connection = this.client.shoukaku.connections.get(guild.id); - let player = this.client.shoukaku.players.get(guild.id); - if (player && connection) { - if (!dispatcher) { - dispatcher = new Dispatcher({ - client: this.client, - guildId: guild.id, - channelId: channel.id, - player, - node: player.node, - }); - this.set(guild.id, dispatcher); - } - } else { - const node = givenNode ?? this.client.shoukaku.options.nodeResolver(this.client.shoukaku.nodes); - player = await this.client.shoukaku.joinVoiceChannel({ - guildId: guild.id, - channelId: voice.id, - shardId: guild.shardId, - deaf: true, - }); - - dispatcher = new Dispatcher({ - client: this.client, - guildId: guild.id, - channelId: channel.id, - player, - node, - }); - - this.set(guild.id, dispatcher); - this.client.shoukaku.emit("playerCreate" as any, dispatcher.player); - } - return dispatcher; - } - - public async search(query: string): Promise { - const node = this.client.shoukaku.options.nodeResolver(this.client.shoukaku.nodes); - const searchQuery = /^https?:\/\//.test(query) ? query : `${this.client.config.searchEngine}:${query}`; - try { - return await node.rest.resolve(searchQuery); - } catch (err) { - console.error("Error during search:", err); - return null; - } - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/structures/Shoukaku.ts b/src/structures/Shoukaku.ts deleted file mode 100644 index e74c4bd2e..000000000 --- a/src/structures/Shoukaku.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Connectors, type NodeOption, Shoukaku } from "shoukaku"; -import type { Lavamusic } from "./index.js"; - -export default class ShoukakuClient extends Shoukaku { - public client: Lavamusic; - constructor(client: Lavamusic, nodes: NodeOption[]) { - super(new Connectors.DiscordJS(client), nodes, { - resume: true, // Whether to resume a connection on disconnect to Lavalink (Server Side) (Note: DOES NOT RESUME WHEN THE LAVALINK SERVER DIES) - resumeTimeout: 30, - resumeByLibrary: true, // Whether to resume the players by doing it in the library side (Client Side) (Note: TRIES TO RESUME REGARDLESS OF WHAT HAPPENED ON A LAVALINK SERVER) - reconnectTries: 5, - reconnectInterval: 5, - restTimeout: 60, - moveOnDisconnect: false, // Whether to move players to a different Lavalink node when a node disconnects - //voiceConnectionTimeout: 15, - nodeResolver: (nodes) => - [...nodes.values()] - .filter((node) => node.state === 2) - .sort((a, b) => a.penalties - b.penalties) - .shift(), - }); - this.client = client; - this.on("ready", (name, reconnected) => { - this.client.shoukaku.emit(reconnected ? "nodeReconnect" : ("nodeConnect" as any), name); - }); - this.on("error", (name, error) => { - this.client.shoukaku.emit("nodeError" as any, name, error); - }); - this.on("close", (name, code, reason) => { - this.client.shoukaku.emit("nodeDestroy" as any, name, code, reason); - }); - this.on("disconnect", (name, count) => { - this.client.shoukaku.emit("nodeDisconnect" as any, name, count); - }); - this.on("debug", (name, reason) => { - this.client.shoukaku.emit("nodeRaw" as any, name, reason); - }); - } -} - -/** - * Project: lavamusic - * Author: Appu - * Main Contributor: LucasB25 - * Company: Coders - * Copyright (c) 2024. All rights reserved. - * This code is the property of Coder and may not be reproduced or - * modified without permission. For more information, contact us at - * https://discord.gg/ns8CTk9J3e - */ diff --git a/src/structures/index.ts b/src/structures/index.ts index 2695994fd..3c11bc944 100644 --- a/src/structures/index.ts +++ b/src/structures/index.ts @@ -1,12 +1,9 @@ -import Command from "./Command.js"; -import Context from "./Context.js"; -import Dispatcher from "./Dispatcher.js"; -import Event from "./Event.js"; -import Lavamusic from "./Lavamusic.js"; -import { Queue } from "./Queue.js"; -import ShoukakuClient from "./Shoukaku.js"; +import Command from "./Command"; +import Context from "./Context"; +import Event from "./Event"; +import Lavamusic from "./Lavamusic"; -export { Event, Queue, Command, Lavamusic, Context, ShoukakuClient, Dispatcher }; +export { Event, Command, Lavamusic, Context }; /** * Project: lavamusic diff --git a/src/types.ts b/src/types.ts index 93ee54df5..9ddde1a5b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -76,6 +76,13 @@ export const LocaleFlags = { [Language.Vietnamese]: "🇻🇳", }; +export interface Requester { + id: string; + username: string; + discriminator?: string; + avatarURL?: string; +} + /** * Project: lavamusic * Author: Appu diff --git a/src/utils/BotLog.ts b/src/utils/BotLog.ts index 612d7db77..5824b381e 100644 --- a/src/utils/BotLog.ts +++ b/src/utils/BotLog.ts @@ -1,11 +1,11 @@ import type { TextChannel } from "discord.js"; -import type { Lavamusic } from "../structures/index.js"; +import type { Lavamusic } from "../structures/index"; export default class BotLog { public static send(client: Lavamusic, message: string, type: "error" | "warn" | "info" | "success" = "info"): void { - if (!client?.channels.cache && client.config.logChannelId) return; + if (!client?.channels.cache && client.env.LOG_CHANNEL_ID) return; - const channel = client.channels.cache.get(client.config.logChannelId!) as TextChannel; + const channel = client.channels.cache.get(client.env.LOG_CHANNEL_ID!) as TextChannel; if (!channel) return; const colors = { diff --git a/src/utils/Buttons.ts b/src/utils/Buttons.ts index 444e44628..aff4d8b17 100644 --- a/src/utils/Buttons.ts +++ b/src/utils/Buttons.ts @@ -1,7 +1,8 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type EmojiIdentifierResolvable } from "discord.js"; -import type { Dispatcher, Lavamusic } from "../structures/index.js"; +import type { Lavamusic } from "../structures/index"; +import type { Player } from "lavalink-client/dist/types"; -function getButtons(player: Dispatcher, client: Lavamusic): ActionRowBuilder[] { +function getButtons(player: Player, client: Lavamusic): ActionRowBuilder[] { const buttonData = [ { customId: "PREV_BUT", diff --git a/src/utils/SetupSystem.ts b/src/utils/SetupSystem.ts index 452d2d0d4..20c0c9885 100644 --- a/src/utils/SetupSystem.ts +++ b/src/utils/SetupSystem.ts @@ -1,21 +1,30 @@ import { type ColorResolvable, EmbedBuilder, type Message, type TextChannel } from "discord.js"; -import { LoadType } from "shoukaku"; -import type { Song } from "../structures/Dispatcher.js"; -import { T } from "../structures/I18n.js"; -import type { Dispatcher, Lavamusic } from "../structures/index.js"; -import { getButtons } from "./Buttons.js"; -function neb(embed: EmbedBuilder, player: Dispatcher, client: Lavamusic, locale: string): EmbedBuilder { - if (!player?.current?.info) return embed; - const iconUrl = client.config.icons[player.current.info.sourceName] || client.user.displayAvatarURL({ extension: "png" }); - const icon = player.current.info.artworkUrl || client.config.links.img; +import { T } from "../structures/I18n"; +import type { Lavamusic } from "../structures/index"; +import { getButtons } from "./Buttons"; +import type { Player, Track } from "lavalink-client"; +import type { Requester } from "../types"; + +/** + * A function that will generate an embed based on the player's current track. + * @param embed The embed that will be modified. + * @param player The player to get the current track from. + * @param client The client to get the config from. + * @param locale The locale to translate the strings. + * @returns The modified embed. + */ +function neb(embed: EmbedBuilder, player: Player, client: Lavamusic, locale: string): EmbedBuilder { + if (!player?.queue.current?.info) return embed; + const iconUrl = client.config.icons[player.queue.current.info.sourceName] || client.user.displayAvatarURL({ extension: "png" }); + const icon = player.queue.current.info.artworkUrl || client.config.links.img; const description = T(locale, "player.setupStart.description", { - title: player.current.info.title, - uri: player.current.info.uri, - author: player.current.info.author, - length: client.utils.formatTime(player.current.info.length), - requester: player.current.info.requester, + title: player.queue.current.info.title, + uri: player.queue.current.info.uri, + author: player.queue.current.info.author, + length: client.utils.formatTime(player.queue.current.info.duration), + requester: `<@${(player.queue.current.requester as Requester).id}>`, }); return embed .setAuthor({ @@ -27,7 +36,16 @@ function neb(embed: EmbedBuilder, player: Dispatcher, client: Lavamusic, locale: .setColor(client.color.main); } -async function setupStart(client: Lavamusic, query: string, player: Dispatcher, message: Message): Promise { +/** + * A function that will generate a setup message or edit an existing one + * with the current song playing. + * @param client The client to get the config from. + * @param query The query to search for. + * @param player The player to get the current track from. + * @param message The message to edit or send the setup message. + * @returns A promise that resolves when the function is done. + */ +async function setupStart(client: Lavamusic, query: string, player: Player, message: Message): Promise { let m: Message; const embed = client.embed(); const n = client.embed().setColor(client.color.main); @@ -45,7 +63,7 @@ async function setupStart(client: Lavamusic, query: string, player: Dispatcher, if (m) { try { if (message.inGuild()) { - const res = await client.queue.search(query); + /* const res = await client.queue.search(query); switch (res.loadType) { case LoadType.ERROR: await message.channel @@ -177,7 +195,7 @@ async function setupStart(client: Lavamusic, query: string, player: Dispatcher, await m.edit({ embeds: [n] }).catch(() => {}); break; } - } + } */ } } catch (error) { client.logger.error(error); @@ -185,15 +203,25 @@ async function setupStart(client: Lavamusic, query: string, player: Dispatcher, } } +/** + * A function that will generate an embed based on the player's current track. + * @param msgId The message ID of the setup message. + * @param channel The channel to send the message in. + * @param player The player to get the current track from. + * @param track The track to generate the embed for. + * @param client The client to get the config from. + * @param locale The locale to translate the strings. + * @returns A promise that resolves when the function is done. + */ async function trackStart( msgId: any, channel: TextChannel, - player: Dispatcher, - track: Song, + player: Player, + track: Track, client: Lavamusic, locale: string, ): Promise { - const icon = player.current ? player.current.info.artworkUrl : client.config.links.img; + const icon = player.queue.current ? player.queue.current.info.artworkUrl : client.config.links.img; let m: Message; try { @@ -202,13 +230,13 @@ async function trackStart( client.logger.error(error); } - const iconUrl = client.config.icons[player.current.info.sourceName] || client.user.displayAvatarURL({ extension: "png" }); + const iconUrl = client.config.icons[player.queue.current.info.sourceName] || client.user.displayAvatarURL({ extension: "png" }); const description = T(locale, "player.setupStart.description", { title: track.info.title, uri: track.info.uri, author: track.info.author, - length: client.utils.formatTime(track.info.length), - requester: track.info.requester, + length: client.utils.formatTime(track.info.duration), + requester: `<@${(track.requester as Requester).id}>`, }); const embed = client @@ -226,24 +254,24 @@ async function trackStart( .edit({ embeds: [embed], components: getButtons(player, client).map((b) => { - b.components.forEach((c) => c.setDisabled(!player?.current)); + b.components.forEach((c) => c.setDisabled(!player?.queue.current)); return b; }), }) - .catch(() => {}); + .catch(() => { }); } else { await channel .send({ embeds: [embed], components: getButtons(player, client).map((b) => { - b.components.forEach((c) => c.setDisabled(!player?.current)); + b.components.forEach((c) => c.setDisabled(!player?.queue.current)); return b; }), }) .then((msg) => { client.db.setSetup(msg.guild.id, msg.id, msg.channel.id); }) - .catch(() => {}); + .catch(() => { }); } } @@ -263,15 +291,15 @@ async function updateSetup(client: Lavamusic, guild: any, locale: string): Promi } } if (m) { - const player = client.queue.get(guild.id); - if (player?.current) { - const iconUrl = client.config.icons[player.current.info.sourceName] || client.user.displayAvatarURL({ extension: "png" }); + const player = client.manager.getPlayer(guild.id); + if (player?.queue.current) { + const iconUrl = client.config.icons[player.queue.current.info.sourceName] || client.user.displayAvatarURL({ extension: "png" }); const description = T(locale, "player.setupStart.description", { - title: player.current.info.title, - uri: player.current.info.uri, - author: player.current.info.author, - length: client.utils.formatTime(player.current.info.length), - requester: player.current.info.requester, + title: player.queue.current.info.title, + uri: player.queue.current.info.uri, + author: player.queue.current.info.author, + length: client.utils.formatTime(player.queue.current.info.duration), + requester: `<@${(player.queue.current.requester as Requester).id}>`, }); const embed = client @@ -282,16 +310,16 @@ async function updateSetup(client: Lavamusic, guild: any, locale: string): Promi }) .setColor(client.color.main) .setDescription(description) - .setImage(player.current.info.artworkUrl); + .setImage(player.queue.current.info.artworkUrl); await m .edit({ embeds: [embed], components: getButtons(player, client).map((b) => { - b.components.forEach((c) => c.setDisabled(!player?.current)); + b.components.forEach((c) => c.setDisabled(!player?.queue.current)); return b; }), }) - .catch(() => {}); + .catch(() => { }); } else { const embed = client .embed() @@ -310,7 +338,7 @@ async function updateSetup(client: Lavamusic, guild: any, locale: string): Promi return b; }), }) - .catch(() => {}); + .catch(() => { }); } } } @@ -319,13 +347,13 @@ async function buttonReply(int: any, args: string, color: ColorResolvable): Prom const embed = new EmbedBuilder(); let m: Message; if (int.replied) { - m = await int.editReply({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => {}); + m = await int.editReply({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => { }); } else { - m = await int.followUp({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => {}); + m = await int.followUp({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => { }); } setTimeout(async () => { if (int && !int.ephemeral) { - await m.delete().catch(() => {}); + await m.delete().catch(() => { }); } }, 2000); } @@ -336,7 +364,7 @@ async function oops(channel: TextChannel, args: string): Promise { const m = await channel.send({ embeds: [embed1], }); - setTimeout(async () => await m.delete().catch(() => {}), 12000); + setTimeout(async () => await m.delete().catch(() => { }), 12000); } catch (e) { return console.error(e); } diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 6b59296ce..b11a3d0b8 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -1,6 +1,5 @@ import { ActionRowBuilder, ActivityType, ButtonBuilder, ButtonStyle, CommandInteraction, type TextChannel } from "discord.js"; -import config from "../config.js"; -import type { Context, Lavamusic } from "../structures/index.js"; +import type { Context, Lavamusic } from "../structures/index"; export class Utils { public static formatTime(ms: number): string { @@ -15,21 +14,21 @@ export class Utils { public static updateStatus(client: Lavamusic, guildId?: string): void { const { user } = client; - if (user && guildId === config.guildId) { - const player = client.queue.get(config.guildId!); + if (user && guildId === client.env.GUILD_ID) { + const player = client.manager.getPlayer(client.env.GUILD_ID); user.setPresence({ activities: [ { - name: player?.current ? `🎶 | ${player.current.info.title}` : config.botActivity, - type: player?.current ? ActivityType.Listening : config.botActivityType, + name: player && player.queue?.current ? `🎶 | ${player.queue?.current.info.title}` : client.env.BOT_ACTIVITY, + type: player && player?.queue?.current ? ActivityType.Listening : client.env.BOT_ACTIVITY_TYPE, }, ], - status: config.botStatus as any, + status: client.env.BOT_STATUS as any, }); } } - public static chunk(array: any[], size: number): any[] { + public static chunk(array: any[], size: number) { const chunked_arr = []; for (let index = 0; index < array.length; index += size) { chunked_arr.push(array.slice(index, size + index)); diff --git a/src/utils/functions/player.ts b/src/utils/functions/player.ts new file mode 100644 index 000000000..5e61171c3 --- /dev/null +++ b/src/utils/functions/player.ts @@ -0,0 +1,24 @@ +import type { Requester } from "../../types"; + +/** + * Transforms a requester into a standardized requester object. + * + * @param {any} requester The requester to transform. Can be a string, a user, or an object with + * the keys `id`, `username`, and `avatarURL`. + * @returns {Requester} The transformed requester object. + */ +export const requesterTransformer = (requester: any): Requester => { + // if it's already the transformed requester + if (typeof requester === "object" && "avatar" in requester && Object.keys(requester).length === 3) return requester as Requester; + // if it's still a string + if (typeof requester === "object" && "displayAvatarURL" in requester) { + // it's a user + return { + id: requester.id, + username: requester.username, + avatarURL: requester.displayAvatarURL({ extension: "png" }), + discriminator: requester.discriminator, + }; + } + return { id: requester!.toString(), username: "unknown" }; +}; diff --git a/tsconfig.json b/tsconfig.json index d3cbf1fd6..eb9ee500e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,14 @@ { + "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { - "target": "esnext", - "module": "esnext", - "lib": ["esnext"], + "lib": ["ESNext", "DOM"], + "rootDir": "./src", + "outDir": "./dist", + "target": "ESNext", + "module": "CommonJS", "declaration": true, "sourceMap": true, "newLine": "crlf", - "outDir": "dist", - "rootDir": "src", "strict": false, "moduleResolution": "node", "esModuleInterop": true, From 356af8a6a2ae3c1792f49b7ccf4189350ccc26f8 Mon Sep 17 00:00:00 2001 From: appujet Date: Sat, 14 Sep 2024 18:10:15 +0530 Subject: [PATCH 02/32] update: --- README.md | 2 +- package.json | 5 ++--- src/structures/Lavamusic.ts | 14 -------------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 3399659f9..ff1aa3afc 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@

Lavamusic

-

Lavamusic is a Discord music bot that uses Discord.js, Shoukaku, and TypeScript. +

Lavamusic is a Discord music bot that uses Discord.js, lavalink-client, and TypeScript.

Invite Lavamusic diff --git a/package.json b/package.json index a5365d550..c4b574235 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "lavamusic", "version": "4.6.7", - "description": "LavaMusic is a music bot for Discord, written in JavaScript using the Discord.js, Typescript, Shoukaku (Lavalink) library.", + "description": "LavaMusic is a music bot for Discord, written in JavaScript using the Discord.js, Typescript, lavalink-client (Lavalink) library.", "main": "dist/index.js", "scripts": { "start": "npm run clean && node .", @@ -21,7 +21,7 @@ "music", "bot", "lavalink", - "shoukaku", + "lavalink-client", "lavamusic", "typescript", "prisma" @@ -49,7 +49,6 @@ "i18n": "^0.15.1", "lavalink-client": "https://pkg.pr.new/moe-music/lavalink-client@f95457c", "node-system-stats": "^1.3.0", - "shoukaku": "^4.1.1", "signale": "^1.4.0", "topgg-autoposter": "^2.0.2", "tslib": "^2.7.0", diff --git a/src/structures/Lavamusic.ts b/src/structures/Lavamusic.ts index d10b6e382..470453c51 100644 --- a/src/structures/Lavamusic.ts +++ b/src/structures/Lavamusic.ts @@ -182,20 +182,6 @@ export default class Lavamusic extends Client { } } - private async getNodes(): Promise { - const params = new URLSearchParams({ - ssl: "false", - version: "v4", - format: "shoukaku", - }); - const res = await fetch(`https://lavainfo-api.deno.dev/nodes?${params.toString()}`, { - headers: { - "Content-Type": "application/json", - }, - }); - return await res.json(); - } - private async loadEvents(): Promise { const eventsPath = fs.readdirSync(path.join(__dirname, "..", "events")); From e6d73e690a3703725cb0e795fbd082ff4811724a Mon Sep 17 00:00:00 2001 From: appujet Date: Sat, 14 Sep 2024 18:16:19 +0530 Subject: [PATCH 03/32] chore: husky --- .editorconfig | 13 +++++++++++++ .husky/commit-msg | 1 + .husky/pre-commit | 1 + commitlint.config.js | 1 + package.json | 9 ++++++++- 5 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .editorconfig create mode 100644 .husky/commit-msg create mode 100644 .husky/pre-commit create mode 100644 commitlint.config.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..72397a9d7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = crlf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = tab +indent_size = 2 +quote_type = single + +[Makefile] +indent_style = tab \ No newline at end of file diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 000000000..34eed8b28 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +npx --no -- commitlint --edit $1 \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 000000000..d0a778429 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged \ No newline at end of file diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 000000000..8567d51d7 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1 @@ +module.exports = { extends: ["@commitlint/config-conventional"] }; \ No newline at end of file diff --git a/package.json b/package.json index c4b574235..224e73354 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,10 @@ "@types/i18n": "^0.13.12", "@types/node": "^22.5.4", "@types/signale": "^1.4.7", + "husky": "^9.1.6", + "lint-staged": "^15.2.10", "prisma": "^5.19.1", + "tslib": "^2.7.0", "typescript": "^5.6.2" }, "dependencies": { @@ -51,10 +54,14 @@ "node-system-stats": "^1.3.0", "signale": "^1.4.0", "topgg-autoposter": "^2.0.2", - "tslib": "^2.7.0", "undici": "^6.19.8", "zod": "^3.23.8" }, + "lint-staged": { + "*.ts": [ + "biome check --write" + ] + }, "signale": { "displayScope": true, "displayBadge": true, From 87f76837d7bc9269318d7c6cd571337ffd7ad33c Mon Sep 17 00:00:00 2001 From: appujet Date: Sat, 14 Sep 2024 18:17:19 +0530 Subject: [PATCH 04/32] change restart cmd --- src/commands/dev/Restart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/dev/Restart.ts b/src/commands/dev/Restart.ts index 0467cde82..9fbe29fa5 100644 --- a/src/commands/dev/Restart.ts +++ b/src/commands/dev/Restart.ts @@ -61,7 +61,7 @@ export default class Restart extends Command { }); await client.destroy(); - exec("node scripts/restart.ts"); + exec("node scripts/restart.js"); process.exit(0); }); From ad5e98f3f55f069e4beabe390ac7918b616f5556 Mon Sep 17 00:00:00 2001 From: appujet Date: Sat, 14 Sep 2024 18:18:18 +0530 Subject: [PATCH 05/32] update: scripts --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 224e73354..4478c37f8 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "build": "tsc --project tsconfig.json", "clean": "node scripts/clean.js && npm run build", "lint": "biome lint --write", + "prepare": "npm run build && husky", "format": "biome format --write" }, "repository": { From bb3247c84fc9dcf3bcac4918fea0d80b6e790fdf Mon Sep 17 00:00:00 2001 From: appujet Date: Sat, 14 Sep 2024 18:19:32 +0530 Subject: [PATCH 06/32] scripts --- SECURITY.md | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 034e84803..000000000 --- a/SECURITY.md +++ /dev/null @@ -1,21 +0,0 @@ -# Security Policy - -## Supported Versions - -Use this section to tell people about which versions of your project are -currently being supported with security updates. - -| Version | Supported | -| ------- | ------------------ | -| 5.1.x | :white_check_mark: | -| 5.0.x | :x: | -| 4.0.x | :white_check_mark: | -| < 4.0 | :x: | - -## Reporting a Vulnerability - -Use this section to tell people how to report a vulnerability. - -Tell them where to go, how often they can expect to get an update on a -reported vulnerability, what to expect if the vulnerability is accepted or -declined, etc. From 383f6939739641301f44b3c620f92e54c5777c23 Mon Sep 17 00:00:00 2001 From: appujet Date: Sat, 14 Sep 2024 18:20:29 +0530 Subject: [PATCH 07/32] clean --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4478c37f8..a77d820dc 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "build": "tsc --project tsconfig.json", "clean": "node scripts/clean.js && npm run build", "lint": "biome lint --write", - "prepare": "npm run build && husky", + "prepare": "npm run clean && husky", "format": "biome format --write" }, "repository": { From 72aad7fd8b2310d1ef969d6f26e8dc6c815158a4 Mon Sep 17 00:00:00 2001 From: appujet Date: Sat, 14 Sep 2024 18:30:10 +0530 Subject: [PATCH 08/32] chore: update --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index a77d820dc..b4d1204ce 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,8 @@ "@biomejs/biome": "^1.9.0", "@types/i18n": "^0.13.12", "@types/node": "^22.5.4", + "@commitlint/cli": "^19.4.0", + "@commitlint/config-conventional": "^19.2.2", "@types/signale": "^1.4.7", "husky": "^9.1.6", "lint-staged": "^15.2.10", From ab21f02ce75cc41cba29eb857f42f89a7dbd9478 Mon Sep 17 00:00:00 2001 From: appujet Date: Sat, 14 Sep 2024 21:25:38 +0530 Subject: [PATCH 09/32] chore: update env and docker yml --- docker-compose.yml | 10 ++------ src/commands/music/Lyrics.ts | 2 +- src/env.ts | 44 +++++++++++++++++++++++++++----- src/structures/LavalinkClient.ts | 6 ++--- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8d42fdbd1..fc21b8cb0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,14 +63,8 @@ services: container_name: lavamusic image: ghcr.io/appujet/lavamusic:main environment: - # Your lavalink url - - LAVALINK_URL=lavalink:2333 - # Your lavalink password - - LAVALINK_AUTH=youshallnotpass - # Your lavalink name - - LAVALINK_NAME=LavaMusic - # Your lavalink secure (true or false) - - LAVALINK_SECURE=false + # lavalink nodes + - NODES=[{"id":"LavaMusic","host":"lavalink","port":2333,"authorization":"youshallnotpass"}] # database url # - DATABASE_URL= put your database url here (mongodb or postgres) # - DATABASE_URL=postgresql://lavamusic:lavamusic@postgres:5432/lavamusic (for postgres) diff --git a/src/commands/music/Lyrics.ts b/src/commands/music/Lyrics.ts index 64c8d1ff6..cd8b47ebd 100644 --- a/src/commands/music/Lyrics.ts +++ b/src/commands/music/Lyrics.ts @@ -45,7 +45,7 @@ export default class Lyrics extends Command { await ctx.sendDeferMessage(ctx.locale("cmd.lyrics.searching", { trackTitle })); const options = { - apiKey: "client.config.lyricsApi", + apiKey: client.env.GENIUS_API, title: trackTitle, artist: artistName, optimizeQuery: true, diff --git a/src/env.ts b/src/env.ts index 5c3a42c4a..75277f8db 100644 --- a/src/env.ts +++ b/src/env.ts @@ -6,6 +6,22 @@ config({ path: path.join(__dirname, "../.env"), }); +const LavalinkNodeSchema = z.object({ + id: z.string(), + host: z.string(), + port: z.number(), + authorization: z.string(), + secure: z.boolean().optional(), + sessionId: z.string().optional(), + regions: z.string().array().optional(), + retryAmount: z.number().optional(), + retryDelay: z.number().optional(), + requestSignalTimeoutMS: z.number().optional(), + closeOnError: z.boolean().optional(), + heartBeatInterval: z.number().optional(), + enablePingOnStatsCheck: z.boolean().optional(), +}); + const envSchema = z.object({ /** * The discord app token @@ -30,7 +46,7 @@ const envSchema = z.object({ /** * The owner ids */ - OWNER_IDS: z.string().array().optional(), + OWNER_IDS: z.preprocess((val) => (typeof val === "string" ? JSON.parse(val) : val), z.string().array().optional()), /** * The guild id for devlopment purposes @@ -45,7 +61,7 @@ const envSchema = z.object({ /** * The keep alive boolean */ - KEEP_ALIVE: z.boolean().default(false), + KEEP_ALIVE: z.preprocess((val) => val === "true", z.boolean().default(false)), /** * The log channel id @@ -70,7 +86,12 @@ const envSchema = z.object({ /** * The bot activity type */ - BOT_ACTIVITY_TYPE: z.number().default(0), + BOT_ACTIVITY_TYPE: z.preprocess((val) => { + if (typeof val === "string") { + return parseInt(val, 10); + } + return val; + }, z.number().default(0)), /** * The database url */ @@ -79,12 +100,23 @@ const envSchema = z.object({ /** * Search engine */ - SEARCH_ENGINE: z.enum(["youtube", "youtubemusic", "soundcloud", "spotify", "apple", "deezer", "yandex", "jiosaavn"]).default("youtube"), - + SEARCH_ENGINE: z.preprocess( + (val) => { + if (typeof val === "string") { + return val.toLowerCase(); + } + return val; + }, + z.enum(["youtube", "youtubemusic", "soundcloud", "spotify", "apple", "deezer", "yandex", "jiosaavn"]).default("youtube"), + ), /** * Node in json */ - NODES: z.string(), + NODES: z.preprocess((val) => (typeof val === "string" ? JSON.parse(val) : val), z.array(LavalinkNodeSchema)), + /** + * Genius api + */ + GENIUS_API: z.string().optional(), }); type Env = z.infer; diff --git a/src/structures/LavalinkClient.ts b/src/structures/LavalinkClient.ts index a98fafc82..78840e5e6 100644 --- a/src/structures/LavalinkClient.ts +++ b/src/structures/LavalinkClient.ts @@ -1,12 +1,12 @@ -import { LavalinkManager, type SearchResult, type SearchPlatform } from "lavalink-client"; -import type Lavamusic from "./Lavamusic"; +import { LavalinkManager, type LavalinkNodeOptions, type SearchPlatform, type SearchResult } from "lavalink-client"; import { requesterTransformer } from "../utils/functions/player"; +import type Lavamusic from "./Lavamusic"; export default class LavalinkClient extends LavalinkManager { public client: Lavamusic; constructor(client: Lavamusic) { super({ - nodes: JSON.parse(client.env.NODES), + nodes: client.env.NODES as LavalinkNodeOptions[], sendToShard: (guildId, payload) => client.guilds.cache.get(guildId)?.shard?.send(payload), queueOptions: { maxPreviousTracks: 25, From af71145ee0762aebc48183393086018982c4504b Mon Sep 17 00:00:00 2001 From: appujet Date: Sat, 14 Sep 2024 22:50:24 +0530 Subject: [PATCH 10/32] chore: added back setup system --- locales/ChineseCN.json | 3 + locales/ChineseTW.json | 3 + locales/EnglishUS.json | 9 +- locales/French.json | 3 + locales/German.json | 3 + locales/Hindi.json | 3 + locales/Indonesian.json | 3 + locales/Japanese.json | 3 + locales/Korean.json | 7 +- locales/Norwegian.json | 3 + locales/Polish.json | 3 + locales/Russian.json | 3 + locales/SpanishES.json | 3 + locales/Vietnamese.json | 3 + src/commands/config/Setup.ts | 4 +- src/commands/filters/8d.ts | 70 +++++++++ src/commands/filters/BassBoost.ts | 109 +++++++++++++ src/commands/filters/Karaoke.ts | 70 +++++++++ src/commands/filters/LowPass.ts | 70 +++++++++ src/commands/filters/NightCore.ts | 70 +++++++++ src/commands/music/Queue.ts | 6 +- src/env.ts | 12 +- src/events/client/SetupButtons.ts | 250 ++++++++++++++++++++++++++++++ src/events/client/SetupSystem.ts | 77 +++++++++ src/events/node/Connect.ts | 2 +- src/events/node/Destroy.ts | 2 +- src/structures/Event.ts | 11 +- src/structures/LavalinkClient.ts | 4 +- src/utils/SetupSystem.ts | 142 ++++------------- src/utils/functions/player.ts | 78 ++++++++++ 30 files changed, 898 insertions(+), 131 deletions(-) create mode 100644 src/commands/filters/8d.ts create mode 100644 src/commands/filters/BassBoost.ts create mode 100644 src/commands/filters/Karaoke.ts create mode 100644 src/commands/filters/LowPass.ts create mode 100644 src/commands/filters/NightCore.ts create mode 100644 src/events/client/SetupButtons.ts create mode 100644 src/events/client/SetupSystem.ts diff --git a/locales/ChineseCN.json b/locales/ChineseCN.json index 322be9fe5..bf16f1eff 100644 --- a/locales/ChineseCN.json +++ b/locales/ChineseCN.json @@ -134,6 +134,9 @@ "messages": { "filter_enabled": "`✅` | 低音增强滤镜已`启用`。\n**请注意,音量过大会损害您的听力!**", "filter_disabled": "`✅` | 低音增强滤镜已`禁用`。" + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/locales/ChineseTW.json b/locales/ChineseTW.json index e9c3d3c33..affb23d34 100644 --- a/locales/ChineseTW.json +++ b/locales/ChineseTW.json @@ -134,6 +134,9 @@ "messages": { "filter_enabled": "`✅` | 低音增強等化器已`啟用`。\n**請注意,音量過大會損害您的聽力!**", "filter_disabled": "`✅` | 低音增強等化器已`停用`。" + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/locales/EnglishUS.json b/locales/EnglishUS.json index d671e74eb..bc479f6d4 100644 --- a/locales/EnglishUS.json +++ b/locales/EnglishUS.json @@ -132,9 +132,14 @@ }, "bassboost": { "description": "on/off bassboost filter", + "options": { + "level": "The bassboost level you want to set" + }, "messages": { - "filter_enabled": "`✅` | Bassboost filter has been `ENABLED`. \n**Be careful, listening too loudly can damage your hearing!**", - "filter_disabled": "`✅` | Bassboost filter has been `DISABLED`." + "high": "`✅` | High bassboost filter has been `ENABLED`.", + "low": "`✅` | Low bassboost filter has been `ENABLED`.", + "medium": "`✅` | Medium bassboost filter has been `ENABLED`.", + "off": "`✅` | Bassboost filter has been `DISABLED`." } }, "distorsion": { diff --git a/locales/French.json b/locales/French.json index c9c70a1d2..b0ca2a7bb 100644 --- a/locales/French.json +++ b/locales/French.json @@ -121,6 +121,9 @@ "messages": { "filter_enabled": "`✅` | Le filtre de basses a été `ACTIVÉ`. \n**Attention, écouter trop fort peut endommager votre audition !**", "filter_disabled": "`✅` | Le filtre de basses a été `DÉSACTIVÉ`." + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/locales/German.json b/locales/German.json index f8e495061..53b894886 100644 --- a/locales/German.json +++ b/locales/German.json @@ -121,6 +121,9 @@ "messages": { "filter_enabled": "`✅` | Bassboost-Filter wurde `AKTIVIERT`. \n**Vorsicht, zu lautes Hören kann deine Ohren schädigen!**", "filter_disabled": "`✅` | Bassboost-Filter wurde `DEAKTIVIERT`." + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/locales/Hindi.json b/locales/Hindi.json index 21760c3ad..e71d90a66 100644 --- a/locales/Hindi.json +++ b/locales/Hindi.json @@ -134,6 +134,9 @@ "messages": { "filter_enabled": "`✅` | Bassboost filter `SAKRIYA` kar diya gaya hai. \n**Savdhan rahein, tej awaz mein sunne se aapke kaano ko nuksan pahunch sakta hai!**", "filter_disabled": "`✅` | Bassboost filter `NIRAKRIYA` kar diya gaya hai." + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/locales/Indonesian.json b/locales/Indonesian.json index 4cd3616fe..9ff592c91 100644 --- a/locales/Indonesian.json +++ b/locales/Indonesian.json @@ -134,6 +134,9 @@ "messages": { "filter_enabled": "`✅` | Filter bassboost telah `DIAKTIFKAN`. \n**Hati-hati, mendengarkan terlalu keras dapat merusak pendengaran Anda!**", "filter_disabled": "`✅` | Filter bassboost telah `DINONAKTIFKAN`." + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/locales/Japanese.json b/locales/Japanese.json index dc8899c8f..3e397571e 100644 --- a/locales/Japanese.json +++ b/locales/Japanese.json @@ -134,6 +134,9 @@ "messages": { "filter_enabled": "`✅` | ベースブーストフィルターが`有効`になりました。\n**音量を上げすぎると聴覚に悪影響を与える可能性があるので注意してください!**", "filter_disabled": "`✅` | ベースブーストフィルターが`無効`になりました。" + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/locales/Korean.json b/locales/Korean.json index 7f3025359..c448eb89c 100644 --- a/locales/Korean.json +++ b/locales/Korean.json @@ -135,6 +135,9 @@ "messages": { "filter_enabled": "`✅` | 베이스부스트 필터가 `활성화되었어요`. \n**조심하세요, 너무 크게 들으면 귀가 나갈 수도 있어요!**", "filter_disabled": "`✅` | 베이스부스트 필터가 `비활성화되었어요`." + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { @@ -551,8 +554,8 @@ "error_searching": "노래를 검색하는 도중 오류가 발생했어요.", "no_results": "검색결과가 없어요.", "nothing_playing": "재생 중인 노래 없음", - "queue_too_long": "대기열에 노래가 너무 많아요. 노래는 최대 {maxQueueSize}개까지만 추가할 수 있어요.", - "playlist_too_long": "플레이리스트 또는 대기열에 노래가 너무 많아요. 노래는 최대 {maxPlaylistSize}개까지만 추가할 수 있어요.", + "queue_too_long": "대기열에 노래가 너무 많아요. 노래는 최대 {maxQueueSize}개까지만 추가할 수 있어요.", + "playlist_too_long": "플레이리스트 또는 대기열에 노래가 너무 많아요. 노래는 최대 {maxPlaylistSize}개까지만 추가할 수 있어요.", "added_to_queue": "대기열에 추가되었어요: [{title}]({uri})", "added_playlist_to_queue": "[{length}]개의 노래가 추가되었어요." } diff --git a/locales/Norwegian.json b/locales/Norwegian.json index 65112849e..163d69550 100644 --- a/locales/Norwegian.json +++ b/locales/Norwegian.json @@ -134,6 +134,9 @@ "messages": { "filter_enabled": "`✅` | Bassboost-filteret er `AKTIVERT`. \n**Vær forsiktig, å lytte for høyt kan skade hørselen!**", "filter_disabled": "`✅` | Bassboost-filteret er `DEAKTIVERT`." + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/locales/Polish.json b/locales/Polish.json index 4014481c7..1c9646971 100644 --- a/locales/Polish.json +++ b/locales/Polish.json @@ -134,6 +134,9 @@ "messages": { "filter_enabled": "`✅` | Filtr wzmocnienia basów został `WŁĄCZONY`. \n**Uwaga, zbyt głośne słuchanie może uszkodzić słuch!**", "filter_disabled": "`✅` | Filtr wzmocnienia basów został `WYŁĄCZONY`." + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/locales/Russian.json b/locales/Russian.json index 4311e12a3..a1d640041 100644 --- a/locales/Russian.json +++ b/locales/Russian.json @@ -134,6 +134,9 @@ "messages": { "filter_enabled": "`✅` | Фильтр усиления басов был `ВКЛЮЧЕН`. \n**Будьте осторожны, слишком громкое прослушивание может повредить слух!**", "filter_disabled": "`✅` | Фильтр усиления басов был `ОТКЛЮЧЕН`." + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/locales/SpanishES.json b/locales/SpanishES.json index 9df924bff..567356249 100644 --- a/locales/SpanishES.json +++ b/locales/SpanishES.json @@ -121,6 +121,9 @@ "messages": { "filter_enabled": "`✅` | El filtro de refuerzo de graves se ha `ACTIVADO`. \n**¡Ten cuidado, escuchar demasiado alto puede dañar tu oído!**", "filter_disabled": "`✅` | El filtro de refuerzo de graves se ha `DESACTIVADO`." + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/locales/Vietnamese.json b/locales/Vietnamese.json index ab5b6b6ea..1db3602ca 100644 --- a/locales/Vietnamese.json +++ b/locales/Vietnamese.json @@ -134,6 +134,9 @@ "messages": { "filter_enabled": "`✅` | Bộ lọc Bassboost đã được `BẬT`. \n**Cẩn thận, nghe quá to có thể gây hại cho thính giác của bạn!**", "filter_disabled": "`✅` | Bộ lọc Bassboost đã được `TẮT`." + }, + "options": { + "level": "The bassboost level you want to set" } }, "distorsion": { diff --git a/src/commands/config/Setup.ts b/src/commands/config/Setup.ts index b2fe14649..c17ca182f 100644 --- a/src/commands/config/Setup.ts +++ b/src/commands/config/Setup.ts @@ -93,7 +93,7 @@ export default class Setup extends Command { const player = this.client.manager.getPlayer(ctx.guild!.id); const image = this.client.config.links.img; const desc = - player.queue.current + player && player.queue.current ? `[${player.queue.current.info.title}](${player.queue.current.info.uri})` : ctx.locale("player.setupStart.nothing_playing"); embed.setDescription(desc).setImage(image); @@ -129,7 +129,7 @@ export default class Setup extends Command { } client.db.deleteSetup(ctx.guild!.id); const textChannel = ctx.guild.channels.cache.get(data2.textId); - if (textChannel) await textChannel.delete().catch(() => { }); + if (textChannel) await textChannel.delete().catch(() => {}); await ctx.sendMessage({ embeds: [ { diff --git a/src/commands/filters/8d.ts b/src/commands/filters/8d.ts new file mode 100644 index 000000000..9990b4655 --- /dev/null +++ b/src/commands/filters/8d.ts @@ -0,0 +1,70 @@ +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class _8d extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "8d", + description: { + content: "cmd.8d.description", + examples: ["8d"], + usage: "8d", + }, + category: "filters", + aliases: ["3d"], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [], + }); + } + + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const filterEnabled = player.filterManager.filters.rotation; + + if (filterEnabled) { + await player.filterManager.toggleRotation(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.8d.messages.filter_disabled"), + color: this.client.color.main, + }, + ], + }); + } else { + await player.filterManager.toggleRotation(0.2); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.8d.messages.filter_enabled"), + color: this.client.color.main, + }, + ], + }); + } + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/commands/filters/BassBoost.ts b/src/commands/filters/BassBoost.ts new file mode 100644 index 000000000..3b9646805 --- /dev/null +++ b/src/commands/filters/BassBoost.ts @@ -0,0 +1,109 @@ +import { ApplicationCommandOptionType } from "discord.js"; +import { EQList } from "lavalink-client"; +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class BassBoost extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "bassboost", + description: { + content: "cmd.bassboost.description", + examples: ["bassboost high", "bassboost medium", "bassboost low", "bassboost off"], + usage: "bassboost [level]", + }, + category: "filters", + aliases: ["bb"], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [ + { + name: "level", + description: "cmd.bassboost.options.level", + type: ApplicationCommandOptionType.String, + required: true, + choices: [ + { name: "high", value: "high" }, + { name: "medium", value: "medium" }, + { name: "low", value: "low" }, + { name: "off", value: "off" }, + ], + }, + ], + }); + } + + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + + switch (ctx.args[0]?.toLowerCase()) { + case "high": + await player.filterManager.setEQ(EQList.BassboostHigh); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.bassboost.messages.high"), + color: this.client.color.main, + }, + ], + }); + break; + case "medium": + await player.filterManager.setEQ(EQList.BassboostMedium); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.bassboost.messages.medium"), + color: this.client.color.main, + }, + ], + }); + break; + case "low": + await player.filterManager.setEQ(EQList.BassboostLow); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.bassboost.messages.low"), + color: this.client.color.main, + }, + ], + }); + break; + case "off": + await player.filterManager.clearEQ(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.bassboost.messages.off"), + color: this.client.color.main, + }, + ], + }); + break; + } + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/commands/filters/Karaoke.ts b/src/commands/filters/Karaoke.ts new file mode 100644 index 000000000..8025f4df8 --- /dev/null +++ b/src/commands/filters/Karaoke.ts @@ -0,0 +1,70 @@ +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class Karaoke extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "karaoke", + description: { + content: "cmd.karaoke.description", + examples: ["karaoke"], + usage: "karaoke", + }, + category: "filters", + aliases: ["kk"], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [], + }); + } + + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const filterEnabled = player.filterManager.filters.karaoke; + + if (filterEnabled) { + await player.filterManager.toggleKaraoke(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.karaoke.messages.filter_disabled"), + color: this.client.color.main, + }, + ], + }); + } else { + await player.filterManager.toggleKaraoke(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.karaoke.messages.filter_enabled"), + color: this.client.color.main, + }, + ], + }); + } + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/commands/filters/LowPass.ts b/src/commands/filters/LowPass.ts new file mode 100644 index 000000000..10d32278d --- /dev/null +++ b/src/commands/filters/LowPass.ts @@ -0,0 +1,70 @@ +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class LowPass extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "lowpass", + description: { + content: "cmd.lowpass.description", + examples: ["lowpass"], + usage: "lowpass ", + }, + category: "filters", + aliases: ["lp"], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [], + }); + } + + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const filterEnabled = player.filterManager.filters.lowPass; + + if (filterEnabled) { + await player.filterManager.toggleLowPass(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.lowpass.messages.filter_disabled"), + color: this.client.color.main, + }, + ], + }); + } else { + await player.filterManager.toggleLowPass(20); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.lowpass.messages.filter_enabled"), + color: this.client.color.main, + }, + ], + }); + } + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/commands/filters/NightCore.ts b/src/commands/filters/NightCore.ts new file mode 100644 index 000000000..b25b63eb9 --- /dev/null +++ b/src/commands/filters/NightCore.ts @@ -0,0 +1,70 @@ +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class NightCore extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "nightcore", + description: { + content: "cmd.nightcore.description", + examples: ["nightcore"], + usage: "nightcore", + }, + category: "filters", + aliases: ["nc"], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [], + }); + } + + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const filterEnabled = player.filterManager.filters.nightcore; + + if (filterEnabled) { + await player.filterManager.toggleNightcore(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.nightcore.messages.filter_disabled"), + color: this.client.color.main, + }, + ], + }); + } else { + await player.filterManager.toggleNightcore(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.nightcore.messages.filter_enabled"), + color: this.client.color.main, + }, + ], + }); + } + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/commands/music/Queue.ts b/src/commands/music/Queue.ts index 72ebaf10d..968fdba6f 100644 --- a/src/commands/music/Queue.ts +++ b/src/commands/music/Queue.ts @@ -51,14 +51,14 @@ export default class Queue extends Command { } const songStrings = []; for (let i = 0; i < player.queue.tracks.length; i++) { - const track = player.queue[i]; + const track = player.queue.tracks[i]; songStrings.push( ctx.locale("cmd.queue.track_info", { index: i + 1, title: track.info.title, uri: track.info.uri, - requester: track?.info.requester, - duration: track.info.isStream ? ctx.locale("cmd.queue.live") : client.utils.formatTime(track.info.length), + requester: `<@${(track.requester as any).id}>`, + duration: track.info.isStream ? ctx.locale("cmd.queue.live") : client.utils.formatTime(track.info.duration), }), ); } diff --git a/src/env.ts b/src/env.ts index 75277f8db..21a0599b2 100644 --- a/src/env.ts +++ b/src/env.ts @@ -74,9 +74,17 @@ const envSchema = z.object({ LOG_COMMANDS_ID: z.string().optional(), /** - * The bot status + * The bot status online | idle | dnd | invisible */ - BOT_STATUS: z.string().default("online"), + BOT_STATUS: z.preprocess( + (val) => { + if (typeof val === "string") { + return val.toLowerCase(); + } + return val; + }, + z.enum(["online", "idle", "dnd", "invisible"]).default("online"), + ), /** * The bot activity diff --git a/src/events/client/SetupButtons.ts b/src/events/client/SetupButtons.ts new file mode 100644 index 000000000..9a04a4d4d --- /dev/null +++ b/src/events/client/SetupButtons.ts @@ -0,0 +1,250 @@ +import type { Message } from "discord.js"; +import { T } from "../../structures/I18n"; +import { Event, type Lavamusic } from "../../structures/index"; +import type { Requester } from "../../types"; +import { getButtons } from "../../utils/Buttons"; +import { buttonReply } from "../../utils/SetupSystem"; +import { checkDj } from "../player/TrackStart"; + +export default class SetupButtons extends Event { + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: "setupButtons", + }); + } + + public async run(interaction: any): Promise { + const locale = await this.client.db.getLanguage(interaction.guildId); + + if (!interaction.replied) await interaction.deferReply().catch(() => {}); + if (!interaction.member.voice.channel) { + return await buttonReply(interaction, T(locale, "event.setupButton.no_voice_channel_button"), this.client.color.red); + } + const clientMember = interaction.guild.members.cache.get(this.client.user.id); + if (clientMember.voice.channel && clientMember.voice.channelId !== interaction.member.voice.channelId) { + return await buttonReply( + interaction, + T(locale, "event.setupButton.different_voice_channel_button", { + channel: clientMember.voice.channel, + }), + this.client.color.red, + ); + } + const player = this.client.manager.getPlayer(interaction.guildId); + if (!player) return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); + if (!player.queue) return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); + if (!player.queue.current) + return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); + const data = await this.client.db.getSetup(interaction.guildId); + const { title, uri, duration, artworkUrl, sourceName, isStream } = player.queue.current.info; + let message: Message; + try { + message = await interaction.channel.messages.fetch(data.messageId, { + cache: true, + }); + } catch (_e) { + /* empty */ + } + + const iconUrl = this.client.config.icons[sourceName] || this.client.user.displayAvatarURL({ extension: "png" }); + const embed = this.client + .embed() + .setAuthor({ + name: T(locale, "event.setupButton.now_playing"), + iconURL: iconUrl, + }) + .setColor(this.client.color.main) + .setDescription( + `[${title}](${uri}) - ${isStream ? T(locale, "event.setupButton.live") : this.client.utils.formatTime(duration)} - ${T(locale, "event.setupButton.requested_by", { requester: `<@${(player.queue.current.requester as Requester).id}>` })}`, + ) + .setImage(artworkUrl || this.client.user.displayAvatarURL({ extension: "png" })); + + if (!interaction.isButton()) return; + if (!(await checkDj(this.client, interaction))) { + return await buttonReply(interaction, T(locale, "event.setupButton.no_dj_permission"), this.client.color.red); + } + if (message) { + const handleVolumeChange = async (change: number) => { + const vol = player.volume + change; + player.setVolume(vol); + await buttonReply(interaction, T(locale, "event.setupButton.volume_set", { vol }), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, "event.setupButton.volume_footer", { + vol, + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + }; + switch (interaction.customId) { + case "LOW_VOL_BUT": + await handleVolumeChange(-10); + break; + case "HIGH_VOL_BUT": + await handleVolumeChange(10); + break; + case "PAUSE_BUT": { + const name = player.paused ? T(locale, "event.setupButton.resumed") : T(locale, "event.setupButton.paused"); + player.pause(); + await buttonReply(interaction, T(locale, "event.setupButton.pause_resume", { name }), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, "event.setupButton.pause_resume_footer", { + name, + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + components: getButtons(player, this.client), + }); + break; + } + case "SKIP_BUT": + if (player.queue.tracks.length === 0) { + return await buttonReply(interaction, T(locale, "event.setupButton.no_music_to_skip"), this.client.color.main); + } + player.skip(); + await buttonReply(interaction, T(locale, "event.setupButton.skipped"), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, "event.setupButton.skipped_footer", { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + break; + case "STOP_BUT": + player.stopPlaying(true, false); + await buttonReply(interaction, T(locale, "event.setupButton.stopped"), this.client.color.main); + await message.edit({ + embeds: [ + embed + .setFooter({ + text: T(locale, "event.setupButton.stopped_footer", { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }) + .setDescription(T(locale, "event.setupButton.nothing_playing")) + .setImage(this.client.config.links.img) + .setAuthor({ + name: this.client.user.username, + iconURL: this.client.user.displayAvatarURL({ + extension: "png", + }), + }), + ], + }); + break; + case "LOOP_BUT": { + const loopOptions: Array<"off" | "queue" | "track"> = ["off", "queue", "track"]; + const newLoop = loopOptions[(loopOptions.indexOf(player.repeatMode) + 1) % loopOptions.length]; + player.setRepeatMode(newLoop); + await buttonReply( + interaction, + T(locale, "event.setupButton.loop_set", { + loop: newLoop, + }), + this.client.color.main, + ); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, "event.setupButton.loop_footer", { + loop: newLoop, + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + break; + } + case "SHUFFLE_BUT": + player.queue.shuffle(); + await buttonReply(interaction, T(locale, "event.setupButton.shuffled"), this.client.color.main); + break; + case "PREV_BUT": + if (!player.queue.previous) { + return await buttonReply(interaction, T(locale, "event.setupButton.no_previous_track"), this.client.color.main); + } + player.play({ + track: player.queue.previous[0], + }); + await buttonReply(interaction, T(locale, "event.setupButton.playing_previous"), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, "event.setupButton.previous_footer", { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + break; + case "REWIND_BUT": { + const time = player.position - 10000; + if (time < 0) { + return await buttonReply(interaction, T(locale, "event.setupButton.rewind_limit"), this.client.color.main); + } + player.seek(time); + await buttonReply(interaction, T(locale, "event.setupButton.rewinded"), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, "event.setupButton.rewind_footer", { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + break; + } + case "FORWARD_BUT": { + const time = player.position + 10000; + if (time > player.queue.current.info.duration) { + return await buttonReply(interaction, T(locale, "event.setupButton.forward_limit"), this.client.color.main); + } + player.seek(time); + await buttonReply(interaction, T(locale, "event.setupButton.forwarded"), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, "event.setupButton.forward_footer", { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + break; + } + default: + await buttonReply(interaction, T(locale, "event.setupButton.button_not_available"), this.client.color.main); + break; + } + } + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/events/client/SetupSystem.ts b/src/events/client/SetupSystem.ts new file mode 100644 index 000000000..b033c07ce --- /dev/null +++ b/src/events/client/SetupSystem.ts @@ -0,0 +1,77 @@ +import { type Message, PermissionsBitField, TextChannel } from "discord.js"; +import { T } from "../../structures/I18n"; +import { Event, type Lavamusic } from "../../structures/index"; +import { oops, setupStart } from "../../utils/SetupSystem"; + +export default class SetupSystem extends Event { + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: "setupSystem", + }); + } + + public async run(message: Message): Promise { + const locale = await this.client.db.getLanguage(message.guildId); + const channel = message.channel as TextChannel; + if (!(channel instanceof TextChannel)) return; + if (!message.member?.voice.channel) { + await oops(channel, T(locale, "event.message.no_voice_channel_queue")); + await message.delete().catch(() => {}); + return; + } + + const voiceChannel = message.member.voice.channel; + const clientUser = this.client.user; + const clientMember = message.guild.members.cache.get(clientUser.id); + + if (!voiceChannel.permissionsFor(clientUser).has(PermissionsBitField.Flags.Connect | PermissionsBitField.Flags.Speak)) { + await oops( + channel, + T(locale, "event.message.no_permission_connect_speak", { + channel: voiceChannel.id, + }), + ); + await message.delete().catch(() => {}); + return; + } + + if (clientMember?.voice.channel && clientMember.voice.channelId !== voiceChannel.id) { + await oops( + channel, + T(locale, "event.message.different_voice_channel_queue", { + channel: clientMember.voice.channelId, + }), + ); + await message.delete().catch(() => {}); + return; + } + + let player = this.client.manager.getPlayer(message.guildId); + if (!player) { + player = this.client.manager.createPlayer({ + guildId: message.guildId, + voiceChannelId: voiceChannel.id, + textChannelId: message.channelId, + selfMute: false, + selfDeaf: true, + instaUpdateFiltersFix: true, + vcRegion: voiceChannel.rtcRegion, + }); + if (!player.connected) await player.connect(); + } + + await setupStart(this.client, message.content, player, message); + await message.delete().catch(() => {}); + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/events/node/Connect.ts b/src/events/node/Connect.ts index d26d19b3a..303ae8040 100644 --- a/src/events/node/Connect.ts +++ b/src/events/node/Connect.ts @@ -49,6 +49,6 @@ export default class Connect extends Event { }, index * 1000); }); - BotLog.send(this.client, `Node ${node} is ready!`, "success"); + BotLog.send(this.client, `Node ${node.id} is ready!`, "success"); } } diff --git a/src/events/node/Destroy.ts b/src/events/node/Destroy.ts index 2b0147c0e..768d6f67a 100644 --- a/src/events/node/Destroy.ts +++ b/src/events/node/Destroy.ts @@ -11,6 +11,6 @@ export default class Destroy extends Event { public async run(node: LavalinkNode, destroyReason?: DestroyReasonsType): Promise { this.client.logger.success(`Node ${node.id} is destroyed!`); - BotLog.send(this.client, `Node ${node} is destroyed: ${destroyReason}`, "warn"); + BotLog.send(this.client, `Node ${node.id} is destroyed: ${destroyReason}`, "warn"); } } diff --git a/src/structures/Event.ts b/src/structures/Event.ts index bb3ca41b4..452be054e 100644 --- a/src/structures/Event.ts +++ b/src/structures/Event.ts @@ -1,8 +1,13 @@ -import type { ClientEvents } from "discord.js"; -import type Lavamusic from "./Lavamusic"; +import type { ButtonInteraction, ClientEvents, Message } from "discord.js"; import type { LavalinkManagerEvents, NodeManagerEvents } from "lavalink-client"; +import type Lavamusic from "./Lavamusic"; -export type AllEvents = LavalinkManagerEvents & NodeManagerEvents & ClientEvents; +// custom client events setupSystem and setupButtons +interface CustomClientEvents { + setupSystem: (message: Message) => void; + setupButtons: (interaction: ButtonInteraction) => void; +} +export type AllEvents = LavalinkManagerEvents & NodeManagerEvents & ClientEvents & CustomClientEvents; interface EventOptions { name: keyof AllEvents; diff --git a/src/structures/LavalinkClient.ts b/src/structures/LavalinkClient.ts index 78840e5e6..6e2bc3c2b 100644 --- a/src/structures/LavalinkClient.ts +++ b/src/structures/LavalinkClient.ts @@ -1,5 +1,5 @@ import { LavalinkManager, type LavalinkNodeOptions, type SearchPlatform, type SearchResult } from "lavalink-client"; -import { requesterTransformer } from "../utils/functions/player"; +import { autoPlayFunction, requesterTransformer } from "../utils/functions/player"; import type Lavamusic from "./Lavamusic"; export default class LavalinkClient extends LavalinkManager { @@ -18,7 +18,7 @@ export default class LavalinkClient extends LavalinkManager { }, requesterTransformer: requesterTransformer, onEmptyQueue: { - //autoPlayFunction, + autoPlayFunction, }, }, }); diff --git a/src/utils/SetupSystem.ts b/src/utils/SetupSystem.ts index 20c0c9885..6eb3b23be 100644 --- a/src/utils/SetupSystem.ts +++ b/src/utils/SetupSystem.ts @@ -1,10 +1,10 @@ import { type ColorResolvable, EmbedBuilder, type Message, type TextChannel } from "discord.js"; +import type { Player, Track } from "lavalink-client"; import { T } from "../structures/I18n"; import type { Lavamusic } from "../structures/index"; -import { getButtons } from "./Buttons"; -import type { Player, Track } from "lavalink-client"; import type { Requester } from "../types"; +import { getButtons } from "./Buttons"; /** * A function that will generate an embed based on the player's current track. @@ -63,139 +63,55 @@ async function setupStart(client: Lavamusic, query: string, player: Player, mess if (m) { try { if (message.inGuild()) { - /* const res = await client.queue.search(query); + const res = await player.search(query, message.author); + switch (res.loadType) { - case LoadType.ERROR: + case "empty": + case "error": await message.channel .send({ embeds: [embed.setColor(client.color.red).setDescription(T(locale, "player.setupStart.error_searching"))], }) .then((msg) => setTimeout(() => msg.delete(), 5000)); break; - case LoadType.EMPTY: + case "search": + case "track": + player.queue.add(res.tracks[0]); await message.channel .send({ - embeds: [embed.setColor(client.color.red).setDescription(T(locale, "player.setupStart.no_results"))], - }) - .then((msg) => setTimeout(() => msg.delete(), 5000)); - break; - case LoadType.TRACK: { - const track = player.buildTrack(res.data, message.author); - if (player.queue.length > client.config.maxQueueSize) { - await message.channel - .send({ - embeds: [ - embed.setColor(client.color.red).setDescription( - T(locale, "player.setupStart.queue_too_long", { - maxQueueSize: client.config.maxQueueSize, + embeds: [ + embed + .setColor(client.color.main) + .setDescription( + T(locale, "player.setupStart.added_to_queue", { + title: res.tracks[0].info.title, + uri: res.tracks[0].info.uri, }), ), - ], - }) - .then((msg) => setTimeout(() => msg.delete(), 5000)); - return; - } - player.queue.push(track); - await player.isPlaying(); - await message.channel - .send({ - embeds: [ - embed.setColor(client.color.main).setDescription( - T(locale, "player.setupStart.added_to_queue", { - title: res.data.info.title, - uri: res.data.info.uri, - }), - ), ], }) .then((msg) => setTimeout(() => msg.delete(), 5000)); neb(n, player, client, locale); await m.edit({ embeds: [n] }).catch(() => {}); break; - } - case LoadType.PLAYLIST: - if (res.data.tracks.length > client.config.maxPlaylistSize) { - await message.channel - .send({ - embeds: [ - embed.setColor(client.color.red).setDescription( - T(locale, "player.setupStart.playlist_too_long", { - maxPlaylistSize: client.config.maxPlaylistSize, - }), - ), - ], - }) - .then((msg) => setTimeout(() => msg.delete(), 5000)); - return; - } - for (const track of res.data.tracks) { - const pl = player.buildTrack(track, message.author); - if (player.queue.length > client.config.maxQueueSize) { - await message.channel - .send({ - embeds: [ - embed.setColor(client.color.red).setDescription( - T(locale, "player.setupStart.queue_too_long", { - maxQueueSize: client.config.maxQueueSize, - }), - ), - ], - }) - .then((msg) => setTimeout(() => msg.delete(), 5000)); - return; - } - player.queue.push(pl); - } - await player.isPlaying(); + case "playlist": + player.queue.add(res.tracks); await message.channel .send({ embeds: [ embed .setColor(client.color.main) .setDescription( - T(locale, "player.setupStart.added_playlist_to_queue", { length: res.data.tracks.length }), - ), - ], - }) - .then((msg) => setTimeout(() => msg.delete(), 5000)); - neb(n, player, client, locale); - await m.edit({ embeds: [n] }).catch(() => {}); - break; - case LoadType.SEARCH: { - const track = player.buildTrack(res.data[0], message.author); - if (player.queue.length > client.config.maxQueueSize) { - await message.channel - .send({ - embeds: [ - embed.setColor(client.color.red).setDescription( - T(locale, "player.setupStart.queue_too_long", { - maxQueueSize: client.config.maxQueueSize, - }), + T(locale, "player.setupStart.added_playlist_to_queue", { length: res.tracks.length }), ), - ], - }) - .then((msg) => setTimeout(() => msg.delete(), 5000)); - return; - } - player.queue.push(track); - await player.isPlaying(); - await message.channel - .send({ - embeds: [ - embed.setColor(client.color.main).setDescription( - T(locale, "player.setupStart.added_to_queue", { - title: res.data[0].info.title, - uri: res.data[0].info.uri, - }), - ), ], }) .then((msg) => setTimeout(() => msg.delete(), 5000)); neb(n, player, client, locale); await m.edit({ embeds: [n] }).catch(() => {}); break; - } - } */ + } + if (!player.playing) await player.play(); } } catch (error) { client.logger.error(error); @@ -258,7 +174,7 @@ async function trackStart( return b; }), }) - .catch(() => { }); + .catch(() => {}); } else { await channel .send({ @@ -271,7 +187,7 @@ async function trackStart( .then((msg) => { client.db.setSetup(msg.guild.id, msg.id, msg.channel.id); }) - .catch(() => { }); + .catch(() => {}); } } @@ -319,7 +235,7 @@ async function updateSetup(client: Lavamusic, guild: any, locale: string): Promi return b; }), }) - .catch(() => { }); + .catch(() => {}); } else { const embed = client .embed() @@ -338,7 +254,7 @@ async function updateSetup(client: Lavamusic, guild: any, locale: string): Promi return b; }), }) - .catch(() => { }); + .catch(() => {}); } } } @@ -347,13 +263,13 @@ async function buttonReply(int: any, args: string, color: ColorResolvable): Prom const embed = new EmbedBuilder(); let m: Message; if (int.replied) { - m = await int.editReply({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => { }); + m = await int.editReply({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => {}); } else { - m = await int.followUp({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => { }); + m = await int.followUp({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => {}); } setTimeout(async () => { if (int && !int.ephemeral) { - await m.delete().catch(() => { }); + await m.delete().catch(() => {}); } }, 2000); } @@ -364,7 +280,7 @@ async function oops(channel: TextChannel, args: string): Promise { const m = await channel.send({ embeds: [embed1], }); - setTimeout(async () => await m.delete().catch(() => { }), 12000); + setTimeout(async () => await m.delete().catch(() => {}), 12000); } catch (e) { return console.error(e); } diff --git a/src/utils/functions/player.ts b/src/utils/functions/player.ts index 5e61171c3..d59f83363 100644 --- a/src/utils/functions/player.ts +++ b/src/utils/functions/player.ts @@ -1,3 +1,4 @@ +import type { Player, Track } from "lavalink-client/dist/types"; import type { Requester } from "../../types"; /** @@ -22,3 +23,80 @@ export const requesterTransformer = (requester: any): Requester => { } return { id: requester!.toString(), username: "unknown" }; }; + +/** + * Function that will be called when the autoplay feature is enabled and the queue + * is empty. It will search for tracks based on the last played track and add them + * to the queue. + * + * @param {Player} player The player instance. + * @param {Track} lastTrack The last played track. + * @returns {Promise} A promise that resolves when the function is done. + */ +export async function autoPlayFunction(player: Player, lastTrack?: Track): Promise { + if (!player.get("autoplay")) return; + if (!lastTrack) return; + + if (lastTrack.info.sourceName === "spotify") { + const filtered = player.queue.previous.filter((v) => v.info.sourceName === "spotify").slice(0, 5); + const ids = filtered.map( + (v) => v.info.identifier || v.info.uri.split("/")?.reverse()?.[0] || v.info.uri.split("/")?.reverse()?.[1], + ); + if (ids.length >= 2) { + const res = await player + .search( + { + query: `seed_tracks=${ids.join(",")}`, //`seed_artists=${artistIds.join(",")}&seed_genres=${genre.join(",")}&seed_tracks=${trackIds.join(",")}`; + source: "sprec", + }, + lastTrack.requester, + ) + .then((response: any) => { + response.tracks = response.tracks.filter((v) => v.info.identifier !== lastTrack.info.identifier); // remove the lastPlayed track if it's in there.. + return response; + }) + .catch(console.warn); + if (res && res.tracks.length > 0) + await player.queue.add( + res.tracks.slice(0, 5).map((track) => { + // transform the track plugininfo so you can figure out if the track is from autoplay or not. + track.pluginInfo.clientData = { ...(track.pluginInfo.clientData || {}), fromAutoplay: true }; + return track; + }), + ); + } + return; + } + if (lastTrack.info.sourceName === "youtube" || lastTrack.info.sourceName === "youtubemusic") { + const res = await player + .search( + { + query: `https://www.youtube.com/watch?v=${lastTrack.info.identifier}&list=RD${lastTrack.info.identifier}`, + source: "youtube", + }, + lastTrack.requester, + ) + .then((response: any) => { + response.tracks = response.tracks.filter((v) => v.info.identifier !== lastTrack.info.identifier); // remove the lastPlayed track if it's in there.. + return response; + }) + .catch(console.warn); + if (res && res.tracks.length > 0) + await player.queue.add( + res.tracks.slice(0, 5).map((track) => { + // transform the track plugininfo so you can figure out if the track is from autoplay or not. + track.pluginInfo.clientData = { ...(track.pluginInfo.clientData || {}), fromAutoplay: true }; + return track; + }), + ); + return; + } + if (lastTrack.info.sourceName === "jiosaavn") { + const res = await player.search({ query: `jsrec:${lastTrack.info.identifier}`, source: "jsrec" }, lastTrack.requester); + if (res.tracks.length > 0) { + const track = res.tracks.filter((v) => v.info.identifier !== lastTrack.info.identifier)[0]; + await player.queue.add(track); + } + } + return; +} From be6ec29a03288a0533e25c142f1c5c06022eb611 Mon Sep 17 00:00:00 2001 From: appujet Date: Sat, 14 Sep 2024 22:55:27 +0530 Subject: [PATCH 11/32] fix: requester embed --- src/commands/music/Grab.ts | 2 +- src/commands/music/Nowplaying.ts | 2 +- src/commands/music/Queue.ts | 4 ++-- src/events/client/SetupButtons.ts | 2 +- src/utils/SetupSystem.ts | 20 +++++++++----------- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/commands/music/Grab.ts b/src/commands/music/Grab.ts index cb421a390..7d0bf1f5d 100644 --- a/src/commands/music/Grab.ts +++ b/src/commands/music/Grab.ts @@ -49,7 +49,7 @@ export default class Grab extends Command { uri: song.info.uri, artworkUrl: song.info.artworkUrl, length: song.info.isStream ? "LIVE" : client.utils.formatTime(song.info.duration), - requester: `<@${(song.requester as Requester).id}>`, + requester: (song.requester as Requester).id, }); try { diff --git a/src/commands/music/Nowplaying.ts b/src/commands/music/Nowplaying.ts index f9ba021c5..196705310 100644 --- a/src/commands/music/Nowplaying.ts +++ b/src/commands/music/Nowplaying.ts @@ -49,7 +49,7 @@ export default class Nowplaying extends Command { ctx.locale("cmd.nowplaying.track_info", { title: track.info.title, uri: track.info.uri, - requester: `<@${(track.requester as any).id}>`, + requester: (track.requester as any).id, bar: bar, }), ) diff --git a/src/commands/music/Queue.ts b/src/commands/music/Queue.ts index 968fdba6f..b3fd7180e 100644 --- a/src/commands/music/Queue.ts +++ b/src/commands/music/Queue.ts @@ -40,7 +40,7 @@ export default class Queue extends Command { ctx.locale("cmd.queue.now_playing", { title: player.queue.current.info.title, uri: player.queue.current.info.uri, - requester: `<@${(player.queue.current.requester as any).id}>`, + requester: (player.queue.current.requester as any).id, duration: player.queue.current.info.isStream ? ctx.locale("cmd.queue.live") : client.utils.formatTime(player.queue.current.info.duration), @@ -57,7 +57,7 @@ export default class Queue extends Command { index: i + 1, title: track.info.title, uri: track.info.uri, - requester: `<@${(track.requester as any).id}>`, + requester: (track.requester as any).id, duration: track.info.isStream ? ctx.locale("cmd.queue.live") : client.utils.formatTime(track.info.duration), }), ); diff --git a/src/events/client/SetupButtons.ts b/src/events/client/SetupButtons.ts index 9a04a4d4d..3b7d6429e 100644 --- a/src/events/client/SetupButtons.ts +++ b/src/events/client/SetupButtons.ts @@ -55,7 +55,7 @@ export default class SetupButtons extends Event { }) .setColor(this.client.color.main) .setDescription( - `[${title}](${uri}) - ${isStream ? T(locale, "event.setupButton.live") : this.client.utils.formatTime(duration)} - ${T(locale, "event.setupButton.requested_by", { requester: `<@${(player.queue.current.requester as Requester).id}>` })}`, + `[${title}](${uri}) - ${isStream ? T(locale, "event.setupButton.live") : this.client.utils.formatTime(duration)} - ${T(locale, "event.setupButton.requested_by", { requester: (player.queue.current.requester as Requester).id })}`, ) .setImage(artworkUrl || this.client.user.displayAvatarURL({ extension: "png" })); diff --git a/src/utils/SetupSystem.ts b/src/utils/SetupSystem.ts index 6eb3b23be..993ff10e5 100644 --- a/src/utils/SetupSystem.ts +++ b/src/utils/SetupSystem.ts @@ -24,7 +24,7 @@ function neb(embed: EmbedBuilder, player: Player, client: Lavamusic, locale: str uri: player.queue.current.info.uri, author: player.queue.current.info.author, length: client.utils.formatTime(player.queue.current.info.duration), - requester: `<@${(player.queue.current.requester as Requester).id}>`, + requester: (player.queue.current.requester as Requester).id, }); return embed .setAuthor({ @@ -80,14 +80,12 @@ async function setupStart(client: Lavamusic, query: string, player: Player, mess await message.channel .send({ embeds: [ - embed - .setColor(client.color.main) - .setDescription( - T(locale, "player.setupStart.added_to_queue", { - title: res.tracks[0].info.title, - uri: res.tracks[0].info.uri, - }), - ), + embed.setColor(client.color.main).setDescription( + T(locale, "player.setupStart.added_to_queue", { + title: res.tracks[0].info.title, + uri: res.tracks[0].info.uri, + }), + ), ], }) .then((msg) => setTimeout(() => msg.delete(), 5000)); @@ -152,7 +150,7 @@ async function trackStart( uri: track.info.uri, author: track.info.author, length: client.utils.formatTime(track.info.duration), - requester: `<@${(track.requester as Requester).id}>`, + requester: (player.queue.current.requester as Requester).id, }); const embed = client @@ -215,7 +213,7 @@ async function updateSetup(client: Lavamusic, guild: any, locale: string): Promi uri: player.queue.current.info.uri, author: player.queue.current.info.author, length: client.utils.formatTime(player.queue.current.info.duration), - requester: `<@${(player.queue.current.requester as Requester).id}>`, + requester: (player.queue.current.requester as Requester).id, }); const embed = client From 196f30dc46b8a1de0b2cbdb9cf396661d23c7629 Mon Sep 17 00:00:00 2001 From: LucasB25 <50886682+LucasB25@users.noreply.github.com> Date: Sun, 15 Sep 2024 15:31:05 +0200 Subject: [PATCH 12/32] chore: update & play(next) --- .env.example | 2 -- commitlint.config.js | 2 +- package.json | 12 ++++---- scripts/clean.js | 11 +++++++ scripts/restart.js | 11 +++++++ src/commands/config/247.ts | 4 +-- src/commands/config/Language.ts | 11 +++++++ src/commands/music/Join.ts | 4 +-- src/commands/music/Loop.ts | 2 +- src/commands/music/Play.ts | 42 +++++++++++++++----------- src/commands/music/PlayNext.ts | 43 ++++++++++++++++----------- src/commands/music/Search.ts | 25 ++++++++-------- src/commands/music/Shuffle.ts | 2 +- src/commands/music/Stop.ts | 2 +- src/commands/playlist/Load.ts | 2 +- src/database/server.ts | 2 +- src/env.ts | 11 +++++++ src/events/client/ChannelDelete.ts | 11 +++++++ src/events/client/VoiceStateUpdate.ts | 1 - src/events/node/Connect.ts | 11 +++++++ src/events/node/Destroy.ts | 11 +++++++ src/events/player/QueueEnd.ts | 17 +++++++++-- src/events/player/TrackEnd.ts | 19 +++++++++--- src/shard.ts | 11 +++++++ src/structures/LavalinkClient.ts | 11 +++++++ src/structures/Lavamusic.ts | 6 ++-- src/utils/Buttons.ts | 13 +++++++- src/utils/ThemeSelector.ts | 11 +++++++ src/utils/functions/player.ts | 11 +++++++ 29 files changed, 243 insertions(+), 78 deletions(-) diff --git a/.env.example b/.env.example index f1f299634..ac3eab50f 100644 --- a/.env.example +++ b/.env.example @@ -14,7 +14,5 @@ BOT_ACTIVITY=" Lavamusic" # Your bot activity. DATABASE_URL= "" # Your database url (If you want to use sqlite, then you can leave it blank.). AUTO_NODE=" false" # true for auto node. It is given from lavainfo-api (https://lavainfo-api.deno.dev). SEARCH_ENGINE= "YouTubeMusic" # Search engine to be used when playing the song. You can use: YouTube, YouTubeMusic, SoundCloud, Spotify, Apple, Deezer, Yandex and JioSaavn -MAX_PLAYLIST_SIZE= "100" # Max playlist size. -MAX_QUEUE_SIZE= "100" # Max queue size. GENIUS_API= "" # Sign up and get your own api at (https://genius.com/) to fetch your lyrics (CLIENT TOKEN) NODES=[{"id":"main","host":"localhost","port":2333,"authorization":"youshallnotpass"}] diff --git a/commitlint.config.js b/commitlint.config.js index 8567d51d7..5073c20db 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1 +1 @@ -module.exports = { extends: ["@commitlint/config-conventional"] }; \ No newline at end of file +module.exports = { extends: ["@commitlint/config-conventional"] }; diff --git a/package.json b/package.json index b4d1204ce..88002e541 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lavamusic", - "version": "4.6.7", + "version": "5.0.0-beta", "description": "LavaMusic is a music bot for Discord, written in JavaScript using the Discord.js, Typescript, lavalink-client (Lavalink) library.", "main": "dist/index.js", "scripts": { @@ -36,9 +36,9 @@ "devDependencies": { "@biomejs/biome": "^1.9.0", "@types/i18n": "^0.13.12", - "@types/node": "^22.5.4", - "@commitlint/cli": "^19.4.0", - "@commitlint/config-conventional": "^19.2.2", + "@types/node": "^22.5.5", + "@commitlint/cli": "^19.5.0", + "@commitlint/config-conventional": "^19.5.0", "@types/signale": "^1.4.7", "husky": "^9.1.6", "lint-staged": "^15.2.10", @@ -61,9 +61,7 @@ "zod": "^3.23.8" }, "lint-staged": { - "*.ts": [ - "biome check --write" - ] + "*.ts": ["biome check --write"] }, "signale": { "displayScope": true, diff --git a/scripts/clean.js b/scripts/clean.js index 671a864e4..d2f1cc37d 100644 --- a/scripts/clean.js +++ b/scripts/clean.js @@ -15,3 +15,14 @@ async function clean() { } clean(); + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/scripts/restart.js b/scripts/restart.js index d377f4397..bd87dda89 100644 --- a/scripts/restart.js +++ b/scripts/restart.js @@ -13,3 +13,14 @@ async function startLavamusic() { } setTimeout(startLavamusic, 5000); + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/commands/config/247.ts b/src/commands/config/247.ts index 7e531c3df..7d123b046 100644 --- a/src/commands/config/247.ts +++ b/src/commands/config/247.ts @@ -33,7 +33,7 @@ export default class _247 extends Command { public async run(client: Lavamusic, ctx: Context): Promise { const embed = this.client.embed(); - let player = client.manager.getPlayer(ctx.guild.id) + let player = client.manager.getPlayer(ctx.guild.id); try { const data = await client.db.get_247(ctx.guild!.id); const member = ctx.member as GuildMember; @@ -58,7 +58,7 @@ export default class _247 extends Command { selfDeaf: true, instaUpdateFiltersFix: true, vcRegion: member.voice.channel.rtcRegion, - }) + }); } if (!player.connected) await player.connect(); return await ctx.sendMessage({ diff --git a/src/commands/config/Language.ts b/src/commands/config/Language.ts index e90de4ea9..011afff35 100644 --- a/src/commands/config/Language.ts +++ b/src/commands/config/Language.ts @@ -145,3 +145,14 @@ export default class LanguageCommand extends Command { await interaction.respond(filtered.slice(0, 25)).catch(console.error); } } + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/commands/music/Join.ts b/src/commands/music/Join.ts index 5b744ae81..62214373a 100644 --- a/src/commands/music/Join.ts +++ b/src/commands/music/Join.ts @@ -47,7 +47,7 @@ export default class Join extends Command { }); } - const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; if (!memberVoiceChannel) { return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.join.no_voice_channel"))], @@ -62,7 +62,7 @@ export default class Join extends Command { selfDeaf: true, instaUpdateFiltersFix: true, vcRegion: memberVoiceChannel.rtcRegion, - }) + }); if (!player.connected) await player.connect(); return await ctx.sendMessage({ embeds: [ diff --git a/src/commands/music/Loop.ts b/src/commands/music/Loop.ts index 817f74cd5..6e1785b87 100644 --- a/src/commands/music/Loop.ts +++ b/src/commands/music/Loop.ts @@ -37,7 +37,7 @@ export default class Loop extends Command { switch (player?.repeatMode) { case "off": - player.setRepeatMode("track") + player.setRepeatMode("track"); loopMessage = ctx.locale("cmd.loop.looping_song"); break; case "track": diff --git a/src/commands/music/Play.ts b/src/commands/music/Play.ts index 00e29a91a..4e202ef1f 100644 --- a/src/commands/music/Play.ts +++ b/src/commands/music/Play.ts @@ -1,6 +1,6 @@ import type { AutocompleteInteraction, VoiceChannel } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; import type { SearchResult } from "lavalink-client/dist/types"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Play extends Command { constructor(client: Lavamusic) { @@ -49,20 +49,21 @@ export default class Play extends Command { const query = args.join(" "); await ctx.sendDeferMessage(ctx.locale("cmd.play.loading")); let player = client.manager.getPlayer(ctx.guild!.id); - const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; - if (!player) player = client.manager.createPlayer({ - guildId: ctx.guild!.id, - voiceChannelId: memberVoiceChannel.id, - textChannelId: ctx.channel.id, - selfMute: false, - selfDeaf: true, - instaUpdateFiltersFix: true, - vcRegion: memberVoiceChannel.rtcRegion, - }) + if (!player) + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: memberVoiceChannel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + instaUpdateFiltersFix: true, + vcRegion: memberVoiceChannel.rtcRegion, + }); if (!player.connected) await player.connect(); - const response = await player.search({ query: query }, ctx.author) as SearchResult; + const response = (await player.search({ query: query }, ctx.author)) as SearchResult; const embed = this.client.embed(); if (!response || response.tracks?.length === 0) { @@ -73,7 +74,7 @@ export default class Play extends Command { } await player.queue.add(response.loadType === "playlist" ? response.tracks : response.tracks[0]); - + if (response.loadType === "playlist") { await ctx.editMessage({ content: "", @@ -87,9 +88,12 @@ export default class Play extends Command { await ctx.editMessage({ content: "", embeds: [ - embed - .setColor(this.client.color.main) - .setDescription(ctx.locale("cmd.play.added_to_queue", { title: response.tracks[0].info.title, uri: response.tracks[0].info.uri })), + embed.setColor(this.client.color.main).setDescription( + ctx.locale("cmd.play.added_to_queue", { + title: response.tracks[0].info.title, + uri: response.tracks[0].info.uri, + }), + ), ], }); } @@ -98,6 +102,10 @@ export default class Play extends Command { public async autocomplete(interaction: AutocompleteInteraction): Promise { const focusedValue = interaction.options.getFocused(); + if (!focusedValue) { + return; + } + const res = await this.client.manager.search(focusedValue, interaction.user); const songs = []; @@ -108,7 +116,7 @@ export default class Play extends Command { name: name.length > 100 ? `${name.substring(0, 97)}...` : name, value: track.info.uri, }); - }) + }); } return await interaction.respond(songs).catch(console.error); diff --git a/src/commands/music/PlayNext.ts b/src/commands/music/PlayNext.ts index 3760db687..3f79cb7a1 100644 --- a/src/commands/music/PlayNext.ts +++ b/src/commands/music/PlayNext.ts @@ -1,6 +1,6 @@ import type { AutocompleteInteraction, VoiceChannel } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; import type { SearchResult } from "lavalink-client/dist/types"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class PlayNext extends Command { constructor(client: Lavamusic) { @@ -48,22 +48,23 @@ export default class PlayNext extends Command { public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { const query = args.join(" "); let player = client.manager.getPlayer(ctx.guild!.id); - const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; - if (!player) player = client.manager.createPlayer({ - guildId: ctx.guild!.id, - voiceChannelId: memberVoiceChannel.id, - textChannelId: ctx.channel.id, - selfMute: false, - selfDeaf: true, - instaUpdateFiltersFix: true, - vcRegion: memberVoiceChannel.rtcRegion, - }) + if (!player) + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: memberVoiceChannel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + instaUpdateFiltersFix: true, + vcRegion: memberVoiceChannel.rtcRegion, + }); if (!player.connected) await player.connect(); await ctx.sendDeferMessage(ctx.locale("cmd.playnext.loading")); - - const response = await player.search({ query: query }, ctx.author) as SearchResult; + + const response = (await player.search({ query: query }, ctx.author)) as SearchResult; const embed = this.client.embed(); if (!response || response.tracks?.length === 0) { @@ -87,18 +88,24 @@ export default class PlayNext extends Command { await ctx.editMessage({ content: "", embeds: [ - embed - .setColor(this.client.color.main) - .setDescription(ctx.locale("cmd.playnext.added_to_play_next", { title: response.tracks[0].info.title, uri: response.tracks[0].info.uri })), + embed.setColor(this.client.color.main).setDescription( + ctx.locale("cmd.playnext.added_to_play_next", { + title: response.tracks[0].info.title, + uri: response.tracks[0].info.uri, + }), + ), ], }); } if (!player.playing) await player.play({ paused: false }); - } public async autocomplete(interaction: AutocompleteInteraction): Promise { const focusedValue = interaction.options.getFocused(); + if (!focusedValue) { + return; + } + const res = await this.client.manager.search(focusedValue, interaction.user); const songs = []; @@ -109,7 +116,7 @@ export default class PlayNext extends Command { name: name.length > 100 ? `${name.substring(0, 97)}...` : name, value: track.info.uri, }); - }) + }); } return await interaction.respond(songs).catch(console.error); diff --git a/src/commands/music/Search.ts b/src/commands/music/Search.ts index 58c8cd133..6b83801da 100644 --- a/src/commands/music/Search.ts +++ b/src/commands/music/Search.ts @@ -1,6 +1,6 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type VoiceChannel } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; import type { SearchResult, Track } from "lavalink-client/dist/types"; +import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Search extends Command { constructor(client: Lavamusic) { @@ -43,19 +43,20 @@ export default class Search extends Command { const embed = this.client.embed().setColor(this.client.color.main); let player = client.manager.getPlayer(ctx.guild!.id); const query = args.join(" "); - const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; - if (!player) player = client.manager.createPlayer({ - guildId: ctx.guild!.id, - voiceChannelId: memberVoiceChannel.id, - textChannelId: ctx.channel.id, - selfMute: false, - selfDeaf: true, - instaUpdateFiltersFix: true, - vcRegion: memberVoiceChannel.rtcRegion, - }) + if (!player) + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: memberVoiceChannel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + instaUpdateFiltersFix: true, + vcRegion: memberVoiceChannel.rtcRegion, + }); if (!player.connected) await player.connect(); - const response = await player.search({ query: query }, ctx.author) as SearchResult; + const response = (await player.search({ query: query }, ctx.author)) as SearchResult; if (!response || response.tracks?.length === 0) { return await ctx.sendMessage({ embeds: [embed.setDescription(ctx.locale("cmd.search.errors.no_results")).setColor(this.client.color.red)], diff --git a/src/commands/music/Shuffle.ts b/src/commands/music/Shuffle.ts index 17322314d..b15c4fbec 100644 --- a/src/commands/music/Shuffle.ts +++ b/src/commands/music/Shuffle.ts @@ -38,7 +38,7 @@ export default class Shuffle extends Command { embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("player.errors.no_song"))], }); } - player.queue.shuffle() + player.queue.shuffle(); return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.shuffle.messages.shuffled"))], }); diff --git a/src/commands/music/Stop.ts b/src/commands/music/Stop.ts index 136eee07d..f3daabdde 100644 --- a/src/commands/music/Stop.ts +++ b/src/commands/music/Stop.ts @@ -34,7 +34,7 @@ export default class Stop extends Command { const player = client.manager.getPlayer(ctx.guild!.id); const embed = this.client.embed(); - player.stopPlaying(true, false) + player.stopPlaying(true, false); return await ctx.sendMessage({ embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.stop.messages.stopped"))], diff --git a/src/commands/playlist/Load.ts b/src/commands/playlist/Load.ts index f273f007e..d6e44a524 100644 --- a/src/commands/playlist/Load.ts +++ b/src/commands/playlist/Load.ts @@ -76,7 +76,7 @@ export default class LoadPlaylist extends Command { selfDeaf: true, instaUpdateFiltersFix: true, vcRegion: member.voice.channel.rtcRegion, - }) + }); if (!player.connected) await player.connect(); } diff --git a/src/database/server.ts b/src/database/server.ts index a04241fcc..c84a7bc2f 100644 --- a/src/database/server.ts +++ b/src/database/server.ts @@ -202,7 +202,7 @@ export default class ServerData { }, }); } else { - throw new Error('Existing tracks are not in an array format.'); + throw new Error("Existing tracks are not in an array format."); } } else { // If no playlist exists, create a new one with the provided tracks diff --git a/src/env.ts b/src/env.ts index 21a0599b2..bb4bade5b 100644 --- a/src/env.ts +++ b/src/env.ts @@ -139,3 +139,14 @@ for (const key in env) { throw new Error(`Missing env variable: ${key}`); } } + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/events/client/ChannelDelete.ts b/src/events/client/ChannelDelete.ts index 5363b007b..5b8d42645 100644 --- a/src/events/client/ChannelDelete.ts +++ b/src/events/client/ChannelDelete.ts @@ -35,3 +35,14 @@ export default class ChannelDelete extends Event { } } } + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/events/client/VoiceStateUpdate.ts b/src/events/client/VoiceStateUpdate.ts index b5af8c3d7..d8ef24c09 100644 --- a/src/events/client/VoiceStateUpdate.ts +++ b/src/events/client/VoiceStateUpdate.ts @@ -57,7 +57,6 @@ export default class VoiceStateUpdate extends Event { if (vc.members instanceof Map && [...vc.members.values()].filter((x: GuildMember) => !x.user.bot).length <= 0) { setTimeout(async () => { - if (!player?.voiceChannelId) return; const playerVoiceChannel = newState.guild.channels.cache.get(player?.voiceChannelId); diff --git a/src/events/node/Connect.ts b/src/events/node/Connect.ts index 303ae8040..201cf77bc 100644 --- a/src/events/node/Connect.ts +++ b/src/events/node/Connect.ts @@ -52,3 +52,14 @@ export default class Connect extends Event { BotLog.send(this.client, `Node ${node.id} is ready!`, "success"); } } + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/events/node/Destroy.ts b/src/events/node/Destroy.ts index 768d6f67a..3b5843ccc 100644 --- a/src/events/node/Destroy.ts +++ b/src/events/node/Destroy.ts @@ -14,3 +14,14 @@ export default class Destroy extends Event { BotLog.send(this.client, `Node ${node.id} is destroyed: ${destroyReason}`, "warn"); } } + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/events/player/QueueEnd.ts b/src/events/player/QueueEnd.ts index 6e009098f..c25e808f2 100644 --- a/src/events/player/QueueEnd.ts +++ b/src/events/player/QueueEnd.ts @@ -1,6 +1,6 @@ +import type { TextChannel } from "discord.js"; import type { Player, Track, TrackStartEvent } from "lavalink-client"; import { Event, type Lavamusic } from "../../structures/index"; -import type { TextChannel } from "discord.js"; // queueEnd export default class QueueEnd extends Event { @@ -24,7 +24,18 @@ export default class QueueEnd extends Event { if (!message) return; if (message.editable) { - await message.edit({components: []}).catch(() => {}); + await message.edit({ components: [] }).catch(() => {}); } } -} \ No newline at end of file +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/events/player/TrackEnd.ts b/src/events/player/TrackEnd.ts index 7462c2d5b..ddfd07469 100644 --- a/src/events/player/TrackEnd.ts +++ b/src/events/player/TrackEnd.ts @@ -1,6 +1,6 @@ +import type { TextChannel } from "discord.js"; import type { Player, Track, TrackStartEvent } from "lavalink-client"; import { Event, type Lavamusic } from "../../structures/index"; -import type { TextChannel } from "discord.js"; export default class TrackEnd extends Event { constructor(client: Lavamusic, file: string) { @@ -19,9 +19,20 @@ export default class TrackEnd extends Event { const channel = guild.channels.cache.get(player.textChannelId) as TextChannel; if (!channel) return; - const message = await channel.messages.fetch(messageId).catch(() => { }); + const message = await channel.messages.fetch(messageId).catch(() => {}); if (!message) return; - message.delete().catch(() => { }); + message.delete().catch(() => {}); } -} \ No newline at end of file +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/shard.ts b/src/shard.ts index 3b8a44d48..6de012e55 100644 --- a/src/shard.ts +++ b/src/shard.ts @@ -20,3 +20,14 @@ export async function shardStart(logger: Logger) { logger.start(`[CLIENT] ${manager.totalShards} shard(s) spawned.`); } + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/structures/LavalinkClient.ts b/src/structures/LavalinkClient.ts index 6e2bc3c2b..6e323306f 100644 --- a/src/structures/LavalinkClient.ts +++ b/src/structures/LavalinkClient.ts @@ -38,3 +38,14 @@ export default class LavalinkClient extends LavalinkManager { return result; } } + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ diff --git a/src/structures/Lavamusic.ts b/src/structures/Lavamusic.ts index 470453c51..2e9742def 100644 --- a/src/structures/Lavamusic.ts +++ b/src/structures/Lavamusic.ts @@ -16,13 +16,13 @@ import { import { Locale } from "discord.js"; import config from "../config"; import ServerData from "../database/server"; +import { env } from "../env"; import loadPlugins from "../plugin/index"; import { Utils } from "../utils/Utils"; import { T, i18n, initI18n, localization } from "./I18n"; +import LavalinkClient from "./LavalinkClient"; import Logger from "./Logger"; import type { Command } from "./index"; -import { env } from "../env"; -import LavalinkClient from "./LavalinkClient"; export default class Lavamusic extends Client { public commands: Collection = new Collection(); @@ -35,7 +35,7 @@ export default class Lavamusic extends Client { public readonly color = config.color; private body: RESTPostAPIChatInputApplicationCommandsJSONBody[] = []; public topGG!: Api; - public utils = Utils;; + public utils = Utils; public env: typeof env = env; public manager: LavalinkClient; public embed(): EmbedBuilder { diff --git a/src/utils/Buttons.ts b/src/utils/Buttons.ts index aff4d8b17..7c1b1472a 100644 --- a/src/utils/Buttons.ts +++ b/src/utils/Buttons.ts @@ -1,6 +1,6 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type EmojiIdentifierResolvable } from "discord.js"; -import type { Lavamusic } from "../structures/index"; import type { Player } from "lavalink-client/dist/types"; +import type { Lavamusic } from "../structures/index"; function getButtons(player: Player, client: Lavamusic): ActionRowBuilder[] { const buttonData = [ @@ -74,3 +74,14 @@ function getButtons(player: Player, client: Lavamusic): ActionRowBuilder Date: Sun, 15 Sep 2024 19:07:08 +0530 Subject: [PATCH 13/32] chore: update lavalink-client --- package.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 88002e541..79c0be722 100644 --- a/package.json +++ b/package.json @@ -35,10 +35,10 @@ "homepage": "https://github.com/appujet/lavamusic#readme", "devDependencies": { "@biomejs/biome": "^1.9.0", - "@types/i18n": "^0.13.12", - "@types/node": "^22.5.5", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", + "@types/i18n": "^0.13.12", + "@types/node": "^22.5.5", "@types/signale": "^1.4.7", "husky": "^9.1.6", "lint-staged": "^15.2.10", @@ -53,7 +53,7 @@ "dotenv": "^16.4.5", "genius-lyrics-api": "^3.2.1", "i18n": "^0.15.1", - "lavalink-client": "https://pkg.pr.new/moe-music/lavalink-client@f95457c", + "lavalink-client": "https://pkg.pr.new/moe-music/lavalink-client@b5bd496", "node-system-stats": "^1.3.0", "signale": "^1.4.0", "topgg-autoposter": "^2.0.2", @@ -61,7 +61,9 @@ "zod": "^3.23.8" }, "lint-staged": { - "*.ts": ["biome check --write"] + "*.ts": [ + "biome check --write" + ] }, "signale": { "displayScope": true, From d25e5290e0a70fe3d4abaa113bdde1284f2b6525 Mon Sep 17 00:00:00 2001 From: appujet Date: Sun, 15 Sep 2024 20:43:38 +0530 Subject: [PATCH 14/32] fixed docker --- .editorconfig | 13 ------------ .gitignore | 2 +- Dockerfile | 39 ++++++++++++---------------------- package.json | 1 - src/commands/music/Play.ts | 2 +- src/commands/music/PlayNext.ts | 2 +- src/commands/music/Search.ts | 2 +- src/utils/Buttons.ts | 2 +- src/utils/functions/player.ts | 2 +- 9 files changed, 20 insertions(+), 45 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 72397a9d7..000000000 --- a/.editorconfig +++ /dev/null @@ -1,13 +0,0 @@ -root = true - -[*] -charset = utf-8 -end_of_line = crlf -trim_trailing_whitespace = true -insert_final_newline = true -indent_style = tab -indent_size = 2 -quote_type = single - -[Makefile] -indent_style = tab \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6af8d2784..d9f01b479 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* - +bun.lockb # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/Dockerfile b/Dockerfile index 67a223ae8..c99191f3b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,26 +3,16 @@ FROM node:22 AS builder WORKDIR /opt/lavamusic/ -# Copy package files and install dependencies +# Copy only package files and install dependencies COPY package*.json ./ +RUN npm install --legacy-peer-deps -# Install necessary tools and update npm -RUN apt-get update && apt-get install -y openssl git \ - && npm install -g npm@latest -RUN npm install -RUN npm config set global --legacy-peer-deps - -# Copy source code +# Copy source code and configuration COPY . . -# Copy tsconfig.json -COPY tsconfig.json ./ -# Copy prisma -COPY prisma ./prisma -# Generate Prisma client -RUN npx prisma db push -# Build TypeScript -RUN npm run build +# Generate Prisma client and build TypeScript +RUN npx prisma db push && \ + npm run build # Stage 2: Create production image FROM node:22-slim @@ -32,18 +22,20 @@ ENV NODE_ENV=production WORKDIR /opt/lavamusic/ # Install necessary tools -RUN apt-get update && apt-get install -y openssl +RUN apt-get update && apt-get install -y --no-install-recommends openssl && \ + rm -rf /var/lib/apt/lists/* -# Copy compiled code and other necessary files from the builder stage +# Copy compiled code and necessary files from the builder stage COPY --from=builder /opt/lavamusic/dist ./dist -COPY --from=builder /opt/lavamusic/src/utils/LavaLogo.txt ./src/utils/LavaLogo.txt COPY --from=builder /opt/lavamusic/prisma ./prisma COPY --from=builder /opt/lavamusic/scripts ./scripts -COPY --from=builder /opt/lavamusic/package*.json ./ COPY --from=builder /opt/lavamusic/locales ./locales +# Install production dependencies +COPY --from=builder /opt/lavamusic/package*.json ./ RUN npm install --omit=dev +# Generate Prisma client RUN npx prisma generate RUN npx prisma db push @@ -53,12 +45,9 @@ RUN rm -rf /opt/lavamusic/application.yml && \ # Run as non-root user RUN addgroup --gid 322 --system lavamusic && \ - adduser --uid 322 --system lavamusic - -# Change ownership of the folder -RUN chown -R lavamusic:lavamusic /opt/lavamusic/ + adduser --uid 322 --system lavamusic && \ + chown -R lavamusic:lavamusic /opt/lavamusic/ -# Switch to the appropriate user USER lavamusic CMD ["node", "dist/index.js"] diff --git a/package.json b/package.json index 79c0be722..b5e7c72ef 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "build": "tsc --project tsconfig.json", "clean": "node scripts/clean.js && npm run build", "lint": "biome lint --write", - "prepare": "npm run clean && husky", "format": "biome format --write" }, "repository": { diff --git a/src/commands/music/Play.ts b/src/commands/music/Play.ts index 4e202ef1f..c36809eb6 100644 --- a/src/commands/music/Play.ts +++ b/src/commands/music/Play.ts @@ -1,5 +1,5 @@ import type { AutocompleteInteraction, VoiceChannel } from "discord.js"; -import type { SearchResult } from "lavalink-client/dist/types"; +import type { SearchResult } from "lavalink-client"; import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Play extends Command { diff --git a/src/commands/music/PlayNext.ts b/src/commands/music/PlayNext.ts index 3f79cb7a1..31e4a9603 100644 --- a/src/commands/music/PlayNext.ts +++ b/src/commands/music/PlayNext.ts @@ -1,5 +1,5 @@ import type { AutocompleteInteraction, VoiceChannel } from "discord.js"; -import type { SearchResult } from "lavalink-client/dist/types"; +import type { SearchResult } from "lavalink-client"; import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class PlayNext extends Command { diff --git a/src/commands/music/Search.ts b/src/commands/music/Search.ts index 6b83801da..17b6edf38 100644 --- a/src/commands/music/Search.ts +++ b/src/commands/music/Search.ts @@ -1,5 +1,5 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type VoiceChannel } from "discord.js"; -import type { SearchResult, Track } from "lavalink-client/dist/types"; +import type { SearchResult, Track } from "lavalink-client"; import { Command, type Context, type Lavamusic } from "../../structures/index"; export default class Search extends Command { diff --git a/src/utils/Buttons.ts b/src/utils/Buttons.ts index 7c1b1472a..c0ff18638 100644 --- a/src/utils/Buttons.ts +++ b/src/utils/Buttons.ts @@ -1,5 +1,5 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type EmojiIdentifierResolvable } from "discord.js"; -import type { Player } from "lavalink-client/dist/types"; +import type { Player } from "lavalink-client"; import type { Lavamusic } from "../structures/index"; function getButtons(player: Player, client: Lavamusic): ActionRowBuilder[] { diff --git a/src/utils/functions/player.ts b/src/utils/functions/player.ts index 95c6dc7e8..ced803e16 100644 --- a/src/utils/functions/player.ts +++ b/src/utils/functions/player.ts @@ -1,4 +1,4 @@ -import type { Player, Track } from "lavalink-client/dist/types"; +import type { Player, Track } from "lavalink-client"; import type { Requester } from "../../types"; /** From 41697fe2c85bcbd55255bdb6675c1014ec8c5ddd Mon Sep 17 00:00:00 2001 From: appujet Date: Sun, 15 Sep 2024 22:31:24 +0530 Subject: [PATCH 15/32] added filters --- .husky/commit-msg | 1 - .husky/pre-commit | 1 - commitlint.config.js | 1 - package.json | 9 ---- src/commands/config/247.ts | 2 +- src/commands/filters/NightCore.ts | 2 +- src/commands/filters/Pitch.ts | 81 +++++++++++++++++++++++++++++++ src/commands/filters/Rate.ts | 81 +++++++++++++++++++++++++++++++ src/commands/filters/Reset.ts | 57 ++++++++++++++++++++++ src/commands/filters/Rotation.ts | 68 ++++++++++++++++++++++++++ src/commands/filters/Speed.ts | 81 +++++++++++++++++++++++++++++++ src/commands/filters/Tremolo.ts | 70 ++++++++++++++++++++++++++ src/commands/filters/Vibrato.ts | 70 ++++++++++++++++++++++++++ src/commands/music/Join.ts | 2 +- src/commands/music/Play.ts | 5 +- src/commands/music/PlayNext.ts | 2 +- src/commands/music/Search.ts | 2 +- src/commands/playlist/Load.ts | 2 +- src/events/client/SetupSystem.ts | 10 ++-- src/events/node/Connect.ts | 2 +- src/events/player/QueueEnd.ts | 4 +- src/structures/LavalinkClient.ts | 3 +- 22 files changed, 526 insertions(+), 30 deletions(-) delete mode 100644 .husky/commit-msg delete mode 100644 .husky/pre-commit delete mode 100644 commitlint.config.js create mode 100644 src/commands/filters/Pitch.ts create mode 100644 src/commands/filters/Rate.ts create mode 100644 src/commands/filters/Reset.ts create mode 100644 src/commands/filters/Rotation.ts create mode 100644 src/commands/filters/Speed.ts create mode 100644 src/commands/filters/Tremolo.ts create mode 100644 src/commands/filters/Vibrato.ts diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100644 index 34eed8b28..000000000 --- a/.husky/commit-msg +++ /dev/null @@ -1 +0,0 @@ -npx --no -- commitlint --edit $1 \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100644 index d0a778429..000000000 --- a/.husky/pre-commit +++ /dev/null @@ -1 +0,0 @@ -npx lint-staged \ No newline at end of file diff --git a/commitlint.config.js b/commitlint.config.js deleted file mode 100644 index 5073c20db..000000000 --- a/commitlint.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = { extends: ["@commitlint/config-conventional"] }; diff --git a/package.json b/package.json index b5e7c72ef..273edc850 100644 --- a/package.json +++ b/package.json @@ -34,13 +34,9 @@ "homepage": "https://github.com/appujet/lavamusic#readme", "devDependencies": { "@biomejs/biome": "^1.9.0", - "@commitlint/cli": "^19.5.0", - "@commitlint/config-conventional": "^19.5.0", "@types/i18n": "^0.13.12", "@types/node": "^22.5.5", "@types/signale": "^1.4.7", - "husky": "^9.1.6", - "lint-staged": "^15.2.10", "prisma": "^5.19.1", "tslib": "^2.7.0", "typescript": "^5.6.2" @@ -59,11 +55,6 @@ "undici": "^6.19.8", "zod": "^3.23.8" }, - "lint-staged": { - "*.ts": [ - "biome check --write" - ] - }, "signale": { "displayScope": true, "displayBadge": true, diff --git a/src/commands/config/247.ts b/src/commands/config/247.ts index 7d123b046..32b60c6fb 100644 --- a/src/commands/config/247.ts +++ b/src/commands/config/247.ts @@ -56,7 +56,7 @@ export default class _247 extends Command { textChannelId: ctx.channel.id, selfMute: false, selfDeaf: true, - instaUpdateFiltersFix: true, + vcRegion: member.voice.channel.rtcRegion, }); } diff --git a/src/commands/filters/NightCore.ts b/src/commands/filters/NightCore.ts index b25b63eb9..baa4d7ce8 100644 --- a/src/commands/filters/NightCore.ts +++ b/src/commands/filters/NightCore.ts @@ -35,7 +35,7 @@ export default class NightCore extends Command { const filterEnabled = player.filterManager.filters.nightcore; if (filterEnabled) { - await player.filterManager.toggleNightcore(); + await player.filterManager.toggleTremolo(); await ctx.sendMessage({ embeds: [ { diff --git a/src/commands/filters/Pitch.ts b/src/commands/filters/Pitch.ts new file mode 100644 index 000000000..2e61a8c07 --- /dev/null +++ b/src/commands/filters/Pitch.ts @@ -0,0 +1,81 @@ +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class Pitch extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "pitch", + description: { + content: "cmd.pitch.description", + examples: ["pitch 1", "pitch 1.5", "pitch 1,5"], + usage: "pitch ", + }, + category: "filters", + aliases: ["ph"], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [ + { + name: "pitch", + description: "cmd.pitch.options.pitch", + type: 3, + required: true, + }, + ], + }); + } + + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const pitchString = args[0].replace(",", "."); + const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(pitchString); + const pitch = parseFloat(pitchString); + + if (!isValidNumber || isNaN(pitch) || pitch < 0.5 || pitch > 5) { + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.pitch.errors.invalid_number"), + color: this.client.color.red, + }, + ], + }); + return; + } + + await player.filterManager.setPitch(pitch); + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.pitch.messages.pitch_set", { + pitch, + }), + color: this.client.color.main, + }, + ], + }); + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ \ No newline at end of file diff --git a/src/commands/filters/Rate.ts b/src/commands/filters/Rate.ts new file mode 100644 index 000000000..9c734a714 --- /dev/null +++ b/src/commands/filters/Rate.ts @@ -0,0 +1,81 @@ +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class Rate extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "rate", + description: { + content: "cmd.rate.description", + examples: ["rate 1", "rate 1.5", "rate 1,5"], + usage: "rate ", + }, + category: "filters", + aliases: ["rt"], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [ + { + name: "rate", + description: "cmd.rate.options.rate", + type: 3, + required: true, + }, + ], + }); + } + + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const rateString = args[0].replace(",", "."); + const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(rateString); + const rate = parseFloat(rateString); + + if (!isValidNumber || isNaN(rate) || rate < 0.5 || rate > 5) { + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.rate.errors.invalid_number"), + color: this.client.color.red, + }, + ], + }); + return; + } + + await player.filterManager.setRate(rate); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.rate.messages.rate_set", { + rate, + }), + color: this.client.color.main, + }, + ], + }); + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ \ No newline at end of file diff --git a/src/commands/filters/Reset.ts b/src/commands/filters/Reset.ts new file mode 100644 index 000000000..cb07b92dd --- /dev/null +++ b/src/commands/filters/Reset.ts @@ -0,0 +1,57 @@ +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class Reset extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "reset", + description: { + content: "cmd.reset.description", + examples: ["reset"], + usage: "reset", + }, + category: "filters", + aliases: ["rs"], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [], + }); + } + + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + player.filterManager.resetFilters(); + player.filterManager.clearEQ(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.reset.messages.filters_reset"), + color: this.client.color.main, + }, + ], + }); + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ \ No newline at end of file diff --git a/src/commands/filters/Rotation.ts b/src/commands/filters/Rotation.ts new file mode 100644 index 000000000..b1bb461ab --- /dev/null +++ b/src/commands/filters/Rotation.ts @@ -0,0 +1,68 @@ +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class Rotation extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "rotation", + description: { + content: "cmd.rotation.description", + examples: ["rotation"], + usage: "rotation", + }, + category: "filters", + aliases: ["rt"], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [], + }); + } + + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + if (player.filterManager.filters.rotation) { + player.filterManager.toggleRotation(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.rotation.messages.disabled"), + color: this.client.color.main, + }, + ], + }); + } else { + player.filterManager.toggleRotation(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.rotation.messages.enabled"), + color: this.client.color.main, + }, + ], + }); + } + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ \ No newline at end of file diff --git a/src/commands/filters/Speed.ts b/src/commands/filters/Speed.ts new file mode 100644 index 000000000..022bb8e27 --- /dev/null +++ b/src/commands/filters/Speed.ts @@ -0,0 +1,81 @@ +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class Speed extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "speed", + description: { + content: "cmd.speed.description", + examples: ["speed 1.5", "speed 1,5"], + usage: "speed ", + }, + category: "filters", + aliases: ["spd"], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [ + { + name: "speed", + description: "cmd.speed.options.speed", + type: 3, + required: true, + }, + ], + }); + } + + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const speedString = args[0].replace(",", "."); + const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(speedString); + const speed = parseFloat(speedString); + + if (!isValidNumber || isNaN(speed) || speed < 0.5 || speed > 5) { + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.speed.messages.invalid_number"), + color: this.client.color.red, + }, + ], + }); + return; + } + + player.filterManager.setSpeed(speed); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.speed.messages.set_speed", { + speed, + }), + color: this.client.color.main, + }, + ], + }); + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ \ No newline at end of file diff --git a/src/commands/filters/Tremolo.ts b/src/commands/filters/Tremolo.ts new file mode 100644 index 000000000..f0b98fdee --- /dev/null +++ b/src/commands/filters/Tremolo.ts @@ -0,0 +1,70 @@ +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class Tremolo extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "tremolo", + description: { + content: "cmd.tremolo.description", + examples: ["tremolo"], + usage: "tremolo", + }, + category: "filters", + aliases: ["tr"], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [], + }); + } + + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const tremoloEnabled = player.filterManager.filters.tremolo + + if (tremoloEnabled) { + player.filterManager.toggleTremolo(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.tremolo.messages.disabled"), + color: this.client.color.main, + }, + ], + }); + } else { + player.filterManager.toggleTremolo(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.tremolo.messages.enabled"), + color: this.client.color.main, + }, + ], + }); + } + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ \ No newline at end of file diff --git a/src/commands/filters/Vibrato.ts b/src/commands/filters/Vibrato.ts new file mode 100644 index 000000000..64391000d --- /dev/null +++ b/src/commands/filters/Vibrato.ts @@ -0,0 +1,70 @@ +import { Command, type Context, type Lavamusic } from "../../structures/index.js"; + +export default class Vibrato extends Command { + constructor(client: Lavamusic) { + super(client, { + name: "vibrato", + description: { + content: "cmd.vibrato.description", + examples: ["vibrato"], + usage: "vibrato", + }, + category: "filters", + aliases: ["vb"], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], + user: [], + }, + slashCommand: true, + options: [], + }); + } + + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const vibratoEnabled = player.filterManager.filters.vibrato + + if (vibratoEnabled) { + player.filterManager.toggleVibrato(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.vibrato.messages.disabled"), + color: this.client.color.main, + }, + ], + }); + } else { + player.filterManager.toggleVibrato(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale("cmd.vibrato.messages.enabled"), + color: this.client.color.main, + }, + ], + }); + } + } +} + +/** + * Project: lavamusic + * Author: Appu + * Main Contributor: LucasB25 + * Company: Coders + * Copyright (c) 2024. All rights reserved. + * This code is the property of Coder and may not be reproduced or + * modified without permission. For more information, contact us at + * https://discord.gg/ns8CTk9J3e + */ \ No newline at end of file diff --git a/src/commands/music/Join.ts b/src/commands/music/Join.ts index 62214373a..2dadd812c 100644 --- a/src/commands/music/Join.ts +++ b/src/commands/music/Join.ts @@ -60,7 +60,7 @@ export default class Join extends Command { textChannelId: ctx.channel.id, selfMute: false, selfDeaf: true, - instaUpdateFiltersFix: true, + vcRegion: memberVoiceChannel.rtcRegion, }); if (!player.connected) await player.connect(); diff --git a/src/commands/music/Play.ts b/src/commands/music/Play.ts index c36809eb6..9c3b40627 100644 --- a/src/commands/music/Play.ts +++ b/src/commands/music/Play.ts @@ -58,7 +58,6 @@ export default class Play extends Command { textChannelId: ctx.channel.id, selfMute: false, selfDeaf: true, - instaUpdateFiltersFix: true, vcRegion: memberVoiceChannel.rtcRegion, }); if (!player.connected) await player.connect(); @@ -103,7 +102,7 @@ export default class Play extends Command { const focusedValue = interaction.options.getFocused(); if (!focusedValue) { - return; + return interaction.respond([]).catch(() => { null }); } const res = await this.client.manager.search(focusedValue, interaction.user); @@ -119,7 +118,7 @@ export default class Play extends Command { }); } - return await interaction.respond(songs).catch(console.error); + return await interaction.respond(songs).catch(() => { null }); } } diff --git a/src/commands/music/PlayNext.ts b/src/commands/music/PlayNext.ts index 31e4a9603..22404edee 100644 --- a/src/commands/music/PlayNext.ts +++ b/src/commands/music/PlayNext.ts @@ -57,7 +57,7 @@ export default class PlayNext extends Command { textChannelId: ctx.channel.id, selfMute: false, selfDeaf: true, - instaUpdateFiltersFix: true, + vcRegion: memberVoiceChannel.rtcRegion, }); if (!player.connected) await player.connect(); diff --git a/src/commands/music/Search.ts b/src/commands/music/Search.ts index 17b6edf38..e44ea220b 100644 --- a/src/commands/music/Search.ts +++ b/src/commands/music/Search.ts @@ -52,7 +52,7 @@ export default class Search extends Command { textChannelId: ctx.channel.id, selfMute: false, selfDeaf: true, - instaUpdateFiltersFix: true, + vcRegion: memberVoiceChannel.rtcRegion, }); if (!player.connected) await player.connect(); diff --git a/src/commands/playlist/Load.ts b/src/commands/playlist/Load.ts index d6e44a524..3b69cae23 100644 --- a/src/commands/playlist/Load.ts +++ b/src/commands/playlist/Load.ts @@ -74,7 +74,7 @@ export default class LoadPlaylist extends Command { textChannelId: ctx.channel.id, selfMute: false, selfDeaf: true, - instaUpdateFiltersFix: true, + vcRegion: member.voice.channel.rtcRegion, }); if (!player.connected) await player.connect(); diff --git a/src/events/client/SetupSystem.ts b/src/events/client/SetupSystem.ts index b033c07ce..179cdfeb9 100644 --- a/src/events/client/SetupSystem.ts +++ b/src/events/client/SetupSystem.ts @@ -16,7 +16,7 @@ export default class SetupSystem extends Event { if (!(channel instanceof TextChannel)) return; if (!message.member?.voice.channel) { await oops(channel, T(locale, "event.message.no_voice_channel_queue")); - await message.delete().catch(() => {}); + await message.delete().catch(() => { }); return; } @@ -31,7 +31,7 @@ export default class SetupSystem extends Event { channel: voiceChannel.id, }), ); - await message.delete().catch(() => {}); + await message.delete().catch(() => { }); return; } @@ -42,7 +42,7 @@ export default class SetupSystem extends Event { channel: clientMember.voice.channelId, }), ); - await message.delete().catch(() => {}); + await message.delete().catch(() => { }); return; } @@ -54,14 +54,14 @@ export default class SetupSystem extends Event { textChannelId: message.channelId, selfMute: false, selfDeaf: true, - instaUpdateFiltersFix: true, + vcRegion: voiceChannel.rtcRegion, }); if (!player.connected) await player.connect(); } await setupStart(this.client, message.content, player, message); - await message.delete().catch(() => {}); + await message.delete().catch(() => { }); } } diff --git a/src/events/node/Connect.ts b/src/events/node/Connect.ts index 201cf77bc..a234824d7 100644 --- a/src/events/node/Connect.ts +++ b/src/events/node/Connect.ts @@ -35,7 +35,7 @@ export default class Connect extends Event { textChannelId: channel.id, selfDeaf: true, selfMute: false, - instaUpdateFiltersFix: true, + }); if (!player.connected) await player.connect(); } catch (error) { diff --git a/src/events/player/QueueEnd.ts b/src/events/player/QueueEnd.ts index c25e808f2..d489603e8 100644 --- a/src/events/player/QueueEnd.ts +++ b/src/events/player/QueueEnd.ts @@ -20,11 +20,11 @@ export default class QueueEnd extends Event { const channel = guild.channels.cache.get(player.textChannelId) as TextChannel; if (!channel) return; - const message = await channel.messages.fetch(messageId).catch(() => {}); + const message = await channel.messages.fetch(messageId).catch(() => { }); if (!message) return; if (message.editable) { - await message.edit({ components: [] }).catch(() => {}); + await message.edit({ components: [] }).catch(() => { }); } } } diff --git a/src/structures/LavalinkClient.ts b/src/structures/LavalinkClient.ts index 6e323306f..dd475e881 100644 --- a/src/structures/LavalinkClient.ts +++ b/src/structures/LavalinkClient.ts @@ -14,7 +14,8 @@ export default class LavalinkClient extends LavalinkManager { playerOptions: { defaultSearchPlatform: client.env.SEARCH_ENGINE, onDisconnect: { - destroyPlayer: true, + autoReconnect: true, + destroyPlayer: false }, requesterTransformer: requesterTransformer, onEmptyQueue: { From 7c51db23c1b119f094de83fdd9a1eacc9fc86534 Mon Sep 17 00:00:00 2001 From: Appu <77108939+appujet@users.noreply.github.com> Date: Sun, 15 Sep 2024 22:54:37 +0530 Subject: [PATCH 16/32] Update NightCore.ts Signed-off-by: Appu <77108939+appujet@users.noreply.github.com> --- src/commands/filters/NightCore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/filters/NightCore.ts b/src/commands/filters/NightCore.ts index baa4d7ce8..b25b63eb9 100644 --- a/src/commands/filters/NightCore.ts +++ b/src/commands/filters/NightCore.ts @@ -35,7 +35,7 @@ export default class NightCore extends Command { const filterEnabled = player.filterManager.filters.nightcore; if (filterEnabled) { - await player.filterManager.toggleTremolo(); + await player.filterManager.toggleNightcore(); await ctx.sendMessage({ embeds: [ { From 9dfa60bccbd730264fe3846744c807ab7c8fe67e Mon Sep 17 00:00:00 2001 From: LucasB25 <50886682+LucasB25@users.noreply.github.com> Date: Tue, 17 Sep 2024 17:41:09 +0200 Subject: [PATCH 17/32] update fix autoplay --- biome.json | 2 +- package.json | 4 ++-- src/commands/filters/Pitch.ts | 2 +- src/commands/filters/Rate.ts | 2 +- src/commands/filters/Reset.ts | 2 +- src/commands/filters/Rotation.ts | 4 ++-- src/commands/filters/Speed.ts | 2 +- src/commands/filters/Tremolo.ts | 4 ++-- src/commands/filters/Vibrato.ts | 4 ++-- src/commands/music/Play.ts | 8 ++++++-- src/events/client/SetupSystem.ts | 8 ++++---- src/events/node/Connect.ts | 1 - src/events/player/QueueEnd.ts | 4 ++-- src/structures/LavalinkClient.ts | 2 +- 14 files changed, 26 insertions(+), 23 deletions(-) diff --git a/biome.json b/biome.json index 0c2f79ce8..52c0883ac 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json", + "$schema": "https://biomejs.dev/schemas/1.9.1/schema.json", "organizeImports": { "enabled": true }, diff --git a/package.json b/package.json index 273edc850..dc0aefb5d 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "homepage": "https://github.com/appujet/lavamusic#readme", "devDependencies": { - "@biomejs/biome": "^1.9.0", + "@biomejs/biome": "^1.9.1", "@types/i18n": "^0.13.12", "@types/node": "^22.5.5", "@types/signale": "^1.4.7", @@ -48,7 +48,7 @@ "dotenv": "^16.4.5", "genius-lyrics-api": "^3.2.1", "i18n": "^0.15.1", - "lavalink-client": "https://pkg.pr.new/moe-music/lavalink-client@b5bd496", + "lavalink-client": "https://pkg.pr.new/moe-music/lavalink-client@0b0f590", "node-system-stats": "^1.3.0", "signale": "^1.4.0", "topgg-autoposter": "^2.0.2", diff --git a/src/commands/filters/Pitch.ts b/src/commands/filters/Pitch.ts index 2e61a8c07..52bd34ba5 100644 --- a/src/commands/filters/Pitch.ts +++ b/src/commands/filters/Pitch.ts @@ -78,4 +78,4 @@ export default class Pitch extends Command { * This code is the property of Coder and may not be reproduced or * modified without permission. For more information, contact us at * https://discord.gg/ns8CTk9J3e - */ \ No newline at end of file + */ diff --git a/src/commands/filters/Rate.ts b/src/commands/filters/Rate.ts index 9c734a714..ccfc1d712 100644 --- a/src/commands/filters/Rate.ts +++ b/src/commands/filters/Rate.ts @@ -78,4 +78,4 @@ export default class Rate extends Command { * This code is the property of Coder and may not be reproduced or * modified without permission. For more information, contact us at * https://discord.gg/ns8CTk9J3e - */ \ No newline at end of file + */ diff --git a/src/commands/filters/Reset.ts b/src/commands/filters/Reset.ts index cb07b92dd..83c2d2c97 100644 --- a/src/commands/filters/Reset.ts +++ b/src/commands/filters/Reset.ts @@ -54,4 +54,4 @@ export default class Reset extends Command { * This code is the property of Coder and may not be reproduced or * modified without permission. For more information, contact us at * https://discord.gg/ns8CTk9J3e - */ \ No newline at end of file + */ diff --git a/src/commands/filters/Rotation.ts b/src/commands/filters/Rotation.ts index b1bb461ab..c0355c74e 100644 --- a/src/commands/filters/Rotation.ts +++ b/src/commands/filters/Rotation.ts @@ -33,7 +33,7 @@ export default class Rotation extends Command { public async run(client: Lavamusic, ctx: Context): Promise { const player = client.manager.getPlayer(ctx.guild!.id); if (player.filterManager.filters.rotation) { - player.filterManager.toggleRotation(); + player.filterManager.toggleRotation(); await ctx.sendMessage({ embeds: [ { @@ -65,4 +65,4 @@ export default class Rotation extends Command { * This code is the property of Coder and may not be reproduced or * modified without permission. For more information, contact us at * https://discord.gg/ns8CTk9J3e - */ \ No newline at end of file + */ diff --git a/src/commands/filters/Speed.ts b/src/commands/filters/Speed.ts index 022bb8e27..6f6b9bc26 100644 --- a/src/commands/filters/Speed.ts +++ b/src/commands/filters/Speed.ts @@ -78,4 +78,4 @@ export default class Speed extends Command { * This code is the property of Coder and may not be reproduced or * modified without permission. For more information, contact us at * https://discord.gg/ns8CTk9J3e - */ \ No newline at end of file + */ diff --git a/src/commands/filters/Tremolo.ts b/src/commands/filters/Tremolo.ts index f0b98fdee..40901d77e 100644 --- a/src/commands/filters/Tremolo.ts +++ b/src/commands/filters/Tremolo.ts @@ -32,7 +32,7 @@ export default class Tremolo extends Command { public async run(client: Lavamusic, ctx: Context): Promise { const player = client.manager.getPlayer(ctx.guild!.id); - const tremoloEnabled = player.filterManager.filters.tremolo + const tremoloEnabled = player.filterManager.filters.tremolo; if (tremoloEnabled) { player.filterManager.toggleTremolo(); @@ -67,4 +67,4 @@ export default class Tremolo extends Command { * This code is the property of Coder and may not be reproduced or * modified without permission. For more information, contact us at * https://discord.gg/ns8CTk9J3e - */ \ No newline at end of file + */ diff --git a/src/commands/filters/Vibrato.ts b/src/commands/filters/Vibrato.ts index 64391000d..33d5769e8 100644 --- a/src/commands/filters/Vibrato.ts +++ b/src/commands/filters/Vibrato.ts @@ -32,7 +32,7 @@ export default class Vibrato extends Command { public async run(client: Lavamusic, ctx: Context): Promise { const player = client.manager.getPlayer(ctx.guild!.id); - const vibratoEnabled = player.filterManager.filters.vibrato + const vibratoEnabled = player.filterManager.filters.vibrato; if (vibratoEnabled) { player.filterManager.toggleVibrato(); @@ -67,4 +67,4 @@ export default class Vibrato extends Command { * This code is the property of Coder and may not be reproduced or * modified without permission. For more information, contact us at * https://discord.gg/ns8CTk9J3e - */ \ No newline at end of file + */ diff --git a/src/commands/music/Play.ts b/src/commands/music/Play.ts index 9c3b40627..332dca778 100644 --- a/src/commands/music/Play.ts +++ b/src/commands/music/Play.ts @@ -102,7 +102,9 @@ export default class Play extends Command { const focusedValue = interaction.options.getFocused(); if (!focusedValue) { - return interaction.respond([]).catch(() => { null }); + return interaction.respond([]).catch(() => { + null; + }); } const res = await this.client.manager.search(focusedValue, interaction.user); @@ -118,7 +120,9 @@ export default class Play extends Command { }); } - return await interaction.respond(songs).catch(() => { null }); + return await interaction.respond(songs).catch(() => { + null; + }); } } diff --git a/src/events/client/SetupSystem.ts b/src/events/client/SetupSystem.ts index 179cdfeb9..ae57b3c1d 100644 --- a/src/events/client/SetupSystem.ts +++ b/src/events/client/SetupSystem.ts @@ -16,7 +16,7 @@ export default class SetupSystem extends Event { if (!(channel instanceof TextChannel)) return; if (!message.member?.voice.channel) { await oops(channel, T(locale, "event.message.no_voice_channel_queue")); - await message.delete().catch(() => { }); + await message.delete().catch(() => {}); return; } @@ -31,7 +31,7 @@ export default class SetupSystem extends Event { channel: voiceChannel.id, }), ); - await message.delete().catch(() => { }); + await message.delete().catch(() => {}); return; } @@ -42,7 +42,7 @@ export default class SetupSystem extends Event { channel: clientMember.voice.channelId, }), ); - await message.delete().catch(() => { }); + await message.delete().catch(() => {}); return; } @@ -61,7 +61,7 @@ export default class SetupSystem extends Event { } await setupStart(this.client, message.content, player, message); - await message.delete().catch(() => { }); + await message.delete().catch(() => {}); } } diff --git a/src/events/node/Connect.ts b/src/events/node/Connect.ts index a234824d7..75b02fe9a 100644 --- a/src/events/node/Connect.ts +++ b/src/events/node/Connect.ts @@ -35,7 +35,6 @@ export default class Connect extends Event { textChannelId: channel.id, selfDeaf: true, selfMute: false, - }); if (!player.connected) await player.connect(); } catch (error) { diff --git a/src/events/player/QueueEnd.ts b/src/events/player/QueueEnd.ts index d489603e8..c25e808f2 100644 --- a/src/events/player/QueueEnd.ts +++ b/src/events/player/QueueEnd.ts @@ -20,11 +20,11 @@ export default class QueueEnd extends Event { const channel = guild.channels.cache.get(player.textChannelId) as TextChannel; if (!channel) return; - const message = await channel.messages.fetch(messageId).catch(() => { }); + const message = await channel.messages.fetch(messageId).catch(() => {}); if (!message) return; if (message.editable) { - await message.edit({ components: [] }).catch(() => { }); + await message.edit({ components: [] }).catch(() => {}); } } } diff --git a/src/structures/LavalinkClient.ts b/src/structures/LavalinkClient.ts index dd475e881..a93faf896 100644 --- a/src/structures/LavalinkClient.ts +++ b/src/structures/LavalinkClient.ts @@ -15,7 +15,7 @@ export default class LavalinkClient extends LavalinkManager { defaultSearchPlatform: client.env.SEARCH_ENGINE, onDisconnect: { autoReconnect: true, - destroyPlayer: false + destroyPlayer: false, }, requesterTransformer: requesterTransformer, onEmptyQueue: { From b2bfe4f517efaee1ca464500f668af5edb6f3dc3 Mon Sep 17 00:00:00 2001 From: hwangsihu Date: Mon, 16 Sep 2024 09:08:15 +0900 Subject: [PATCH 18/32] Update --- .env.example | 36 +++---- Lavalink/example.application.yml | 47 +++++--- biome.json | 66 ++++++------ locales/ChineseCN.json | 1 - locales/ChineseTW.json | 1 - locales/EnglishUS.json | 7 +- locales/French.json | 1 - locales/German.json | 1 - locales/Hindi.json | 1 - locales/Indonesian.json | 1 - locales/Japanese.json | 1 - locales/Korean.json | 35 +++--- locales/Norwegian.json | 1 - locales/Polish.json | 1 - locales/PortuguesePT.json | 1 - locales/Russian.json | 1 - locales/SpanishES.json | 1 - locales/Vietnamese.json | 1 - run.bat | 19 +--- src/LavaClient.ts | 2 +- src/commands/config/Setup.ts | 7 +- src/commands/dev/GuildList.ts | 2 +- src/events/client/Ready.ts | 2 +- src/events/client/SetupButtons.ts | 172 +++++++++++++++--------------- src/events/player/QueueEnd.ts | 1 - src/events/player/TrackStart.ts | 6 +- src/index.ts | 2 +- src/plugin/plugins/keepAlive.ts | 2 +- src/utils/Utils.ts | 4 +- tsconfig.json | 6 +- 30 files changed, 204 insertions(+), 225 deletions(-) diff --git a/.env.example b/.env.example index ac3eab50f..8e9ab5b7a 100644 --- a/.env.example +++ b/.env.example @@ -1,18 +1,18 @@ -TOKEN= "" # Your bot token. -CLIENT_ID= "" # Your bot's client ID (If this value is left blank, bots cannot be invited using /invite or /about commands.). -DEFAULT_LANGUAGE= "EnglishUS" # Default language for bot -PREFIX= "!" # Your prefix. -OWNER_IDS= ["",""] # Your discord id (You can add multiple ids.). -GUILD_ID= "" # Your server ID (If you want to use the bot for a single server). -TOPGG= "" # Your Top.gg API key. Obtain this from https://top.gg -KEEP_ALIVE= "false" # true for keep alive in https://replit.com -LOG_CHANNEL_ID= "" # If you enter this, you will be able to receive the status of Lavalink nodes and guild join/leave logs through the corresponding channel. -LOG_COMMANDS_ID= "" # The channel ID where command usage logs will be sent. -BOT_STATUS= "online" # Your bot status (online, dnd, idle, invisible or offline). -BOT_ACTIVITY_TYPE= 0 # Activity type is a number from 0 to 5. See more here: https://discord.com/developers/docs/topics/gateway-events#activity-object-activity-types -BOT_ACTIVITY=" Lavamusic" # Your bot activity. -DATABASE_URL= "" # Your database url (If you want to use sqlite, then you can leave it blank.). -AUTO_NODE=" false" # true for auto node. It is given from lavainfo-api (https://lavainfo-api.deno.dev). -SEARCH_ENGINE= "YouTubeMusic" # Search engine to be used when playing the song. You can use: YouTube, YouTubeMusic, SoundCloud, Spotify, Apple, Deezer, Yandex and JioSaavn -GENIUS_API= "" # Sign up and get your own api at (https://genius.com/) to fetch your lyrics (CLIENT TOKEN) -NODES=[{"id":"main","host":"localhost","port":2333,"authorization":"youshallnotpass"}] +TOKEN="" # Your bot token. +CLIENT_ID="" # Your bot's client ID (If this value is left blank, bots cannot be invited using /invite or /about commands.). +DEFAULT_LANGUAGE="EnglishUS" # Default language for bot +PREFIX="!" # Your prefix. +OWNER_IDS=["",""] # Your discord id (You can add multiple ids.). +GUILD_ID="" # Your server ID (If you want to use the bot for a single server). +TOPGG="" # Your Top.gg API key. Obtain this from https://top.gg +KEEP_ALIVE="false" # true for keep alive in https://replit.com +LOG_CHANNEL_ID="" # If you enter this, you will be able to receive the status of Lavalink nodes and guild join/leave logs through the corresponding channel. +LOG_COMMANDS_ID="" # The channel ID where command usage logs will be sent. +BOT_STATUS="online" # Your bot status (online, dnd, idle, invisible or offline). +BOT_ACTIVITY_TYPE=0 # Activity type is a number from 0 to 5. See more here: https://discord.com/developers/docs/topics/gateway-events#activity-object-activity-types +BOT_ACTIVITY=="Lavamusic" # Your bot activity. +DATABASE_URL="" # Your database url (If you want to use sqlite, then you can leave it blank.). +AUTO_NODE=="false" # true for auto node. It is given from lavainfo-api (https://lavainfo-api.deno.dev). +SEARCH_ENGINE="YouTubeMusic" # Search engine to be used when playing the song. You can use: YouTube, YouTubeMusic, SoundCloud, Spotify, Apple, Deezer, Yandex and JioSaavn +GENIUS_API="" # Sign up and get your own api at (https://genius.com/) to fetch your lyrics (CLIENT TOKEN) +NODES=[{"id":"Local Node","host":"localhost","port":2333,"authorization":"youshallnotpass"}] diff --git a/Lavalink/example.application.yml b/Lavalink/example.application.yml index d2ae0f771..244512cd4 100644 --- a/Lavalink/example.application.yml +++ b/Lavalink/example.application.yml @@ -18,8 +18,15 @@ plugins: mixcloud: true # mixcloud.com soundgasm: true # soundgasm.net pixeldrain: true # pixeldrain.com - jiosaavn: + jiosaavn: apiURL: "https://saavn.dev/api" # The API URL for JioSaavn + lavalyrics: + # sources is used to sort the different lyrics sources by priority (from highest to lowest) + sources: + - spotify + - youtube + - deezer + - yandexMusic lavasrc: providers: # Custom providers for track loading. This is the default # - "dzisrc:%ISRC%" # Deezer ISRC provider @@ -34,18 +41,21 @@ plugins: yandexmusic: false # Enable Yandex Music source flowerytts: false # Enable Flowery TTS source youtube: true # Enable YouTube search source (https://github.com/topi314/LavaSearch) + vkmusic: false # Enable Vk Music source lyrics-sources: - spotify: false # Enable Spotify lyrics source - deezer: false # Enable Deezer lyrics source - youtube: false # Enable YouTube lyrics source - yandexmusic: false # Enable Yandex Music lyrics source + spotify: true # Enable Spotify lyrics source + deezer: true # Enable Deezer lyrics source + youtube: true # Enable YouTube lyrics source + yandexmusic: true # Enable Yandex Music lyrics source + vkmusic: true # Enable Vk Music lyrics source spotify: clientId: "your client id" clientSecret: "your client secret" - spDc: "your sp dc cookie" # the sp dc cookie used for accessing the spotify lyrics api + # spDc: "your sp dc cookie" # the sp dc cookie used for accessing the spotify lyrics api countryCode: "US" # the country code you want to use for filtering the artists top tracks. See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 playlistLoadLimit: 6 # The number of pages at 100 tracks each albumLoadLimit: 6 # The number of pages at 50 tracks each + resolveArtistsInSearch: true # Whether to resolve artists in track search results (can be slow) localFiles: false # Enable local files support with Spotify playlists. Please note `uri` & `isrc` will be `null` & `identifier` will be `"local"` applemusic: countryCode: "US" # the country code you want to use for filtering the artists top tracks and language. See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 @@ -61,6 +71,8 @@ plugins: albumLoadLimit: 6 # The number of pages at 300 tracks each deezer: masterDecryptionKey: "your master decryption key" # the master key used for decrypting the deezer tracks. (yes this is not here you need to get it from somewhere else) + # arl: "your deezer arl" # the arl cookie used for accessing the deezer api this is optional but required for formats above MP3_128 + formats: [ "FLAC", "MP3_320", "MP3_256", "MP3_128", "MP3_64", "AAC_64" ] # the formats you want to use for the deezer tracks. "FLAC", "MP3_320", "MP3_256" & "AAC_64" are only available for premium users and require a valid arl yandexmusic: accessToken: "your access token" # the token used for accessing the yandex music api. See https://github.com/TopiSenpai/LavaSrc#yandex-music playlistLoadLimit: 1 # The number of pages at 100 tracks each @@ -74,6 +86,11 @@ plugins: audioFormat: "mp3" # supported formats are: mp3, ogg_opus, ogg_vorbis, aac, wav, and flac. Default format is mp3 youtube: countryCode: "US" # the country code you want to use for searching lyrics via ISRC. See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 + vkmusic: + userToken: "your user token" # This token is needed for authorization in the api. Guide: https://github.com/topi314/LavaSrc#vk-music + playlistLoadLimit: 1 # The number of pages at 50 tracks each + artistLoadLimit: 1 # The number of pages at 10 tracks each + recommendationsLoadLimit: 10 # Number of tracks youtube: enabled: true # Whether this source can be used. allowSearch: true # Whether "ytsearch:" and "ytmsearch:" can be used. @@ -85,11 +102,7 @@ plugins: - MUSIC - WEB - ANDROID_TESTSUITE - - ANDROID_MUSIC - TVHTML5EMBEDDED - - ANDROID_LITE - - MEDIA_CONNECT - - IOS # This secure your lavalink is working and playing music if you used this two things below. #oauth: # enabled: true # IF YOU RUN YOUR LAVALINK CHECK YOUR CONSOLE AND CLICK THE GOOGLE.COM/DEVICES LINK AND THERE IS A CODE THAT YOU NEED TO PUT IN TO THE GOOGLE AND USE A BURNER ACCOUNT OR ALT ACCOUNT THERE IS A POSSIBLE CHANGE YOU CAN GET BANNED FROM GOOGLE OR YOUTUBE SO JUST USE A BURNER ACCOUNT IN CASE. @@ -101,15 +114,17 @@ lavalink: - dependency: "com.github.appujet:jiosaavn-plugin:0.1.7" repository: "https://jitpack.io" - dependency: "com.dunctebot:skybot-lavalink-plugin:1.7.0" - snapshot: false # set to true if you want to use snapshot builds. + snapshot: false # set to true if you want to use snapshot builds + - dependency: "com.github.topi314.lavalyrics:lavalyrics-plugin:1.0.0" + snapshot: false # set to true if you want to use snapshot builds (see below) - dependency: "com.github.topi314.lavasearch:lavasearch-plugin:1.0.0" - snapshot: false # set to true if you want to use snapshot builds. + snapshot: false # set to true if you want to use snapshot builds - dependency: "com.github.topi314.lavasrc:lavasrc-plugin:4.2.0" - snapshot: false # set to true if you want to use snapshot builds. + snapshot: false # set to true if you want to use snapshot builds - dependency: "com.github.topi314.sponsorblock:sponsorblock-plugin:3.0.1" - snapshot: false # set to true if you want to use snapshot builds. - - dependency: "dev.lavalink.youtube:youtube-plugin:1.7.2" - snapshot: false # set to true if you want to use snapshot builds. + snapshot: false # set to true if you want to use snapshot builds + - dependency: "dev.lavalink.youtube:youtube-plugin:1.8.0" + snapshot: false # set to true if you want to use snapshot builds pluginsDir: './plugins' server: password: "youshallnotpass" diff --git a/biome.json b/biome.json index 52c0883ac..b85551a9e 100644 --- a/biome.json +++ b/biome.json @@ -7,16 +7,25 @@ "enabled": true, "rules": { "all": true, - "suspicious": { - "noConfusingVoidType": "off", - "noConsole": "off", - "noConsoleLog": "off", - "noEmptyBlockStatements": "off", - "noEvolvingTypes": "off", - "noExplicitAny": "off", - "noGlobalIsFinite": "off", - "noGlobalIsNan": "off", - "useAwait": "off" + "complexity": { + "noBannedTypes": "off", + "noExcessiveCognitiveComplexity": "off", + "noForEach": "off", + "noStaticOnlyClass": "off" + }, + "correctness": { + "noNodejsModules": "off", + "noUnusedFunctionParameters": "off", + "noUnusedVariables": "off", + "noVoidTypeReturn": "off", + "useImportExtensions": "off" + }, + "performance": { + "noBarrelFile": "off", + "useTopLevelRegex": "off" + }, + "security": { + "noGlobalEval": "off" }, "style": { "noDefaultExport": "off", @@ -27,34 +36,21 @@ "useBlockStatements": "off", "useDefaultSwitchClause": "off", "useFilenamingConvention": "off", + "useImportType": "warn", "useNamingConvention": "off", "useNumberNamespace": "off", - "useSingleCaseStatement": "off", - "useImportType": "warn" + "useSingleCaseStatement": "off" }, - "complexity": { - "noBannedTypes": "off", - "noForEach": "off", - "useOptionalChain": "off", - "noStaticOnlyClass": "off", - "noExcessiveCognitiveComplexity": { - "level": "warn", - "options": { - "maxAllowedComplexity": 255 - } - } - }, - "security": { - "noGlobalEval": "off" - }, - "correctness": { - "noNodejsModules": "off", - "noVoidTypeReturn": "off", - "useImportExtensions": "off" - }, - "performance": { - "noBarrelFile": "off", - "useTopLevelRegex": "off" + "suspicious": { + "noConfusingVoidType": "off", + "noConsole": "off", + "noConsoleLog": "off", + "noEmptyBlockStatements": "off", + "noEvolvingTypes": "off", + "noExplicitAny": "off", + "noGlobalIsFinite": "off", + "noGlobalIsNan": "off", + "useAwait": "off" } } }, diff --git a/locales/ChineseCN.json b/locales/ChineseCN.json index bf16f1eff..2dc1bb82f 100644 --- a/locales/ChineseCN.json +++ b/locales/ChineseCN.json @@ -619,7 +619,6 @@ "no_previous_track": "没有上一首曲目。", "playing_previous": "正在播放上一首曲目。", "previous_footer": "由 {displayName} 播放上一首曲目", - "rewind_limit": "您不能将音乐倒回到超过歌曲长度的位置。", "rewinded": "已倒回音乐。", "rewind_footer": "由 {displayName} 倒回", "forward_limit": "您不能将音乐快进到超过歌曲长度的位置。", diff --git a/locales/ChineseTW.json b/locales/ChineseTW.json index affb23d34..03ce7471d 100644 --- a/locales/ChineseTW.json +++ b/locales/ChineseTW.json @@ -619,7 +619,6 @@ "no_previous_track": "沒有上一首曲目。", "playing_previous": "正在播放上一首曲目。", "previous_footer": "由 {displayName} 播放上一首曲目", - "rewind_limit": "您不能將音樂倒帶到超過歌曲長度的位置。", "rewinded": "已倒帶音樂。", "rewind_footer": "由 {displayName} 資訊", "forward_limit": "您不能將音樂快轉到超過歌曲長度的位置。", diff --git a/locales/EnglishUS.json b/locales/EnglishUS.json index bc479f6d4..f7b96855f 100644 --- a/locales/EnglishUS.json +++ b/locales/EnglishUS.json @@ -33,7 +33,7 @@ "options": { "command": "The command you want to get info on" }, - "content": "Hey there! I'm {bot}, a music bot made with [Lavamusic](https://github.com/appujet/lavamusic) and Discord. You can use `{prefix}help ` to get more info on a command.", + "content": "Hey there! I'm {bot}, a music bot made with [Lavamusic](https://github.com/appujet/lavamusic) and Discord.js. You can use `{prefix}help ` to get more info on a command.", "title": "Help Menu", "not_found": "This `{cmdName}` command does not exist.", "help_cmd": "**Description:** {description}\n**Usage:** {usage}\n**Examples:** {examples}\n**Aliases:** {aliases}\n**Category:** {category}\n**Cooldown:** {cooldown} seconds\n**Permissions:** {premUser}\n**Bot Permissions:** {premBot}\n**Developer Only:** {dev}\n**Slash Command:** {slash}\n**Args:** {args}\n**Player:** {player}\n**DJ:** {dj}\n**DJ Permissions:** {djPerm}\n**Voice:** {voice}", @@ -41,7 +41,7 @@ }, "botinfo": { "description": "Information about the bot", - "content": "Bot Information:\n- **Operating System**: {osInfo}\n- **Uptime**: {osUptime}\n- **Hostname**: {osHostname}\n- **CPU Architecture**: {cpuInfo}\n- **CPU Usage**: {cpuUsed}%\n- **Memory Usage**: {memUsed}MB / {memTotal}GB\n- **Node Version**: {nodeVersion}\n- **Discord Version**: {discordJsVersion}\n- **Connected to** {guilds} guilds, {channels} channels, and {users} users\n- **Total Commands**: {commands}" + "content": "Bot Information:\n- **Operating System**: {osInfo}\n- **Uptime**: {osUptime}\n- **Hostname**: {osHostname}\n- **CPU Architecture**: {cpuInfo}\n- **CPU Usage**: {cpuUsed}%\n- **Memory Usage**: {memUsed}MB / {memTotal}GB\n- **Node Version**: {nodeVersion}\n- **Discord.js Version**: {discordJsVersion}\n- **Connected to** {guilds} guilds, {channels} channels, and {users} users\n- **Total Commands**: {commands}" }, "about": { "description": "Shows information about the bot", @@ -110,7 +110,7 @@ "errors": { "channel_exists": "The song request channel already exists.", "channel_not_exists": "The song request channel doesn't exist.", - "channel_delete_fail": "The song request channel has been deleted. If the channel is not deleted normally, please delete it yourself." + "channel_delete_fail": "The setup channel has been deleted from the database. Please delete the channel yourself." }, "messages": { "channel_created": "The song request channel has been created in <#{channelId}>.", @@ -631,7 +631,6 @@ "no_previous_track": "There is no previous track.", "playing_previous": "Playing the previous track.", "previous_footer": "Playing the previous track by {displayName}", - "rewind_limit": "You cannot rewind the music more than the length of the song.", "rewinded": "Rewinded the music.", "rewind_footer": "Rewinded by {displayName}", "forward_limit": "You cannot forward the music more than the length of the song.", diff --git a/locales/French.json b/locales/French.json index b0ca2a7bb..60a8a58e2 100644 --- a/locales/French.json +++ b/locales/French.json @@ -619,7 +619,6 @@ "no_previous_track": "Il n'y a pas de piste précédente.", "playing_previous": "Lecture de la piste précédente.", "previous_footer": "Lecture de la piste précédente par {displayName}", - "rewind_limit": "Vous ne pouvez pas rembobiner la musique plus que la longueur de la chanson.", "rewinded": "Rembobinage de la musique.", "rewind_footer": "Rembobiné par {displayName}", "forward_limit": "Vous ne pouvez pas avancer la musique plus que la longueur de la chanson.", diff --git a/locales/German.json b/locales/German.json index 53b894886..22540ea90 100644 --- a/locales/German.json +++ b/locales/German.json @@ -619,7 +619,6 @@ "no_previous_track": "Es gibt keinen vorherigen Track.", "playing_previous": "Spiele den vorherigen Track ab.", "previous_footer": "Spiele den vorherigen Track ab von {displayName}", - "rewind_limit": "Du kannst die Musik nicht mehr als die Länge des Songs zurückspulen.", "rewinded": "Musik zurückgespult.", "rewind_footer": "Zurückgespult von {displayName}", "forward_limit": "Du kannst die Musik nicht mehr als die Länge des Songs vorspulen.", diff --git a/locales/Hindi.json b/locales/Hindi.json index e71d90a66..515cc00f9 100644 --- a/locales/Hindi.json +++ b/locales/Hindi.json @@ -619,7 +619,6 @@ "no_previous_track": "Koi pichhla track nahin hai.", "playing_previous": "Pichhla track play कर रहा हूँ.", "previous_footer": "{displayName} dwara pichhla track play kiya जा रहा है", - "rewind_limit": "Aap music ko song ki lambai se zyada rewind nahin kar sakte.", "rewinded": "Music ko rewind कर diya गया है.", "rewind_footer": "{displayName} dwara rewind kiya गया", "forward_limit": "Aap music ko song ki lambai se zyada forward nahin kar sakte.", diff --git a/locales/Indonesian.json b/locales/Indonesian.json index 9ff592c91..c777fa29d 100644 --- a/locales/Indonesian.json +++ b/locales/Indonesian.json @@ -619,7 +619,6 @@ "no_previous_track": "Tidak ada lagu sebelumnya.", "playing_previous": "Memutar lagu sebelumnya.", "previous_footer": "Memutar lagu sebelumnya oleh {displayName}", - "rewind_limit": "Anda tidak dapat memutar mundur lagu lebih dari panjang lagu.", "rewinded": "Memutar mundur lagu.", "rewind_footer": "Diputar mundur oleh {displayName}", "forward_limit": "Anda tidak dapat memajukan lagu lebih dari panjang lagu.", diff --git a/locales/Japanese.json b/locales/Japanese.json index 3e397571e..878e9f547 100644 --- a/locales/Japanese.json +++ b/locales/Japanese.json @@ -619,7 +619,6 @@ "no_previous_track": "前のトラックがありません。", "playing_previous": "前のトラックを再生中です。", "previous_footer": "{displayName} によって前のトラックを再生中です", - "rewind_limit": "曲の長さ以上に巻き戻すことはできません。", "rewinded": "音楽を巻き戻しました。", "rewind_footer": "{displayName} によって巻き戻されました", "forward_limit": "曲の長さ以上に早送りすることはできません。", diff --git a/locales/Korean.json b/locales/Korean.json index c448eb89c..647b2c4c4 100644 --- a/locales/Korean.json +++ b/locales/Korean.json @@ -33,7 +33,7 @@ "options": { "command": "특정 명령어의 정보를 얻고 싶다면 여기에 입력해주세요" }, - "content": "안녕하세요! 저는 [Lavamusic](https://github.com/appujet/lavamusic)과 Discord로 만들어진 음악 봇이에요. `{prefix}help <명령어>`를 사용하여 그 명령어의 자세한 정보를 얻을 수 있어요.", + "content": "안녕하세요! 저는 [Lavamusic](https://github.com/appujet/lavamusic)와 Discord.js로 만들어진 음악 봇, {bot}이에요. 명령어에 대한 자세한 정보를 얻고 싶다면 `{prefix}help <명령어>`를 사용해보세요.", "title": "도움말 메뉴", "not_found": "`{cmdName}` 명령어는 존재하지 않아요.", "help_cmd": "**설명:** {description}\n**사용 방법:** {usage}\n**예시:** {examples}\n**줄인 명령어:** {aliases}\n**카테고리:** {category}\n**쿨다운:** {cooldown}초\n**필요한 권한:** {premUser}\n**봇에게 필요한 권한:** {premBot}\n**개발자 전용:** {dev}\n**빗금 명령어 사용 가능:** {slash}\n**인수 필요:** {args}\n**노래 재생 중에만 사용 가능:** {player}\n**DJ만 사용 가능:** {dj}\n**DJ에게 필요한 권한:** {djPerm}\n**음성 채널 접속 중에만 사용 가능:** {voice}", @@ -41,7 +41,7 @@ }, "botinfo": { "description": "봇에 대한 정보를 표시해요", - "content": "봇 정보:\n- **운영 체제**: {osInfo}\n- **업타임**: {osUptime}\n- **호스트 이름**: {osHostname}\n- **CPU 아키텍처**: {cpuInfo}\n- **CPU 사용량**: {cpuUsed}%\n- **메모리 사용량**: {memUsed}MB / {memTotal}GB\n- **Node 버전**: {nodeVersion}\n- **Discord 버전**: {discordJsVersion}\n- 서버 {guilds}개, 채널 {channels}개, 유저 {users}명\n- **명령어 수**: {commands}개" + "content": "봇 정보:\n- **운영 체제**: {osInfo}\n- **업타임**: {osUptime}\n- **호스트 이름**: {osHostname}\n- **CPU 아키텍처**: {cpuInfo}\n- **CPU 사용량**: {cpuUsed}%\n- **메모리 사용량**: {memUsed}MB / {memTotal}GB\n- **Node 버전**: {nodeVersion}\n- **Discord.js 버전**: {discordJsVersion}\n- 서버 {guilds}개, 채널 {channels}개, 유저 {users}명\n- **명령어 수**: {commands}개" }, "about": { "description": "봇에 대한 정보를 확인해요", @@ -132,12 +132,14 @@ }, "bassboost": { "description": "베이스부스트 필터를 토글해요", - "messages": { - "filter_enabled": "`✅` | 베이스부스트 필터가 `활성화되었어요`. \n**조심하세요, 너무 크게 들으면 귀가 나갈 수도 있어요!**", - "filter_disabled": "`✅` | 베이스부스트 필터가 `비활성화되었어요`." - }, "options": { - "level": "The bassboost level you want to set" + "level": "설정할 베이스부스트 레벨" + }, + "messages": { + "high": "`✅` | High 베이스부스트 필터가 `활성화되었어요`.", + "low": "`✅` | Low 베이스부스트 필터가 `활성화되었어요`.", + "medium": "`✅` | Medium 베이스부스트 필터가 `활성화되었어요`.", + "off": "`✅` | 베이스부스트 필터가 `비활성화되었어요`." } }, "distorsion": { @@ -267,9 +269,9 @@ "looping_off": "**반복이 꺼졌어요.**" }, "lyrics": { - "description": "현재 재생중인 노래의 가사를 가져와요", - "lyrics_track": "### [{trackTitle}]({trackUrl})의 노래 가사\n**`{lyrics}`**", - "searching": "`🔍` **{trackTitle}**의 가사 검색 중...", + "description": "현재 재생중인 노래의 가사를 확인해요", + "lyrics_track": "### [{trackTitle}]({trackUrl}) 노래 가사\n**`{lyrics}`**", + "searching": "`🔍` **{trackTitle}** 노래 가사 검색 중...", "errors": { "no_results": "가사를 찾지 못했어요.", "lyrics_error": "가사를 검색하는 도중 오류가 발생했어요." @@ -598,15 +600,15 @@ "cooldown": "`{command}` 명령어를 사용하려면 {time}초동안 기다려야 해요.", "no_mention_everyone": "이 명령어는 everyone나 here 멘션으로 사용할 수 없어요. 빗금 명령어로 사용해주세요.", "error": "오류: `{error}`", - "no_voice_channel_queue": "대기열에 노래를 추가하려면 음성 채널에 접속해주세요.", + "no_voice_channel_queue": "대기열에 노래를 추가하려면 음성 채널에 있어야 해요.", "no_permission_connect_speak": "<#{channel}>에서 연결/말하기 권한이 없어요.", "different_voice_channel_queue": "노래를 대기열에 추가하려면 <#{channel}> 채널에 있어야 해요.", "vote_button": "투표하기", "vote_message": "잠깐! 이 명령어를 사용하려면 top.gg에서 투표해야 해요." }, "setupButton": { - "no_voice_channel_button": "이 버튼을 사용하려면 음성 채널에 접속해주세요.", - "different_voice_channel_button": "이 버튼을 사용하려면 {channel} 채널에 접속해주세요.", + "no_voice_channel_button": "이 버튼을 사용하려면 음성 채널에 있어야 해요.", + "different_voice_channel_button": "이 버튼을 사용하려면 {channel} 채널에 있어야 해요.", "now_playing": "재생 중", "live": "라이브", "requested_by": "요청자: <@{requester}>", @@ -623,16 +625,15 @@ "stopped": "노래를 정지했어요.", "stopped_footer": "{displayName}님에 의해 정지됨", "nothing_playing": "재생 중인 노래 없음", - "loop_set": "{loop} 반복으로 설정했어요.", - "loop_footer": "{displayName}님에 의해 {loop} 반복으로 설정됨", + "loop_set": "반복 모드가 {loop}로 변경되었어요.", + "loop_footer": "{displayName}님에 의해 반복 모드가 {loop}로 설정됨", "shuffled": "노래를 섞었어요.", "no_previous_track": "이전 노래가 없어요.", "playing_previous": "이전 노래를 재생할게요.", "previous_footer": "{displayName}님에 의해 이전 노래 재생 중", - "rewind_limit": "현재 노래 길이보다 더 되감기할 수 없어요.", "rewinded": "노래를 되감기했어요.", "rewind_footer": "{displayName}님에 의해 노래 되감기됨", - "forward_limit": "현재 노래 길이보다 더 빨리감기할 수 없어요.", + "forward_limit": "더 이상 빨리감기할 수 없어요.", "forwarded": "노래를 빨리감기했어요.", "forward_footer": "{displayName}님에 의해 빨리감기됨", "button_not_available": "이 버튼은 사용할 수 없어요.", diff --git a/locales/Norwegian.json b/locales/Norwegian.json index 163d69550..83bf4d283 100644 --- a/locales/Norwegian.json +++ b/locales/Norwegian.json @@ -619,7 +619,6 @@ "no_previous_track": "Det er ingen forrige spor.", "playing_previous": "Spiller av forrige spor.", "previous_footer": "Spiller av forrige spor av {displayName}", - "rewind_limit": "Du kan ikke spole musikken tilbake mer enn lengden på sangen.", "rewinded": "Spolet musikken tilbake.", "rewind_footer": "Spolet tilbake av {displayName}", "forward_limit": "Du kan ikke spole musikken fremover mer enn lengden på sangen.", diff --git a/locales/Polish.json b/locales/Polish.json index 1c9646971..8aab02e92 100644 --- a/locales/Polish.json +++ b/locales/Polish.json @@ -619,7 +619,6 @@ "no_previous_track": "Nie ma żadnego poprzedniego utworu.", "playing_previous": "Odtwarzanie poprzedniego utworu", "previous_footer": "Utwór ostatnio odtwarzany przez {displayName}", - "rewind_limit": "Nie można cofnąć muzyki o więcej niż długość utworu.", "rewinded": "Cofnięto muzykę.", "rewind_footer": "Cofnięty przez {displayName}", "forward_limit": "Nie można przesunąć muzyki do przodu o więcej niż długość utworu.", diff --git a/locales/PortuguesePT.json b/locales/PortuguesePT.json index 55515ed2f..cb696ad46 100644 --- a/locales/PortuguesePT.json +++ b/locales/PortuguesePT.json @@ -613,7 +613,6 @@ "no_previous_track": "Não há nenhuma faixa anterior.", "playing_previous": "A tocar a faixa anterior.", "previous_footer": "A tocar a faixa anterior por {displayName}", - "rewind_limit": "Não pode retroceder a música mais do que o comprimento da música.", "rewinded": "A música foi retrocedida.", "rewind_footer": "Retrocedido por {displayName}", "forward_limit": "Não pode avançar a música mais do que o comprimento da música.", diff --git a/locales/Russian.json b/locales/Russian.json index a1d640041..2ec89a062 100644 --- a/locales/Russian.json +++ b/locales/Russian.json @@ -619,7 +619,6 @@ "no_previous_track": "Нет предыдущей дорожки.", "playing_previous": "Проигрывается предыдущая дорожка.", "previous_footer": "Проигрывается предыдущая дорожка {displayName}", - "rewind_limit": "Вы не можете перемотать музыку больше, чем длина песни.", "rewinded": "Музыка перемотана назад.", "rewind_footer": "Перемотано назад {displayName}", "forward_limit": "Вы не можете перемотать музыку вперед больше, чем длина песни.", diff --git a/locales/SpanishES.json b/locales/SpanishES.json index 567356249..895f00fb8 100644 --- a/locales/SpanishES.json +++ b/locales/SpanishES.json @@ -619,7 +619,6 @@ "no_previous_track": "No hay ninguna pista anterior.", "playing_previous": "Reproduciendo la pista anterior.", "previous_footer": "Reproduciendo la pista anterior por {displayName}", - "rewind_limit": "No puedes rebobinar la música más de la duración de la canción.", "rewinded": "Se ha rebobinado la música.", "rewind_footer": "Rebobinado por {displayName}", "forward_limit": "No puedes avanzar la música más de la duración de la canción.", diff --git a/locales/Vietnamese.json b/locales/Vietnamese.json index 1db3602ca..dbc0884b7 100644 --- a/locales/Vietnamese.json +++ b/locales/Vietnamese.json @@ -619,7 +619,6 @@ "no_previous_track": "Không có bài hát trước.", "playing_previous": "Đang phát bài hát trước.", "previous_footer": "Đang phát bài hát trước bởi {displayName}", - "rewind_limit": "Bạn không thể quay lại nhạc nhiều hơn độ dài của bài hát.", "rewinded": "Đã quay lại nhạc.", "rewind_footer": "Quay lại bởi {displayName}", "forward_limit": "Bạn không thể tiến tới nhạc nhiều hơn độ dài của bài hát.", diff --git a/run.bat b/run.bat index 5b7f8f7b8..a2fe91338 100644 --- a/run.bat +++ b/run.bat @@ -1,19 +1,2 @@ @echo off -setlocal enabledelayedexpansion - -:: Check if pnpm is installed - -where pnpm >nul 2>nul -if %errorlevel% equ 0 ( - set package_manager=pnpm -) else ( - set package_manager=npm -) - -:: Check if node_modules exists -if not exist node_modules ( - %package_manager% install -) - -:: start the project -%package_manager% run start \ No newline at end of file +npm run start diff --git a/src/LavaClient.ts b/src/LavaClient.ts index b173f4665..4428a2117 100644 --- a/src/LavaClient.ts +++ b/src/LavaClient.ts @@ -1,6 +1,6 @@ import { type ClientOptions, GatewayIntentBits } from "discord.js"; -import Lavamusic from "./structures/Lavamusic"; import { env } from "./env"; +import Lavamusic from "./structures/Lavamusic"; const { GuildMembers, MessageContent, GuildVoiceStates, GuildMessages, Guilds, GuildMessageTyping } = GatewayIntentBits; diff --git a/src/commands/config/Setup.ts b/src/commands/config/Setup.ts index c17ca182f..d93337ba6 100644 --- a/src/commands/config/Setup.ts +++ b/src/commands/config/Setup.ts @@ -92,10 +92,9 @@ export default class Setup extends Command { }); const player = this.client.manager.getPlayer(ctx.guild!.id); const image = this.client.config.links.img; - const desc = - player && player.queue.current - ? `[${player.queue.current.info.title}](${player.queue.current.info.uri})` - : ctx.locale("player.setupStart.nothing_playing"); + const desc = player?.queue.current + ? `[${player.queue.current.info.title}](${player.queue.current.info.uri})` + : ctx.locale("player.setupStart.nothing_playing"); embed.setDescription(desc).setImage(image); await textChannel .send({ diff --git a/src/commands/dev/GuildList.ts b/src/commands/dev/GuildList.ts index cb92967b7..4dfb2e58f 100644 --- a/src/commands/dev/GuildList.ts +++ b/src/commands/dev/GuildList.ts @@ -33,7 +33,7 @@ export default class GuildList extends Command { const guilds = await client.shard.broadcastEval((c) => c.guilds.cache.map((guild) => ({ name: guild.name, id: guild.id }))); const allGuilds = guilds.reduce((acc, val) => acc.concat(val), []); - const guildList = allGuilds.map((guild) => `- **${guild.name}** - (${guild.id})`); + const guildList = allGuilds.map((guild) => `- **${guild.name}** - ${guild.id}`); const chunks = client.utils.chunk(guildList, 10) || [[]]; const pages = chunks.map((chunk, index) => { return this.client diff --git a/src/events/client/Ready.ts b/src/events/client/Ready.ts index 9e42ab8dd..178e2f510 100644 --- a/src/events/client/Ready.ts +++ b/src/events/client/Ready.ts @@ -1,6 +1,6 @@ import { AutoPoster } from "topgg-autoposter"; -import { Event, type Lavamusic } from "../../structures/index.js"; import { env } from "../../env.js"; +import { Event, type Lavamusic } from "../../structures/index.js"; export default class Ready extends Event { constructor(client: Lavamusic, file: string) { diff --git a/src/events/client/SetupButtons.ts b/src/events/client/SetupButtons.ts index 3b7d6429e..f1766d280 100644 --- a/src/events/client/SetupButtons.ts +++ b/src/events/client/SetupButtons.ts @@ -42,9 +42,7 @@ export default class SetupButtons extends Event { message = await interaction.channel.messages.fetch(data.messageId, { cache: true, }); - } catch (_e) { - /* empty */ - } + } catch (_e) {} const iconUrl = this.client.config.icons[sourceName] || this.client.user.displayAvatarURL({ extension: "png" }); const embed = this.client @@ -81,15 +79,52 @@ export default class SetupButtons extends Event { }); }; switch (interaction.customId) { - case "LOW_VOL_BUT": - await handleVolumeChange(-10); + case "PREV_BUT": + if (!player.queue.previous) { + return await buttonReply(interaction, T(locale, "event.setupButton.no_previous_track"), this.client.color.main); + } + player.play({ + track: player.queue.previous[0], + }); + await buttonReply(interaction, T(locale, "event.setupButton.playing_previous"), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, "event.setupButton.previous_footer", { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); break; - case "HIGH_VOL_BUT": - await handleVolumeChange(10); + case "REWIND_BUT": { + const time = player.position - 10000; + if (time < 0) { + player.seek(0); + } else { + player.seek(time); + } + await buttonReply(interaction, T(locale, "event.setupButton.rewinded"), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, "event.setupButton.rewind_footer", { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); break; + } case "PAUSE_BUT": { const name = player.paused ? T(locale, "event.setupButton.resumed") : T(locale, "event.setupButton.paused"); - player.pause(); + if (player.paused) { + player.resume(); + } else { + player.pause(); + } await buttonReply(interaction, T(locale, "event.setupButton.pause_resume", { name }), this.client.color.main); await message.edit({ embeds: [ @@ -105,6 +140,25 @@ export default class SetupButtons extends Event { }); break; } + case "FORWARD_BUT": { + const time = player.position + 10000; + if (time > player.queue.current.info.duration) { + return await buttonReply(interaction, T(locale, "event.setupButton.forward_limit"), this.client.color.main); + } + player.seek(time); + await buttonReply(interaction, T(locale, "event.setupButton.forwarded"), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, "event.setupButton.forward_footer", { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + break; + } case "SKIP_BUT": if (player.queue.tracks.length === 0) { return await buttonReply(interaction, T(locale, "event.setupButton.no_music_to_skip"), this.client.color.main); @@ -122,28 +176,8 @@ export default class SetupButtons extends Event { ], }); break; - case "STOP_BUT": - player.stopPlaying(true, false); - await buttonReply(interaction, T(locale, "event.setupButton.stopped"), this.client.color.main); - await message.edit({ - embeds: [ - embed - .setFooter({ - text: T(locale, "event.setupButton.stopped_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }) - .setDescription(T(locale, "event.setupButton.nothing_playing")) - .setImage(this.client.config.links.img) - .setAuthor({ - name: this.client.user.username, - iconURL: this.client.user.displayAvatarURL({ - extension: "png", - }), - }), - ], - }); + case "LOW_VOL_BUT": + await handleVolumeChange(-10); break; case "LOOP_BUT": { const loopOptions: Array<"off" | "queue" | "track"> = ["off", "queue", "track"]; @@ -169,69 +203,35 @@ export default class SetupButtons extends Event { }); break; } - case "SHUFFLE_BUT": - player.queue.shuffle(); - await buttonReply(interaction, T(locale, "event.setupButton.shuffled"), this.client.color.main); - break; - case "PREV_BUT": - if (!player.queue.previous) { - return await buttonReply(interaction, T(locale, "event.setupButton.no_previous_track"), this.client.color.main); - } - player.play({ - track: player.queue.previous[0], - }); - await buttonReply(interaction, T(locale, "event.setupButton.playing_previous"), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.previous_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - break; - case "REWIND_BUT": { - const time = player.position - 10000; - if (time < 0) { - return await buttonReply(interaction, T(locale, "event.setupButton.rewind_limit"), this.client.color.main); - } - player.seek(time); - await buttonReply(interaction, T(locale, "event.setupButton.rewinded"), this.client.color.main); + case "STOP_BUT": + player.stopPlaying(true, false); + await buttonReply(interaction, T(locale, "event.setupButton.stopped"), this.client.color.main); await message.edit({ embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.rewind_footer", { - displayName: interaction.member.displayName, + embed + .setFooter({ + text: T(locale, "event.setupButton.stopped_footer", { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }) + .setDescription(T(locale, "event.setupButton.nothing_playing")) + .setImage(this.client.config.links.img) + .setAuthor({ + name: this.client.user.username, + iconURL: this.client.user.displayAvatarURL({ + extension: "png", + }), }), - iconURL: interaction.member.displayAvatarURL({}), - }), ], }); break; - } - case "FORWARD_BUT": { - const time = player.position + 10000; - if (time > player.queue.current.info.duration) { - return await buttonReply(interaction, T(locale, "event.setupButton.forward_limit"), this.client.color.main); - } - player.seek(time); - await buttonReply(interaction, T(locale, "event.setupButton.forwarded"), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.forward_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); + case "SHUFFLE_BUT": + player.queue.shuffle(); + await buttonReply(interaction, T(locale, "event.setupButton.shuffled"), this.client.color.main); break; - } - default: - await buttonReply(interaction, T(locale, "event.setupButton.button_not_available"), this.client.color.main); + case "HIGH_VOL_BUT": + await handleVolumeChange(10); break; } } diff --git a/src/events/player/QueueEnd.ts b/src/events/player/QueueEnd.ts index c25e808f2..24e26dbba 100644 --- a/src/events/player/QueueEnd.ts +++ b/src/events/player/QueueEnd.ts @@ -2,7 +2,6 @@ import type { TextChannel } from "discord.js"; import type { Player, Track, TrackStartEvent } from "lavalink-client"; import { Event, type Lavamusic } from "../../structures/index"; -// queueEnd export default class QueueEnd extends Event { constructor(client: Lavamusic, file: string) { super(client, file, { diff --git a/src/events/player/TrackStart.ts b/src/events/player/TrackStart.ts index 77550d8ee..2542e41b6 100644 --- a/src/events/player/TrackStart.ts +++ b/src/events/player/TrackStart.ts @@ -1,6 +1,3 @@ -import type { Player, Track, TrackStartEvent } from "lavalink-client"; -import { Event, type Lavamusic } from "../../structures/index"; -import { T } from "../../structures/I18n"; import { ActionRowBuilder, ButtonBuilder, @@ -15,6 +12,9 @@ import { type TextChannel, type UserSelectMenuInteraction, } from "discord.js"; +import type { Player, Track, TrackStartEvent } from "lavalink-client"; +import { T } from "../../structures/I18n"; +import { Event, type Lavamusic } from "../../structures/index"; import type { Requester } from "../../types"; import { trackStart } from "../../utils/SetupSystem"; diff --git a/src/index.ts b/src/index.ts index 6b3e189fd..737885c4f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import * as fs from "node:fs"; +import { shardStart } from "./shard"; import Logger from "./structures/Logger"; import { ThemeSelector } from "./utils/ThemeSelector"; -import { shardStart } from "./shard"; const logger = new Logger(); diff --git a/src/plugin/plugins/keepAlive.ts b/src/plugin/plugins/keepAlive.ts index 4087c0ef2..982d00df1 100644 --- a/src/plugin/plugins/keepAlive.ts +++ b/src/plugin/plugins/keepAlive.ts @@ -1,7 +1,7 @@ import http from "node:http"; +import { env } from "../../env"; import type { Lavamusic } from "../../structures/index"; import type { BotPlugin } from "../index"; -import { env } from "../../env"; const keepAlive: BotPlugin = { name: "KeepAlive Plugin", diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index b11a3d0b8..c9095d9f2 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -19,8 +19,8 @@ export class Utils { user.setPresence({ activities: [ { - name: player && player.queue?.current ? `🎶 | ${player.queue?.current.info.title}` : client.env.BOT_ACTIVITY, - type: player && player?.queue?.current ? ActivityType.Listening : client.env.BOT_ACTIVITY_TYPE, + name: player?.queue?.current ? `🎶 | ${player.queue?.current.info.title}` : client.env.BOT_ACTIVITY, + type: player?.queue?.current ? ActivityType.Listening : client.env.BOT_ACTIVITY_TYPE, }, ], status: client.env.BOT_STATUS as any, diff --git a/tsconfig.json b/tsconfig.json index eb9ee500e..42b245339 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,11 @@ { "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { - "lib": ["ESNext", "DOM"], + "lib": ["dom", "esnext"], "rootDir": "./src", "outDir": "./dist", - "target": "ESNext", - "module": "CommonJS", + "target": "esnext", + "module": "commonjs", "declaration": true, "sourceMap": true, "newLine": "crlf", From 6cadd8278580bb44161ea24f4825b8e4bf979dd8 Mon Sep 17 00:00:00 2001 From: hwangsihu Date: Wed, 18 Sep 2024 17:53:14 +0900 Subject: [PATCH 19/32] Remove LavaLyrics Plugin --- Lavalink/example.application.yml | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/Lavalink/example.application.yml b/Lavalink/example.application.yml index 244512cd4..790624a2e 100644 --- a/Lavalink/example.application.yml +++ b/Lavalink/example.application.yml @@ -20,13 +20,6 @@ plugins: pixeldrain: true # pixeldrain.com jiosaavn: apiURL: "https://saavn.dev/api" # The API URL for JioSaavn - lavalyrics: - # sources is used to sort the different lyrics sources by priority (from highest to lowest) - sources: - - spotify - - youtube - - deezer - - yandexMusic lavasrc: providers: # Custom providers for track loading. This is the default # - "dzisrc:%ISRC%" # Deezer ISRC provider @@ -43,11 +36,11 @@ plugins: youtube: true # Enable YouTube search source (https://github.com/topi314/LavaSearch) vkmusic: false # Enable Vk Music source lyrics-sources: - spotify: true # Enable Spotify lyrics source - deezer: true # Enable Deezer lyrics source - youtube: true # Enable YouTube lyrics source - yandexmusic: true # Enable Yandex Music lyrics source - vkmusic: true # Enable Vk Music lyrics source + spotify: false # Enable Spotify lyrics source + deezer: false # Enable Deezer lyrics source + youtube: false # Enable YouTube lyrics source + yandexmusic: false # Enable Yandex Music lyrics source + vkmusic: false # Enable Vk Music lyrics source spotify: clientId: "your client id" clientSecret: "your client secret" @@ -115,8 +108,6 @@ lavalink: repository: "https://jitpack.io" - dependency: "com.dunctebot:skybot-lavalink-plugin:1.7.0" snapshot: false # set to true if you want to use snapshot builds - - dependency: "com.github.topi314.lavalyrics:lavalyrics-plugin:1.0.0" - snapshot: false # set to true if you want to use snapshot builds (see below) - dependency: "com.github.topi314.lavasearch:lavasearch-plugin:1.0.0" snapshot: false # set to true if you want to use snapshot builds - dependency: "com.github.topi314.lavasrc:lavasrc-plugin:4.2.0" From eef2786ec3be650960c8588d614920a9c97f81d5 Mon Sep 17 00:00:00 2001 From: hwangsihu Date: Wed, 18 Sep 2024 18:41:32 +0900 Subject: [PATCH 20/32] Update prisma --- prisma/example.mongodb.schema.prisma | 108 ++++++++---------------- prisma/example.postgresql.schema.prisma | 76 ++++------------- 2 files changed, 50 insertions(+), 134 deletions(-) diff --git a/prisma/example.mongodb.schema.prisma b/prisma/example.mongodb.schema.prisma index bc9a024ae..f8e3a20dc 100644 --- a/prisma/example.mongodb.schema.prisma +++ b/prisma/example.mongodb.schema.prisma @@ -14,96 +14,54 @@ datasource db { url = env("DATABASE_URL") } +model Bot { + botId String @unique + totalPlaySong Int +} + model Guild { - guildId String @id @map("_id") - prefix String - language String? - stay Stay? - dj Dj? - roles Role[] - setup Setup? + guildId String @id + prefix String + language String? @default("EnglishUS") + stay Stay? + dj Dj? + roles Role[] + setup Setup? } model Stay { - guildId String @id @map("_id") - textId String - voiceId String - Guild Guild @relation(fields: [guildId], references: [guildId]) + guildId String @id + textId String + voiceId String + Guild Guild @relation(fields: [guildId], references: [guildId]) } model Dj { - guildId String @id @map("_id") - mode Boolean - Guild Guild @relation(fields: [guildId], references: [guildId]) + guildId String @id + mode Boolean + Guild Guild @relation(fields: [guildId], references: [guildId]) } model Role { - guildId String @id @map("_id") - roleId String - Guild Guild @relation(fields: [guildId], references: [guildId]) + guildId String + roleId String + Guild Guild @relation(fields: [guildId], references: [guildId]) - @@unique([guildId, roleId]) + @@unique([guildId, roleId]) } model Playlist { - id String @id @default(cuid()) @map("_id") - userId String - name String - songs Song[] - - @@unique([userId, name]) -} - -model Song { - id String @id @map("_id") @default(cuid()) - track String - playlistId String - playlist Playlist @relation(fields: [playlistId], references: [id]) + id String @id @default(uuid()) + userId String + name String + tracks String? // Store the array of encoded tracks as a JSON string - @@unique([track, playlistId]) + @@unique([userId, name]) } model Setup { - guildId String @id @map("_id") - textId String - messageId String - Guild Guild @relation(fields: [guildId], references: [guildId]) -} - -model Premium { - userId String @id @map("_id") - guildId String -} - -enum Languages { - EnglishUS - EnglishGB - German - Bulgarian - ChineseCN - ChineseTW - Croatian - Czech - Danish - Dutch - Finnish - French - Greek - Hindi - Hungarian - Italian - Japanese - Korean - Lithuanian - Norwegian - Polish - PortugueseBR - Romanian - Russian - SpanishES - Swedish - Thai - Turkish - Ukrainian - Vietnamese -} + guildId String @id + textId String + messageId String + Guild Guild @relation(fields: [guildId], references: [guildId]) +} \ No newline at end of file diff --git a/prisma/example.postgresql.schema.prisma b/prisma/example.postgresql.schema.prisma index 3d131913d..a2f5f8307 100644 --- a/prisma/example.postgresql.schema.prisma +++ b/prisma/example.postgresql.schema.prisma @@ -14,14 +14,19 @@ datasource db { url = env("DATABASE_URL") } +model Bot { + botId String @unique + totalPlaySong Int +} + model Guild { - guildId String @id - prefix String - language String? - stay Stay? - dj Dj? - roles Role[] - setup Setup? + guildId String @id + prefix String + language String? @default("EnglishUS") + stay Stay? + dj Dj? + roles Role[] + setup Setup? } model Stay { @@ -46,64 +51,17 @@ model Role { } model Playlist { - id String @id @default(uuid()) - userId String - name String - songs Song[] + id String @id @default(uuid()) + userId String + name String + tracks String? // Store the array of encoded tracks as a JSON string @@unique([userId, name]) } -model Song { - id String @id @default(uuid()) - track String - playlistId String - playlist Playlist @relation(fields: [playlistId], references: [id]) - - @@unique([track, playlistId]) -} - model Setup { guildId String @id textId String messageId String Guild Guild @relation(fields: [guildId], references: [guildId]) -} - -model Premium { - userId String @id - guildId String -} - -enum Languages { - EnglishUS - EnglishGB - German - Bulgarian - ChineseCN - ChineseTW - Croatian - Czech - Danish - Dutch - Finnish - French - Greek - Hindi - Hungarian - Italian - Japanese - Korean - Lithuanian - Norwegian - Polish - PortugueseBR - Romanian - Russian - SpanishES - Swedish - Thai - Turkish - Ukrainian - Vietnamese -} +} \ No newline at end of file From b6f1bb7d51fa352ddbe22acb701cbb731f4ae2c8 Mon Sep 17 00:00:00 2001 From: hwangsihu Date: Wed, 18 Sep 2024 23:25:29 +0900 Subject: [PATCH 21/32] Lavalink update --- Lavalink/example.application.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Lavalink/example.application.yml b/Lavalink/example.application.yml index 790624a2e..6ca3ad5d6 100644 --- a/Lavalink/example.application.yml +++ b/Lavalink/example.application.yml @@ -34,13 +34,11 @@ plugins: yandexmusic: false # Enable Yandex Music source flowerytts: false # Enable Flowery TTS source youtube: true # Enable YouTube search source (https://github.com/topi314/LavaSearch) - vkmusic: false # Enable Vk Music source lyrics-sources: spotify: false # Enable Spotify lyrics source deezer: false # Enable Deezer lyrics source youtube: false # Enable YouTube lyrics source yandexmusic: false # Enable Yandex Music lyrics source - vkmusic: false # Enable Vk Music lyrics source spotify: clientId: "your client id" clientSecret: "your client secret" @@ -79,11 +77,6 @@ plugins: audioFormat: "mp3" # supported formats are: mp3, ogg_opus, ogg_vorbis, aac, wav, and flac. Default format is mp3 youtube: countryCode: "US" # the country code you want to use for searching lyrics via ISRC. See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 - vkmusic: - userToken: "your user token" # This token is needed for authorization in the api. Guide: https://github.com/topi314/LavaSrc#vk-music - playlistLoadLimit: 1 # The number of pages at 50 tracks each - artistLoadLimit: 1 # The number of pages at 10 tracks each - recommendationsLoadLimit: 10 # Number of tracks youtube: enabled: true # Whether this source can be used. allowSearch: true # Whether "ytsearch:" and "ytmsearch:" can be used. From 64a5c06e252e16ffa1831dfed9ee679d894d70d7 Mon Sep 17 00:00:00 2001 From: hwangsihu Date: Wed, 18 Sep 2024 23:28:03 +0900 Subject: [PATCH 22/32] Developer Bypass is back --- src/events/client/MessageCreate.ts | 267 +++++++++++++++-------------- 1 file changed, 134 insertions(+), 133 deletions(-) diff --git a/src/events/client/MessageCreate.ts b/src/events/client/MessageCreate.ts index faa336011..7e28fc4b1 100644 --- a/src/events/client/MessageCreate.ts +++ b/src/events/client/MessageCreate.ts @@ -55,6 +55,8 @@ export default class MessageCreate extends Event { ctx.guildLocale = locale; const clientMember = message.guild.members.resolve(this.client.user); + const isDev = this.client.env.OWNER_IDS?.includes(message.author.id); + if (!(message.inGuild() && message.channel.permissionsFor(clientMember)?.has(PermissionFlagsBits.ViewChannel))) return; if ( @@ -86,172 +88,171 @@ export default class MessageCreate extends Event { } if (command.permissions?.user) { - if (!(message.member as GuildMember).permissions.has(command.permissions.user)) { + if (!(isDev || (message.member as GuildMember).permissions.has(command.permissions.user))) { return await message.reply({ content: T(locale, "event.message.no_user_permission"), }); } + } - if (command.permissions?.dev && this.client.env.OWNER_IDS) { - const isDev = this.client.env.OWNER_IDS.includes(message.author.id); - if (!isDev) return; - } + if (command.permissions?.dev && this.client.env.OWNER_IDS) { + if (!isDev) return; } + } - if (command.vote && this.client.env.TOPGG) { - const voted = await this.client.topGG.hasVoted(message.author.id); - if (!voted) { - const voteBtn = new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setLabel(T(locale, "event.message.vote_button")) - .setURL(`https://top.gg/bot/${this.client.user.id}/vote`) - .setStyle(ButtonStyle.Link), - ); + if (command.vote && this.client.env.TOPGG) { + const voted = await this.client.topGG.hasVoted(message.author.id); + if (!(isDev || voted)) { + const voteBtn = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setLabel(T(locale, "event.message.vote_button")) + .setURL(`https://top.gg/bot/${this.client.user.id}/vote`) + .setStyle(ButtonStyle.Link), + ); + + return await message.reply({ + content: T(locale, "event.message.vote_message"), + components: [voteBtn], + }); + } + } + if (command.player) { + if (command.player.voice) { + if (!(message.member as GuildMember).voice.channel) { return await message.reply({ - content: T(locale, "event.message.vote_message"), - components: [voteBtn], + content: T(locale, "event.message.no_voice_channel", { command: command.name }), }); } - } - if (command.player) { - if (command.player.voice) { - if (!(message.member as GuildMember).voice.channel) { - return await message.reply({ - content: T(locale, "event.message.no_voice_channel", { command: command.name }), - }); - } + if (!clientMember.permissions.has(PermissionFlagsBits.Connect)) { + return await message.reply({ + content: T(locale, "event.message.no_connect_permission", { command: command.name }), + }); + } - if (!clientMember.permissions.has(PermissionFlagsBits.Connect)) { - return await message.reply({ - content: T(locale, "event.message.no_connect_permission", { command: command.name }), - }); - } + if (!clientMember.permissions.has(PermissionFlagsBits.Speak)) { + return await message.reply({ + content: T(locale, "event.message.no_speak_permission", { command: command.name }), + }); + } - if (!clientMember.permissions.has(PermissionFlagsBits.Speak)) { - return await message.reply({ - content: T(locale, "event.message.no_speak_permission", { command: command.name }), - }); - } + if ( + (message.member as GuildMember).voice.channel.type === ChannelType.GuildStageVoice && + !clientMember.permissions.has(PermissionFlagsBits.RequestToSpeak) + ) { + return await message.reply({ + content: T(locale, "event.message.no_request_to_speak", { command: command.name }), + }); + } - if ( - (message.member as GuildMember).voice.channel.type === ChannelType.GuildStageVoice && - !clientMember.permissions.has(PermissionFlagsBits.RequestToSpeak) - ) { - return await message.reply({ - content: T(locale, "event.message.no_request_to_speak", { command: command.name }), - }); - } + if (clientMember.voice.channel && clientMember.voice.channelId !== (message.member as GuildMember).voice.channelId) { + return await message.reply({ + content: T(locale, "event.message.different_voice_channel", { + channel: `<#${clientMember.voice.channelId}>`, + command: command.name, + }), + }); + } + } - if (clientMember.voice.channel && clientMember.voice.channelId !== (message.member as GuildMember).voice.channelId) { - return await message.reply({ - content: T(locale, "event.message.different_voice_channel", { - channel: `<#${clientMember.voice.channelId}>`, - command: command.name, - }), - }); - } + if (command.player.active) { + const queue = this.client.manager.getPlayer(message.guildId); + if (!queue?.queue.current) { + return await message.reply({ + content: T(locale, "event.message.no_music_playing"), + }); } + } - if (command.player.active) { - const queue = this.client.manager.getPlayer(message.guildId); - if (!queue?.queue.current) { + if (command.player.dj) { + const dj = await this.client.db.getDj(message.guildId); + if (dj?.mode) { + const djRole = await this.client.db.getRoles(message.guildId); + if (!djRole) { return await message.reply({ - content: T(locale, "event.message.no_music_playing"), + content: T(locale, "event.message.no_dj_role"), }); } - } - if (command.player.dj) { - const dj = await this.client.db.getDj(message.guildId); - if (dj?.mode) { - const djRole = await this.client.db.getRoles(message.guildId); - if (!djRole) { - return await message.reply({ - content: T(locale, "event.message.no_dj_role"), - }); - } - - const hasDJRole = (message.member as GuildMember).roles.cache.some((role) => - djRole.map((r) => r.roleId).includes(role.id), - ); - if (!(hasDJRole && !(message.member as GuildMember).permissions.has(PermissionFlagsBits.ManageGuild))) { - return await message.reply({ - content: T(locale, "event.message.no_dj_permission"), - }); - } + const hasDJRole = (message.member as GuildMember).roles.cache.some((role) => + djRole.map((r) => r.roleId).includes(role.id), + ); + if (!(isDev || (hasDJRole && !(message.member as GuildMember).permissions.has(PermissionFlagsBits.ManageGuild)))) { + return await message.reply({ + content: T(locale, "event.message.no_dj_permission"), + }); } } } + } - if (command.args && args.length === 0) { - const embed = this.client - .embed() - .setColor(this.client.color.red) - .setTitle(T(locale, "event.message.missing_arguments")) - .setDescription( - T(locale, "event.message.missing_arguments_description", { - command: command.name, - examples: command.description.examples ? command.description.examples.join("\n") : "None", - }), - ) - .setFooter({ text: T(locale, "event.message.syntax_footer") }); - await message.reply({ embeds: [embed] }); - return; - } - - if (!this.client.cooldown.has(cmd)) { - this.client.cooldown.set(cmd, new Collection()); - } - const now = Date.now(); - const timestamps = this.client.cooldown.get(cmd)!; - const cooldownAmount = (command.cooldown || 5) * 1000; + if (command.args && args.length === 0) { + const embed = this.client + .embed() + .setColor(this.client.color.red) + .setTitle(T(locale, "event.message.missing_arguments")) + .setDescription( + T(locale, "event.message.missing_arguments_description", { + command: command.name, + examples: command.description.examples ? command.description.examples.join("\n") : "None", + }), + ) + .setFooter({ text: T(locale, "event.message.syntax_footer") }); + await message.reply({ embeds: [embed] }); + return; + } - if (timestamps.has(message.author.id)) { - const expirationTime = timestamps.get(message.author.id)! + cooldownAmount; - const timeLeft = (expirationTime - now) / 1000; - if (now < expirationTime && timeLeft > 0.9) { - return await message.reply({ - content: T(locale, "event.message.cooldown", { time: timeLeft.toFixed(1), command: cmd }), - }); - } - timestamps.set(message.author.id, now); - setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); - } else { - timestamps.set(message.author.id, now); - setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); - } + if (!this.client.cooldown.has(cmd)) { + this.client.cooldown.set(cmd, new Collection()); + } + const now = Date.now(); + const timestamps = this.client.cooldown.get(cmd)!; + const cooldownAmount = (command.cooldown || 5) * 1000; - if (args.includes("@everyone") || args.includes("@here")) { + if (timestamps.has(message.author.id)) { + const expirationTime = timestamps.get(message.author.id)! + cooldownAmount; + const timeLeft = (expirationTime - now) / 1000; + if (now < expirationTime && timeLeft > 0.9) { return await message.reply({ - content: T(locale, "event.message.no_mention_everyone"), + content: T(locale, "event.message.cooldown", { time: timeLeft.toFixed(1), command: cmd }), }); } + timestamps.set(message.author.id, now); + setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); + } else { + timestamps.set(message.author.id, now); + setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); + } - try { - return command.run(this.client, ctx, ctx.args); - } catch (error) { - this.client.logger.error(error); - await message.reply({ - content: T(locale, "event.message.error", { error: error.message || "Unknown error" }), - }); - } finally { - const logs = this.client.channels.cache.get(this.client.env.LOG_COMMANDS_ID); - if (logs) { - const embed = new EmbedBuilder() - .setAuthor({ - name: "Prefix - Command Logs", - iconURL: this.client.user?.avatarURL({ size: 2048 }), - }) - .setColor(this.client.config.color.green) - .setDescription( - `**\`${command.name}\`** | Used By **${message.author.tag} \`${message.author.id}\`** From **${message.guild.name} \`${message.guild.id}\`**`, - ) - .setTimestamp(); + if (args.includes("@everyone") || args.includes("@here")) { + return await message.reply({ + content: T(locale, "event.message.no_mention_everyone"), + }); + } - await (logs as TextChannel).send({ embeds: [embed] }); - } + try { + return command.run(this.client, ctx, ctx.args); + } catch (error) { + this.client.logger.error(error); + await message.reply({ + content: T(locale, "event.message.error", { error: error.message || "Unknown error" }), + }); + } finally { + const logs = this.client.channels.cache.get(this.client.env.LOG_COMMANDS_ID); + if (logs) { + const embed = new EmbedBuilder() + .setAuthor({ + name: "Prefix - Command Logs", + iconURL: this.client.user?.avatarURL({ size: 2048 }), + }) + .setColor(this.client.config.color.green) + .setDescription( + `**\`${command.name}\`** | Used By **${message.author.tag} \`${message.author.id}\`** From **${message.guild.name} \`${message.guild.id}\`**`, + ) + .setTimestamp(); + + await (logs as TextChannel).send({ embeds: [embed] }); } } } From dea0f4f18667fb6a74defb887098719fd708e727 Mon Sep 17 00:00:00 2001 From: hwangsihu <129564966+hwangsihu@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:21:11 +0900 Subject: [PATCH 23/32] example.application.yml --- Lavalink/example.application.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lavalink/example.application.yml b/Lavalink/example.application.yml index 6ca3ad5d6..8a4317dd2 100644 --- a/Lavalink/example.application.yml +++ b/Lavalink/example.application.yml @@ -88,7 +88,11 @@ plugins: - MUSIC - WEB - ANDROID_TESTSUITE + - ANDROID_MUSIC - TVHTML5EMBEDDED + - ANDROID_LITE + - MEDIA_CONNECT + - IOS # This secure your lavalink is working and playing music if you used this two things below. #oauth: # enabled: true # IF YOU RUN YOUR LAVALINK CHECK YOUR CONSOLE AND CLICK THE GOOGLE.COM/DEVICES LINK AND THERE IS A CODE THAT YOU NEED TO PUT IN TO THE GOOGLE AND USE A BURNER ACCOUNT OR ALT ACCOUNT THERE IS A POSSIBLE CHANGE YOU CAN GET BANNED FROM GOOGLE OR YOUTUBE SO JUST USE A BURNER ACCOUNT IN CASE. From d343846180cf0cf9c0d264829666f6743e0ad5ab Mon Sep 17 00:00:00 2001 From: hwangsihu <129564966+hwangsihu@users.noreply.github.com> Date: Thu, 19 Sep 2024 00:24:06 +0900 Subject: [PATCH 24/32] example.application.yml --- Lavalink/example.application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lavalink/example.application.yml b/Lavalink/example.application.yml index 8a4317dd2..8f4383fd3 100644 --- a/Lavalink/example.application.yml +++ b/Lavalink/example.application.yml @@ -42,7 +42,7 @@ plugins: spotify: clientId: "your client id" clientSecret: "your client secret" - # spDc: "your sp dc cookie" # the sp dc cookie used for accessing the spotify lyrics api + spDc: "your sp dc cookie" # the sp dc cookie used for accessing the spotify lyrics api countryCode: "US" # the country code you want to use for filtering the artists top tracks. See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 playlistLoadLimit: 6 # The number of pages at 100 tracks each albumLoadLimit: 6 # The number of pages at 50 tracks each From 2aa65b7f957b4a904d363b2cc1a2d3d645b72aab Mon Sep 17 00:00:00 2001 From: LucasB25 <50886682+LucasB25@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:42:27 +0200 Subject: [PATCH 25/32] remove --- Lavalink/example.application.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Lavalink/example.application.yml b/Lavalink/example.application.yml index 8f4383fd3..9e39a288b 100644 --- a/Lavalink/example.application.yml +++ b/Lavalink/example.application.yml @@ -18,7 +18,7 @@ plugins: mixcloud: true # mixcloud.com soundgasm: true # soundgasm.net pixeldrain: true # pixeldrain.com - jiosaavn: + jiosaavn: apiURL: "https://saavn.dev/api" # The API URL for JioSaavn lavasrc: providers: # Custom providers for track loading. This is the default @@ -42,7 +42,7 @@ plugins: spotify: clientId: "your client id" clientSecret: "your client secret" - spDc: "your sp dc cookie" # the sp dc cookie used for accessing the spotify lyrics api + # spDc: "your sp dc cookie" # the sp dc cookie used for accessing the spotify lyrics api countryCode: "US" # the country code you want to use for filtering the artists top tracks. See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 playlistLoadLimit: 6 # The number of pages at 100 tracks each albumLoadLimit: 6 # The number of pages at 50 tracks each @@ -62,8 +62,6 @@ plugins: albumLoadLimit: 6 # The number of pages at 300 tracks each deezer: masterDecryptionKey: "your master decryption key" # the master key used for decrypting the deezer tracks. (yes this is not here you need to get it from somewhere else) - # arl: "your deezer arl" # the arl cookie used for accessing the deezer api this is optional but required for formats above MP3_128 - formats: [ "FLAC", "MP3_320", "MP3_256", "MP3_128", "MP3_64", "AAC_64" ] # the formats you want to use for the deezer tracks. "FLAC", "MP3_320", "MP3_256" & "AAC_64" are only available for premium users and require a valid arl yandexmusic: accessToken: "your access token" # the token used for accessing the yandex music api. See https://github.com/TopiSenpai/LavaSrc#yandex-music playlistLoadLimit: 1 # The number of pages at 100 tracks each From fcda1e2e0bd53e0433ed53eb68ee8fa867759793 Mon Sep 17 00:00:00 2001 From: hwangsihu Date: Thu, 19 Sep 2024 23:40:06 +0900 Subject: [PATCH 26/32] Biome update --- biome.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/biome.json b/biome.json index b85551a9e..a1652fdb5 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.1/schema.json", + "$schema": "https://biomejs.dev/schemas/1.9.2/schema.json", "organizeImports": { "enabled": true }, diff --git a/package.json b/package.json index dc0aefb5d..f63351dee 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "homepage": "https://github.com/appujet/lavamusic#readme", "devDependencies": { - "@biomejs/biome": "^1.9.1", + "@biomejs/biome": "^1.9.2", "@types/i18n": "^0.13.12", "@types/node": "^22.5.5", "@types/signale": "^1.4.7", From f14b08d58241a457d493eda1f4c784a6429252e3 Mon Sep 17 00:00:00 2001 From: LucasB25 <50886682+LucasB25@users.noreply.github.com> Date: Fri, 20 Sep 2024 18:31:46 +0200 Subject: [PATCH 27/32] Update example.application.yml Signed-off-by: LucasB25 <50886682+LucasB25@users.noreply.github.com> --- Lavalink/example.application.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/Lavalink/example.application.yml b/Lavalink/example.application.yml index 9e39a288b..7b309cbcd 100644 --- a/Lavalink/example.application.yml +++ b/Lavalink/example.application.yml @@ -46,7 +46,6 @@ plugins: countryCode: "US" # the country code you want to use for filtering the artists top tracks. See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 playlistLoadLimit: 6 # The number of pages at 100 tracks each albumLoadLimit: 6 # The number of pages at 50 tracks each - resolveArtistsInSearch: true # Whether to resolve artists in track search results (can be slow) localFiles: false # Enable local files support with Spotify playlists. Please note `uri` & `isrc` will be `null` & `identifier` will be `"local"` applemusic: countryCode: "US" # the country code you want to use for filtering the artists top tracks and language. See https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 From 35eb60540bdcc9100a0a88d92e02726fa224513f Mon Sep 17 00:00:00 2001 From: LucasB25 <50886682+LucasB25@users.noreply.github.com> Date: Sun, 22 Sep 2024 12:07:31 +0200 Subject: [PATCH 28/32] update --- .github/ISSUE_TEMPLATE/bug_report.yaml | 69 +++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.yaml | 64 +++++++++++++++++++ src/commands/config/247.ts | 1 - src/commands/music/Join.ts | 1 - src/commands/music/PlayNext.ts | 1 - src/commands/music/Search.ts | 1 - src/commands/playlist/Load.ts | 1 - 7 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yaml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yaml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 000000000..220f982f0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,69 @@ +name: Bug Report +description: Report incorrect or unexpected behavior. +title: "[Bug]: " +labels: ["bug"] +assignees: + - appujet +body: + - type: markdown + attributes: + value: "## 🐞 Bug Report\n\nPlease fill out the information below to help us resolve the issue as quickly as possible." + + - type: textarea + id: description + attributes: + label: "Bug description" + description: "Provide a clear and concise description of the bug." + placeholder: "Describe the issue..." + validations: + required: true + + - type: textarea + id: reproduce-step + attributes: + label: "Steps to reproduce" + description: "Detail the steps to reproduce the bug. Include any specific setup information that might be important." + placeholder: "Step 1: ...\nStep 2: ...\nStep 3: ..." + validations: + required: true + + - type: input + id: os + attributes: + label: "Operating System" + description: "Which operating system and version are you using?" + placeholder: "e.g., Windows 10, macOS 13, Ubuntu 20.04" + validations: + required: true + + - type: input + id: nodejs-version + attributes: + label: "Node.js version" + description: "Specify the version of Node.js that you are using." + placeholder: "e.g., 14.17.0" + validations: + required: true + + - type: input + id: app-version + attributes: + label: "Application/Library version" + description: "Specify the version of the application or library where the bug occurred." + placeholder: "e.g., v1.2.3" + validations: + required: true + + - type: textarea + id: error-logs + attributes: + label: "Error logs or screenshots" + description: "Include relevant error logs or screenshots that might help in diagnosing the issue." + placeholder: "" + + - type: textarea + id: additional-info + attributes: + label: "Additional context" + description: "Provide any other context or information that might be helpful." + placeholder: "Any additional details..." diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 000000000..5777c6042 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,64 @@ +name: "Feature Request" +description: "Propose a new idea or improvement for this repository" +title: "[Feature Request]: " +labels: ["enhancement", "feature-request"] +assignees: + - appujet +body: + - type: markdown + attributes: + value: | + ## Feature Request + + Please provide a detailed description of the feature you're suggesting. If applicable, describe any related problem and the potential benefits of implementing this feature. + - type: input + id: title + attributes: + label: "Feature Title" + description: "Provide a brief title for your feature request" + placeholder: "Enter a concise title" + validations: + required: true + - type: textarea + id: problem_description + attributes: + label: "Is your feature request related to a problem?" + description: "Describe the problem you're trying to solve, if applicable" + placeholder: "Describe the problem" + validations: + required: false + - type: textarea + id: suggestion_description + attributes: + label: "Feature Description" + description: "Provide a detailed description of your idea. Include examples, screenshots, or mockups if possible." + placeholder: "Explain your idea in detail" + validations: + required: true + - type: dropdown + id: priority + attributes: + label: "Priority Level" + description: "Indicate how important this feature is" + options: + - "Low" + - "Medium" + - "High" + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: "Alternatives Considered" + description: "Have you considered any alternative solutions? Please describe them here." + placeholder: "Mention any alternative approaches" + validations: + required: false + - type: textarea + id: additional_context + attributes: + label: "Additional Context" + description: "Add any other context or screenshots that could help explain your feature request." + placeholder: "Additional context, links, or references" + validations: + required: false diff --git a/src/commands/config/247.ts b/src/commands/config/247.ts index 32b60c6fb..c87bde37f 100644 --- a/src/commands/config/247.ts +++ b/src/commands/config/247.ts @@ -56,7 +56,6 @@ export default class _247 extends Command { textChannelId: ctx.channel.id, selfMute: false, selfDeaf: true, - vcRegion: member.voice.channel.rtcRegion, }); } diff --git a/src/commands/music/Join.ts b/src/commands/music/Join.ts index 2dadd812c..b18791a4c 100644 --- a/src/commands/music/Join.ts +++ b/src/commands/music/Join.ts @@ -60,7 +60,6 @@ export default class Join extends Command { textChannelId: ctx.channel.id, selfMute: false, selfDeaf: true, - vcRegion: memberVoiceChannel.rtcRegion, }); if (!player.connected) await player.connect(); diff --git a/src/commands/music/PlayNext.ts b/src/commands/music/PlayNext.ts index 22404edee..14be280b2 100644 --- a/src/commands/music/PlayNext.ts +++ b/src/commands/music/PlayNext.ts @@ -57,7 +57,6 @@ export default class PlayNext extends Command { textChannelId: ctx.channel.id, selfMute: false, selfDeaf: true, - vcRegion: memberVoiceChannel.rtcRegion, }); if (!player.connected) await player.connect(); diff --git a/src/commands/music/Search.ts b/src/commands/music/Search.ts index e44ea220b..a0837da17 100644 --- a/src/commands/music/Search.ts +++ b/src/commands/music/Search.ts @@ -52,7 +52,6 @@ export default class Search extends Command { textChannelId: ctx.channel.id, selfMute: false, selfDeaf: true, - vcRegion: memberVoiceChannel.rtcRegion, }); if (!player.connected) await player.connect(); diff --git a/src/commands/playlist/Load.ts b/src/commands/playlist/Load.ts index 3b69cae23..bd6145af6 100644 --- a/src/commands/playlist/Load.ts +++ b/src/commands/playlist/Load.ts @@ -74,7 +74,6 @@ export default class LoadPlaylist extends Command { textChannelId: ctx.channel.id, selfMute: false, selfDeaf: true, - vcRegion: member.voice.channel.rtcRegion, }); if (!player.connected) await player.connect(); From da13a4e21d97884ee602414238591e437656c83b Mon Sep 17 00:00:00 2001 From: LucasB25 <50886682+LucasB25@users.noreply.github.com> Date: Sun, 22 Sep 2024 12:10:25 +0200 Subject: [PATCH 29/32] Update bug_report.yaml --- .github/ISSUE_TEMPLATE/bug_report.yaml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 220f982f0..969fa97bb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -45,6 +45,15 @@ body: validations: required: true + - type: input + id: java-version + attributes: + label: "java version" + description: "Specify the version of Java that you are using." + placeholder: "e.g., 17" + validations: + required: true + - type: input id: app-version attributes: @@ -66,4 +75,4 @@ body: attributes: label: "Additional context" description: "Provide any other context or information that might be helpful." - placeholder: "Any additional details..." + placeholder: "Any additional details..." \ No newline at end of file From 9ad079192bfc7aae8520d824611adbc79f696783 Mon Sep 17 00:00:00 2001 From: Appu Date: Tue, 24 Sep 2024 20:55:56 +0530 Subject: [PATCH 30/32] update --- .hintrc | 6 + biome.json | 169 ++++---- locales/ChineseCN.json | 2 +- locales/ChineseTW.json | 2 +- locales/EnglishUS.json | 2 +- locales/French.json | 2 +- locales/German.json | 2 +- locales/Hindi.json | 2 +- locales/Indonesian.json | 2 +- locales/Japanese.json | 2 +- locales/Korean.json | 2 +- locales/Norwegian.json | 2 +- locales/Polish.json | 2 +- locales/PortuguesePT.json | 2 +- locales/Russian.json | 2 +- locales/SpanishES.json | 2 +- locales/Vietnamese.json | 2 +- package.json | 2 +- process.json | 16 +- scripts/clean.js | 24 +- scripts/restart.js | 20 +- src/LavaClient.ts | 10 +- src/commands/config/247.ts | 138 +++---- src/commands/config/Dj.ts | 350 +++++++++-------- src/commands/config/Language.ts | 288 +++++++------- src/commands/config/Prefix.ts | 204 +++++----- src/commands/config/Setup.ts | 348 ++++++++-------- src/commands/dev/CreateInvite.ts | 125 +++--- src/commands/dev/DeleteInvites.ts | 82 ++-- src/commands/dev/Deploy.ts | 188 ++++----- src/commands/dev/Eval.ts | 140 +++---- src/commands/dev/GuildLeave.ts | 130 +++--- src/commands/dev/GuildList.ts | 86 ++-- src/commands/dev/Restart.ts | 137 +++---- src/commands/filters/8d.ts | 108 ++--- src/commands/filters/BassBoost.ts | 190 ++++----- src/commands/filters/Karaoke.ts | 108 ++--- src/commands/filters/LowPass.ts | 108 ++--- src/commands/filters/NightCore.ts | 108 ++--- src/commands/filters/Pitch.ts | 128 +++--- src/commands/filters/Rate.ts | 128 +++--- src/commands/filters/Reset.ts | 84 ++-- src/commands/filters/Rotation.ts | 106 ++--- src/commands/filters/Speed.ts | 128 +++--- src/commands/filters/Tremolo.ts | 108 ++--- src/commands/filters/Vibrato.ts | 108 ++--- src/commands/info/About.ts | 158 ++++---- src/commands/info/Botinfo.ts | 146 +++---- src/commands/info/Help.ts | 206 +++++----- src/commands/info/Invite.ts | 95 ++--- src/commands/info/LavaLink.ts | 150 +++---- src/commands/info/Ping.ts | 126 +++--- src/commands/music/Autoplay.ts | 102 ++--- src/commands/music/ClearQueue.ts | 94 ++--- src/commands/music/Grab.ts | 136 +++---- src/commands/music/Join.ts | 138 +++---- src/commands/music/Leave.ts | 86 ++-- src/commands/music/Loop.ts | 102 ++--- src/commands/music/Lyrics.ts | 335 ++++++++-------- src/commands/music/Nowplaying.ts | 114 +++--- src/commands/music/Pause.ts | 84 ++-- src/commands/music/Play.ts | 228 +++++------ src/commands/music/PlayNext.ts | 220 +++++------ src/commands/music/Queue.ts | 162 ++++---- src/commands/music/Remove.ts | 118 +++--- src/commands/music/Replay.ts | 84 ++-- src/commands/music/Resume.ts | 84 ++-- src/commands/music/Search.ts | 209 +++++----- src/commands/music/Seek.ts | 146 +++---- src/commands/music/Shuffle.ts | 84 ++-- src/commands/music/Skip.ts | 106 ++--- src/commands/music/Skipto.ts | 112 +++--- src/commands/music/Stop.ts | 74 ++-- src/commands/music/Volume.ts | 122 +++--- src/commands/playlist/AddSong.ts | 246 ++++++------ src/commands/playlist/Create.ts | 128 +++--- src/commands/playlist/Delete.ts | 150 +++---- src/commands/playlist/List.ts | 228 +++++------ src/commands/playlist/Load.ts | 225 +++++------ src/commands/playlist/RemoveSong.ts | 166 ++++---- src/commands/playlist/Steal.ts | 387 +++++++++--------- src/config.ts | 84 ++-- src/database/server.ts | 524 ++++++++++++------------- src/env.ts | 246 ++++++------ src/events/client/ChannelDelete.ts | 60 +-- src/events/client/GuildCreate.ts | 128 +++--- src/events/client/GuildDelete.ts | 129 +++--- src/events/client/InteractionCreate.ts | 470 +++++++++++----------- src/events/client/MessageCreate.ts | 468 +++++++++++----------- src/events/client/Raw.ts | 18 +- src/events/client/Ready.ts | 60 +-- src/events/client/SetupButtons.ts | 487 ++++++++++++----------- src/events/client/SetupSystem.ts | 97 +++-- src/events/client/VoiceStateUpdate.ts | 124 +++--- src/events/node/Connect.ts | 86 ++-- src/events/node/Destroy.ts | 24 +- src/events/player/QueueEnd.ts | 42 +- src/events/player/TrackEnd.ts | 38 +- src/events/player/TrackStart.ts | 493 ++++++++++++----------- src/index.ts | 34 +- src/plugin/index.ts | 42 +- src/plugin/plugins/antiCrash.ts | 48 +-- src/plugin/plugins/keepAlive.ts | 36 +- src/plugin/plugins/updateStatus.ts | 16 +- src/shard.ts | 32 +- src/structures/Command.ts | 142 +++---- src/structures/Context.ts | 269 +++++++------ src/structures/Event.ts | 44 +-- src/structures/I18n.ts | 72 ++-- src/structures/LavalinkClient.ts | 76 ++-- src/structures/Lavamusic.ts | 404 +++++++++---------- src/structures/Logger.ts | 100 ++--- src/structures/index.ts | 8 +- src/types.ts | 148 +++---- src/utils/BotLog.ts | 32 +- src/utils/Buttons.ts | 136 +++---- src/utils/SetupSystem.ts | 470 +++++++++++----------- src/utils/ThemeSelector.ts | 128 +++--- src/utils/Utils.ts | 335 ++++++++-------- src/utils/functions/player.ts | 166 ++++---- tsconfig.json | 34 +- 121 files changed, 7902 insertions(+), 7704 deletions(-) create mode 100644 .hintrc diff --git a/.hintrc b/.hintrc new file mode 100644 index 000000000..f45382f88 --- /dev/null +++ b/.hintrc @@ -0,0 +1,6 @@ +{ + "extends": ["development"], + "hints": { + "typescript-config/consistent-casing": "off" + } +} diff --git a/biome.json b/biome.json index a1652fdb5..47187d652 100644 --- a/biome.json +++ b/biome.json @@ -1,88 +1,85 @@ { - "$schema": "https://biomejs.dev/schemas/1.9.2/schema.json", - "organizeImports": { - "enabled": true - }, - "linter": { - "enabled": true, - "rules": { - "all": true, - "complexity": { - "noBannedTypes": "off", - "noExcessiveCognitiveComplexity": "off", - "noForEach": "off", - "noStaticOnlyClass": "off" - }, - "correctness": { - "noNodejsModules": "off", - "noUnusedFunctionParameters": "off", - "noUnusedVariables": "off", - "noVoidTypeReturn": "off", - "useImportExtensions": "off" - }, - "performance": { - "noBarrelFile": "off", - "useTopLevelRegex": "off" - }, - "security": { - "noGlobalEval": "off" - }, - "style": { - "noDefaultExport": "off", - "noInferrableTypes": "off", - "noNamespaceImport": "off", - "noNonNullAssertion": "off", - "noParameterAssign": "off", - "useBlockStatements": "off", - "useDefaultSwitchClause": "off", - "useFilenamingConvention": "off", - "useImportType": "warn", - "useNamingConvention": "off", - "useNumberNamespace": "off", - "useSingleCaseStatement": "off" - }, - "suspicious": { - "noConfusingVoidType": "off", - "noConsole": "off", - "noConsoleLog": "off", - "noEmptyBlockStatements": "off", - "noEvolvingTypes": "off", - "noExplicitAny": "off", - "noGlobalIsFinite": "off", - "noGlobalIsNan": "off", - "useAwait": "off" - } - } - }, - "formatter": { - "enabled": true, - "indentWidth": 4, - "indentStyle": "space", - "lineEnding": "crlf", - "lineWidth": 140, - "formatWithErrors": true - }, - "json": { - "linter": { - "enabled": true - }, - "formatter": { - "enabled": true, - "indentWidth": 2, - "lineEnding": "crlf", - "lineWidth": 80 - } - }, - "javascript": { - "formatter": { - "quoteStyle": "double", - "arrowParentheses": "always", - "bracketSameLine": true, - "semicolons": "always" - } - }, - "files": { - "ignoreUnknown": false, - "ignore": [".vscode", "dist", "locales", "node_modules"] - } + "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true, + "defaultBranch": "main" + }, + "files": { + "ignoreUnknown": true, + "ignore": ["node_modules/", "dist", "package.json", "tsconfig.json", ".vscode"] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab", + "indentWidth": 2, + "lineWidth": 120, + "lineEnding": "crlf", + "formatWithErrors": true + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": false, + "all": true, + "security": { + "noGlobalEval": "off" + }, + "suspicious": { + "noExplicitAny": "off", + "noAssignInExpressions": "off", + "useAwait": "off", + "noConfusingVoidType": "off", + "noAsyncPromiseExecutor": "off", + "noUnsafeDeclarationMerging": "off", + "noEmptyInterface": "off", + "noThenProperty": "off" + }, + "correctness": { + "noNodejsModules": "off", + "useImportExtensions": "off", + "noUnusedFunctionParameters": "off", + "noUnusedVariables": "off" + }, + "style": { + "noDefaultExport": "off", + "useBlockStatements": "off", + "noParameterProperties": "off", + "useNamingConvention": "off", + "noNonNullAssertion": "off", + "useForOf": "off", + "useDefaultSwitchClause": "off", + "noParameterAssign": "off", + "useFilenamingConvention": "off", + "useEnumInitializers": "off", + "useExplicitLengthCheck": "off", + "noNamespaceImport": "off", + "noInferrableTypes": "info" + }, + "complexity": { + "noForEach": "off", + "noExcessiveCognitiveComplexity": "off", + "noUselessConstructor": "off", + "noBannedTypes": "off" + }, + "performance": { + "noBarrelFile": "off", + "noDelete": "off", + "noReExportAll": "off", + "useTopLevelRegex": "off" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "semicolons": "always", + "arrowParentheses": "asNeeded", + "bracketSameLine": true + } + } } diff --git a/locales/ChineseCN.json b/locales/ChineseCN.json index 2dc1bb82f..f823bfbc2 100644 --- a/locales/ChineseCN.json +++ b/locales/ChineseCN.json @@ -632,4 +632,4 @@ "Leave a guild": "离开服务器", "List all guilds the bot is in": "列出机器人所在的所有服务器", "Restart the bot": "重启机器人" -} \ No newline at end of file +} diff --git a/locales/ChineseTW.json b/locales/ChineseTW.json index 03ce7471d..7b8b9e8d1 100644 --- a/locales/ChineseTW.json +++ b/locales/ChineseTW.json @@ -632,4 +632,4 @@ "Leave a guild": "離開伺服器", "List all guilds the bot is in": "列出機器人所在的所有伺服器", "Restart the bot": "重啟機器人" -} \ No newline at end of file +} diff --git a/locales/EnglishUS.json b/locales/EnglishUS.json index f7b96855f..d33e950a7 100644 --- a/locales/EnglishUS.json +++ b/locales/EnglishUS.json @@ -644,4 +644,4 @@ "Leave a guild": "Leave a guild", "List all guilds the bot is in": "List all guilds the bot is in", "Restart the bot": "Restart the bot" -} \ No newline at end of file +} diff --git a/locales/French.json b/locales/French.json index 60a8a58e2..f07637397 100644 --- a/locales/French.json +++ b/locales/French.json @@ -632,4 +632,4 @@ "Leave a guild": "Leave a guild", "List all guilds the bot is in": "List all guilds the bot is in", "Restart the bot": "Restart the bot" -} \ No newline at end of file +} diff --git a/locales/German.json b/locales/German.json index 22540ea90..6fbaba4cf 100644 --- a/locales/German.json +++ b/locales/German.json @@ -632,4 +632,4 @@ "Leave a guild": "Leave a guild", "List all guilds the bot is in": "List all guilds the bot is in", "Restart the bot": "Restart the bot" -} \ No newline at end of file +} diff --git a/locales/Hindi.json b/locales/Hindi.json index 515cc00f9..0eae2fa7b 100644 --- a/locales/Hindi.json +++ b/locales/Hindi.json @@ -632,4 +632,4 @@ "Leave a guild": "Leave a guild", "List all guilds the bot is in": "List all guilds the bot is in", "Restart the bot": "Restart the bot" -} \ No newline at end of file +} diff --git a/locales/Indonesian.json b/locales/Indonesian.json index c777fa29d..b741f8492 100644 --- a/locales/Indonesian.json +++ b/locales/Indonesian.json @@ -632,4 +632,4 @@ "Leave a guild": "Tinggalkan server", "List all guilds the bot is in": "Daftar semua server tempat bot berada", "Restart the bot": "Restart bot" -} \ No newline at end of file +} diff --git a/locales/Japanese.json b/locales/Japanese.json index 878e9f547..3c2ef6f15 100644 --- a/locales/Japanese.json +++ b/locales/Japanese.json @@ -632,4 +632,4 @@ "Leave a guild": "ギルドを離れる", "List all guilds the bot is in": "ボットが所属しているすべてのギルドを一覧表示", "Restart the bot": "ボットを再起動" -} \ No newline at end of file +} diff --git a/locales/Korean.json b/locales/Korean.json index 647b2c4c4..ca1dd509f 100644 --- a/locales/Korean.json +++ b/locales/Korean.json @@ -644,4 +644,4 @@ "Leave a guild": "서버를 떠나요", "List all guilds the bot is in": "봇이 들어가 있는 서버를 알려줘요", "Restart the bot": "봇을 재시작해요" -} \ No newline at end of file +} diff --git a/locales/Norwegian.json b/locales/Norwegian.json index 83bf4d283..d2707dd03 100644 --- a/locales/Norwegian.json +++ b/locales/Norwegian.json @@ -632,4 +632,4 @@ "Leave a guild": "Forlat en server", "List all guilds the bot is in": "List opp alle servere boten er på", "Restart the bot": "Start boten på nytt" -} \ No newline at end of file +} diff --git a/locales/Polish.json b/locales/Polish.json index 8aab02e92..316103047 100644 --- a/locales/Polish.json +++ b/locales/Polish.json @@ -632,4 +632,4 @@ "Leave a guild": "Opusć serwer", "List all guilds the bot is in": "Lista wszystkich serwerów, w których jest bot", "Restart the bot": "Uruchom ponownie bota" -} \ No newline at end of file +} diff --git a/locales/PortuguesePT.json b/locales/PortuguesePT.json index cb696ad46..62e0773a4 100644 --- a/locales/PortuguesePT.json +++ b/locales/PortuguesePT.json @@ -622,4 +622,4 @@ "no_music_playing": "Nada está a ser reproduzido neste momento." } } -} \ No newline at end of file +} diff --git a/locales/Russian.json b/locales/Russian.json index 2ec89a062..2ec622eca 100644 --- a/locales/Russian.json +++ b/locales/Russian.json @@ -632,4 +632,4 @@ "Leave a guild": "Leave a guild", "List all guilds the bot is in": "List all guilds the bot is in", "Restart the bot": "Restart the bot" -} \ No newline at end of file +} diff --git a/locales/SpanishES.json b/locales/SpanishES.json index 895f00fb8..988a1ff5a 100644 --- a/locales/SpanishES.json +++ b/locales/SpanishES.json @@ -632,4 +632,4 @@ "Leave a guild": "Leave a guild", "List all guilds the bot is in": "List all guilds the bot is in", "Restart the bot": "Restart the bot" -} \ No newline at end of file +} diff --git a/locales/Vietnamese.json b/locales/Vietnamese.json index dbc0884b7..a1560056b 100644 --- a/locales/Vietnamese.json +++ b/locales/Vietnamese.json @@ -632,4 +632,4 @@ "Leave a guild": "Rời khỏi một guild", "List all guilds the bot is in": "Danh sách tất cả guild mà bot đang ở", "Restart the bot": "Khởi động lại bot" -} \ No newline at end of file +} diff --git a/package.json b/package.json index f63351dee..7f16de3b0 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "dotenv": "^16.4.5", "genius-lyrics-api": "^3.2.1", "i18n": "^0.15.1", - "lavalink-client": "https://pkg.pr.new/moe-music/lavalink-client@0b0f590", + "lavalink-client": "https://pkg.pr.new/moe-music/lavalink-client@1c1f8f3", "node-system-stats": "^1.3.0", "signale": "^1.4.0", "topgg-autoposter": "^2.0.2", diff --git a/process.json b/process.json index bda8eaceb..62bcfb731 100644 --- a/process.json +++ b/process.json @@ -1,10 +1,10 @@ { - "apps": [ - { - "name": "lavamusic", - "script": "dist/index.js", - "node_args": ["--enable-source-maps"], - "restart_delay": 10000 - } - ] + "apps": [ + { + "name": "lavamusic", + "script": "dist/index.js", + "node_args": ["--enable-source-maps"], + "restart_delay": 10000 + } + ] } diff --git a/scripts/clean.js b/scripts/clean.js index d2f1cc37d..11693396a 100644 --- a/scripts/clean.js +++ b/scripts/clean.js @@ -1,17 +1,17 @@ -const fs = require("node:fs"); -const { rm } = require("node:fs").promises; -const path = require("node:path"); +const fs = require('node:fs'); +const { rm } = require('node:fs').promises; +const path = require('node:path'); async function clean() { - try { - const distPath = path.resolve("dist"); - if (fs.existsSync(distPath)) { - await rm(distPath, { recursive: true, force: true }); - } - } catch (error) { - console.error("Error while cleaning dist folder:", error); - process.exit(1); - } + try { + const distPath = path.resolve('dist'); + if (fs.existsSync(distPath)) { + await rm(distPath, { recursive: true, force: true }); + } + } catch (error) { + console.error('Error while cleaning dist folder:', error); + process.exit(1); + } } clean(); diff --git a/scripts/restart.js b/scripts/restart.js index bd87dda89..feedb373f 100644 --- a/scripts/restart.js +++ b/scripts/restart.js @@ -1,15 +1,15 @@ -const { exec } = require("node:child_process"); +const { exec } = require('node:child_process'); async function startLavamusic() { - exec("npm start", (error, stdout, stderr) => { - if (error) { - console.error(`Error starting Lavamusic: ${error.message}`); - return; - } - if (stderr) { - console.error(`Error starting Lavamusic: ${stderr}`); - } - }); + exec('npm start', (error, stdout, stderr) => { + if (error) { + console.error(`Error starting Lavamusic: ${error.message}`); + return; + } + if (stderr) { + console.error(`Error starting Lavamusic: ${stderr}`); + } + }); } setTimeout(startLavamusic, 5000); diff --git a/src/LavaClient.ts b/src/LavaClient.ts index 4428a2117..867e73264 100644 --- a/src/LavaClient.ts +++ b/src/LavaClient.ts @@ -1,12 +1,12 @@ -import { type ClientOptions, GatewayIntentBits } from "discord.js"; -import { env } from "./env"; -import Lavamusic from "./structures/Lavamusic"; +import { type ClientOptions, GatewayIntentBits } from 'discord.js'; +import { env } from './env'; +import Lavamusic from './structures/Lavamusic'; const { GuildMembers, MessageContent, GuildVoiceStates, GuildMessages, Guilds, GuildMessageTyping } = GatewayIntentBits; const clientOptions: ClientOptions = { - intents: [Guilds, GuildMessages, MessageContent, GuildVoiceStates, GuildMembers, GuildMessageTyping], - allowedMentions: { parse: ["users", "roles"], repliedUser: false }, + intents: [Guilds, GuildMessages, MessageContent, GuildVoiceStates, GuildMembers, GuildMessageTyping], + allowedMentions: { parse: ['users', 'roles'], repliedUser: false }, }; const client = new Lavamusic(clientOptions); diff --git a/src/commands/config/247.ts b/src/commands/config/247.ts index c87bde37f..1bbe2f759 100644 --- a/src/commands/config/247.ts +++ b/src/commands/config/247.ts @@ -1,75 +1,75 @@ -import type { GuildMember } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { GuildMember } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class _247 extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "247", - description: { - content: "cmd.247.description", - examples: ["247"], - usage: "247", - }, - category: "config", - aliases: ["stay"], - cooldown: 3, - args: false, - vote: true, - player: { - voice: true, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: ["ManageGuild"], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: '247', + description: { + content: 'cmd.247.description', + examples: ['247'], + usage: '247', + }, + category: 'config', + aliases: ['stay'], + cooldown: 3, + args: false, + vote: true, + player: { + voice: true, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: ['ManageGuild'], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const embed = this.client.embed(); - let player = client.manager.getPlayer(ctx.guild.id); - try { - const data = await client.db.get_247(ctx.guild!.id); - const member = ctx.member as GuildMember; - if (!member.voice.channel) { - return await ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.247.errors.not_in_voice")).setColor(client.color.red)], - }); - } - if (data) { - await client.db.delete_247(ctx.guild!.id); - return await ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.247.messages.disabled")).setColor(client.color.red)], - }); - } - await client.db.set_247(ctx.guild!.id, ctx.channel.id, member.voice.channel.id); - if (!player) { - player = client.manager.createPlayer({ - guildId: ctx.guild!.id, - voiceChannelId: member.voice.channel.id, - textChannelId: ctx.channel.id, - selfMute: false, - selfDeaf: true, - vcRegion: member.voice.channel.rtcRegion, - }); - } - if (!player.connected) await player.connect(); - return await ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.247.messages.enabled")).setColor(this.client.color.main)], - }); - } catch (error) { - console.error("Error in 247 command:", error); - return await ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.247.errors.generic")).setColor(client.color.red)], - }); - } - } + public async run(client: Lavamusic, ctx: Context): Promise { + const embed = this.client.embed(); + let player = client.manager.getPlayer(ctx.guild!.id); + try { + const data = await client.db.get_247(ctx.guild!.id); + const member = ctx.member as GuildMember; + if (!member.voice.channel) { + return await ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.247.errors.not_in_voice')).setColor(client.color.red)], + }); + } + if (data) { + await client.db.delete_247(ctx.guild!.id); + return await ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.247.messages.disabled')).setColor(client.color.red)], + }); + } + await client.db.set_247(ctx.guild!.id, ctx.channel!.id, member.voice.channel.id); + if (!player) { + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: member.voice.channel.id, + textChannelId: ctx.channel!.id, + selfMute: false, + selfDeaf: true, + vcRegion: member.voice.channel.rtcRegion!, + }); + } + if (!player.connected) await player.connect(); + return await ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.247.messages.enabled')).setColor(this.client.color.main)], + }); + } catch (error) { + console.error('Error in 247 command:', error); + return await ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.247.errors.generic')).setColor(client.color.red)], + }); + } + } } /** diff --git a/src/commands/config/Dj.ts b/src/commands/config/Dj.ts index 99b92922d..afbcbb944 100644 --- a/src/commands/config/Dj.ts +++ b/src/commands/config/Dj.ts @@ -1,185 +1,189 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Dj extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "dj", - description: { - content: "cmd.dj.description", - examples: ["dj add @role", "dj remove @role", "dj clear", "dj toggle"], - usage: "dj", - }, - category: "general", - aliases: ["dj"], - cooldown: 3, - args: true, - vote: true, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: ["ManageGuild"], - }, - slashCommand: true, - options: [ - { - name: "add", - description: "cmd.dj.options.add", - type: 1, - options: [ - { - name: "role", - description: "cmd.dj.options.role", - type: 8, - required: true, - }, - ], - }, - { - name: "remove", - description: "cmd.dj.options.remove", - type: 1, - options: [ - { - name: "role", - description: "cmd.dj.options.role", - type: 8, - required: true, - }, - ], - }, - { - name: "clear", - description: "cmd.dj.options.clear", - type: 1, - }, - { - name: "toggle", - description: "cmd.dj.options.toggle", - type: 1, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'dj', + description: { + content: 'cmd.dj.description', + examples: ['dj add @role', 'dj remove @role', 'dj clear', 'dj toggle'], + usage: 'dj', + }, + category: 'general', + aliases: ['dj'], + cooldown: 3, + args: true, + vote: true, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: ['ManageGuild'], + }, + slashCommand: true, + options: [ + { + name: 'add', + description: 'cmd.dj.options.add', + type: 1, + options: [ + { + name: 'role', + description: 'cmd.dj.options.role', + type: 8, + required: true, + }, + ], + }, + { + name: 'remove', + description: 'cmd.dj.options.remove', + type: 1, + options: [ + { + name: 'role', + description: 'cmd.dj.options.role', + type: 8, + required: true, + }, + ], + }, + { + name: 'clear', + description: 'cmd.dj.options.clear', + type: 1, + }, + { + name: 'toggle', + description: 'cmd.dj.options.toggle', + type: 1, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const embed = this.client.embed().setColor(this.client.color.main); - const dj = await client.db.getDj(ctx.guild!.id); - let subCommand: string; - let role: any; + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const embed = this.client.embed().setColor(this.client.color.main); + const dj = await client.db.getDj(ctx.guild!.id); + let subCommand: string | undefined; + let role: any | undefined; - if (ctx.isInteraction) { - subCommand = ctx.interaction.options.data[0].name; - if (subCommand === "add" || subCommand === "remove") { - role = ctx.interaction.options.data[0].options[0].role; - } - } else { - subCommand = args[0]; - role = ctx.message.mentions.roles.first() || ctx.guild.roles.cache.get(args[1]); - } + if (ctx.isInteraction) { + subCommand = ctx.options.getSubCommand(); + if (subCommand === 'add' || subCommand === 'remove') { + role = ctx.options.getRole('role'); + } + } else { + subCommand = args[0]; + role = ctx.message?.mentions.roles.first() || ctx.guild?.roles.cache.get(args[1]); + } - switch (subCommand) { - case "add": - if (!role) { - return ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.dj.errors.provide_role"))], - }); - } - if (await client.db.getRoles(ctx.guild!.id).then((r) => r.some((re) => re.roleId === role.id))) { - return ctx.sendMessage({ - embeds: [ - embed.setDescription( - ctx.locale("cmd.dj.messages.role_exists", { - roleId: role.id, - }), - ), - ], - }); - } - await client.db.addRole(ctx.guild!.id, role.id); - await client.db.setDj(ctx.guild!.id, true); - return ctx.sendMessage({ - embeds: [ - embed.setDescription( - ctx.locale("cmd.dj.messages.role_added", { - roleId: role.id, - }), - ), - ], - }); + switch (subCommand) { + case 'add': { + if (!role) { + return ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.dj.errors.provide_role'))], + }); + } + if (await client.db.getRoles(ctx.guild!.id).then(r => r.some(re => re.roleId === role.id))) { + return ctx.sendMessage({ + embeds: [ + embed.setDescription( + ctx.locale('cmd.dj.messages.role_exists', { + roleId: role.id, + }), + ), + ], + }); + } + await client.db.addRole(ctx.guild!.id, role.id); + await client.db.setDj(ctx.guild!.id, true); + return ctx.sendMessage({ + embeds: [ + embed.setDescription( + ctx.locale('cmd.dj.messages.role_added', { + roleId: role.id, + }), + ), + ], + }); + } - case "remove": - if (!role) { - return ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.dj.errors.provide_role"))], - }); - } - if (!(await client.db.getRoles(ctx.guild!.id).then((r) => r.some((re) => re.roleId === role.id)))) { - return ctx.sendMessage({ - embeds: [ - embed.setDescription( - ctx.locale("cmd.dj.messages.role_not_found", { - roleId: role.id, - }), - ), - ], - }); - } - await client.db.removeRole(ctx.guild!.id, role.id); - return ctx.sendMessage({ - embeds: [ - embed.setDescription( - ctx.locale("cmd.dj.messages.role_removed", { - roleId: role.id, - }), - ), - ], - }); + case 'remove': { + if (!role) { + return ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.dj.errors.provide_role'))], + }); + } + if (!(await client.db.getRoles(ctx.guild!.id).then(r => r.some(re => re.roleId === role.id)))) { + return ctx.sendMessage({ + embeds: [ + embed.setDescription( + ctx.locale('cmd.dj.messages.role_not_found', { + roleId: role.id, + }), + ), + ], + }); + } + await client.db.removeRole(ctx.guild!.id, role.id); + return ctx.sendMessage({ + embeds: [ + embed.setDescription( + ctx.locale('cmd.dj.messages.role_removed', { + roleId: role.id, + }), + ), + ], + }); + } - case "clear": - if (!dj) { - return ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.dj.errors.no_roles"))], - }); - } - await client.db.clearRoles(ctx.guild!.id); - return ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.dj.messages.all_roles_cleared"))], - }); + case 'clear': { + if (!dj) { + return ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.dj.errors.no_roles'))], + }); + } + await client.db.clearRoles(ctx.guild!.id); + return ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.dj.messages.all_roles_cleared'))], + }); + } - case "toggle": - if (!dj) { - return ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.dj.errors.no_roles"))], - }); - } - await client.db.setDj(ctx.guild!.id, !dj.mode); - return ctx.sendMessage({ - embeds: [ - embed.setDescription( - ctx.locale("cmd.dj.messages.toggle", { - status: dj.mode ? "disabled" : "enabled", - }), - ), - ], - }); + case 'toggle': { + if (!dj) { + return ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.dj.errors.no_roles'))], + }); + } + await client.db.setDj(ctx.guild!.id, !dj.mode); + return ctx.sendMessage({ + embeds: [ + embed.setDescription( + ctx.locale('cmd.dj.messages.toggle', { + status: dj.mode ? 'disabled' : 'enabled', + }), + ), + ], + }); + } - default: - return ctx.sendMessage({ - embeds: [ - embed.setDescription(ctx.locale("cmd.dj.errors.invalid_subcommand")).addFields({ - name: ctx.locale("cmd.dj.subcommands"), - value: "`add`, `remove`, `clear`, `toggle`", - }), - ], - }); - } - } + default: + return ctx.sendMessage({ + embeds: [ + embed.setDescription(ctx.locale('cmd.dj.errors.invalid_subcommand')).addFields({ + name: ctx.locale('cmd.dj.subcommands'), + value: '`add`, `remove`, `clear`, `toggle`', + }), + ], + }); + } + } } /** diff --git a/src/commands/config/Language.ts b/src/commands/config/Language.ts index 011afff35..f19f553bd 100644 --- a/src/commands/config/Language.ts +++ b/src/commands/config/Language.ts @@ -1,149 +1,149 @@ -import type { AutocompleteInteraction } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; -import { Language, LocaleFlags } from "../../types"; +import type { AutocompleteInteraction } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; +import { Language, LocaleFlags } from '../../types'; export default class LanguageCommand extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "language", - description: { - content: "cmd.language.description", - examples: ["language set `EnglishUS`", "language reset"], - usage: "language", - }, - category: "config", - aliases: ["lang"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: ["ManageGuild"], - }, - slashCommand: true, - options: [ - { - name: "set", - description: "cmd.language.options.set", - type: 1, - options: [ - { - name: "language", - description: "cmd.language.options.language", - type: 3, - required: true, - autocomplete: true, - }, - ], - }, - { - name: "reset", - description: "cmd.language.options.reset", - type: 1, - }, - ], - }); - } - - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - let subCommand: string; - - if (ctx.isInteraction) { - subCommand = ctx.interaction.options.data[0].name; - } else { - subCommand = args.shift(); - } - if (subCommand === "set") { - const embed = client.embed().setColor(this.client.color.main); - - const locale = await client.db.getLanguage(ctx.guild!.id); - - let lang: string; - - if (ctx.isInteraction) { - lang = ctx.interaction.options.data[0].options[0].value as string; - } else { - lang = args[0]; - } - - if (!Object.values(Language).includes(lang as Language)) { - const availableLanguages = Object.entries(LocaleFlags) - .map(([key, value]) => `${value}:\`${key}\``) - .reduce((acc, curr, index) => { - if (index % 2 === 0) { - return acc + curr + (index === Object.entries(LocaleFlags).length - 1 ? "" : " "); - } - return `${acc + curr}\n`; - }, ""); - return ctx.sendMessage({ - embeds: [ - embed.setDescription( - ctx.locale("cmd.language.invalid_language", { - languages: availableLanguages, - }), - ), - ], - }); - } - - if (locale && locale === lang) { - return ctx.sendMessage({ - embeds: [ - embed.setDescription( - ctx.locale("cmd.language.already_set", { - language: lang, - }), - ), - ], - }); - } - - await client.db.updateLanguage(ctx.guild!.id, lang); - ctx.guildLocale = lang; - - return ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.language.set", { language: lang }))], - }); - } - if (subCommand === "reset") { - const embed = client.embed().setColor(this.client.color.main); - - const locale = await client.db.getLanguage(ctx.guild!.id); - - if (!locale) { - return ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.language.not_set"))], - }); - } - - await client.db.updateLanguage(ctx.guild!.id, Language.EnglishUS); - ctx.guildLocale = Language.EnglishUS; - - return ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.language.reset"))], - }); - } - } - - public async autocomplete(interaction: AutocompleteInteraction): Promise { - const focusedValue = interaction.options.getFocused(); - - const languages = Object.values(Language).map((language) => ({ - name: language, - value: language, - })); - - const filtered = languages.filter((language) => language.name.toLowerCase().includes(focusedValue.toLowerCase())); - - await interaction.respond(filtered.slice(0, 25)).catch(console.error); - } + constructor(client: Lavamusic) { + super(client, { + name: 'language', + description: { + content: 'cmd.language.description', + examples: ['language set `EnglishUS`', 'language reset'], + usage: 'language', + }, + category: 'config', + aliases: ['lang'], + cooldown: 3, + args: true, + vote: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: ['ManageGuild'], + }, + slashCommand: true, + options: [ + { + name: 'set', + description: 'cmd.language.options.set', + type: 1, + options: [ + { + name: 'language', + description: 'cmd.language.options.language', + type: 3, + required: true, + autocomplete: true, + }, + ], + }, + { + name: 'reset', + description: 'cmd.language.options.reset', + type: 1, + }, + ], + }); + } + + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + let subCommand: string | undefined; + + if (ctx.isInteraction) { + subCommand = ctx.options.getSubCommand(); + } else { + subCommand = args.shift(); + } + if (subCommand === 'set') { + const embed = client.embed().setColor(this.client.color.main); + + const locale = await client.db.getLanguage(ctx.guild!.id); + + let lang: string; + + if (ctx.isInteraction) { + lang = ctx.options.get('language')?.value as string; + } else { + lang = args[0]; + } + + if (!Object.values(Language).includes(lang as Language)) { + const availableLanguages = Object.entries(LocaleFlags) + .map(([key, value]) => `${value}:\`${key}\``) + .reduce((acc, curr, index) => { + if (index % 2 === 0) { + return acc + curr + (index === Object.entries(LocaleFlags).length - 1 ? '' : ' '); + } + return `${acc + curr}\n`; + }, ''); + return ctx.sendMessage({ + embeds: [ + embed.setDescription( + ctx.locale('cmd.language.invalid_language', { + languages: availableLanguages, + }), + ), + ], + }); + } + + if (locale && locale === lang) { + return ctx.sendMessage({ + embeds: [ + embed.setDescription( + ctx.locale('cmd.language.already_set', { + language: lang, + }), + ), + ], + }); + } + + await client.db.updateLanguage(ctx.guild!.id, lang); + ctx.guildLocale = lang; + + return ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.language.set', { language: lang }))], + }); + } + if (subCommand === 'reset') { + const embed = client.embed().setColor(this.client.color.main); + + const locale = await client.db.getLanguage(ctx.guild!.id); + + if (!locale) { + return ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.language.not_set'))], + }); + } + + await client.db.updateLanguage(ctx.guild!.id, Language.EnglishUS); + ctx.guildLocale = Language.EnglishUS; + + return ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.language.reset'))], + }); + } + } + + public async autocomplete(interaction: AutocompleteInteraction): Promise { + const focusedValue = interaction.options.getFocused(); + + const languages = Object.values(Language).map(language => ({ + name: language, + value: language, + })); + + const filtered = languages.filter(language => language.name.toLowerCase().includes(focusedValue.toLowerCase())); + + await interaction.respond(filtered.slice(0, 25)).catch(console.error); + } } /** diff --git a/src/commands/config/Prefix.ts b/src/commands/config/Prefix.ts index a4dd58624..9970846b6 100644 --- a/src/commands/config/Prefix.ts +++ b/src/commands/config/Prefix.ts @@ -1,110 +1,110 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Prefix extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "prefix", - description: { - content: "cmd.prefix.description", - examples: ["prefix set !", "prefix reset"], - usage: "prefix", - }, - category: "general", - aliases: ["pf"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: ["ManageGuild"], - }, - slashCommand: true, - options: [ - { - name: "set", - description: "cmd.prefix.options.set", - type: 1, - options: [ - { - name: "prefix", - description: "cmd.prefix.options.prefix", - type: 3, - required: true, - }, - ], - }, - { - name: "reset", - description: "cmd.prefix.options.reset", - type: 1, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'prefix', + description: { + content: 'cmd.prefix.description', + examples: ['prefix set !', 'prefix reset'], + usage: 'prefix', + }, + category: 'general', + aliases: ['pf'], + cooldown: 3, + args: true, + vote: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: ['ManageGuild'], + }, + slashCommand: true, + options: [ + { + name: 'set', + description: 'cmd.prefix.options.set', + type: 1, + options: [ + { + name: 'prefix', + description: 'cmd.prefix.options.prefix', + type: 3, + required: true, + }, + ], + }, + { + name: 'reset', + description: 'cmd.prefix.options.reset', + type: 1, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const embed = client.embed().setColor(this.client.color.main); - const guildId = ctx.guild!.id; - const guildData = await client.db.get(guildId); - const isInteraction = ctx.isInteraction; - let subCommand: string; - let prefix: string; + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const embed = client.embed().setColor(this.client.color.main); + const guildId = ctx.guild!.id; + const guildData = await client.db.get(guildId); + const isInteraction = ctx.isInteraction; + let subCommand: string | undefined; + let prefix: string | undefined; - if (isInteraction) { - subCommand = ctx.interaction.options.data[0].name; - prefix = ctx.interaction.options.data[0].options[0]?.value.toString(); - } else { - subCommand = args[0] || ""; - prefix = args[1] || ""; - } + if (isInteraction) { + subCommand = ctx.options.getSubCommand(); + prefix = ctx.options.get('prefix')?.value?.toString(); + } else { + subCommand = args[0] || ''; + prefix = args[1] || ''; + } - switch (subCommand) { - case "set": { - if (!prefix) { - const currentPrefix = guildData ? guildData.prefix : client.env.PREFIX; - embed.setDescription( - ctx.locale("cmd.prefix.messages.current_prefix", { - prefix: currentPrefix, - }), - ); - return await ctx.sendMessage({ embeds: [embed] }); - } - if (prefix.length > 3) { - embed.setDescription(ctx.locale("cmd.prefix.errors.prefix_too_long")); - return await ctx.sendMessage({ embeds: [embed] }); - } - await client.db.setPrefix(guildId, prefix); - embed.setDescription(ctx.locale("cmd.prefix.messages.prefix_set", { prefix })); - return await ctx.sendMessage({ embeds: [embed] }); - } - case "reset": { - const defaultPrefix = client.env.PREFIX; - await client.db.setPrefix(guildId, defaultPrefix); - embed.setDescription( - ctx.locale("cmd.prefix.messages.prefix_reset", { - prefix: defaultPrefix, - }), - ); - return await ctx.sendMessage({ embeds: [embed] }); - } - default: { - const currentPrefix = guildData ? guildData.prefix : client.env.PREFIX; - embed.setDescription( - ctx.locale("cmd.prefix.messages.current_prefix", { - prefix: currentPrefix, - }), - ); - return await ctx.sendMessage({ embeds: [embed] }); - } - } - } + switch (subCommand) { + case 'set': { + if (!prefix) { + const currentPrefix = guildData ? guildData.prefix : client.env.PREFIX; + embed.setDescription( + ctx.locale('cmd.prefix.messages.current_prefix', { + prefix: currentPrefix, + }), + ); + return await ctx.sendMessage({ embeds: [embed] }); + } + if (prefix.length > 3) { + embed.setDescription(ctx.locale('cmd.prefix.errors.prefix_too_long')); + return await ctx.sendMessage({ embeds: [embed] }); + } + await client.db.setPrefix(guildId, prefix); + embed.setDescription(ctx.locale('cmd.prefix.messages.prefix_set', { prefix })); + return await ctx.sendMessage({ embeds: [embed] }); + } + case 'reset': { + const defaultPrefix = client.env.PREFIX; + await client.db.setPrefix(guildId, defaultPrefix); + embed.setDescription( + ctx.locale('cmd.prefix.messages.prefix_reset', { + prefix: defaultPrefix, + }), + ); + return await ctx.sendMessage({ embeds: [embed] }); + } + default: { + const currentPrefix = guildData ? guildData.prefix : client.env.PREFIX; + embed.setDescription( + ctx.locale('cmd.prefix.messages.current_prefix', { + prefix: currentPrefix, + }), + ); + return await ctx.sendMessage({ embeds: [embed] }); + } + } + } } /** diff --git a/src/commands/config/Setup.ts b/src/commands/config/Setup.ts index d93337ba6..a88be8355 100644 --- a/src/commands/config/Setup.ts +++ b/src/commands/config/Setup.ts @@ -1,180 +1,180 @@ -import { ChannelType, OverwriteType, PermissionFlagsBits } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; -import { getButtons } from "../../utils/Buttons"; +import { ChannelType, OverwriteType, PermissionFlagsBits } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; +import { getButtons } from '../../utils/Buttons'; export default class Setup extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "setup", - description: { - content: "cmd.setup.description", - examples: ["setup create", "setup delete", "setup info"], - usage: "setup", - }, - category: "config", - aliases: ["set"], - cooldown: 3, - args: true, - vote: true, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks", "ManageChannels"], - user: ["ManageGuild"], - }, - slashCommand: true, - options: [ - { - name: "create", - description: "cmd.setup.options.create", - type: 1, - }, - { - name: "delete", - description: "cmd.setup.options.delete", - type: 1, - }, - { - name: "info", - description: "cmd.setup.options.info", - type: 1, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'setup', + description: { + content: 'cmd.setup.description', + examples: ['setup create', 'setup delete', 'setup info'], + usage: 'setup', + }, + category: 'config', + aliases: ['set'], + cooldown: 3, + args: true, + vote: true, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks', 'ManageChannels'], + user: ['ManageGuild'], + }, + slashCommand: true, + options: [ + { + name: 'create', + description: 'cmd.setup.options.create', + type: 1, + }, + { + name: 'delete', + description: 'cmd.setup.options.delete', + type: 1, + }, + { + name: 'info', + description: 'cmd.setup.options.info', + type: 1, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const subCommand = ctx.isInteraction ? ctx.interaction.options.data[0].name : args[0]; - const embed = client.embed().setColor(this.client.color.main); - switch (subCommand) { - case "create": { - const data = await client.db.getSetup(ctx.guild!.id); - if (data?.textId && data.messageId) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.setup.errors.channel_exists"), - color: client.color.red, - }, - ], - }); - } - const textChannel = await ctx.guild.channels.create({ - name: `${this.client.user.username}-song-requests`, - type: ChannelType.GuildText, - topic: "Song requests for the music bot.", - permissionOverwrites: [ - { - type: OverwriteType.Member, - id: this.client.user.id, - allow: [ - PermissionFlagsBits.ViewChannel, - PermissionFlagsBits.SendMessages, - PermissionFlagsBits.EmbedLinks, - PermissionFlagsBits.ReadMessageHistory, - ], - }, - { - type: OverwriteType.Role, - id: ctx.guild.roles.everyone.id, - allow: [ - PermissionFlagsBits.ViewChannel, - PermissionFlagsBits.SendMessages, - PermissionFlagsBits.ReadMessageHistory, - ], - }, - ], - }); - const player = this.client.manager.getPlayer(ctx.guild!.id); - const image = this.client.config.links.img; - const desc = player?.queue.current - ? `[${player.queue.current.info.title}](${player.queue.current.info.uri})` - : ctx.locale("player.setupStart.nothing_playing"); - embed.setDescription(desc).setImage(image); - await textChannel - .send({ - embeds: [embed], - components: getButtons(player, client), - }) - .then((msg) => { - client.db.setSetup(ctx.guild!.id, textChannel.id, msg.id); - }); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.setup.messages.channel_created", { channelId: textChannel.id }), - color: this.client.color.main, - }, - ], - }); - break; - } - case "delete": { - const data2 = await client.db.getSetup(ctx.guild!.id); - if (!data2) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.setup.errors.channel_not_exists"), - color: client.color.red, - }, - ], - }); - } - client.db.deleteSetup(ctx.guild!.id); - const textChannel = ctx.guild.channels.cache.get(data2.textId); - if (textChannel) await textChannel.delete().catch(() => {}); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.setup.messages.channel_deleted"), - color: this.client.color.main, - }, - ], - }); - break; - } - case "info": { - const data3 = await client.db.getSetup(ctx.guild!.id); - if (!data3) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.setup.errors.channel_not_exists"), - color: client.color.red, - }, - ], - }); - } - const channel = ctx.guild.channels.cache.get(data3.textId); - if (channel) { - embed.setDescription( - ctx.locale("cmd.setup.messages.channel_info", { - channelId: channel.id, - }), - ); - await ctx.sendMessage({ embeds: [embed] }); - } else { - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.setup.errors.channel_not_exists"), - color: client.color.red, - }, - ], - }); - } - break; - } - default: - break; - } - } + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const subCommand = ctx.isInteraction ? ctx.options.getSubCommand() : args[0]; + const embed = client.embed().setColor(this.client.color.main); + switch (subCommand) { + case 'create': { + const data = await client.db.getSetup(ctx.guild!.id); + if (data?.textId && data.messageId) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.setup.errors.channel_exists'), + color: client.color.red, + }, + ], + }); + } + const textChannel = await ctx.guild.channels.create({ + name: `${client.user?.username}-song-requests`, + type: ChannelType.GuildText, + topic: 'Song requests for the music bot.', + permissionOverwrites: [ + { + type: OverwriteType.Member, + id: client.user?.id!, + allow: [ + PermissionFlagsBits.ViewChannel, + PermissionFlagsBits.SendMessages, + PermissionFlagsBits.EmbedLinks, + PermissionFlagsBits.ReadMessageHistory, + ], + }, + { + type: OverwriteType.Role, + id: ctx.guild.roles.everyone.id, + allow: [ + PermissionFlagsBits.ViewChannel, + PermissionFlagsBits.SendMessages, + PermissionFlagsBits.ReadMessageHistory, + ], + }, + ], + }); + const player = this.client.manager.getPlayer(ctx.guild!.id); + const image = this.client.config.links.img; + const desc = player?.queue.current + ? `[${player.queue.current.info.title}](${player.queue.current.info.uri})` + : ctx.locale('player.setupStart.nothing_playing'); + embed.setDescription(desc).setImage(image); + await textChannel + .send({ + embeds: [embed], + components: getButtons(player, client), + }) + .then(msg => { + client.db.setSetup(ctx.guild!.id, textChannel.id, msg.id); + }); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.setup.messages.channel_created', { channelId: textChannel.id }), + color: this.client.color.main, + }, + ], + }); + break; + } + case 'delete': { + const data2 = await client.db.getSetup(ctx.guild!.id); + if (!data2) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.setup.errors.channel_not_exists'), + color: client.color.red, + }, + ], + }); + } + client.db.deleteSetup(ctx.guild!.id); + const textChannel = ctx.guild.channels.cache.get(data2.textId); + if (textChannel) await textChannel.delete().catch(() => {}); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.setup.messages.channel_deleted'), + color: this.client.color.main, + }, + ], + }); + break; + } + case 'info': { + const data3 = await client.db.getSetup(ctx.guild!.id); + if (!data3) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.setup.errors.channel_not_exists'), + color: client.color.red, + }, + ], + }); + } + const channel = ctx.guild.channels.cache.get(data3.textId); + if (channel) { + embed.setDescription( + ctx.locale('cmd.setup.messages.channel_info', { + channelId: channel.id, + }), + ); + await ctx.sendMessage({ embeds: [embed] }); + } else { + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.setup.errors.channel_not_exists'), + color: client.color.red, + }, + ], + }); + } + break; + } + default: + break; + } + } } /** diff --git a/src/commands/dev/CreateInvite.ts b/src/commands/dev/CreateInvite.ts index df994bdfc..47b51c2dd 100644 --- a/src/commands/dev/CreateInvite.ts +++ b/src/commands/dev/CreateInvite.ts @@ -1,70 +1,77 @@ -import { ChannelType, PermissionFlagsBits, type TextChannel } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { ChannelType, PermissionFlagsBits, type TextChannel } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class CreateInvite extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "createinvite", - description: { - content: "Create an invite link for a guild", - examples: ["createinvite 0000000000000000000"], - usage: "createinvite ", - }, - category: "dev", - aliases: ["ci", "gi", "ginvite", "guildinvite"], - cooldown: 3, - args: true, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: true, - client: ["SendMessages", "CreateInstantInvite", "ReadMessageHistory", "EmbedLinks", "ViewChannel"], - user: [], - }, - slashCommand: false, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'createinvite', + description: { + content: 'Create an invite link for a guild', + examples: ['createinvite 0000000000000000000'], + usage: 'createinvite ', + }, + category: 'dev', + aliases: ['ci', 'gi', 'ginvite', 'guildinvite'], + cooldown: 3, + args: true, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: true, + client: ['SendMessages', 'CreateInstantInvite', 'ReadMessageHistory', 'EmbedLinks', 'ViewChannel'], + user: [], + }, + slashCommand: false, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const guild = client.guilds.cache.get(args[0]); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const guild = client.guilds.cache.get(args[0]); - if (!guild) { - return await ctx.sendMessage({ - embeds: [this.client.embed().setColor(this.client.color.red).setDescription("Guild not found")], - }); - } + if (!guild) { + return await ctx.sendMessage({ + embeds: [this.client.embed().setColor(this.client.color.red).setDescription('Guild not found')], + }); + } - const textChannel = guild.channels.cache.find( - (c) => - c.type === ChannelType.GuildText && - c - .permissionsFor(guild.members.me!) - ?.has(PermissionFlagsBits.CreateInstantInvite | PermissionFlagsBits.SendMessages | PermissionFlagsBits.ViewChannel), - ) as TextChannel | undefined; + const textChannel = guild.channels.cache.find( + c => + c.type === ChannelType.GuildText && + c + .permissionsFor(guild.members.me!) + ?.has( + PermissionFlagsBits.CreateInstantInvite | + PermissionFlagsBits.SendMessages | + PermissionFlagsBits.ViewChannel, + ), + ) as TextChannel | undefined; - if (!textChannel) { - return await ctx.sendMessage({ - embeds: [this.client.embed().setColor(this.client.color.red).setDescription("No suitable channel found")], - }); - } + if (!textChannel) { + return await ctx.sendMessage({ + embeds: [this.client.embed().setColor(this.client.color.red).setDescription('No suitable channel found')], + }); + } - const invite = await textChannel.createInvite({ - maxAge: 3600, - maxUses: 0, - reason: `Requested by developer: ${ctx.author.username}`, - }); + const invite = await textChannel.createInvite({ + maxAge: 3600, + maxUses: 0, + reason: `Requested by developer: ${ctx.author?.username}`, + }); - return await ctx.sendMessage({ - embeds: [ - this.client.embed().setColor(this.client.color.main).setDescription(`Invite link for ${guild.name}: [Link](${invite.url})`), - ], - }); - } + return await ctx.sendMessage({ + embeds: [ + this.client + .embed() + .setColor(this.client.color.main) + .setDescription(`Invite link for ${guild.name}: [Link](${invite.url})`), + ], + }); + } } /** diff --git a/src/commands/dev/DeleteInvites.ts b/src/commands/dev/DeleteInvites.ts index 31eeb71a0..1d03df006 100644 --- a/src/commands/dev/DeleteInvites.ts +++ b/src/commands/dev/DeleteInvites.ts @@ -1,51 +1,51 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class DestroyInvites extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "destroyinvites", - description: { - content: "Destroy all invite links created by the bot in a guild", - examples: ["destroyinvites 0000000000000000000"], - usage: "destroyinvites ", - }, - category: "dev", - aliases: ["di"], - cooldown: 3, - args: true, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: true, - client: ["SendMessages", "ManageGuild", "ReadMessageHistory", "ViewChannel"], - user: [], - }, - slashCommand: false, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'destroyinvites', + description: { + content: 'Destroy all invite links created by the bot in a guild', + examples: ['destroyinvites 0000000000000000000'], + usage: 'destroyinvites ', + }, + category: 'dev', + aliases: ['di'], + cooldown: 3, + args: true, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: true, + client: ['SendMessages', 'ManageGuild', 'ReadMessageHistory', 'ViewChannel'], + user: [], + }, + slashCommand: false, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const guild = client.guilds.cache.get(args[0]); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const guild = client.guilds.cache.get(args[0]); - if (!guild) { - return await ctx.sendMessage("Guild not found."); - } + if (!guild) { + return await ctx.sendMessage('Guild not found.'); + } - try { - const botInvites = (await guild.invites.fetch()).filter((invite) => invite.inviter?.id === client.user?.id); + try { + const botInvites = (await guild.invites.fetch()).filter(invite => invite.inviter?.id === client.user?.id); - await Promise.all(botInvites.map((invite) => invite.delete())); + await Promise.all(botInvites.map(invite => invite.delete())); - return await ctx.sendMessage(`Destroyed ${botInvites.size} invite(s) created by the bot.`); - } catch { - return await ctx.sendMessage("Failed to destroy invites."); - } - } + return await ctx.sendMessage(`Destroyed ${botInvites.size} invite(s) created by the bot.`); + } catch { + return await ctx.sendMessage('Failed to destroy invites.'); + } + } } /** diff --git a/src/commands/dev/Deploy.ts b/src/commands/dev/Deploy.ts index 2d011ae57..b8da8c881 100644 --- a/src/commands/dev/Deploy.ts +++ b/src/commands/dev/Deploy.ts @@ -1,101 +1,109 @@ -import { ActionRowBuilder, ButtonBuilder, type ButtonInteraction, ButtonStyle, ComponentType, type Message } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { + ActionRowBuilder, + ButtonBuilder, + type ButtonInteraction, + ButtonStyle, + ComponentType, + type Message, + type TextChannel, +} from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Deploy extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "deploy", - description: { - content: "Deploy commands", - examples: ["deploy"], - usage: "deploy", - }, - category: "dev", - aliases: ["deploy-commands"], - cooldown: 3, - args: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: true, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: false, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'deploy', + description: { + content: 'Deploy commands', + examples: ['deploy'], + usage: 'deploy', + }, + category: 'dev', + aliases: ['deploy-commands'], + cooldown: 3, + args: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: true, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: false, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context, _args: string[]): Promise { - const row = new ActionRowBuilder().addComponents( - new ButtonBuilder().setCustomId("deploy-global").setLabel("Global").setStyle(ButtonStyle.Secondary), - new ButtonBuilder().setCustomId("deploy-guild").setLabel("Guild").setStyle(ButtonStyle.Secondary), - ); + public async run(client: Lavamusic, ctx: Context, _args: string[]): Promise { + const row = new ActionRowBuilder().addComponents( + new ButtonBuilder().setCustomId('deploy-global').setLabel('Global').setStyle(ButtonStyle.Secondary), + new ButtonBuilder().setCustomId('deploy-guild').setLabel('Guild').setStyle(ButtonStyle.Secondary), + ); - let msg: Message | undefined; - try { - msg = await ctx.sendMessage({ - content: "Where do you want to deploy the commands?", - components: [row], - }); - } catch (error) { - console.error("Failed to send the initial message:", error); - return; - } + let msg: Message | undefined; + try { + msg = await ctx.sendMessage({ + content: 'Where do you want to deploy the commands?', + components: [row], + }); + } catch (error) { + console.error('Failed to send the initial message:', error); + return; + } - const filter = (interaction: ButtonInteraction<"cached">) => { - if (interaction.user.id !== ctx.author.id) { - interaction - .reply({ - content: "You can't interact with this message", - ephemeral: true, - }) - .catch(console.error); - return false; - } - return true; - }; + const filter = (interaction: ButtonInteraction<'cached'>) => { + if (interaction.user.id !== ctx.author?.id) { + interaction + .reply({ + content: "You can't interact with this message", + ephemeral: true, + }) + .catch(console.error); + return false; + } + return true; + }; - const collector = ctx.channel.createMessageComponentCollector({ - filter, - componentType: ComponentType.Button, - time: 30000, - }); + const collector = (ctx.channel as TextChannel).createMessageComponentCollector({ + filter, + componentType: ComponentType.Button, + time: 30000, + }); - collector.on("collect", async (interaction) => { - try { - if (interaction.customId === "deploy-global") { - await client.deployCommands(); - await ctx.editMessage({ - content: "Commands deployed globally.", - components: [], - }); - } else if (interaction.customId === "deploy-guild") { - await client.deployCommands(interaction.guild!.id); - await ctx.editMessage({ - content: "Commands deployed in this guild.", - components: [], - }); - } - } catch (error) { - console.error("Failed to handle interaction:", error); - } - }); + collector.on('collect', async interaction => { + try { + if (interaction.customId === 'deploy-global') { + await client.deployCommands(); + await ctx.editMessage({ + content: 'Commands deployed globally.', + components: [], + }); + } else if (interaction.customId === 'deploy-guild') { + await client.deployCommands(interaction.guild!.id); + await ctx.editMessage({ + content: 'Commands deployed in this guild.', + components: [], + }); + } + } catch (error) { + console.error('Failed to handle interaction:', error); + } + }); - collector.on("end", async (_collected, reason) => { - if (reason === "time" && msg) { - try { - await msg.delete(); - } catch (error) { - console.error("Failed to delete the message:", error); - } - } - }); - } + collector.on('end', async (_collected, reason) => { + if (reason === 'time' && msg) { + try { + await msg.delete(); + } catch (error) { + console.error('Failed to delete the message:', error); + } + } + }); + } } /** diff --git a/src/commands/dev/Eval.ts b/src/commands/dev/Eval.ts index 810128993..1a5eda618 100644 --- a/src/commands/dev/Eval.ts +++ b/src/commands/dev/Eval.ts @@ -1,81 +1,81 @@ -import util from "node:util"; -import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; -import { fetch } from "undici"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import util from 'node:util'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; +import { fetch } from 'undici'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Eval extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "eval", - description: { - content: "Evaluate code", - examples: ["eval"], - usage: "eval", - }, - category: "dev", - aliases: ["ev"], - cooldown: 3, - args: true, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: true, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: false, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'eval', + description: { + content: 'Evaluate code', + examples: ['eval'], + usage: 'eval', + }, + category: 'dev', + aliases: ['ev'], + cooldown: 3, + args: true, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: true, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: false, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const code = args.join(" "); - try { - let evaled = eval(code); - if (evaled === client.config) evaled = "Nice try"; + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const code = args.join(' '); + try { + let evaled = eval(code); + if (evaled === client.config) evaled = 'Nice try'; - if (typeof evaled !== "string") evaled = util.inspect(evaled); - if (evaled.length > 2000) { - const response = await fetch("https://hasteb.in/post", { - method: "POST", - headers: { - "Content-Type": "text/plain", - }, - body: evaled, - }); - const json: any = await response.json(); - evaled = `https://hasteb.in/${json.key}`; - return await ctx.sendMessage({ - content: evaled, - }); - } + if (typeof evaled !== 'string') evaled = util.inspect(evaled); + if (evaled.length > 2000) { + const response = await fetch('https://hasteb.in/post', { + method: 'POST', + headers: { + 'Content-Type': 'text/plain', + }, + body: evaled, + }); + const json: any = await response.json(); + evaled = `https://hasteb.in/${json.key}`; + return await ctx.sendMessage({ + content: evaled, + }); + } - const button = new ButtonBuilder().setStyle(ButtonStyle.Danger).setLabel("Delete").setCustomId("eval-delete"); - const row = new ActionRowBuilder().addComponents(button); + const button = new ButtonBuilder().setStyle(ButtonStyle.Danger).setLabel('Delete').setCustomId('eval-delete'); + const row = new ActionRowBuilder().addComponents(button); - const msg = await ctx.sendMessage({ - content: `\`\`\`js\n${evaled}\n\`\`\``, - components: [row], - }); + const msg = await ctx.sendMessage({ + content: `\`\`\`js\n${evaled}\n\`\`\``, + components: [row], + }); - const filter = (i: any) => i.customId === "eval-delete" && i.user.id === ctx.author.id; - const collector = msg.createMessageComponentCollector({ - time: 60000, - filter: filter, - }); + const filter = (i: any) => i.customId === 'eval-delete' && i.user.id === ctx.author?.id; + const collector = msg.createMessageComponentCollector({ + time: 60000, + filter: filter, + }); - collector.on("collect", async (i) => { - await i.deferUpdate(); - await msg.delete(); - }); - } catch (e) { - ctx.sendMessage(`\`\`\`js\n${e}\n\`\`\``); - } - } + collector.on('collect', async i => { + await i.deferUpdate(); + await msg.delete(); + }); + } catch (e) { + ctx.sendMessage(`\`\`\`js\n${e}\n\`\`\``); + } + } } /** diff --git a/src/commands/dev/GuildLeave.ts b/src/commands/dev/GuildLeave.ts index d8f5ff80c..f038ca065 100644 --- a/src/commands/dev/GuildLeave.ts +++ b/src/commands/dev/GuildLeave.ts @@ -1,75 +1,75 @@ -import { ChannelType, type TextChannel } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { ChannelType, type TextChannel } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class GuildLeave extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "guildleave", - description: { - content: "Leave a guild", - examples: ["guildleave "], - usage: "guildleave ", - }, - category: "dev", - aliases: ["gl"], - cooldown: 3, - args: true, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: true, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: false, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'guildleave', + description: { + content: 'Leave a guild', + examples: ['guildleave '], + usage: 'guildleave ', + }, + category: 'dev', + aliases: ['gl'], + cooldown: 3, + args: true, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: true, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: false, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const guildId = args[0]; + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const guildId = args[0]; - const guild = await client.shard - .broadcastEval( - (c, { guildId }) => { - const guild = c.guilds.cache.get(guildId); - return guild ? { id: guild.id, name: guild.name } : null; - }, - { context: { guildId } }, - ) - .then((results) => results.find((g) => g !== null)); + const guild = await client.shard + ?.broadcastEval( + (c, { guildId }) => { + const guild = c.guilds.cache.get(guildId); + return guild ? { id: guild.id, name: guild.name } : null; + }, + { context: { guildId } }, + ) + .then(results => results.find(g => g !== null)); - if (!guild) { - return await ctx.sendMessage("Guild not found."); - } + if (!guild) { + return await ctx.sendMessage('Guild not found.'); + } - try { - await client.shard.broadcastEval( - async (c, { guildId }) => { - const guild = c.guilds.cache.get(guildId); - if (guild) { - await guild.leave(); - } - }, - { context: { guildId } }, - ); - await ctx.sendMessage(`Left guild ${guild.name}`); - } catch { - await ctx.sendMessage(`Failed to leave guild ${guild.name}`); - } + try { + await client.shard?.broadcastEval( + async (c, { guildId }) => { + const guild = c.guilds.cache.get(guildId); + if (guild) { + await guild.leave(); + } + }, + { context: { guildId } }, + ); + await ctx.sendMessage(`Left guild ${guild.name}`); + } catch { + await ctx.sendMessage(`Failed to leave guild ${guild.name}`); + } - const logChannelId = process.env.LOG_CHANNEL_ID; - if (logChannelId) { - const logChannel = client.channels.cache.get(logChannelId) as TextChannel; - if (logChannel && logChannel.type === ChannelType.GuildText) { - await logChannel.send(`Bot has left guild: ${guild.name} (ID: ${guild.id})`); - } - } - } + const logChannelId = process.env.LOG_CHANNEL_ID; + if (logChannelId) { + const logChannel = client.channels.cache.get(logChannelId) as TextChannel; + if (logChannel && logChannel.type === ChannelType.GuildText) { + await logChannel.send(`Bot has left guild: ${guild.name} (ID: ${guild.id})`); + } + } + } } /** diff --git a/src/commands/dev/GuildList.ts b/src/commands/dev/GuildList.ts index 4dfb2e58f..43ed3e15b 100644 --- a/src/commands/dev/GuildList.ts +++ b/src/commands/dev/GuildList.ts @@ -1,49 +1,51 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class GuildList extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "guildlist", - description: { - content: "List all guilds the bot is in", - examples: ["guildlist"], - usage: "guildlist", - }, - category: "dev", - aliases: ["glst"], - cooldown: 3, - args: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: true, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: false, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'guildlist', + description: { + content: 'List all guilds the bot is in', + examples: ['guildlist'], + usage: 'guildlist', + }, + category: 'dev', + aliases: ['glst'], + cooldown: 3, + args: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: true, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: false, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const guilds = await client.shard.broadcastEval((c) => c.guilds.cache.map((guild) => ({ name: guild.name, id: guild.id }))); - const allGuilds = guilds.reduce((acc, val) => acc.concat(val), []); + public async run(client: Lavamusic, ctx: Context): Promise { + const guilds = await client.shard?.broadcastEval(c => + c.guilds.cache.map(guild => ({ name: guild.name, id: guild.id })), + ); + const allGuilds = guilds?.reduce((acc, val) => acc.concat(val), []); - const guildList = allGuilds.map((guild) => `- **${guild.name}** - ${guild.id}`); - const chunks = client.utils.chunk(guildList, 10) || [[]]; - const pages = chunks.map((chunk, index) => { - return this.client - .embed() - .setColor(this.client.color.main) - .setDescription(chunk.join("\n")) - .setFooter({ text: `Page ${index + 1} of ${chunks.length}` }); - }); - await client.utils.paginate(client, ctx, pages); - } + const guildList = allGuilds?.map(guild => `- **${guild.name}** - ${guild.id}`); + const chunks = client.utils.chunk(guildList!, 10) || [[]]; + const pages = chunks.map((chunk, index) => { + return this.client + .embed() + .setColor(this.client.color.main) + .setDescription(chunk.join('\n')) + .setFooter({ text: `Page ${index + 1} of ${chunks.length}` }); + }); + await client.utils.paginate(client, ctx, pages); + } } /** diff --git a/src/commands/dev/Restart.ts b/src/commands/dev/Restart.ts index 9fbe29fa5..357ef6719 100644 --- a/src/commands/dev/Restart.ts +++ b/src/commands/dev/Restart.ts @@ -1,79 +1,82 @@ -import { exec } from "node:child_process"; -import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { exec } from 'node:child_process'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Restart extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "restart", - description: { - content: "Restart the bot", - examples: ["restart"], - usage: "restart", - }, - category: "dev", - aliases: ["reboot"], - cooldown: 3, - args: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: true, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: false, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'restart', + description: { + content: 'Restart the bot', + examples: ['restart'], + usage: 'restart', + }, + category: 'dev', + aliases: ['reboot'], + cooldown: 3, + args: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: true, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: false, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const embed = this.client.embed(); - const button = new ButtonBuilder().setStyle(ButtonStyle.Danger).setLabel("Confirm Restart").setCustomId("confirm-restart"); - const row = new ActionRowBuilder().addComponents(button); - const restartEmbed = embed - .setColor(this.client.color.red) - .setDescription(`**Are you sure you want to restart **\`${client.user.username}\`?`) - .setTimestamp(); + public async run(client: Lavamusic, ctx: Context): Promise { + const embed = this.client.embed(); + const button = new ButtonBuilder() + .setStyle(ButtonStyle.Danger) + .setLabel('Confirm Restart') + .setCustomId('confirm-restart'); + const row = new ActionRowBuilder().addComponents(button); + const restartEmbed = embed + .setColor(this.client.color.red) + .setDescription(`**Are you sure you want to restart **\`${client.user?.username}\`?`) + .setTimestamp(); - const msg = await ctx.sendMessage({ - embeds: [restartEmbed], - components: [row], - }); + const msg = await ctx.sendMessage({ + embeds: [restartEmbed], + components: [row], + }); - const filter = (i: any) => i.customId === "confirm-restart" && i.user.id === ctx.author.id; - const collector = msg.createMessageComponentCollector({ - time: 30000, - filter, - }); + const filter = (i: any) => i.customId === 'confirm-restart' && i.user.id === ctx.author?.id; + const collector = msg.createMessageComponentCollector({ + time: 30000, + filter, + }); - collector.on("collect", async (i) => { - await i.deferUpdate(); + collector.on('collect', async i => { + await i.deferUpdate(); - await msg.edit({ - content: "Restarting the bot...", - embeds: [], - components: [], - }); + await msg.edit({ + content: 'Restarting the bot...', + embeds: [], + components: [], + }); - await client.destroy(); - exec("node scripts/restart.js"); - process.exit(0); - }); + await client.destroy(); + exec('node scripts/restart.js'); + process.exit(0); + }); - collector.on("end", async () => { - if (collector.collected.size === 0) { - await msg.edit({ - content: "Restart cancelled.", - components: [], - }); - } - }); - } + collector.on('end', async () => { + if (collector.collected.size === 0) { + await msg.edit({ + content: 'Restart cancelled.', + components: [], + }); + } + }); + } } /** diff --git a/src/commands/filters/8d.ts b/src/commands/filters/8d.ts index 9990b4655..8d106a29c 100644 --- a/src/commands/filters/8d.ts +++ b/src/commands/filters/8d.ts @@ -1,61 +1,61 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class _8d extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "8d", - description: { - content: "cmd.8d.description", - examples: ["8d"], - usage: "8d", - }, - category: "filters", - aliases: ["3d"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: '8d', + description: { + content: 'cmd.8d.description', + examples: ['8d'], + usage: '8d', + }, + category: 'filters', + aliases: ['3d'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const filterEnabled = player.filterManager.filters.rotation; + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const filterEnabled = player.filterManager.filters.rotation; - if (filterEnabled) { - await player.filterManager.toggleRotation(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.8d.messages.filter_disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - await player.filterManager.toggleRotation(0.2); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.8d.messages.filter_enabled"), - color: this.client.color.main, - }, - ], - }); - } - } + if (filterEnabled) { + await player.filterManager.toggleRotation(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.8d.messages.filter_disabled'), + color: this.client.color.main, + }, + ], + }); + } else { + await player.filterManager.toggleRotation(0.2); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.8d.messages.filter_enabled'), + color: this.client.color.main, + }, + ], + }); + } + } } /** diff --git a/src/commands/filters/BassBoost.ts b/src/commands/filters/BassBoost.ts index 3b9646805..fa95cef36 100644 --- a/src/commands/filters/BassBoost.ts +++ b/src/commands/filters/BassBoost.ts @@ -1,100 +1,104 @@ -import { ApplicationCommandOptionType } from "discord.js"; -import { EQList } from "lavalink-client"; -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { ApplicationCommandOptionType } from 'discord.js'; +import { EQList } from 'lavalink-client'; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class BassBoost extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "bassboost", - description: { - content: "cmd.bassboost.description", - examples: ["bassboost high", "bassboost medium", "bassboost low", "bassboost off"], - usage: "bassboost [level]", - }, - category: "filters", - aliases: ["bb"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "level", - description: "cmd.bassboost.options.level", - type: ApplicationCommandOptionType.String, - required: true, - choices: [ - { name: "high", value: "high" }, - { name: "medium", value: "medium" }, - { name: "low", value: "low" }, - { name: "off", value: "off" }, - ], - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'bassboost', + description: { + content: 'cmd.bassboost.description', + examples: ['bassboost high', 'bassboost medium', 'bassboost low', 'bassboost off'], + usage: 'bassboost [level]', + }, + category: 'filters', + aliases: ['bb'], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'level', + description: 'cmd.bassboost.options.level', + type: ApplicationCommandOptionType.String, + required: true, + choices: [ + { name: 'high', value: 'high' }, + { name: 'medium', value: 'medium' }, + { name: 'low', value: 'low' }, + { name: 'off', value: 'off' }, + ], + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); - switch (ctx.args[0]?.toLowerCase()) { - case "high": - await player.filterManager.setEQ(EQList.BassboostHigh); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.bassboost.messages.high"), - color: this.client.color.main, - }, - ], - }); - break; - case "medium": - await player.filterManager.setEQ(EQList.BassboostMedium); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.bassboost.messages.medium"), - color: this.client.color.main, - }, - ], - }); - break; - case "low": - await player.filterManager.setEQ(EQList.BassboostLow); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.bassboost.messages.low"), - color: this.client.color.main, - }, - ], - }); - break; - case "off": - await player.filterManager.clearEQ(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.bassboost.messages.off"), - color: this.client.color.main, - }, - ], - }); - break; - } - } + switch (ctx.args[0]?.toLowerCase()) { + case 'high': { + await player.filterManager.setEQ(EQList.BassboostHigh); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.bassboost.messages.high'), + color: this.client.color.main, + }, + ], + }); + break; + } + case 'medium': { + await player.filterManager.setEQ(EQList.BassboostMedium); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.bassboost.messages.medium'), + color: this.client.color.main, + }, + ], + }); + break; + } + case 'low': { + await player.filterManager.setEQ(EQList.BassboostLow); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.bassboost.messages.low'), + color: this.client.color.main, + }, + ], + }); + break; + } + case 'off': { + await player.filterManager.clearEQ(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.bassboost.messages.off'), + color: this.client.color.main, + }, + ], + }); + break; + } + } + } } /** diff --git a/src/commands/filters/Karaoke.ts b/src/commands/filters/Karaoke.ts index 8025f4df8..f23ed6367 100644 --- a/src/commands/filters/Karaoke.ts +++ b/src/commands/filters/Karaoke.ts @@ -1,61 +1,61 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class Karaoke extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "karaoke", - description: { - content: "cmd.karaoke.description", - examples: ["karaoke"], - usage: "karaoke", - }, - category: "filters", - aliases: ["kk"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'karaoke', + description: { + content: 'cmd.karaoke.description', + examples: ['karaoke'], + usage: 'karaoke', + }, + category: 'filters', + aliases: ['kk'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const filterEnabled = player.filterManager.filters.karaoke; + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const filterEnabled = player.filterManager.filters.karaoke; - if (filterEnabled) { - await player.filterManager.toggleKaraoke(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.karaoke.messages.filter_disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - await player.filterManager.toggleKaraoke(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.karaoke.messages.filter_enabled"), - color: this.client.color.main, - }, - ], - }); - } - } + if (filterEnabled) { + await player.filterManager.toggleKaraoke(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.karaoke.messages.filter_disabled'), + color: this.client.color.main, + }, + ], + }); + } else { + await player.filterManager.toggleKaraoke(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.karaoke.messages.filter_enabled'), + color: this.client.color.main, + }, + ], + }); + } + } } /** diff --git a/src/commands/filters/LowPass.ts b/src/commands/filters/LowPass.ts index 10d32278d..1d751a19c 100644 --- a/src/commands/filters/LowPass.ts +++ b/src/commands/filters/LowPass.ts @@ -1,61 +1,61 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class LowPass extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "lowpass", - description: { - content: "cmd.lowpass.description", - examples: ["lowpass"], - usage: "lowpass ", - }, - category: "filters", - aliases: ["lp"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'lowpass', + description: { + content: 'cmd.lowpass.description', + examples: ['lowpass'], + usage: 'lowpass ', + }, + category: 'filters', + aliases: ['lp'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const filterEnabled = player.filterManager.filters.lowPass; + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const filterEnabled = player.filterManager.filters.lowPass; - if (filterEnabled) { - await player.filterManager.toggleLowPass(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.lowpass.messages.filter_disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - await player.filterManager.toggleLowPass(20); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.lowpass.messages.filter_enabled"), - color: this.client.color.main, - }, - ], - }); - } - } + if (filterEnabled) { + await player.filterManager.toggleLowPass(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.lowpass.messages.filter_disabled'), + color: this.client.color.main, + }, + ], + }); + } else { + await player.filterManager.toggleLowPass(20); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.lowpass.messages.filter_enabled'), + color: this.client.color.main, + }, + ], + }); + } + } } /** diff --git a/src/commands/filters/NightCore.ts b/src/commands/filters/NightCore.ts index b25b63eb9..043a25f5a 100644 --- a/src/commands/filters/NightCore.ts +++ b/src/commands/filters/NightCore.ts @@ -1,61 +1,61 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class NightCore extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "nightcore", - description: { - content: "cmd.nightcore.description", - examples: ["nightcore"], - usage: "nightcore", - }, - category: "filters", - aliases: ["nc"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'nightcore', + description: { + content: 'cmd.nightcore.description', + examples: ['nightcore'], + usage: 'nightcore', + }, + category: 'filters', + aliases: ['nc'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const filterEnabled = player.filterManager.filters.nightcore; + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const filterEnabled = player.filterManager.filters.nightcore; - if (filterEnabled) { - await player.filterManager.toggleNightcore(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.nightcore.messages.filter_disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - await player.filterManager.toggleNightcore(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.nightcore.messages.filter_enabled"), - color: this.client.color.main, - }, - ], - }); - } - } + if (filterEnabled) { + await player.filterManager.toggleNightcore(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.nightcore.messages.filter_disabled'), + color: this.client.color.main, + }, + ], + }); + } else { + await player.filterManager.toggleNightcore(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.nightcore.messages.filter_enabled'), + color: this.client.color.main, + }, + ], + }); + } + } } /** diff --git a/src/commands/filters/Pitch.ts b/src/commands/filters/Pitch.ts index 52bd34ba5..7098df309 100644 --- a/src/commands/filters/Pitch.ts +++ b/src/commands/filters/Pitch.ts @@ -1,72 +1,72 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class Pitch extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "pitch", - description: { - content: "cmd.pitch.description", - examples: ["pitch 1", "pitch 1.5", "pitch 1,5"], - usage: "pitch ", - }, - category: "filters", - aliases: ["ph"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "pitch", - description: "cmd.pitch.options.pitch", - type: 3, - required: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'pitch', + description: { + content: 'cmd.pitch.description', + examples: ['pitch 1', 'pitch 1.5', 'pitch 1,5'], + usage: 'pitch ', + }, + category: 'filters', + aliases: ['ph'], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'pitch', + description: 'cmd.pitch.options.pitch', + type: 3, + required: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const pitchString = args[0].replace(",", "."); - const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(pitchString); - const pitch = parseFloat(pitchString); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const pitchString = args[0].replace(',', '.'); + const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(pitchString); + const pitch = Number.parseFloat(pitchString); - if (!isValidNumber || isNaN(pitch) || pitch < 0.5 || pitch > 5) { - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.pitch.errors.invalid_number"), - color: this.client.color.red, - }, - ], - }); - return; - } + if (!isValidNumber || Number.isNaN(pitch) || pitch < 0.5 || pitch > 5) { + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.pitch.errors.invalid_number'), + color: this.client.color.red, + }, + ], + }); + return; + } - await player.filterManager.setPitch(pitch); - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.pitch.messages.pitch_set", { - pitch, - }), - color: this.client.color.main, - }, - ], - }); - } + await player.filterManager.setPitch(pitch); + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.pitch.messages.pitch_set', { + pitch, + }), + color: this.client.color.main, + }, + ], + }); + } } /** diff --git a/src/commands/filters/Rate.ts b/src/commands/filters/Rate.ts index ccfc1d712..8c8204178 100644 --- a/src/commands/filters/Rate.ts +++ b/src/commands/filters/Rate.ts @@ -1,72 +1,72 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class Rate extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "rate", - description: { - content: "cmd.rate.description", - examples: ["rate 1", "rate 1.5", "rate 1,5"], - usage: "rate ", - }, - category: "filters", - aliases: ["rt"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "rate", - description: "cmd.rate.options.rate", - type: 3, - required: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'rate', + description: { + content: 'cmd.rate.description', + examples: ['rate 1', 'rate 1.5', 'rate 1,5'], + usage: 'rate ', + }, + category: 'filters', + aliases: ['rt'], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'rate', + description: 'cmd.rate.options.rate', + type: 3, + required: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const rateString = args[0].replace(",", "."); - const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(rateString); - const rate = parseFloat(rateString); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const rateString = args[0].replace(',', '.'); + const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(rateString); + const rate = Number.parseFloat(rateString); - if (!isValidNumber || isNaN(rate) || rate < 0.5 || rate > 5) { - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.rate.errors.invalid_number"), - color: this.client.color.red, - }, - ], - }); - return; - } + if (!isValidNumber || Number.isNaN(rate) || rate < 0.5 || rate > 5) { + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.rate.errors.invalid_number'), + color: this.client.color.red, + }, + ], + }); + return; + } - await player.filterManager.setRate(rate); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.rate.messages.rate_set", { - rate, - }), - color: this.client.color.main, - }, - ], - }); - } + await player.filterManager.setRate(rate); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.rate.messages.rate_set', { + rate, + }), + color: this.client.color.main, + }, + ], + }); + } } /** diff --git a/src/commands/filters/Reset.ts b/src/commands/filters/Reset.ts index 83c2d2c97..f527932ac 100644 --- a/src/commands/filters/Reset.ts +++ b/src/commands/filters/Reset.ts @@ -1,48 +1,48 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class Reset extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "reset", - description: { - content: "cmd.reset.description", - examples: ["reset"], - usage: "reset", - }, - category: "filters", - aliases: ["rs"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'reset', + description: { + content: 'cmd.reset.description', + examples: ['reset'], + usage: 'reset', + }, + category: 'filters', + aliases: ['rs'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - player.filterManager.resetFilters(); - player.filterManager.clearEQ(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.reset.messages.filters_reset"), - color: this.client.color.main, - }, - ], - }); - } + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + player.filterManager.resetFilters(); + player.filterManager.clearEQ(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.reset.messages.filters_reset'), + color: this.client.color.main, + }, + ], + }); + } } /** diff --git a/src/commands/filters/Rotation.ts b/src/commands/filters/Rotation.ts index c0355c74e..32cff3e57 100644 --- a/src/commands/filters/Rotation.ts +++ b/src/commands/filters/Rotation.ts @@ -1,59 +1,59 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class Rotation extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "rotation", - description: { - content: "cmd.rotation.description", - examples: ["rotation"], - usage: "rotation", - }, - category: "filters", - aliases: ["rt"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'rotation', + description: { + content: 'cmd.rotation.description', + examples: ['rotation'], + usage: 'rotation', + }, + category: 'filters', + aliases: ['rt'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - if (player.filterManager.filters.rotation) { - player.filterManager.toggleRotation(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.rotation.messages.disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - player.filterManager.toggleRotation(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.rotation.messages.enabled"), - color: this.client.color.main, - }, - ], - }); - } - } + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + if (player.filterManager.filters.rotation) { + player.filterManager.toggleRotation(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.rotation.messages.disabled'), + color: this.client.color.main, + }, + ], + }); + } else { + player.filterManager.toggleRotation(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.rotation.messages.enabled'), + color: this.client.color.main, + }, + ], + }); + } + } } /** diff --git a/src/commands/filters/Speed.ts b/src/commands/filters/Speed.ts index 6f6b9bc26..1c26e0f9a 100644 --- a/src/commands/filters/Speed.ts +++ b/src/commands/filters/Speed.ts @@ -1,72 +1,72 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class Speed extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "speed", - description: { - content: "cmd.speed.description", - examples: ["speed 1.5", "speed 1,5"], - usage: "speed ", - }, - category: "filters", - aliases: ["spd"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "speed", - description: "cmd.speed.options.speed", - type: 3, - required: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'speed', + description: { + content: 'cmd.speed.description', + examples: ['speed 1.5', 'speed 1,5'], + usage: 'speed ', + }, + category: 'filters', + aliases: ['spd'], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'speed', + description: 'cmd.speed.options.speed', + type: 3, + required: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const speedString = args[0].replace(",", "."); - const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(speedString); - const speed = parseFloat(speedString); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const speedString = args[0].replace(',', '.'); + const isValidNumber = /^[0-9]*\.?[0-9]+$/.test(speedString); + const speed = Number.parseFloat(speedString); - if (!isValidNumber || isNaN(speed) || speed < 0.5 || speed > 5) { - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.speed.messages.invalid_number"), - color: this.client.color.red, - }, - ], - }); - return; - } + if (!isValidNumber || Number.isNaN(speed) || speed < 0.5 || speed > 5) { + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.speed.messages.invalid_number'), + color: this.client.color.red, + }, + ], + }); + return; + } - player.filterManager.setSpeed(speed); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.speed.messages.set_speed", { - speed, - }), - color: this.client.color.main, - }, - ], - }); - } + player.filterManager.setSpeed(speed); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.speed.messages.set_speed', { + speed, + }), + color: this.client.color.main, + }, + ], + }); + } } /** diff --git a/src/commands/filters/Tremolo.ts b/src/commands/filters/Tremolo.ts index 40901d77e..bb38235d5 100644 --- a/src/commands/filters/Tremolo.ts +++ b/src/commands/filters/Tremolo.ts @@ -1,61 +1,61 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class Tremolo extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "tremolo", - description: { - content: "cmd.tremolo.description", - examples: ["tremolo"], - usage: "tremolo", - }, - category: "filters", - aliases: ["tr"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'tremolo', + description: { + content: 'cmd.tremolo.description', + examples: ['tremolo'], + usage: 'tremolo', + }, + category: 'filters', + aliases: ['tr'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const tremoloEnabled = player.filterManager.filters.tremolo; + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const tremoloEnabled = player.filterManager.filters.tremolo; - if (tremoloEnabled) { - player.filterManager.toggleTremolo(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.tremolo.messages.disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - player.filterManager.toggleTremolo(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.tremolo.messages.enabled"), - color: this.client.color.main, - }, - ], - }); - } - } + if (tremoloEnabled) { + player.filterManager.toggleTremolo(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.tremolo.messages.disabled'), + color: this.client.color.main, + }, + ], + }); + } else { + player.filterManager.toggleTremolo(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.tremolo.messages.enabled'), + color: this.client.color.main, + }, + ], + }); + } + } } /** diff --git a/src/commands/filters/Vibrato.ts b/src/commands/filters/Vibrato.ts index 33d5769e8..10eee4423 100644 --- a/src/commands/filters/Vibrato.ts +++ b/src/commands/filters/Vibrato.ts @@ -1,61 +1,61 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index.js"; +import { Command, type Context, type Lavamusic } from '../../structures/index.js'; export default class Vibrato extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "vibrato", - description: { - content: "cmd.vibrato.description", - examples: ["vibrato"], - usage: "vibrato", - }, - category: "filters", - aliases: ["vb"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'vibrato', + description: { + content: 'cmd.vibrato.description', + examples: ['vibrato'], + usage: 'vibrato', + }, + category: 'filters', + aliases: ['vb'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const vibratoEnabled = player.filterManager.filters.vibrato; + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const vibratoEnabled = player.filterManager.filters.vibrato; - if (vibratoEnabled) { - player.filterManager.toggleVibrato(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.vibrato.messages.disabled"), - color: this.client.color.main, - }, - ], - }); - } else { - player.filterManager.toggleVibrato(); - await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.vibrato.messages.enabled"), - color: this.client.color.main, - }, - ], - }); - } - } + if (vibratoEnabled) { + player.filterManager.toggleVibrato(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.vibrato.messages.disabled'), + color: this.client.color.main, + }, + ], + }); + } else { + player.filterManager.toggleVibrato(); + await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.vibrato.messages.enabled'), + color: this.client.color.main, + }, + ], + }); + } + } } /** diff --git a/src/commands/info/About.ts b/src/commands/info/About.ts index 5893d3c28..73093e8b6 100644 --- a/src/commands/info/About.ts +++ b/src/commands/info/About.ts @@ -1,84 +1,86 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class About extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "about", - description: { - content: "cmd.about.description", - examples: ["about"], - usage: "about", - }, - category: "info", - aliases: ["ab"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'about', + description: { + content: 'cmd.about.description', + examples: ['about'], + usage: 'about', + }, + category: 'info', + aliases: ['ab'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const inviteButton = new ButtonBuilder() - .setLabel(ctx.locale("buttons.invite")) - .setStyle(ButtonStyle.Link) - .setURL( - `https://discord.com/api/oauth2/authorize?client_id=${client.env.CLIENT_ID}&permissions=8&scope=bot%20applications.commands`, - ); - const supportButton = new ButtonBuilder() - .setLabel(ctx.locale("buttons.support")) - .setStyle(ButtonStyle.Link) - .setURL("https://discord.gg/ns8CTk9J3e"); - const row = new ActionRowBuilder().addComponents(inviteButton, supportButton); - const embed = this.client - .embed() - .setAuthor({ - name: "Lavamusic", - iconURL: "https://media.discordapp.net/attachments/876035356460462090/888434725235097610/20210820_124325.png", - }) - .setThumbnail("https://media.discordapp.net/attachments/876035356460462090/888434725235097610/20210820_124325.png") - .setColor(this.client.color.main) - .addFields( - { - name: ctx.locale("cmd.about.fields.creator"), - value: "[appujet](https://github.com/appujet)", - inline: true, - }, - { - name: ctx.locale("cmd.about.fields.repository"), - value: "[Here](https://github.com/appujet/lavamusic)", - inline: true, - }, - { - name: ctx.locale("cmd.about.fields.support"), - value: "[Here](https://discord.gg/ns8CTk9J3e)", - inline: true, - }, - { - name: "\u200b", - value: ctx.locale("cmd.about.fields.description"), - inline: true, - }, - ); - await ctx.sendMessage({ - content: "", - embeds: [embed], - components: [row], - }); - } + public async run(client: Lavamusic, ctx: Context): Promise { + const inviteButton = new ButtonBuilder() + .setLabel(ctx.locale('buttons.invite')) + .setStyle(ButtonStyle.Link) + .setURL( + `https://discord.com/api/oauth2/authorize?client_id=${client.env.CLIENT_ID}&permissions=8&scope=bot%20applications.commands`, + ); + const supportButton = new ButtonBuilder() + .setLabel(ctx.locale('buttons.support')) + .setStyle(ButtonStyle.Link) + .setURL('https://discord.gg/ns8CTk9J3e'); + const row = new ActionRowBuilder().addComponents(inviteButton, supportButton); + const embed = this.client + .embed() + .setAuthor({ + name: 'Lavamusic', + iconURL: 'https://media.discordapp.net/attachments/876035356460462090/888434725235097610/20210820_124325.png', + }) + .setThumbnail( + 'https://media.discordapp.net/attachments/876035356460462090/888434725235097610/20210820_124325.png', + ) + .setColor(this.client.color.main) + .addFields( + { + name: ctx.locale('cmd.about.fields.creator'), + value: '[appujet](https://github.com/appujet)', + inline: true, + }, + { + name: ctx.locale('cmd.about.fields.repository'), + value: '[Here](https://github.com/appujet/lavamusic)', + inline: true, + }, + { + name: ctx.locale('cmd.about.fields.support'), + value: '[Here](https://discord.gg/ns8CTk9J3e)', + inline: true, + }, + { + name: '\u200b', + value: ctx.locale('cmd.about.fields.description'), + inline: true, + }, + ); + await ctx.sendMessage({ + content: '', + embeds: [embed], + components: [row], + }); + } } /** diff --git a/src/commands/info/Botinfo.ts b/src/commands/info/Botinfo.ts index d09dd89c8..a073f0406 100644 --- a/src/commands/info/Botinfo.ts +++ b/src/commands/info/Botinfo.ts @@ -1,83 +1,83 @@ -import os from "node:os"; -import { version } from "discord.js"; -import { showTotalMemory, usagePercent } from "node-system-stats"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import os from 'node:os'; +import { version } from 'discord.js'; +import { showTotalMemory, usagePercent } from 'node-system-stats'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Botinfo extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "botinfo", - description: { - content: "cmd.botinfo.description", - examples: ["botinfo"], - usage: "botinfo", - }, - category: "info", - aliases: ["bi", "info", "stats", "status"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'botinfo', + description: { + content: 'cmd.botinfo.description', + examples: ['botinfo'], + usage: 'botinfo', + }, + category: 'info', + aliases: ['bi', 'info', 'stats', 'status'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const osInfo = `${os.type()} ${os.release()}`; - const osUptime = client.utils.formatTime(os.uptime()); - const osHostname = os.hostname(); - const cpuInfo = `${os.arch()} (${os.cpus().length} cores)`; - const cpuUsed = (await usagePercent({ coreIndex: 0, sampleMs: 2000 })).percent; - const memTotal = showTotalMemory(true); - const memUsed = (process.memoryUsage().rss / 1024 ** 2).toFixed(2); - const nodeVersion = process.version; - const discordJsVersion = version; - const commands = client.commands.size; + public async run(client: Lavamusic, ctx: Context): Promise { + const osInfo = `${os.type()} ${os.release()}`; + const osUptime = client.utils.formatTime(os.uptime()); + const osHostname = os.hostname(); + const cpuInfo = `${os.arch()} (${os.cpus().length} cores)`; + const cpuUsed = (await usagePercent({ coreIndex: 0, sampleMs: 2000 })).percent; + const memTotal = showTotalMemory(true); + const memUsed = (process.memoryUsage().rss / 1024 ** 2).toFixed(2); + const nodeVersion = process.version; + const discordJsVersion = version; + const commands = client.commands.size; - const promises = [ - client.shard.broadcastEval((client) => client.guilds.cache.size), - client.shard.broadcastEval((client) => client.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)), - client.shard.broadcastEval((client) => client.channels.cache.size), - ]; - return Promise.all(promises).then(async (results) => { - const guilds = results[0].reduce((acc, guildCount) => acc + guildCount, 0); - const users = results[1].reduce((acc, memberCount) => acc + memberCount, 0); - const channels = results[2].reduce((acc, channelCount) => acc + channelCount, 0); + const promises = [ + client.shard?.broadcastEval(client => client.guilds.cache.size), + client.shard?.broadcastEval(client => client.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)), + client.shard?.broadcastEval(client => client.channels.cache.size), + ]; + return Promise.all(promises).then(async results => { + const guilds = results[0]?.reduce((acc, guildCount) => acc + guildCount, 0); + const users = results[1]?.reduce((acc, memberCount) => acc + memberCount, 0); + const channels = results[2]?.reduce((acc, channelCount) => acc + channelCount, 0); - const botInfo = ctx.locale("cmd.botinfo.content", { - osInfo, - osUptime, - osHostname, - cpuInfo, - cpuUsed, - memUsed, - memTotal, - nodeVersion, - discordJsVersion, - guilds, - channels, - users, - commands, - }); + const botInfo = ctx.locale('cmd.botinfo.content', { + osInfo, + osUptime, + osHostname, + cpuInfo, + cpuUsed, + memUsed, + memTotal, + nodeVersion, + discordJsVersion, + guilds, + channels, + users, + commands, + }); - const embed = this.client.embed().setColor(this.client.color.main).setDescription(botInfo); + const embed = this.client.embed().setColor(this.client.color.main).setDescription(botInfo); - return await ctx.sendMessage({ - embeds: [embed], - }); - }); - } + return await ctx.sendMessage({ + embeds: [embed], + }); + }); + } } /** diff --git a/src/commands/info/Help.ts b/src/commands/info/Help.ts index 404b6d3ae..499f0e7b3 100644 --- a/src/commands/info/Help.ts +++ b/src/commands/info/Help.ts @@ -1,112 +1,114 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Help extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "help", - description: { - content: "cmd.help.description", - examples: ["help"], - usage: "help", - }, - category: "info", - aliases: ["h"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "command", - description: "cmd.help.options.command", - type: 3, - required: false, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'help', + description: { + content: 'cmd.help.description', + examples: ['help'], + usage: 'help', + }, + category: 'info', + aliases: ['h'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'command', + description: 'cmd.help.options.command', + type: 3, + required: false, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const embed = this.client.embed(); - const guild = await client.db.get(ctx.guild.id); - const commands = this.client.commands.filter((cmd) => cmd.category !== "dev"); - const categories = [...new Set(commands.map((cmd) => cmd.category))]; + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const embed = this.client.embed(); + const guild = await client.db.get(ctx.guild!.id); + const commands = this.client.commands.filter(cmd => cmd.category !== 'dev'); + const categories = [...new Set(commands.map(cmd => cmd.category))]; - if (args[0]) { - const command = this.client.commands.get(args[0].toLowerCase()); - if (!command) { - return await ctx.sendMessage({ - embeds: [ - embed.setColor(this.client.color.red).setDescription( - ctx.locale("cmd.help.not_found", { - cmdName: args[0], - }), - ), - ], - }); - } - const helpEmbed = embed - .setColor(client.color.main) - .setTitle(`${ctx.locale("cmd.help.title")} - ${command.name}`) - .setDescription( - ctx.locale("cmd.help.help_cmd", { - description: ctx.locale(command.description.content), - usage: `${guild.prefix}${command.description.usage}`, - examples: command.description.examples.map((example) => `${guild.prefix}${example}`).join(", "), - aliases: command.aliases.map((alias) => `\`${alias}\``).join(", "), - category: command.category, - cooldown: command.cooldown, - premUser: - command.permissions.user.length > 0 ? command.permissions.user.map((perm) => `\`${perm}\``).join(", ") : "None", - premBot: command.permissions.client.map((perm) => `\`${perm}\``).join(", "), - dev: command.permissions.dev ? "Yes" : "No", - slash: command.slashCommand ? "Yes" : "No", - args: command.args ? "Yes" : "No", - player: command.player.active ? "Yes" : "No", - dj: command.player.dj ? "Yes" : "No", - djPerm: command.player.djPerm ? command.player.djPerm : "None", - voice: command.player.voice ? "Yes" : "No", - }), - ); - return await ctx.sendMessage({ embeds: [helpEmbed] }); - } + if (args[0]) { + const command = this.client.commands.get(args[0].toLowerCase()); + if (!command) { + return await ctx.sendMessage({ + embeds: [ + embed.setColor(this.client.color.red).setDescription( + ctx.locale('cmd.help.not_found', { + cmdName: args[0], + }), + ), + ], + }); + } + const helpEmbed = embed + .setColor(client.color.main) + .setTitle(`${ctx.locale('cmd.help.title')} - ${command.name}`) + .setDescription( + ctx.locale('cmd.help.help_cmd', { + description: ctx.locale(command.description.content), + usage: `${guild.prefix}${command.description.usage}`, + examples: command.description.examples.map((example: string) => `${guild.prefix}${example}`).join(', '), + aliases: command.aliases.map((alias: string) => `\`${alias}\``).join(', '), + category: command.category, + cooldown: command.cooldown, + premUser: + command.permissions.user.length > 0 + ? command.permissions.user.map((perm: string) => `\`${perm}\``).join(', ') + : 'None', + premBot: command.permissions.client.map((perm: string) => `\`${perm}\``).join(', '), + dev: command.permissions.dev ? 'Yes' : 'No', + slash: command.slashCommand ? 'Yes' : 'No', + args: command.args ? 'Yes' : 'No', + player: command.player.active ? 'Yes' : 'No', + dj: command.player.dj ? 'Yes' : 'No', + djPerm: command.player.djPerm ? command.player.djPerm : 'None', + voice: command.player.voice ? 'Yes' : 'No', + }), + ); + return await ctx.sendMessage({ embeds: [helpEmbed] }); + } - const fields = categories.map((category) => ({ - name: category, - value: commands - .filter((cmd) => cmd.category === category) - .map((cmd) => `\`${cmd.name}\``) - .join(", "), - inline: false, - })); + const fields = categories.map(category => ({ + name: category, + value: commands + .filter(cmd => cmd.category === category) + .map(cmd => `\`${cmd.name}\``) + .join(', '), + inline: false, + })); - const helpEmbed = embed - .setColor(client.color.main) - .setTitle(ctx.locale("cmd.help.title")) - .setDescription( - ctx.locale("cmd.help.content", { - bot: client.user.username, - prefix: guild.prefix, - }), - ) - .setFooter({ - text: ctx.locale("cmd.help.footer", { prefix: guild.prefix }), - }) - .addFields(...fields); + const helpEmbed = embed + .setColor(client.color.main) + .setTitle(ctx.locale('cmd.help.title')) + .setDescription( + ctx.locale('cmd.help.content', { + bot: client.user?.username, + prefix: guild.prefix, + }), + ) + .setFooter({ + text: ctx.locale('cmd.help.footer', { prefix: guild.prefix }), + }) + .addFields(...fields); - return await ctx.sendMessage({ embeds: [helpEmbed] }); - } + return await ctx.sendMessage({ embeds: [helpEmbed] }); + } } /** diff --git a/src/commands/info/Invite.ts b/src/commands/info/Invite.ts index aa1bc6b05..871e32e81 100644 --- a/src/commands/info/Invite.ts +++ b/src/commands/info/Invite.ts @@ -1,52 +1,55 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Invite extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "invite", - description: { - content: "cmd.invite.description", - examples: ["invite"], - usage: "invite", - }, - category: "info", - aliases: ["iv"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'invite', + description: { + content: 'cmd.invite.description', + examples: ['invite'], + usage: 'invite', + }, + category: 'info', + aliases: ['iv'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const embed = this.client.embed(); - const row = new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setLabel(ctx.locale("buttons.invite")) - .setStyle(ButtonStyle.Link) - .setURL( - `https://discord.com/api/oauth2/authorize?client_id=${client.env.CLIENT_ID}&permissions=8&scope=bot%20applications.commands`, - ), - new ButtonBuilder().setLabel(ctx.locale("buttons.support")).setStyle(ButtonStyle.Link).setURL("https://discord.gg/STXurwnZD5"), - ); - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.invite.content"))], - components: [row], - }); - } + public async run(client: Lavamusic, ctx: Context): Promise { + const embed = this.client.embed(); + const row = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setLabel(ctx.locale('buttons.invite')) + .setStyle(ButtonStyle.Link) + .setURL( + `https://discord.com/api/oauth2/authorize?client_id=${client.env.CLIENT_ID}&permissions=8&scope=bot%20applications.commands`, + ), + new ButtonBuilder() + .setLabel(ctx.locale('buttons.support')) + .setStyle(ButtonStyle.Link) + .setURL('https://discord.gg/STXurwnZD5'), + ); + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale('cmd.invite.content'))], + components: [row], + }); + } } /** diff --git a/src/commands/info/LavaLink.ts b/src/commands/info/LavaLink.ts index ca1f8ff9b..9f0cb5cc8 100644 --- a/src/commands/info/LavaLink.ts +++ b/src/commands/info/LavaLink.ts @@ -1,88 +1,88 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class LavaLink extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "lavalink", - description: { - content: "cmd.lavalink.description", - examples: ["lavalink"], - usage: "lavalink", - }, - category: "info", - aliases: ["ll"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'lavalink', + description: { + content: 'cmd.lavalink.description', + examples: ['lavalink'], + usage: 'lavalink', + }, + category: 'info', + aliases: ['ll'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const nodes = client.manager.nodeManager.nodes; - const nodesPerPage = 2; + public async run(client: Lavamusic, ctx: Context): Promise { + const nodes = client.manager.nodeManager.nodes; + const nodesPerPage = 2; - const nodeArray = Array.from(nodes.values()); - const chunks = client.utils.chunk(nodeArray, nodesPerPage); + const nodeArray = Array.from(nodes.values()); + const chunks = client.utils.chunk(nodeArray, nodesPerPage); - if (chunks.length === 0) chunks.push(nodeArray); + if (chunks.length === 0) chunks.push(nodeArray); - const pages = chunks.map((chunk, index) => { - const embed = this.client - .embed() - .setTitle(ctx.locale("cmd.lavalink.title")) - .setColor(this.client.color.main) - .setThumbnail(this.client.user.avatarURL()) - .setTimestamp(); + const pages = chunks.map((chunk, index) => { + const embed = this.client + .embed() + .setTitle(ctx.locale('cmd.lavalink.title')) + .setColor(this.client.color.main) + .setThumbnail(client.user?.avatarURL()!) + .setTimestamp(); - chunk.forEach((node) => { - const statusEmoji = node.stats ? "🟢" : "🔴"; - const stats = node.stats || { - players: 0, - playingPlayers: 0, - uptime: 0, - cpu: { cores: 0, systemLoad: 0, lavalinkLoad: 0 }, - memory: { used: 0, reservable: 0 }, - }; + chunk.forEach(node => { + const statusEmoji = node.stats ? '🟢' : '🔴'; + const stats = node.stats || { + players: 0, + playingPlayers: 0, + uptime: 0, + cpu: { cores: 0, systemLoad: 0, lavalinkLoad: 0 }, + memory: { used: 0, reservable: 0 }, + }; - embed.addFields({ - name: `${node.name} (${statusEmoji})`, - value: `\`\`\`yaml\n${ctx.locale("cmd.lavalink.content", { - players: stats.players, - playingPlayers: stats.playingPlayers, - uptime: client.utils.formatTime(stats.uptime), - cores: stats.cpu.cores, - used: client.utils.formatBytes(stats.memory.used), - reservable: client.utils.formatBytes(stats.memory.reservable), - systemLoad: (stats.cpu.systemLoad * 100).toFixed(2), - lavalinkLoad: (stats.cpu.lavalinkLoad * 100).toFixed(2), - })}\n\`\`\``, - }); - }); + embed.addFields({ + name: `${node.name} (${statusEmoji})`, + value: `\`\`\`yaml\n${ctx.locale('cmd.lavalink.content', { + players: stats.players, + playingPlayers: stats.playingPlayers, + uptime: client.utils.formatTime(stats.uptime), + cores: stats.cpu.cores, + used: client.utils.formatBytes(stats.memory.used), + reservable: client.utils.formatBytes(stats.memory.reservable), + systemLoad: (stats.cpu.systemLoad * 100).toFixed(2), + lavalinkLoad: (stats.cpu.lavalinkLoad * 100).toFixed(2), + })}\n\`\`\``, + }); + }); - embed.setFooter({ - text: ctx.locale("cmd.lavalink.page_info", { - index: index + 1, - total: chunks.length, - }), - }); + embed.setFooter({ + text: ctx.locale('cmd.lavalink.page_info', { + index: index + 1, + total: chunks.length, + }), + }); - return embed; - }); - return await client.utils.paginate(client, ctx, pages); - } + return embed; + }); + return await client.utils.paginate(client, ctx, pages); + } } /** diff --git a/src/commands/info/Ping.ts b/src/commands/info/Ping.ts index 55422a28e..079101fd9 100644 --- a/src/commands/info/Ping.ts +++ b/src/commands/info/Ping.ts @@ -1,73 +1,73 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Ping extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "ping", - description: { - content: "cmd.ping.description", - examples: ["ping"], - usage: "ping", - }, - category: "general", - aliases: ["pong"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'ping', + description: { + content: 'cmd.ping.description', + examples: ['ping'], + usage: 'ping', + }, + category: 'general', + aliases: ['pong'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(_client: Lavamusic, ctx: Context): Promise { - const msg = await ctx.sendDeferMessage(ctx.locale("cmd.ping.content")); + public async run(client: Lavamusic, ctx: Context): Promise { + const msg = await ctx.sendDeferMessage(ctx.locale('cmd.ping.content')); - const botLatency = msg.createdTimestamp - ctx.createdTimestamp; - const apiLatency = Math.round(ctx.client.ws.ping); + const botLatency = msg.createdTimestamp - ctx.createdTimestamp; + const apiLatency = Math.round(ctx.client.ws.ping); - const botLatencySign = botLatency < 600 ? "+" : "-"; - const apiLatencySign = apiLatency < 500 ? "+" : "-"; + const botLatencySign = botLatency < 600 ? '+' : '-'; + const apiLatencySign = apiLatency < 500 ? '+' : '-'; - const embed = this.client - .embed() - .setAuthor({ - name: "Pong", - iconURL: this.client.user.displayAvatarURL(), - }) - .setColor(this.client.color.main) - .addFields([ - { - name: ctx.locale("cmd.ping.bot_latency"), - value: `\`\`\`diff\n${botLatencySign} ${botLatency}ms\n\`\`\``, - inline: true, - }, - { - name: ctx.locale("cmd.ping.api_latency"), - value: `\`\`\`diff\n${apiLatencySign} ${apiLatency}ms\n\`\`\``, - inline: true, - }, - ]) - .setFooter({ - text: ctx.locale("cmd.ping.requested_by", { - author: ctx.author.tag, - }), - iconURL: ctx.author.avatarURL({}), - }) - .setTimestamp(); + const embed = this.client + .embed() + .setAuthor({ + name: 'Pong', + iconURL: client.user?.displayAvatarURL(), + }) + .setColor(this.client.color.main) + .addFields([ + { + name: ctx.locale('cmd.ping.bot_latency'), + value: `\`\`\`diff\n${botLatencySign} ${botLatency}ms\n\`\`\``, + inline: true, + }, + { + name: ctx.locale('cmd.ping.api_latency'), + value: `\`\`\`diff\n${apiLatencySign} ${apiLatency}ms\n\`\`\``, + inline: true, + }, + ]) + .setFooter({ + text: ctx.locale('cmd.ping.requested_by', { + author: ctx.author?.tag, + }), + iconURL: ctx.author?.displayAvatarURL({}), + }) + .setTimestamp(); - return await ctx.editMessage({ content: "", embeds: [embed] }); - } + return await ctx.editMessage({ content: '', embeds: [embed] }); + } } /** diff --git a/src/commands/music/Autoplay.ts b/src/commands/music/Autoplay.ts index 1a568df5d..3a457aa52 100644 --- a/src/commands/music/Autoplay.ts +++ b/src/commands/music/Autoplay.ts @@ -1,61 +1,61 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Autoplay extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "autoplay", - description: { - content: "cmd.autoplay.description", - examples: ["autoplay"], - usage: "autoplay", - }, - category: "music", - aliases: ["ap"], - cooldown: 3, - args: false, - vote: true, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'autoplay', + description: { + content: 'cmd.autoplay.description', + examples: ['autoplay'], + usage: 'autoplay', + }, + category: 'music', + aliases: ['ap'], + cooldown: 3, + args: false, + vote: true, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - if (!player) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("player.errors.no_player"), - color: this.client.color.red, - }, - ], - }); - } + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + if (!player) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('player.errors.no_player'), + color: this.client.color.red, + }, + ], + }); + } - const embed = this.client.embed(); - const autoplay = player.get("autoplay"); + const embed = this.client.embed(); + const autoplay = player.get('autoplay'); - player.set("autoplay", !autoplay); + player.set('autoplay', !autoplay); - if (autoplay) { - embed.setDescription(ctx.locale("cmd.autoplay.messages.disabled")).setColor(this.client.color.main); - } else { - embed.setDescription(ctx.locale("cmd.autoplay.messages.enabled")).setColor(this.client.color.main); - } + if (autoplay) { + embed.setDescription(ctx.locale('cmd.autoplay.messages.disabled')).setColor(this.client.color.main); + } else { + embed.setDescription(ctx.locale('cmd.autoplay.messages.enabled')).setColor(this.client.color.main); + } - await ctx.sendMessage({ embeds: [embed] }); - } + await ctx.sendMessage({ embeds: [embed] }); + } } /** diff --git a/src/commands/music/ClearQueue.ts b/src/commands/music/ClearQueue.ts index 289690d34..05eb69d0e 100644 --- a/src/commands/music/ClearQueue.ts +++ b/src/commands/music/ClearQueue.ts @@ -1,56 +1,56 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class ClearQueue extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "clearqueue", - description: { - content: "cmd.clearqueue.description", - examples: ["clearqueue"], - usage: "clearqueue", - }, - category: "music", - aliases: ["cq"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'clearqueue', + description: { + content: 'cmd.clearqueue.description', + examples: ['clearqueue'], + usage: 'clearqueue', + }, + category: 'music', + aliases: ['cq'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); - if (!player) { - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("player.errors.no_player"))], - }); - } + if (!player) { + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('player.errors.no_player'))], + }); + } - if (player.queue.tracks.length === 0) { - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("player.errors.no_song"))], - }); - } + if (player.queue.tracks.length === 0) { + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('player.errors.no_song'))], + }); + } - player.queue.tracks.splice(0, player.queue.tracks.length); - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.clearqueue.messages.cleared"))], - }); - } + player.queue.tracks.splice(0, player.queue.tracks.length); + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale('cmd.clearqueue.messages.cleared'))], + }); + } } /** diff --git a/src/commands/music/Grab.ts b/src/commands/music/Grab.ts index 7d0bf1f5d..fc9768f6b 100644 --- a/src/commands/music/Grab.ts +++ b/src/commands/music/Grab.ts @@ -1,79 +1,81 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; -import type { Requester } from "../../types"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; +import type { Requester } from '../../types'; export default class Grab extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "grab", - description: { - content: "cmd.grab.description", - examples: ["grab"], - usage: "grab", - }, - category: "music", - aliases: ["gr"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: false, - dj: false, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'grab', + description: { + content: 'cmd.grab.description', + examples: ['grab'], + usage: 'grab', + }, + category: 'music', + aliases: ['gr'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: false, + dj: false, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); - await ctx.sendDeferMessage(ctx.locale("cmd.grab.loading")); + await ctx.sendDeferMessage(ctx.locale('cmd.grab.loading')); - if (!player?.queue.current) { - return await ctx.sendMessage({ - embeds: [this.client.embed().setColor(this.client.color.red).setDescription(ctx.locale("player.errors.no_song"))], - }); - } + if (!player?.queue.current) { + return await ctx.sendMessage({ + embeds: [ + this.client.embed().setColor(this.client.color.red).setDescription(ctx.locale('player.errors.no_song')), + ], + }); + } - const song = player.queue.current; + const song = player.queue.current; - const songInfo = ctx.locale("cmd.grab.content", { - title: song.info.title, - uri: song.info.uri, - artworkUrl: song.info.artworkUrl, - length: song.info.isStream ? "LIVE" : client.utils.formatTime(song.info.duration), - requester: (song.requester as Requester).id, - }); + const songInfo = ctx.locale('cmd.grab.content', { + title: song.info.title, + uri: song.info.uri, + artworkUrl: song.info.artworkUrl, + length: song.info.isStream ? 'LIVE' : client.utils.formatTime(song.info.duration), + requester: (song.requester as Requester).id, + }); - try { - await ctx.author?.send({ - embeds: [ - this.client - .embed() - .setTitle(`**${song.info.title}**`) - .setURL(song.info.uri!) - .setThumbnail(song.info.artworkUrl!) - .setDescription(songInfo) - .setColor(this.client.color.main), - ], - }); + try { + await ctx.author?.send({ + embeds: [ + this.client + .embed() + .setTitle(`**${song.info.title}**`) + .setURL(song.info.uri!) + .setThumbnail(song.info.artworkUrl!) + .setDescription(songInfo) + .setColor(this.client.color.main), + ], + }); - return await ctx.editMessage({ - embeds: [this.client.embed().setDescription(ctx.locale("cmd.grab.check_dm")).setColor(this.client.color.green)], - }); - } catch (_e) { - return await ctx.editMessage({ - embeds: [this.client.embed().setDescription(ctx.locale("cmd.grab.dm_failed")).setColor(this.client.color.red)], - }); - } - } + return await ctx.editMessage({ + embeds: [this.client.embed().setDescription(ctx.locale('cmd.grab.check_dm')).setColor(this.client.color.green)], + }); + } catch (_e) { + return await ctx.editMessage({ + embeds: [this.client.embed().setDescription(ctx.locale('cmd.grab.dm_failed')).setColor(this.client.color.red)], + }); + } + } } /** diff --git a/src/commands/music/Join.ts b/src/commands/music/Join.ts index b18791a4c..01428353e 100644 --- a/src/commands/music/Join.ts +++ b/src/commands/music/Join.ts @@ -1,78 +1,78 @@ -import type { VoiceChannel } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { VoiceChannel } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Join extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "join", - description: { - content: "cmd.join.description", - examples: ["join"], - usage: "join", - }, - category: "music", - aliases: ["come", "j"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks", "Connect", "Speak"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'join', + description: { + content: 'cmd.join.description', + examples: ['join'], + usage: 'join', + }, + category: 'music', + aliases: ['come', 'j'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks', 'Connect', 'Speak'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const embed = this.client.embed(); - let player = client.manager.getPlayer(ctx.guild!.id); + public async run(client: Lavamusic, ctx: Context): Promise { + const embed = this.client.embed(); + let player = client.manager.getPlayer(ctx.guild!.id); - if (player) { - return await ctx.sendMessage({ - embeds: [ - embed.setColor(this.client.color.main).setDescription( - ctx.locale("cmd.join.already_connected", { - channelId: player.voiceChannelId, - }), - ), - ], - }); - } + if (player) { + return await ctx.sendMessage({ + embeds: [ + embed.setColor(this.client.color.main).setDescription( + ctx.locale('cmd.join.already_connected', { + channelId: player.voiceChannelId, + }), + ), + ], + }); + } - const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; - if (!memberVoiceChannel) { - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.join.no_voice_channel"))], - }); - } + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; + if (!memberVoiceChannel) { + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('cmd.join.no_voice_channel'))], + }); + } - player = client.manager.createPlayer({ - guildId: ctx.guild!.id, - voiceChannelId: memberVoiceChannel.id, - textChannelId: ctx.channel.id, - selfMute: false, - selfDeaf: true, - vcRegion: memberVoiceChannel.rtcRegion, - }); - if (!player.connected) await player.connect(); - return await ctx.sendMessage({ - embeds: [ - embed.setColor(this.client.color.main).setDescription( - ctx.locale("cmd.join.joined", { - channelId: player.voiceChannelId, - }), - ), - ], - }); - } + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: memberVoiceChannel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + vcRegion: memberVoiceChannel.rtcRegion!, + }); + if (!player.connected) await player.connect(); + return await ctx.sendMessage({ + embeds: [ + embed.setColor(this.client.color.main).setDescription( + ctx.locale('cmd.join.joined', { + channelId: player.voiceChannelId, + }), + ), + ], + }); + } } /** diff --git a/src/commands/music/Leave.ts b/src/commands/music/Leave.ts index 75797e27b..1e17c5240 100644 --- a/src/commands/music/Leave.ts +++ b/src/commands/music/Leave.ts @@ -1,50 +1,50 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Leave extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "leave", - description: { - content: "cmd.leave.description", - examples: ["leave"], - usage: "leave", - }, - category: "music", - aliases: ["l"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'leave', + description: { + content: 'cmd.leave.description', + examples: ['leave'], + usage: 'leave', + }, + category: 'music', + aliases: ['l'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); - if (player) { - const channelId = player.voiceChannelId; - player.destroy(); - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.leave.left", { channelId }))], - }); - } - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.leave.not_in_channel"))], - }); - } + if (player) { + const channelId = player.voiceChannelId; + player.destroy(); + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale('cmd.leave.left', { channelId }))], + }); + } + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('cmd.leave.not_in_channel'))], + }); + } } /** diff --git a/src/commands/music/Loop.ts b/src/commands/music/Loop.ts index 6e1785b87..f3a35e3ea 100644 --- a/src/commands/music/Loop.ts +++ b/src/commands/music/Loop.ts @@ -1,59 +1,59 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Loop extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "loop", - description: { - content: "cmd.loop.description", - examples: ["loop", "loop queue", "loop song"], - usage: "loop", - }, - category: "general", - aliases: ["loop"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: false, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'loop', + description: { + content: 'cmd.loop.description', + examples: ['loop', 'loop queue', 'loop song'], + usage: 'loop', + }, + category: 'general', + aliases: ['loop'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: false, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const embed = this.client.embed().setColor(this.client.color.main); - const player = client.manager.getPlayer(ctx.guild!.id); - let loopMessage = ""; + public async run(client: Lavamusic, ctx: Context): Promise { + const embed = this.client.embed().setColor(this.client.color.main); + const player = client.manager.getPlayer(ctx.guild!.id); + let loopMessage = ''; - switch (player?.repeatMode) { - case "off": - player.setRepeatMode("track"); - loopMessage = ctx.locale("cmd.loop.looping_song"); - break; - case "track": - player.setRepeatMode("queue"); - loopMessage = ctx.locale("cmd.loop.looping_queue"); - break; - case "queue": - player.setRepeatMode("off"); - loopMessage = ctx.locale("cmd.loop.looping_off"); - break; - } + switch (player?.repeatMode) { + case 'off': + player.setRepeatMode('track'); + loopMessage = ctx.locale('cmd.loop.looping_song'); + break; + case 'track': + player.setRepeatMode('queue'); + loopMessage = ctx.locale('cmd.loop.looping_queue'); + break; + case 'queue': + player.setRepeatMode('off'); + loopMessage = ctx.locale('cmd.loop.looping_off'); + break; + } - return await ctx.sendMessage({ - embeds: [embed.setDescription(loopMessage)], - }); - } + return await ctx.sendMessage({ + embeds: [embed.setDescription(loopMessage)], + }); + } } /** diff --git a/src/commands/music/Lyrics.ts b/src/commands/music/Lyrics.ts index cd8b47ebd..c4ff1cf69 100644 --- a/src/commands/music/Lyrics.ts +++ b/src/commands/music/Lyrics.ts @@ -1,168 +1,177 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType } from "discord.js"; -import { getLyrics } from "genius-lyrics-api"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { + ActionRowBuilder, + ButtonBuilder, + type ButtonInteraction, + ButtonStyle, + ComponentType, + type TextChannel, +} from 'discord.js'; +import { getLyrics } from 'genius-lyrics-api'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Lyrics extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "lyrics", - description: { - content: "cmd.lyrics.description", - examples: ["lyrics"], - usage: "lyrics", - }, - category: "music", - aliases: ["ly"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: false, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); - - const currentTrack = player.queue.current; - const trackTitle = currentTrack.info.title.replace(/\[.*?\]/g, "").trim(); - const artistName = currentTrack.info.author.replace(/\[.*?\]/g, "").trim(); - const trackUrl = currentTrack.info.uri; - const artworkUrl = currentTrack.info.artworkUrl; - - await ctx.sendDeferMessage(ctx.locale("cmd.lyrics.searching", { trackTitle })); - - const options = { - apiKey: client.env.GENIUS_API, - title: trackTitle, - artist: artistName, - optimizeQuery: true, - }; - - try { - const lyrics = await getLyrics(options); - if (lyrics) { - const lyricsPages = this.paginateLyrics(lyrics); - let currentPage = 0; - - const row = new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setCustomId("prev") - .setEmoji(this.client.emoji.page.back) - .setStyle(ButtonStyle.Secondary) - .setDisabled(true), - new ButtonBuilder().setCustomId("stop").setEmoji(this.client.emoji.page.cancel).setStyle(ButtonStyle.Danger), - new ButtonBuilder() - .setCustomId("next") - .setEmoji(this.client.emoji.page.next) - .setStyle(ButtonStyle.Secondary) - .setDisabled(lyricsPages.length <= 1), - ); - - await ctx.editMessage({ - embeds: [ - embed - .setColor(client.color.main) - .setDescription( - ctx.locale("cmd.lyrics.lyrics_track", { trackTitle, trackUrl, lyrics: lyricsPages[currentPage] }), - ) - .setThumbnail(artworkUrl) - .setTimestamp(), - ], - components: [row], - }); - - const filter = (interaction) => interaction.user.id === ctx.author.id; - const collector = ctx.channel.createMessageComponentCollector({ - filter, - componentType: ComponentType.Button, - time: 60000, - }); - - collector.on("collect", async (interaction) => { - if (interaction.customId === "prev") { - currentPage--; - } else if (interaction.customId === "next") { - currentPage++; - } else if (interaction.customId === "stop") { - collector.stop(); - return interaction.update({ components: [] }); - } - - await interaction.update({ - embeds: [ - embed - .setDescription( - ctx.locale("cmd.lyrics.lyrics_track", { trackTitle, trackUrl, lyrics: lyricsPages[currentPage] }), - ) - .setThumbnail(artworkUrl) - .setTimestamp(), - ], - components: [ - new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setCustomId("prev") - .setEmoji(this.client.emoji.page.back) - .setStyle(ButtonStyle.Secondary) - .setDisabled(currentPage === 0), - new ButtonBuilder() - .setCustomId("stop") - .setEmoji(this.client.emoji.page.cancel) - .setStyle(ButtonStyle.Danger), - new ButtonBuilder() - .setCustomId("next") - .setEmoji(this.client.emoji.page.next) - .setStyle(ButtonStyle.Secondary) - .setDisabled(currentPage === lyricsPages.length - 1), - ), - ], - }); - }); - - collector.on("end", () => { - ctx.editMessage({ components: [] }); - }); - } else { - await ctx.editMessage({ - embeds: [embed.setColor(client.color.red).setDescription(ctx.locale("cmd.lyrics.errors.no_results"))], - }); - } - } catch (error) { - console.error(error); - await ctx.editMessage({ - embeds: [embed.setColor(client.color.red).setDescription(ctx.locale("cmd.lyrics.errors.lyrics_error"))], - }); - } - } - - paginateLyrics(lyrics) { - const lines = lyrics.split("\n"); - const pages = []; - let page = ""; - - for (const line of lines) { - if (page.length + line.length > 2048) { - pages.push(page); - page = ""; - } - page += `${line}\n`; - } - - if (page) pages.push(page); - return pages; - } + constructor(client: Lavamusic) { + super(client, { + name: 'lyrics', + description: { + content: 'cmd.lyrics.description', + examples: ['lyrics'], + usage: 'lyrics', + }, + category: 'music', + aliases: ['ly'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: false, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } + + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); + + const currentTrack = player.queue.current!; + const trackTitle = currentTrack.info.title.replace(/\[.*?\]/g, '').trim(); + const artistName = currentTrack.info.author.replace(/\[.*?\]/g, '').trim(); + const trackUrl = currentTrack.info.uri; + const artworkUrl = currentTrack.info.artworkUrl; + + await ctx.sendDeferMessage(ctx.locale('cmd.lyrics.searching', { trackTitle })); + + const options = { + apiKey: client.env.GENIUS_API, + title: trackTitle, + artist: artistName, + optimizeQuery: true, + }; + + try { + const lyrics = await getLyrics(options); + if (lyrics) { + const lyricsPages = this.paginateLyrics(lyrics); + let currentPage = 0; + + const row = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId('prev') + .setEmoji(this.client.emoji.page.back) + .setStyle(ButtonStyle.Secondary) + .setDisabled(true), + new ButtonBuilder().setCustomId('stop').setEmoji(this.client.emoji.page.cancel).setStyle(ButtonStyle.Danger), + new ButtonBuilder() + .setCustomId('next') + .setEmoji(this.client.emoji.page.next) + .setStyle(ButtonStyle.Secondary) + .setDisabled(lyricsPages.length <= 1), + ); + + await ctx.editMessage({ + embeds: [ + embed + .setColor(client.color.main) + .setDescription( + ctx.locale('cmd.lyrics.lyrics_track', { trackTitle, trackUrl, lyrics: lyricsPages[currentPage] }), + ) + .setThumbnail(artworkUrl) + .setTimestamp(), + ], + components: [row], + }); + + const filter = (interaction: ButtonInteraction<'cached'>) => interaction.user.id === ctx.author?.id; + const collector = (ctx.channel as TextChannel).createMessageComponentCollector({ + filter, + componentType: ComponentType.Button, + time: 60000, + }); + + collector.on('collect', async (interaction: ButtonInteraction) => { + if (interaction.customId === 'prev') { + currentPage--; + } else if (interaction.customId === 'next') { + currentPage++; + } else if (interaction.customId === 'stop') { + collector.stop(); + return interaction.update({ components: [] }); + } + + await interaction.update({ + embeds: [ + embed + .setDescription( + ctx.locale('cmd.lyrics.lyrics_track', { trackTitle, trackUrl, lyrics: lyricsPages[currentPage] }), + ) + .setThumbnail(artworkUrl) + .setTimestamp(), + ], + components: [ + new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId('prev') + .setEmoji(this.client.emoji.page.back) + .setStyle(ButtonStyle.Secondary) + .setDisabled(currentPage === 0), + new ButtonBuilder() + .setCustomId('stop') + .setEmoji(this.client.emoji.page.cancel) + .setStyle(ButtonStyle.Danger), + new ButtonBuilder() + .setCustomId('next') + .setEmoji(this.client.emoji.page.next) + .setStyle(ButtonStyle.Secondary) + .setDisabled(currentPage === lyricsPages.length - 1), + ), + ], + }); + return; + }); + + collector.on('end', () => { + ctx.editMessage({ components: [] }); + }); + } else { + await ctx.editMessage({ + embeds: [embed.setColor(client.color.red).setDescription(ctx.locale('cmd.lyrics.errors.no_results'))], + }); + } + } catch (error) { + // biome-ignore lint/suspicious/noConsole: + console.error(error); + await ctx.editMessage({ + embeds: [embed.setColor(client.color.red).setDescription(ctx.locale('cmd.lyrics.errors.lyrics_error'))], + }); + } + } + + paginateLyrics(lyrics: string) { + const lines = lyrics.split('\n'); + const pages: any = []; + let page = ''; + + for (const line of lines) { + if (page.length + line.length > 2048) { + pages.push(page); + page = ''; + } + page += `${line}\n`; + } + + if (page) pages.push(page); + return pages; + } } /** diff --git a/src/commands/music/Nowplaying.ts b/src/commands/music/Nowplaying.ts index 196705310..ac9d7a014 100644 --- a/src/commands/music/Nowplaying.ts +++ b/src/commands/music/Nowplaying.ts @@ -1,65 +1,65 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Nowplaying extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "nowplaying", - description: { - content: "cmd.nowplaying.description", - examples: ["nowplaying"], - usage: "nowplaying", - }, - category: "music", - aliases: ["np"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: false, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'nowplaying', + description: { + content: 'cmd.nowplaying.description', + examples: ['nowplaying'], + usage: 'nowplaying', + }, + category: 'music', + aliases: ['np'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: false, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const track = player.queue.current!; - const position = player.position; - const duration = track.info.duration; - const bar = client.utils.progressBar(position, duration, 20); + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const track = player.queue.current!; + const position = player.position; + const duration = track.info.duration; + const bar = client.utils.progressBar(position, duration, 20); - const embed = this.client - .embed() - .setColor(this.client.color.main) - .setAuthor({ - name: ctx.locale("cmd.nowplaying.now_playing"), - iconURL: ctx.guild?.iconURL({})!, - }) - .setThumbnail(track.info.artworkUrl!) - .setDescription( - ctx.locale("cmd.nowplaying.track_info", { - title: track.info.title, - uri: track.info.uri, - requester: (track.requester as any).id, - bar: bar, - }), - ) - .addFields({ - name: "\u200b", - value: `\`${client.utils.formatTime(position)} / ${client.utils.formatTime(duration)}\``, - }); + const embed = this.client + .embed() + .setColor(this.client.color.main) + .setAuthor({ + name: ctx.locale('cmd.nowplaying.now_playing'), + iconURL: ctx.guild?.iconURL({})!, + }) + .setThumbnail(track.info.artworkUrl!) + .setDescription( + ctx.locale('cmd.nowplaying.track_info', { + title: track.info.title, + uri: track.info.uri, + requester: (track.requester as any).id, + bar: bar, + }), + ) + .addFields({ + name: '\u200b', + value: `\`${client.utils.formatTime(position)} / ${client.utils.formatTime(duration)}\``, + }); - return await ctx.sendMessage({ embeds: [embed] }); - } + return await ctx.sendMessage({ embeds: [embed] }); + } } /** diff --git a/src/commands/music/Pause.ts b/src/commands/music/Pause.ts index e55783ce7..0a78a879f 100644 --- a/src/commands/music/Pause.ts +++ b/src/commands/music/Pause.ts @@ -1,51 +1,51 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Pause extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "pause", - description: { - content: "cmd.pause.description", - examples: ["pause"], - usage: "pause", - }, - category: "music", - aliases: ["pu"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: false, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'pause', + description: { + content: 'cmd.pause.description', + examples: ['pause'], + usage: 'pause', + }, + category: 'music', + aliases: ['pu'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: false, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); - if (player?.paused) { - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("player.errors.already_paused"))], - }); - } + if (player?.paused) { + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('player.errors.already_paused'))], + }); + } - player?.pause(); + player?.pause(); - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.pause.successfully_paused"))], - }); - } + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale('cmd.pause.successfully_paused'))], + }); + } } /** diff --git a/src/commands/music/Play.ts b/src/commands/music/Play.ts index 332dca778..b05483263 100644 --- a/src/commands/music/Play.ts +++ b/src/commands/music/Play.ts @@ -1,129 +1,129 @@ -import type { AutocompleteInteraction, VoiceChannel } from "discord.js"; -import type { SearchResult } from "lavalink-client"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { ApplicationCommandOptionChoiceData, AutocompleteInteraction, VoiceChannel } from 'discord.js'; +import type { SearchResult } from 'lavalink-client'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Play extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "play", - description: { - content: "cmd.play.description", - examples: [ - "play example", - "play https://www.youtube.com/watch?v=example", - "play https://open.spotify.com/track/example", - "play http://www.example.com/example.mp3", - ], - usage: "play ", - }, - category: "music", - aliases: ["p"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: true, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks", "Connect", "Speak"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "song", - description: "cmd.play.options.song", - type: 3, - required: true, - autocomplete: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'play', + description: { + content: 'cmd.play.description', + examples: [ + 'play example', + 'play https://www.youtube.com/watch?v=example', + 'play https://open.spotify.com/track/example', + 'play http://www.example.com/example.mp3', + ], + usage: 'play ', + }, + category: 'music', + aliases: ['p'], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks', 'Connect', 'Speak'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'song', + description: 'cmd.play.options.song', + type: 3, + required: true, + autocomplete: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const query = args.join(" "); - await ctx.sendDeferMessage(ctx.locale("cmd.play.loading")); - let player = client.manager.getPlayer(ctx.guild!.id); - const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const query = args.join(' '); + await ctx.sendDeferMessage(ctx.locale('cmd.play.loading')); + let player = client.manager.getPlayer(ctx.guild!.id); + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; - if (!player) - player = client.manager.createPlayer({ - guildId: ctx.guild!.id, - voiceChannelId: memberVoiceChannel.id, - textChannelId: ctx.channel.id, - selfMute: false, - selfDeaf: true, - vcRegion: memberVoiceChannel.rtcRegion, - }); - if (!player.connected) await player.connect(); + if (!player) + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: memberVoiceChannel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + vcRegion: memberVoiceChannel.rtcRegion!, + }); + if (!player.connected) await player.connect(); - const response = (await player.search({ query: query }, ctx.author)) as SearchResult; - const embed = this.client.embed(); + const response = (await player.search({ query: query }, ctx.author)) as SearchResult; + const embed = this.client.embed(); - if (!response || response.tracks?.length === 0) { - return await ctx.editMessage({ - content: "", - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.play.errors.search_error"))], - }); - } + if (!response || response.tracks?.length === 0) { + return await ctx.editMessage({ + content: '', + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('cmd.play.errors.search_error'))], + }); + } - await player.queue.add(response.loadType === "playlist" ? response.tracks : response.tracks[0]); + await player.queue.add(response.loadType === 'playlist' ? response.tracks : response.tracks[0]); - if (response.loadType === "playlist") { - await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.main) - .setDescription(ctx.locale("cmd.play.added_playlist_to_queue", { length: response.tracks.length })), - ], - }); - } else { - await ctx.editMessage({ - content: "", - embeds: [ - embed.setColor(this.client.color.main).setDescription( - ctx.locale("cmd.play.added_to_queue", { - title: response.tracks[0].info.title, - uri: response.tracks[0].info.uri, - }), - ), - ], - }); - } - if (!player.playing) await player.play({ paused: false }); - } - public async autocomplete(interaction: AutocompleteInteraction): Promise { - const focusedValue = interaction.options.getFocused(); + if (response.loadType === 'playlist') { + await ctx.editMessage({ + content: '', + embeds: [ + embed + .setColor(this.client.color.main) + .setDescription(ctx.locale('cmd.play.added_playlist_to_queue', { length: response.tracks.length })), + ], + }); + } else { + await ctx.editMessage({ + content: '', + embeds: [ + embed.setColor(this.client.color.main).setDescription( + ctx.locale('cmd.play.added_to_queue', { + title: response.tracks[0].info.title, + uri: response.tracks[0].info.uri, + }), + ), + ], + }); + } + if (!player.playing) await player.play({ paused: false }); + } + public async autocomplete(interaction: AutocompleteInteraction): Promise { + const focusedValue = interaction.options.getFocused(); - if (!focusedValue) { - return interaction.respond([]).catch(() => { - null; - }); - } + if (!focusedValue) { + return interaction.respond([]).catch(() => { + null; + }); + } - const res = await this.client.manager.search(focusedValue, interaction.user); - const songs = []; + const res = await this.client.manager.search(focusedValue, interaction.user); + const songs: ApplicationCommandOptionChoiceData[] = []; - if (res.loadType === "search") { - res.tracks.slice(0, 10).forEach((track) => { - const name = `${track.info.title} by ${track.info.author}`; - songs.push({ - name: name.length > 100 ? `${name.substring(0, 97)}...` : name, - value: track.info.uri, - }); - }); - } + if (res.loadType === 'search') { + res.tracks.slice(0, 10).forEach(track => { + const name = `${track.info.title} by ${track.info.author}`; + songs.push({ + name: name.length > 100 ? `${name.substring(0, 97)}...` : name, + value: track.info.uri, + }); + }); + } - return await interaction.respond(songs).catch(() => { - null; - }); - } + return await interaction.respond(songs).catch(() => { + null; + }); + } } /** diff --git a/src/commands/music/PlayNext.ts b/src/commands/music/PlayNext.ts index 14be280b2..a0b167532 100644 --- a/src/commands/music/PlayNext.ts +++ b/src/commands/music/PlayNext.ts @@ -1,125 +1,125 @@ -import type { AutocompleteInteraction, VoiceChannel } from "discord.js"; -import type { SearchResult } from "lavalink-client"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { ApplicationCommandOptionChoiceData, AutocompleteInteraction, VoiceChannel } from 'discord.js'; +import type { SearchResult } from 'lavalink-client'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class PlayNext extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "playnext", - description: { - content: "cmd.playnext.description", - examples: [ - "playnext example", - "playnext https://www.youtube.com/watch?v=example", - "playnext https://open.spotify.com/track/example", - "playnext http://www.example.com/example.mp3", - ], - usage: "playnext ", - }, - category: "music", - aliases: ["pn"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: true, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks", "Connect", "Speak"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "song", - description: "cmd.playnext.options.song", - type: 3, - required: true, - autocomplete: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'playnext', + description: { + content: 'cmd.playnext.description', + examples: [ + 'playnext example', + 'playnext https://www.youtube.com/watch?v=example', + 'playnext https://open.spotify.com/track/example', + 'playnext http://www.example.com/example.mp3', + ], + usage: 'playnext ', + }, + category: 'music', + aliases: ['pn'], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks', 'Connect', 'Speak'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'song', + description: 'cmd.playnext.options.song', + type: 3, + required: true, + autocomplete: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const query = args.join(" "); - let player = client.manager.getPlayer(ctx.guild!.id); - const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const query = args.join(' '); + let player = client.manager.getPlayer(ctx.guild!.id); + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; - if (!player) - player = client.manager.createPlayer({ - guildId: ctx.guild!.id, - voiceChannelId: memberVoiceChannel.id, - textChannelId: ctx.channel.id, - selfMute: false, - selfDeaf: true, - vcRegion: memberVoiceChannel.rtcRegion, - }); - if (!player.connected) await player.connect(); + if (!player) + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: memberVoiceChannel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + vcRegion: memberVoiceChannel.rtcRegion!, + }); + if (!player.connected) await player.connect(); - await ctx.sendDeferMessage(ctx.locale("cmd.playnext.loading")); + await ctx.sendDeferMessage(ctx.locale('cmd.playnext.loading')); - const response = (await player.search({ query: query }, ctx.author)) as SearchResult; - const embed = this.client.embed(); + const response = (await player.search({ query: query }, ctx.author)) as SearchResult; + const embed = this.client.embed(); - if (!response || response.tracks?.length === 0) { - return await ctx.editMessage({ - content: "", - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.play.errors.search_error"))], - }); - } - await player.queue.splice(0, 0, response.loadType === "playlist" ? response.tracks : response.tracks[0]); + if (!response || response.tracks?.length === 0) { + return await ctx.editMessage({ + content: '', + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('cmd.play.errors.search_error'))], + }); + } + await player.queue.splice(0, 0, response.loadType === 'playlist' ? response.tracks : response.tracks[0]); - if (response.loadType === "playlist") { - await ctx.editMessage({ - content: "", - embeds: [ - embed - .setColor(this.client.color.main) - .setDescription(ctx.locale("cmd.playnext.added_playlist_to_play_next", { length: response.tracks.length })), - ], - }); - } else { - await ctx.editMessage({ - content: "", - embeds: [ - embed.setColor(this.client.color.main).setDescription( - ctx.locale("cmd.playnext.added_to_play_next", { - title: response.tracks[0].info.title, - uri: response.tracks[0].info.uri, - }), - ), - ], - }); - } - if (!player.playing) await player.play({ paused: false }); - } - public async autocomplete(interaction: AutocompleteInteraction): Promise { - const focusedValue = interaction.options.getFocused(); + if (response.loadType === 'playlist') { + await ctx.editMessage({ + content: '', + embeds: [ + embed + .setColor(this.client.color.main) + .setDescription(ctx.locale('cmd.playnext.added_playlist_to_play_next', { length: response.tracks.length })), + ], + }); + } else { + await ctx.editMessage({ + content: '', + embeds: [ + embed.setColor(this.client.color.main).setDescription( + ctx.locale('cmd.playnext.added_to_play_next', { + title: response.tracks[0].info.title, + uri: response.tracks[0].info.uri, + }), + ), + ], + }); + } + if (!player.playing) await player.play({ paused: false }); + } + public async autocomplete(interaction: AutocompleteInteraction): Promise { + const focusedValue = interaction.options.getFocused(); - if (!focusedValue) { - return; - } + if (!focusedValue) { + return; + } - const res = await this.client.manager.search(focusedValue, interaction.user); - const songs = []; + const res = await this.client.manager.search(focusedValue, interaction.user); + const songs: ApplicationCommandOptionChoiceData[] = []; - if (res.loadType === "search") { - res.tracks.slice(0, 10).forEach((track) => { - const name = `${track.info.title} by ${track.info.author}`; - songs.push({ - name: name.length > 100 ? `${name.substring(0, 97)}...` : name, - value: track.info.uri, - }); - }); - } + if (res.loadType === 'search') { + res.tracks.slice(0, 10).forEach(track => { + const name = `${track.info.title} by ${track.info.author}`; + songs.push({ + name: name.length > 100 ? `${name.substring(0, 97)}...` : name, + value: track.info.uri, + }); + }); + } - return await interaction.respond(songs).catch(console.error); - } + return await interaction.respond(songs).catch(console.error); + } } /** diff --git a/src/commands/music/Queue.ts b/src/commands/music/Queue.ts index b3fd7180e..ecd5b7e2f 100644 --- a/src/commands/music/Queue.ts +++ b/src/commands/music/Queue.ts @@ -1,89 +1,89 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Queue extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "queue", - description: { - content: "cmd.queue.description", - examples: ["queue"], - usage: "queue", - }, - category: "music", - aliases: ["q"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: false, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'queue', + description: { + content: 'cmd.queue.description', + examples: ['queue'], + usage: 'queue', + }, + category: 'music', + aliases: ['q'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: false, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); - if (player.queue.tracks.length === 0) { - return await ctx.sendMessage({ - embeds: [ - embed.setColor(this.client.color.main).setDescription( - ctx.locale("cmd.queue.now_playing", { - title: player.queue.current.info.title, - uri: player.queue.current.info.uri, - requester: (player.queue.current.requester as any).id, - duration: player.queue.current.info.isStream - ? ctx.locale("cmd.queue.live") - : client.utils.formatTime(player.queue.current.info.duration), - }), - ), - ], - }); - } - const songStrings = []; - for (let i = 0; i < player.queue.tracks.length; i++) { - const track = player.queue.tracks[i]; - songStrings.push( - ctx.locale("cmd.queue.track_info", { - index: i + 1, - title: track.info.title, - uri: track.info.uri, - requester: (track.requester as any).id, - duration: track.info.isStream ? ctx.locale("cmd.queue.live") : client.utils.formatTime(track.info.duration), - }), - ); - } - let chunks = client.utils.chunk(songStrings, 10); + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); + if (player.queue.current && player.queue.tracks.length === 0) { + return await ctx.sendMessage({ + embeds: [ + embed.setColor(this.client.color.main).setDescription( + ctx.locale('cmd.queue.now_playing', { + title: player.queue.current.info.title, + uri: player.queue.current.info.uri, + requester: (player.queue.current.requester as any).id, + duration: player.queue.current.info.isStream + ? ctx.locale('cmd.queue.live') + : client.utils.formatTime(player.queue.current.info.duration), + }), + ), + ], + }); + } + const songStrings = []; + for (let i = 0; i < player.queue.tracks.length; i++) { + const track = player.queue.tracks[i]; + songStrings.push( + ctx.locale('cmd.queue.track_info', { + index: i + 1, + title: track.info.title, + uri: track.info.uri, + requester: (track.requester as any).id, + duration: track.info.isStream ? ctx.locale('cmd.queue.live') : client.utils.formatTime(track.info.duration!), + }), + ); + } + let chunks = client.utils.chunk(songStrings, 10); - if (chunks.length === 0) chunks = [songStrings]; + if (chunks.length === 0) chunks = [songStrings]; - const pages = chunks.map((chunk, index) => { - return this.client - .embed() - .setColor(this.client.color.main) - .setAuthor({ - name: ctx.locale("cmd.queue.title"), - iconURL: ctx.guild.iconURL({}), - }) - .setDescription(chunk.join("\n")) - .setFooter({ - text: ctx.locale("cmd.queue.page_info", { - index: index + 1, - total: chunks.length, - }), - }); - }); - return await client.utils.paginate(client, ctx, pages); - } + const pages = chunks.map((chunk, index) => { + return this.client + .embed() + .setColor(this.client.color.main) + .setAuthor({ + name: ctx.locale('cmd.queue.title'), + iconURL: ctx.guild.icon ? ctx.guild.iconURL()! : ctx.author?.displayAvatarURL(), + }) + .setDescription(chunk.join('\n')) + .setFooter({ + text: ctx.locale('cmd.queue.page_info', { + index: index + 1, + total: chunks.length, + }), + }); + }); + return await client.utils.paginate(client, ctx, pages); + } } /** diff --git a/src/commands/music/Remove.ts b/src/commands/music/Remove.ts index 62784febf..92d7264a8 100644 --- a/src/commands/music/Remove.ts +++ b/src/commands/music/Remove.ts @@ -1,68 +1,68 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Remove extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "remove", - description: { - content: "cmd.remove.description", - examples: ["remove 1"], - usage: "remove ", - }, - category: "music", - aliases: ["rm"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "song", - description: "cmd.remove.options.song", - type: 4, - required: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'remove', + description: { + content: 'cmd.remove.description', + examples: ['remove 1'], + usage: 'remove ', + }, + category: 'music', + aliases: ['rm'], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'song', + description: 'cmd.remove.options.song', + type: 4, + required: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); - if (player.queue.tracks.length === 0) - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.remove.errors.no_songs"))], - }); + if (player.queue.tracks.length === 0) + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('cmd.remove.errors.no_songs'))], + }); - const songNumber = Number(args[0]); - if (isNaN(songNumber) || songNumber <= 0 || songNumber > player.queue.tracks.length) - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.remove.errors.invalid_number"))], - }); + const songNumber = Number(args[0]); + if (Number.isNaN(songNumber) || songNumber <= 0 || songNumber > player.queue.tracks.length) + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('cmd.remove.errors.invalid_number'))], + }); - player.queue.remove(songNumber - 1); - return await ctx.sendMessage({ - embeds: [ - embed.setColor(this.client.color.main).setDescription( - ctx.locale("cmd.remove.messages.removed", { - songNumber, - }), - ), - ], - }); - } + player.queue.remove(songNumber - 1); + return await ctx.sendMessage({ + embeds: [ + embed.setColor(this.client.color.main).setDescription( + ctx.locale('cmd.remove.messages.removed', { + songNumber, + }), + ), + ], + }); + } } /** diff --git a/src/commands/music/Replay.ts b/src/commands/music/Replay.ts index de61b40ad..5b63284b4 100644 --- a/src/commands/music/Replay.ts +++ b/src/commands/music/Replay.ts @@ -1,50 +1,50 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Replay extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "replay", - description: { - content: "cmd.replay.description", - examples: ["replay"], - usage: "replay", - }, - category: "music", - aliases: ["rp"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: false, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'replay', + description: { + content: 'cmd.replay.description', + examples: ['replay'], + usage: 'replay', + }, + category: 'music', + aliases: ['rp'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: false, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); - if (!player.queue.current?.info.isSeekable) { - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.replay.errors.not_seekable"))], - }); - } + if (!player.queue.current?.info.isSeekable) { + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('cmd.replay.errors.not_seekable'))], + }); + } - player.seek(0); - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.replay.messages.replaying"))], - }); - } + player.seek(0); + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale('cmd.replay.messages.replaying'))], + }); + } } /** diff --git a/src/commands/music/Resume.ts b/src/commands/music/Resume.ts index fab194b11..020d6ea7a 100644 --- a/src/commands/music/Resume.ts +++ b/src/commands/music/Resume.ts @@ -1,50 +1,50 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Resume extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "resume", - description: { - content: "cmd.resume.description", - examples: ["resume"], - usage: "resume", - }, - category: "music", - aliases: ["r"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: false, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'resume', + description: { + content: 'cmd.resume.description', + examples: ['resume'], + usage: 'resume', + }, + category: 'music', + aliases: ['r'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: false, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); - if (!player.paused) { - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.resume.errors.not_paused"))], - }); - } + if (!player.paused) { + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('cmd.resume.errors.not_paused'))], + }); + } - player.resume(); - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.resume.messages.resumed"))], - }); - } + player.resume(); + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale('cmd.resume.messages.resumed'))], + }); + } } /** diff --git a/src/commands/music/Search.ts b/src/commands/music/Search.ts index a0837da17..3735bbde7 100644 --- a/src/commands/music/Search.ts +++ b/src/commands/music/Search.ts @@ -1,111 +1,112 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type VoiceChannel } from "discord.js"; -import type { SearchResult, Track } from "lavalink-client"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type TextChannel, type VoiceChannel } from 'discord.js'; +import type { SearchResult, Track } from 'lavalink-client'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Search extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "search", - description: { - content: "cmd.search.description", - examples: ["search example"], - usage: "search ", - }, - category: "music", - aliases: ["sc"], - cooldown: 3, - args: true, - vote: true, - player: { - voice: true, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "song", - description: "cmd.search.options.song", - type: 3, - required: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'search', + description: { + content: 'cmd.search.description', + examples: ['search example'], + usage: 'search ', + }, + category: 'music', + aliases: ['sc'], + cooldown: 3, + args: true, + vote: true, + player: { + voice: true, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'song', + description: 'cmd.search.options.song', + type: 3, + required: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const embed = this.client.embed().setColor(this.client.color.main); - let player = client.manager.getPlayer(ctx.guild!.id); - const query = args.join(" "); - const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const embed = this.client.embed().setColor(this.client.color.main); + let player = client.manager.getPlayer(ctx.guild!.id); + const query = args.join(' '); + const memberVoiceChannel = (ctx.member as any).voice.channel as VoiceChannel; - if (!player) - player = client.manager.createPlayer({ - guildId: ctx.guild!.id, - voiceChannelId: memberVoiceChannel.id, - textChannelId: ctx.channel.id, - selfMute: false, - selfDeaf: true, - vcRegion: memberVoiceChannel.rtcRegion, - }); - if (!player.connected) await player.connect(); - const response = (await player.search({ query: query }, ctx.author)) as SearchResult; - if (!response || response.tracks?.length === 0) { - return await ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.search.errors.no_results")).setColor(this.client.color.red)], - }); - } - const row = new ActionRowBuilder().addComponents( - new ButtonBuilder().setCustomId("1").setLabel("1").setStyle(ButtonStyle.Primary), - new ButtonBuilder().setCustomId("2").setLabel("2").setStyle(ButtonStyle.Primary), - new ButtonBuilder().setCustomId("3").setLabel("3").setStyle(ButtonStyle.Primary), - new ButtonBuilder().setCustomId("4").setLabel("4").setStyle(ButtonStyle.Primary), - new ButtonBuilder().setCustomId("5").setLabel("5").setStyle(ButtonStyle.Primary), - ); - if (response.loadType === "search" && response.tracks.length > 5) { - const embeds = response.tracks.map( - (track: Track, index: number) => `${index + 1}. [${track.info.title}](${track.info.uri}) - \`${track.info.author}\``, - ); - await ctx.sendMessage({ - embeds: [embed.setDescription(embeds.join("\n"))], - components: [row], - }); - } - const collector = ctx.channel.createMessageComponentCollector({ - filter: (f: any) => f.user.id === ctx.author.id, - max: 1, - time: 60000, - idle: 60000 / 2, - }); - collector.on("collect", async (int: any) => { - const track = response.tracks[parseInt(int.customId) - 1]; - await int.deferUpdate(); - if (!track) return; - player.queue.add(track); - if (!player.playing) await player.play({ paused: false }); - await ctx.editMessage({ - embeds: [ - embed.setDescription( - ctx.locale("cmd.search.messages.added_to_queue", { - title: track.info.title, - uri: track.info.uri, - }), - ), - ], - components: [], - }); - return collector.stop(); - }); - collector.on("end", async () => { - await ctx.editMessage({ components: [] }); - }); - } + if (!player) + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: memberVoiceChannel.id, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + vcRegion: memberVoiceChannel.rtcRegion!, + }); + if (!player.connected) await player.connect(); + const response = (await player.search({ query: query }, ctx.author)) as SearchResult; + if (!response || response.tracks?.length === 0) { + return await ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.search.errors.no_results')).setColor(this.client.color.red)], + }); + } + const row = new ActionRowBuilder().addComponents( + new ButtonBuilder().setCustomId('1').setLabel('1').setStyle(ButtonStyle.Primary), + new ButtonBuilder().setCustomId('2').setLabel('2').setStyle(ButtonStyle.Primary), + new ButtonBuilder().setCustomId('3').setLabel('3').setStyle(ButtonStyle.Primary), + new ButtonBuilder().setCustomId('4').setLabel('4').setStyle(ButtonStyle.Primary), + new ButtonBuilder().setCustomId('5').setLabel('5').setStyle(ButtonStyle.Primary), + ); + if (response.loadType === 'search' && response.tracks.length > 5) { + const embeds = response.tracks.map( + (track: Track, index: number) => + `${index + 1}. [${track.info.title}](${track.info.uri}) - \`${track.info.author}\``, + ); + await ctx.sendMessage({ + embeds: [embed.setDescription(embeds.join('\n'))], + components: [row], + }); + } + const collector = (ctx.channel as TextChannel).createMessageComponentCollector({ + filter: (f: any) => f.user.id === ctx.author?.id, + max: 1, + time: 60000, + idle: 60000 / 2, + }); + collector.on('collect', async (int: any) => { + const track = response.tracks[Number.parseInt(int.customId) - 1]; + await int.deferUpdate(); + if (!track) return; + player.queue.add(track); + if (!player.playing) await player.play({ paused: false }); + await ctx.editMessage({ + embeds: [ + embed.setDescription( + ctx.locale('cmd.search.messages.added_to_queue', { + title: track.info.title, + uri: track.info.uri, + }), + ), + ], + components: [], + }); + return collector.stop(); + }); + collector.on('end', async () => { + await ctx.editMessage({ components: [] }); + }); + } } /** diff --git a/src/commands/music/Seek.ts b/src/commands/music/Seek.ts index 3192eedaf..8b62f79a3 100644 --- a/src/commands/music/Seek.ts +++ b/src/commands/music/Seek.ts @@ -1,79 +1,79 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Seek extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "seek", - description: { - content: "cmd.seek.description", - examples: ["seek 1m, seek 1h 30m", "seek 1h 30m 30s"], - usage: "seek ", - }, - category: "music", - aliases: ["s"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: true, - dj: false, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "duration", - description: "cmd.seek.options.duration", - type: 3, - required: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'seek', + description: { + content: 'cmd.seek.description', + examples: ['seek 1m, seek 1h 30m', 'seek 1h 30m 30s'], + usage: 'seek ', + }, + category: 'music', + aliases: ['s'], + cooldown: 3, + args: true, + vote: false, + player: { + voice: true, + dj: false, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'duration', + description: 'cmd.seek.options.duration', + type: 3, + required: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const current = player.queue.current.info; - const embed = this.client.embed(); - const duration = client.utils.parseTime(args.join(" ")); - if (!duration) { - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.seek.errors.invalid_format"))], - }); - } - if (!current.isSeekable) { - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.seek.errors.not_seekable"))], - }); - } - if (duration > current.duration) { - return await ctx.sendMessage({ - embeds: [ - embed.setColor(this.client.color.red).setDescription( - ctx.locale("cmd.seek.errors.beyond_duration", { - length: client.utils.formatTime(current.duration), - }), - ), - ], - }); - } - player.seek(duration); - return await ctx.sendMessage({ - embeds: [ - embed.setColor(this.client.color.main).setDescription( - ctx.locale("cmd.seek.messages.seeked_to", { - duration: client.utils.formatTime(duration), - }), - ), - ], - }); - } + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const current = player.queue.current?.info; + const embed = this.client.embed(); + const duration = client.utils.parseTime(args.join(' ')); + if (!duration) { + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('cmd.seek.errors.invalid_format'))], + }); + } + if (!current?.isSeekable) { + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('cmd.seek.errors.not_seekable'))], + }); + } + if (duration > current.duration) { + return await ctx.sendMessage({ + embeds: [ + embed.setColor(this.client.color.red).setDescription( + ctx.locale('cmd.seek.errors.beyond_duration', { + length: client.utils.formatTime(current.duration), + }), + ), + ], + }); + } + player.seek(duration); + return await ctx.sendMessage({ + embeds: [ + embed.setColor(this.client.color.main).setDescription( + ctx.locale('cmd.seek.messages.seeked_to', { + duration: client.utils.formatTime(duration), + }), + ), + ], + }); + } } /** diff --git a/src/commands/music/Shuffle.ts b/src/commands/music/Shuffle.ts index b15c4fbec..4a3eeaaf4 100644 --- a/src/commands/music/Shuffle.ts +++ b/src/commands/music/Shuffle.ts @@ -1,48 +1,48 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Shuffle extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "shuffle", - description: { - content: "cmd.shuffle.description", - examples: ["shuffle"], - usage: "shuffle", - }, - category: "music", - aliases: ["sh"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'shuffle', + description: { + content: 'cmd.shuffle.description', + examples: ['shuffle'], + usage: 'shuffle', + }, + category: 'music', + aliases: ['sh'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); - if (player.queue.tracks.length === 0) { - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("player.errors.no_song"))], - }); - } - player.queue.shuffle(); - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.shuffle.messages.shuffled"))], - }); - } + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); + if (player.queue.tracks.length === 0) { + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('player.errors.no_song'))], + }); + } + player.queue.shuffle(); + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale('cmd.shuffle.messages.shuffled'))], + }); + } } /** diff --git a/src/commands/music/Skip.ts b/src/commands/music/Skip.ts index fa54dd522..e31aa161e 100644 --- a/src/commands/music/Skip.ts +++ b/src/commands/music/Skip.ts @@ -1,59 +1,59 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Skip extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "skip", - description: { - content: "cmd.skip.description", - examples: ["skip"], - usage: "skip", - }, - category: "music", - aliases: ["sk"], - cooldown: 3, - args: false, - vote: true, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'skip', + description: { + content: 'cmd.skip.description', + examples: ['skip'], + usage: 'skip', + }, + category: 'music', + aliases: ['sk'], + cooldown: 3, + args: false, + vote: true, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); - if (player.queue.tracks.length === 0) { - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("player.errors.no_song"))], - }); - } - const currentTrack = player.queue.current.info; - player.skip(); - if (ctx.isInteraction) { - return await ctx.sendMessage({ - embeds: [ - embed.setColor(this.client.color.main).setDescription( - ctx.locale("cmd.skip.messages.skipped", { - title: currentTrack.title, - uri: currentTrack.uri, - }), - ), - ], - }); - } - ctx.message?.react("👍"); - } + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); + if (player.queue.tracks.length === 0) { + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('player.errors.no_song'))], + }); + } + const currentTrack = player.queue.current?.info; + player.skip(); + if (ctx.isInteraction) { + return await ctx.sendMessage({ + embeds: [ + embed.setColor(this.client.color.main).setDescription( + ctx.locale('cmd.skip.messages.skipped', { + title: currentTrack?.title, + uri: currentTrack?.uri, + }), + ), + ], + }); + } + ctx.message?.react('👍'); + } } /** diff --git a/src/commands/music/Skipto.ts b/src/commands/music/Skipto.ts index 7b60c6508..5820d1c10 100644 --- a/src/commands/music/Skipto.ts +++ b/src/commands/music/Skipto.ts @@ -1,64 +1,64 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Skipto extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "skipto", - description: { - content: "cmd.skipto.description", - examples: ["skipto 3"], - usage: "skipto ", - }, - category: "music", - aliases: ["skt"], - cooldown: 3, - args: true, - vote: true, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "number", - description: "cmd.skipto.options.number", - type: 4, - required: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'skipto', + description: { + content: 'cmd.skipto.description', + examples: ['skipto 3'], + usage: 'skipto ', + }, + category: 'music', + aliases: ['skt'], + cooldown: 3, + args: true, + vote: true, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'number', + description: 'cmd.skipto.options.number', + type: 4, + required: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); - const num = Number(args[0]); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); + const num = Number(args[0]); - if (player.queue.tracks.length === 0 || isNaN(num) || num > player.queue.tracks.length || num < 1) { - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale("cmd.skipto.errors.invalid_number"))], - }); - } + if (player.queue.tracks.length === 0 || Number.isNaN(num) || num > player.queue.tracks.length || num < 1) { + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(ctx.locale('cmd.skipto.errors.invalid_number'))], + }); + } - player.skip(num); - return await ctx.sendMessage({ - embeds: [ - embed.setColor(this.client.color.main).setDescription( - ctx.locale("cmd.skipto.messages.skipped_to", { - number: num, - }), - ), - ], - }); - } + player.skip(num); + return await ctx.sendMessage({ + embeds: [ + embed.setColor(this.client.color.main).setDescription( + ctx.locale('cmd.skipto.messages.skipped_to', { + number: num, + }), + ), + ], + }); + } } /** diff --git a/src/commands/music/Stop.ts b/src/commands/music/Stop.ts index f3daabdde..12054564c 100644 --- a/src/commands/music/Stop.ts +++ b/src/commands/music/Stop.ts @@ -1,45 +1,45 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Stop extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "stop", - description: { - content: "cmd.stop.description", - examples: ["stop"], - usage: "stop", - }, - category: "music", - aliases: ["sp"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'stop', + description: { + content: 'cmd.stop.description', + examples: ['stop'], + usage: 'stop', + }, + category: 'music', + aliases: ['sp'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); + public async run(client: Lavamusic, ctx: Context): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); - player.stopPlaying(true, false); + player.stopPlaying(true, false); - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale("cmd.stop.messages.stopped"))], - }); - } + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.main).setDescription(ctx.locale('cmd.stop.messages.stopped'))], + }); + } } /** diff --git a/src/commands/music/Volume.ts b/src/commands/music/Volume.ts index c63568870..91dcc0f7f 100644 --- a/src/commands/music/Volume.ts +++ b/src/commands/music/Volume.ts @@ -1,71 +1,71 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class Volume extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "volume", - description: { - content: "cmd.volume.description", - examples: ["volume 100"], - usage: "volume ", - }, - category: "music", - aliases: ["v", "vol"], - cooldown: 3, - args: true, - vote: true, - player: { - voice: true, - dj: true, - active: true, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "number", - description: "cmd.volume.options.number", - type: 4, - required: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'volume', + description: { + content: 'cmd.volume.description', + examples: ['volume 100'], + usage: 'volume ', + }, + category: 'music', + aliases: ['v', 'vol'], + cooldown: 3, + args: true, + vote: true, + player: { + voice: true, + dj: true, + active: true, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'number', + description: 'cmd.volume.options.number', + type: 4, + required: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const player = client.manager.getPlayer(ctx.guild!.id); - const embed = this.client.embed(); - const number = Number(args[0]); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const player = client.manager.getPlayer(ctx.guild!.id); + const embed = this.client.embed(); + const number = Number(args[0]); - if (isNaN(number) || number < 0 || number > 200) { - let description = ""; - if (isNaN(number)) description = ctx.locale("cmd.volume.messages.invalid_number"); - else if (number < 0) description = ctx.locale("cmd.volume.messages.too_low"); - else if (number > 200) description = ctx.locale("cmd.volume.messages.too_high"); + if (Number.isNaN(number) || number < 0 || number > 200) { + let description = ''; + if (Number.isNaN(number)) description = ctx.locale('cmd.volume.messages.invalid_number'); + else if (number < 0) description = ctx.locale('cmd.volume.messages.too_low'); + else if (number > 200) description = ctx.locale('cmd.volume.messages.too_high'); - return await ctx.sendMessage({ - embeds: [embed.setColor(this.client.color.red).setDescription(description)], - }); - } + return await ctx.sendMessage({ + embeds: [embed.setColor(this.client.color.red).setDescription(description)], + }); + } - await player.setVolume(number); - const currentVolume = player.volume; + await player.setVolume(number); + const currentVolume = player.volume; - return await ctx.sendMessage({ - embeds: [ - embed.setColor(this.client.color.main).setDescription( - ctx.locale("cmd.volume.messages.set", { - volume: currentVolume, - }), - ), - ], - }); - } + return await ctx.sendMessage({ + embeds: [ + embed.setColor(this.client.color.main).setDescription( + ctx.locale('cmd.volume.messages.set', { + volume: currentVolume, + }), + ), + ], + }); + } } /** diff --git a/src/commands/playlist/AddSong.ts b/src/commands/playlist/AddSong.ts index 9d14b86f9..237ccf48a 100644 --- a/src/commands/playlist/AddSong.ts +++ b/src/commands/playlist/AddSong.ts @@ -1,139 +1,139 @@ -import type { AutocompleteInteraction } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { AutocompleteInteraction } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class AddSong extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "addsong", - description: { - content: "cmd.addsong.description", - examples: ["addsong test exemple", "addsong exemple https://www.youtube.com/watch?v=example"], - usage: "addsong ", - }, - category: "playlist", - aliases: ["as"], - cooldown: 3, - args: true, - vote: true, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "playlist", - description: "cmd.addsong.options.playlist", - type: 3, - required: true, - autocomplete: true, - }, - { - name: "song", - description: "cmd.addsong.options.song", - type: 3, - required: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'addsong', + description: { + content: 'cmd.addsong.description', + examples: ['addsong test exemple', 'addsong exemple https://www.youtube.com/watch?v=example'], + usage: 'addsong ', + }, + category: 'playlist', + aliases: ['as'], + cooldown: 3, + args: true, + vote: true, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'playlist', + description: 'cmd.addsong.options.playlist', + type: 3, + required: true, + autocomplete: true, + }, + { + name: 'song', + description: 'cmd.addsong.options.song', + type: 3, + required: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const playlist = args.shift(); - const song = args.join(" "); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const playlist = args.shift(); + const song = args.join(' '); - if (!playlist) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.addsong.messages.no_playlist"), - color: this.client.color.red, - }, - ], - }); - } + if (!playlist) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.addsong.messages.no_playlist'), + color: this.client.color.red, + }, + ], + }); + } - if (!song) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.addsong.messages.no_song"), - color: this.client.color.red, - }, - ], - }); - } - const res = await client.manager.search(song, ctx.author); - if (!res) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.addsong.messages.no_songs_found"), - color: this.client.color.red, - }, - ], - }); - } + if (!song) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.addsong.messages.no_song'), + color: this.client.color.red, + }, + ], + }); + } + const res = await client.manager.search(song, ctx.author); + if (!res) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.addsong.messages.no_songs_found'), + color: this.client.color.red, + }, + ], + }); + } - const playlistData = await client.db.getPlaylist(ctx.author.id, playlist); - if (!playlistData) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.addsong.messages.playlist_not_found"), - color: this.client.color.red, - }, - ], - }); - } + const playlistData = await client.db.getPlaylist(ctx.author?.id!, playlist); + if (!playlistData) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.addsong.messages.playlist_not_found'), + color: this.client.color.red, + }, + ], + }); + } - let trackStrings: any; - let count: number; - if (res.loadType === "playlist") { - trackStrings = res.tracks.map((track) => track.encoded); - count = res.tracks.length; - } else if (res.loadType === "track") { - trackStrings = [res.tracks[0].encoded]; - count = 1; - } else if (res.loadType === "search") { - trackStrings = [res.tracks[0].encoded]; - count = 1; - } + let trackStrings: any; + let count: number = 0; + if (res.loadType === 'playlist') { + trackStrings = res.tracks.map(track => track.encoded); + count = res.tracks.length; + } else if (res.loadType === 'track') { + trackStrings = [res.tracks[0].encoded]; + count = 1; + } else if (res.loadType === 'search') { + trackStrings = [res.tracks[0].encoded]; + count = 1; + } - await client.db.addTracksToPlaylist(ctx.author.id, playlist, trackStrings); + await client.db.addTracksToPlaylist(ctx.author?.id!, playlist, trackStrings); - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.addsong.messages.added", { playlist: playlistData.name, count }), - color: this.client.color.green, - }, - ], - }); - } + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.addsong.messages.added', { playlist: playlistData.name, count }), + color: this.client.color.green, + }, + ], + }); + } - public async autocomplete(interaction: AutocompleteInteraction): Promise { - const focusedValue = interaction.options.getFocused(); - const userId = interaction.user.id; + public async autocomplete(interaction: AutocompleteInteraction): Promise { + const focusedValue = interaction.options.getFocused(); + const userId = interaction.user.id; - const playlists = await this.client.db.getUserPlaylists(userId); + const playlists = await this.client.db.getUserPlaylists(userId); - const filtered = playlists.filter((playlist) => playlist.name.toLowerCase().startsWith(focusedValue.toLowerCase())); + const filtered = playlists.filter(playlist => playlist.name.toLowerCase().startsWith(focusedValue.toLowerCase())); - return await interaction.respond( - filtered.map((playlist) => ({ - name: playlist.name, - value: playlist.name, - })), - ); - } + return await interaction.respond( + filtered.map(playlist => ({ + name: playlist.name, + value: playlist.name, + })), + ); + } } /** diff --git a/src/commands/playlist/Create.ts b/src/commands/playlist/Create.ts index 252268df2..2aa9d947b 100644 --- a/src/commands/playlist/Create.ts +++ b/src/commands/playlist/Create.ts @@ -1,72 +1,74 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class CreatePlaylist extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "create", - description: { - content: "cmd.create.description", - examples: ["create "], - usage: "create ", - }, - category: "playlist", - aliases: ["cre"], - cooldown: 3, - args: true, - vote: true, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "name", - description: "cmd.create.options.name", - type: 3, - required: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'create', + description: { + content: 'cmd.create.description', + examples: ['create '], + usage: 'create ', + }, + category: 'playlist', + aliases: ['cre'], + cooldown: 3, + args: true, + vote: true, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'name', + description: 'cmd.create.options.name', + type: 3, + required: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const name = args.join(" ").trim(); - const embed = this.client.embed(); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const name = args.join(' ').trim(); + const embed = this.client.embed(); - if (name.length > 50) { - return await ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.create.messages.name_too_long")).setColor(this.client.color.red)], - }); - } + if (name.length > 50) { + return await ctx.sendMessage({ + embeds: [embed.setDescription(ctx.locale('cmd.create.messages.name_too_long')).setColor(this.client.color.red)], + }); + } - const playlistExists = await client.db.getPlaylist(ctx.author.id, name); - if (playlistExists) { - return await ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.create.messages.playlist_exists")).setColor(this.client.color.red)], - }); - } + const playlistExists = await client.db.getPlaylist(ctx.author?.id!, name); + if (playlistExists) { + return await ctx.sendMessage({ + embeds: [ + embed.setDescription(ctx.locale('cmd.create.messages.playlist_exists')).setColor(this.client.color.red), + ], + }); + } - await client.db.createPlaylist(ctx.author.id, name); - return await ctx.sendMessage({ - embeds: [ - embed - .setDescription( - ctx.locale("cmd.create.messages.playlist_created", { - name, - }), - ) - .setColor(this.client.color.green), - ], - }); - } + await client.db.createPlaylist(ctx.author?.id!, name); + return await ctx.sendMessage({ + embeds: [ + embed + .setDescription( + ctx.locale('cmd.create.messages.playlist_created', { + name, + }), + ) + .setColor(this.client.color.green), + ], + }); + } } /** diff --git a/src/commands/playlist/Delete.ts b/src/commands/playlist/Delete.ts index ab06f0e78..4a0846e9b 100644 --- a/src/commands/playlist/Delete.ts +++ b/src/commands/playlist/Delete.ts @@ -1,87 +1,89 @@ -import type { AutocompleteInteraction } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { AutocompleteInteraction } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class DeletePlaylist extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "delete", - description: { - content: "cmd.delete.description", - examples: ["delete "], - usage: "delete ", - }, - category: "playlist", - aliases: ["del"], - cooldown: 3, - args: true, - vote: true, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "playlist", - description: "cmd.delete.options.playlist", - type: 3, - required: true, - autocomplete: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'delete', + description: { + content: 'cmd.delete.description', + examples: ['delete '], + usage: 'delete ', + }, + category: 'playlist', + aliases: ['del'], + cooldown: 3, + args: true, + vote: true, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'playlist', + description: 'cmd.delete.options.playlist', + type: 3, + required: true, + autocomplete: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const playlistName = args.join(" ").trim(); - const embed = this.client.embed(); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const playlistName = args.join(' ').trim(); + const embed = this.client.embed(); - const playlistExists = await client.db.getPlaylist(ctx.author.id, playlistName); - if (!playlistExists) { - return await ctx.sendMessage({ - embeds: [embed.setDescription(ctx.locale("cmd.delete.messages.playlist_not_found")).setColor(this.client.color.red)], - }); - } + const playlistExists = await client.db.getPlaylist(ctx.author?.id!, playlistName); + if (!playlistExists) { + return await ctx.sendMessage({ + embeds: [ + embed.setDescription(ctx.locale('cmd.delete.messages.playlist_not_found')).setColor(this.client.color.red), + ], + }); + } - // First, delete all songs from the playlist - await client.db.deleteSongsFromPlaylist(ctx.author.id, playlistName); + // First, delete all songs from the playlist + await client.db.deleteSongsFromPlaylist(ctx.author?.id!, playlistName); - await client.db.deletePlaylist(ctx.author.id, playlistName); - return await ctx.sendMessage({ - embeds: [ - embed - .setDescription( - ctx.locale("cmd.delete.messages.playlist_deleted", { - playlistName, - }), - ) - .setColor(this.client.color.green), - ], - }); - } + await client.db.deletePlaylist(ctx.author?.id!, playlistName); + return await ctx.sendMessage({ + embeds: [ + embed + .setDescription( + ctx.locale('cmd.delete.messages.playlist_deleted', { + playlistName, + }), + ) + .setColor(this.client.color.green), + ], + }); + } - public async autocomplete(interaction: AutocompleteInteraction): Promise { - const focusedValue = interaction.options.getFocused(); - const userId = interaction.user.id; + public async autocomplete(interaction: AutocompleteInteraction): Promise { + const focusedValue = interaction.options.getFocused(); + const userId = interaction.user.id; - const playlists = await this.client.db.getUserPlaylists(userId); + const playlists = await this.client.db.getUserPlaylists(userId); - const filtered = playlists.filter((playlist) => playlist.name.toLowerCase().startsWith(focusedValue.toLowerCase())); + const filtered = playlists.filter(playlist => playlist.name.toLowerCase().startsWith(focusedValue.toLowerCase())); - await interaction.respond( - filtered.map((playlist) => ({ - name: playlist.name, - value: playlist.name, - })), - ); - } + await interaction.respond( + filtered.map(playlist => ({ + name: playlist.name, + value: playlist.name, + })), + ); + } } /** diff --git a/src/commands/playlist/List.ts b/src/commands/playlist/List.ts index 56d170cac..842438e56 100644 --- a/src/commands/playlist/List.ts +++ b/src/commands/playlist/List.ts @@ -1,128 +1,128 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class GetPlaylists extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "list", - description: { - content: "cmd.list.description", - examples: ["list", "list @user"], - usage: "list [@user]", - }, - category: "playlist", - aliases: ["lst"], - cooldown: 3, - args: false, - vote: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "user", - description: "cmd.list.options.user", - type: 6, - required: false, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'list', + description: { + content: 'cmd.list.description', + examples: ['list', 'list @user'], + usage: 'list [@user]', + }, + category: 'playlist', + aliases: ['lst'], + cooldown: 3, + args: false, + vote: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'user', + description: 'cmd.list.options.user', + type: 6, + required: false, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context): Promise { - try { - let userId: string | null = null; - let targetUser = ctx.args[0]; + public async run(client: Lavamusic, ctx: Context): Promise { + try { + let userId: string | undefined; + let targetUser = ctx.args[0]; - if (targetUser?.startsWith("<@") && targetUser.endsWith(">")) { - targetUser = targetUser.slice(2, -1); + if (targetUser?.startsWith('<@') && targetUser.endsWith('>')) { + targetUser = targetUser.slice(2, -1); - if (targetUser.startsWith("!")) { - targetUser = targetUser.slice(1); - } + if (targetUser.startsWith('!')) { + targetUser = targetUser.slice(1); + } - targetUser = await client.users.fetch(targetUser); - userId = targetUser.id; - } else if (targetUser) { - try { - targetUser = await client.users.fetch(targetUser); - userId = targetUser.id; - } catch (_error) { - const users = client.users.cache.filter((user) => user.username.toLowerCase() === targetUser.toLowerCase()); + targetUser = await client.users.fetch(targetUser); + userId = targetUser.id; + } else if (targetUser) { + try { + targetUser = await client.users.fetch(targetUser); + userId = targetUser.id; + } catch (_error) { + const users = client.users.cache.filter(user => user.username.toLowerCase() === targetUser.toLowerCase()); - if (users.size > 0) { - targetUser = users.first(); - userId = targetUser?.id ?? null; - } else { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.list.messages.invalid_username"), - color: this.client.color.red, - }, - ], - }); - } - } - } else { - userId = ctx.author.id; - targetUser = ctx.author; - } + if (users.size > 0) { + targetUser = users.first(); + userId = targetUser?.id ?? null; + } else { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.list.messages.invalid_username'), + color: this.client.color.red, + }, + ], + }); + } + } + } else { + userId = ctx.author?.id; + targetUser = ctx.author; + } - if (!userId) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.list.messages.invalid_userid"), - color: this.client.color.red, - }, - ], - }); - } + if (!userId) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.list.messages.invalid_userid'), + color: this.client.color.red, + }, + ], + }); + } - const playlists = await client.db.getUserPlaylists(userId); + const playlists = await client.db.getUserPlaylists(userId); - if (!playlists || playlists.length === 0) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.list.messages.no_playlists"), - color: this.client.color.red, - }, - ], - }); - } + if (!playlists || playlists.length === 0) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.list.messages.no_playlists'), + color: this.client.color.red, + }, + ], + }); + } - const targetUsername = targetUser ? targetUser.username : ctx.locale("cmd.list.messages.your"); - return await ctx.sendMessage({ - embeds: [ - { - title: ctx.locale("cmd.list.messages.playlists_title", { username: targetUsername }), - description: playlists.map((playlist: any) => playlist.name).join("\n"), - color: this.client.color.main, - }, - ], - }); - } catch (error) { - console.error(error); - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.list.messages.error"), - color: this.client.color.red, - }, - ], - }); - } - } + const targetUsername = targetUser ? targetUser.username : ctx.locale('cmd.list.messages.your'); + return await ctx.sendMessage({ + embeds: [ + { + title: ctx.locale('cmd.list.messages.playlists_title', { username: targetUsername }), + description: playlists.map((playlist: any) => playlist.name).join('\n'), + color: this.client.color.main, + }, + ], + }); + } catch (error) { + console.error(error); + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.list.messages.error'), + color: this.client.color.red, + }, + ], + }); + } + } } /** diff --git a/src/commands/playlist/Load.ts b/src/commands/playlist/Load.ts index bd6145af6..9f6ca3689 100644 --- a/src/commands/playlist/Load.ts +++ b/src/commands/playlist/Load.ts @@ -1,126 +1,129 @@ -import type { AutocompleteInteraction, GuildMember } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { AutocompleteInteraction, GuildMember } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class LoadPlaylist extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "load", - description: { - content: "cmd.load.description", - examples: ["load "], - usage: "load ", - }, - category: "playlist", - aliases: ["lo"], - cooldown: 3, - args: true, - vote: true, - player: { - voice: true, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "playlist", - description: "cmd.load.options.playlist", - type: 3, - required: true, - autocomplete: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'load', + description: { + content: 'cmd.load.description', + examples: ['load '], + usage: 'load ', + }, + category: 'playlist', + aliases: ['lo'], + cooldown: 3, + args: true, + vote: true, + player: { + voice: true, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'playlist', + description: 'cmd.load.options.playlist', + type: 3, + required: true, + autocomplete: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - let player = client.manager.getPlayer(ctx.guild!.id); - const playlistName = args.join(" ").trim(); - const playlistData = await client.db.getPlaylist(ctx.author.id, playlistName); - if (!playlistData) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.load.messages.playlist_not_exist"), - color: this.client.color.red, - }, - ], - }); - } + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + let player = client.manager.getPlayer(ctx.guild!.id); + const playlistName = args.join(' ').trim(); + const playlistData = await client.db.getPlaylist(ctx.author?.id!, playlistName); + if (!playlistData) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.load.messages.playlist_not_exist'), + color: this.client.color.red, + }, + ], + }); + } - const songs = await client.db.getTracksFromPlaylist(ctx.author.id, playlistName); - if (songs.length === 0) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.load.messages.playlist_empty"), - color: client.color.red, - }, - ], - }); - } + const songs = await client.db.getTracksFromPlaylist(ctx.author?.id!, playlistName); + if (songs.length === 0) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.load.messages.playlist_empty'), + color: client.color.red, + }, + ], + }); + } - const member = ctx.member as GuildMember; - if (!player) { - player = client.manager.createPlayer({ - guildId: ctx.guild!.id, - voiceChannelId: member.voice.channelId, - textChannelId: ctx.channel.id, - selfMute: false, - selfDeaf: true, - vcRegion: member.voice.channel.rtcRegion, - }); - if (!player.connected) await player.connect(); - } + const member = ctx.member as GuildMember; + if (!player) { + player = client.manager.createPlayer({ + guildId: ctx.guild!.id, + voiceChannelId: member.voice.channelId!, + textChannelId: ctx.channel.id, + selfMute: false, + selfDeaf: true, + vcRegion: member.voice.channel?.rtcRegion!, + }); + if (!player.connected) await player.connect(); + } - const nodes = client.manager.nodeManager.leastUsedNodes(); - const node = nodes[Math.floor(Math.random() * nodes.length)]; - const tracks = await node.decode.multipleTracks(songs as any, ctx.author); - if (tracks.length === 0) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.load.messages.playlist_empty"), - color: client.color.red, - }, - ], - }); - } - player.queue.add(tracks); + const nodes = client.manager.nodeManager.leastUsedNodes(); + const node = nodes[Math.floor(Math.random() * nodes.length)]; + const tracks = await node.decode.multipleTracks(songs as any, ctx.author); + if (tracks.length === 0) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.load.messages.playlist_empty'), + color: client.color.red, + }, + ], + }); + } + player.queue.add(tracks); - if (!player.playing) await player.play({ paused: false }); + if (!player.playing) await player.play({ paused: false }); - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.load.messages.playlist_loaded", { name: playlistData.name, count: songs.length }), - color: this.client.color.main, - }, - ], - }); - } + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.load.messages.playlist_loaded', { + name: playlistData.name, + count: songs.length, + }), + color: this.client.color.main, + }, + ], + }); + } - public async autocomplete(interaction: AutocompleteInteraction): Promise { - const focusedValue = interaction.options.getFocused(); - const userId = interaction.user.id; + public async autocomplete(interaction: AutocompleteInteraction): Promise { + const focusedValue = interaction.options.getFocused(); + const userId = interaction.user.id; - const playlists = await this.client.db.getUserPlaylists(userId); + const playlists = await this.client.db.getUserPlaylists(userId); - const filtered = playlists.filter((playlist) => playlist.name.toLowerCase().startsWith(focusedValue.toLowerCase())); + const filtered = playlists.filter(playlist => playlist.name.toLowerCase().startsWith(focusedValue.toLowerCase())); - await interaction.respond( - filtered.map((playlist) => ({ - name: playlist.name, - value: playlist.name, - })), - ); - } + await interaction.respond( + filtered.map(playlist => ({ + name: playlist.name, + value: playlist.name, + })), + ); + } } /** diff --git a/src/commands/playlist/RemoveSong.ts b/src/commands/playlist/RemoveSong.ts index 900b95301..ca60f6a99 100644 --- a/src/commands/playlist/RemoveSong.ts +++ b/src/commands/playlist/RemoveSong.ts @@ -1,81 +1,81 @@ -import type { AutocompleteInteraction } from "discord.js"; -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { AutocompleteInteraction } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class RemoveSong extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "removesong", - description: { - content: "cmd.removesong.description", - examples: ["removesong "], - usage: "removesong ", - }, - category: "playlist", - aliases: ["rs"], - cooldown: 3, - args: true, - vote: true, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "playlist", - description: "cmd.removesong.options.playlist", - type: 3, - required: true, - autocomplete: true, - }, - { - name: "song", - description: "cmd.removesong.options.song", - type: 3, - required: true, - }, - ], - }); - } + constructor(client: Lavamusic) { + super(client, { + name: 'removesong', + description: { + content: 'cmd.removesong.description', + examples: ['removesong '], + usage: 'removesong ', + }, + category: 'playlist', + aliases: ['rs'], + cooldown: 3, + args: true, + vote: true, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'playlist', + description: 'cmd.removesong.options.playlist', + type: 3, + required: true, + autocomplete: true, + }, + { + name: 'song', + description: 'cmd.removesong.options.song', + type: 3, + required: true, + }, + ], + }); + } - public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { - const playlist = args.shift(); - const song = args.join(" "); + public async run(client: Lavamusic, ctx: Context, args: string[]): Promise { + const playlist = args.shift(); + const song = args.join(' '); - if (!playlist) { - const errorMessage = this.client - .embed() - .setDescription(ctx.locale("cmd.removesong.messages.provide_playlist")) - .setColor(this.client.color.red); - return await ctx.sendMessage({ embeds: [errorMessage] }); - } + if (!playlist) { + const errorMessage = this.client + .embed() + .setDescription(ctx.locale('cmd.removesong.messages.provide_playlist')) + .setColor(this.client.color.red); + return await ctx.sendMessage({ embeds: [errorMessage] }); + } - if (!song) { - const errorMessage = this.client - .embed() - .setDescription(ctx.locale("cmd.removesong.messages.provide_song")) - .setColor(this.client.color.red); - return await ctx.sendMessage({ embeds: [errorMessage] }); - } + if (!song) { + const errorMessage = this.client + .embed() + .setDescription(ctx.locale('cmd.removesong.messages.provide_song')) + .setColor(this.client.color.red); + return await ctx.sendMessage({ embeds: [errorMessage] }); + } - const playlistData = await client.db.getPlaylist(ctx.author.id, playlist); + const playlistData = await client.db.getPlaylist(ctx.author?.id!, playlist); - if (!playlistData) { - const playlistNotFoundError = this.client - .embed() - .setDescription(ctx.locale("cmd.removesong.messages.playlist_not_exist")) - .setColor(this.client.color.red); - return await ctx.sendMessage({ embeds: [playlistNotFoundError] }); - } + if (!playlistData) { + const playlistNotFoundError = this.client + .embed() + .setDescription(ctx.locale('cmd.removesong.messages.playlist_not_exist')) + .setColor(this.client.color.red); + return await ctx.sendMessage({ embeds: [playlistNotFoundError] }); + } - /* const res = await client.queue.search(song); + /* const res = await client.queue.search(song); if (!res || res.loadType !== LoadType.TRACK) { const noSongsFoundError = this.client @@ -108,22 +108,22 @@ export default class RemoveSong extends Command { .setColor(this.client.color.red); return await ctx.sendMessage({ embeds: [genericError] }); } */ - } - public async autocomplete(interaction: AutocompleteInteraction): Promise { - const focusedValue = interaction.options.getFocused(); - const userId = interaction.user.id; + } + public async autocomplete(interaction: AutocompleteInteraction): Promise { + const focusedValue = interaction.options.getFocused(); + const userId = interaction.user.id; - const playlists = await this.client.db.getUserPlaylists(userId); + const playlists = await this.client.db.getUserPlaylists(userId); - const filtered = playlists.filter((playlist) => playlist.name.toLowerCase().startsWith(focusedValue.toLowerCase())); + const filtered = playlists.filter(playlist => playlist.name.toLowerCase().startsWith(focusedValue.toLowerCase())); - await interaction.respond( - filtered.map((playlist) => ({ - name: playlist.name, - value: playlist.name, - })), - ); - } + await interaction.respond( + filtered.map(playlist => ({ + name: playlist.name, + value: playlist.name, + })), + ); + } } /** diff --git a/src/commands/playlist/Steal.ts b/src/commands/playlist/Steal.ts index 6cb52bab3..0cedbbb6a 100644 --- a/src/commands/playlist/Steal.ts +++ b/src/commands/playlist/Steal.ts @@ -1,197 +1,200 @@ -import { Command, type Context, type Lavamusic } from "../../structures/index"; +import type { AutocompleteInteraction } from 'discord.js'; +import { Command, type Context, type Lavamusic } from '../../structures/index'; export default class StealPlaylist extends Command { - constructor(client: Lavamusic) { - super(client, { - name: "steal", - description: { - content: "cmd.steal.description", - examples: ["steal <@user> "], - usage: "steal <@user> ", - }, - category: "playlist", - aliases: ["st"], - cooldown: 3, - args: true, - vote: false, - player: { - voice: false, - dj: false, - active: false, - djPerm: null, - }, - permissions: { - dev: false, - client: ["SendMessages", "ReadMessageHistory", "ViewChannel", "EmbedLinks"], - user: [], - }, - slashCommand: true, - options: [ - { - name: "user", - description: "cmd.steal.options.user", - type: 6, - required: true, - }, - { - name: "playlist", - description: "cmd.steal.options.playlist", - type: 3, - required: true, - autocomplete: true, - }, - ], - }); - } - - public async run(client: Lavamusic, ctx: Context): Promise { - let targetUser = ctx.args[0]; - const playlistName = ctx.args[1]; - let targetUserId: string | null = null; - - if (targetUser?.startsWith("<@") && targetUser.endsWith(">")) { - targetUser = targetUser.slice(2, -1); - if (targetUser.startsWith("!")) { - targetUser = targetUser.slice(1); - } - targetUser = await client.users.fetch(targetUser); - targetUserId = targetUser.id; - } else if (targetUser) { - try { - targetUser = await client.users.fetch(targetUser); - targetUserId = targetUser.id; - } catch (_error) { - const users = client.users.cache.filter((user) => user.username.toLowerCase() === targetUser.toLowerCase()); - - if (users.size > 0) { - targetUser = users.first(); - targetUserId = targetUser.id; - } else { - return await ctx.sendMessage({ - embeds: [ - { - description: "Invalid username or user not found.", - color: this.client.color.red, - }, - ], - }); - } - } - } - - if (!playlistName) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.steal.messages.provide_playlist"), - color: this.client.color.red, - }, - ], - }); - } - - if (!targetUserId) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.steal.messages.provide_user"), - color: this.client.color.red, - }, - ], - }); - } - - try { - const targetPlaylist = await client.db.getPlaylist(targetUserId, playlistName); - - if (!targetPlaylist) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.steal.messages.playlist_not_exist"), - color: this.client.color.red, - }, - ], - }); - } - - const targetSongs = await client.db.getTracksFromPlaylist(targetUserId, playlistName); - - const existingPlaylist = await client.db.getPlaylist(ctx.author.id, playlistName); - if (existingPlaylist) { - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.steal.messages.playlist_exists", { playlist: playlistName }), - color: this.client.color.red, - }, - ], - }); - } - - await client.db.createPlaylistWithTracks(ctx.author.id, playlistName, targetSongs); - - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.steal.messages.playlist_stolen", { - playlist: playlistName, - user: targetUser.username, - }), - color: this.client.color.main, - }, - ], - }); - } catch (error) { - console.error(error); - return await ctx.sendMessage({ - embeds: [ - { - description: ctx.locale("cmd.steal.messages.error_occurred"), - color: this.client.color.red, - }, - ], - }); - } - } - - public async autocomplete(interaction) { - try { - const focusedValue = interaction.options.getFocused(); - const userOptionId = interaction.options.get("user")?.value; - - if (!userOptionId) { - await interaction - .respond([{ name: "Please specify a user to search their playlists.", value: "NoUser" }]) - .catch(console.error); - return; - } - - const user = await interaction.client.users.fetch(userOptionId); - if (!user) { - await interaction.respond([{ name: "User not found.", value: "NoUserFound" }]).catch(console.error); - return; - } - - const playlists = await this.client.db.getUserPlaylists(user.id); - - if (!playlists || playlists.length === 0) { - await interaction.respond([{ name: "No playlists found for this user.", value: "NoPlaylists" }]).catch(console.error); - return; - } - - const filtered = playlists.filter((playlist) => playlist.name.toLowerCase().startsWith(focusedValue.toLowerCase())); - - return await interaction - .respond(filtered.map((playlist) => ({ name: playlist.name, value: playlist.name }))) - .catch(console.error); - } catch (error) { - console.error("Error in autocomplete interaction:", error); - return await interaction - .respond([{ name: "An error occurred while fetching playlists.", value: "Error" }]) - .catch(console.error); - } - } + constructor(client: Lavamusic) { + super(client, { + name: 'steal', + description: { + content: 'cmd.steal.description', + examples: ['steal <@user> '], + usage: 'steal <@user> ', + }, + category: 'playlist', + aliases: ['st'], + cooldown: 3, + args: true, + vote: false, + player: { + voice: false, + dj: false, + active: false, + djPerm: null, + }, + permissions: { + dev: false, + client: ['SendMessages', 'ReadMessageHistory', 'ViewChannel', 'EmbedLinks'], + user: [], + }, + slashCommand: true, + options: [ + { + name: 'user', + description: 'cmd.steal.options.user', + type: 6, + required: true, + }, + { + name: 'playlist', + description: 'cmd.steal.options.playlist', + type: 3, + required: true, + autocomplete: true, + }, + ], + }); + } + + public async run(client: Lavamusic, ctx: Context): Promise { + let targetUser = ctx.args[0]; + const playlistName = ctx.args[1]; + let targetUserId: string | null = null; + + if (targetUser?.startsWith('<@') && targetUser.endsWith('>')) { + targetUser = targetUser.slice(2, -1); + if (targetUser.startsWith('!')) { + targetUser = targetUser.slice(1); + } + targetUser = await client.users.fetch(targetUser); + targetUserId = targetUser.id; + } else if (targetUser) { + try { + targetUser = await client.users.fetch(targetUser); + targetUserId = targetUser.id; + } catch (_error) { + const users = client.users.cache.filter(user => user.username.toLowerCase() === targetUser.toLowerCase()); + + if (users.size > 0) { + targetUser = users.first(); + targetUserId = targetUser.id; + } else { + return await ctx.sendMessage({ + embeds: [ + { + description: 'Invalid username or user not found.', + color: this.client.color.red, + }, + ], + }); + } + } + } + + if (!playlistName) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.steal.messages.provide_playlist'), + color: this.client.color.red, + }, + ], + }); + } + + if (!targetUserId) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.steal.messages.provide_user'), + color: this.client.color.red, + }, + ], + }); + } + + try { + const targetPlaylist = await client.db.getPlaylist(targetUserId, playlistName); + + if (!targetPlaylist) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.steal.messages.playlist_not_exist'), + color: this.client.color.red, + }, + ], + }); + } + + const targetSongs = await client.db.getTracksFromPlaylist(targetUserId, playlistName); + + const existingPlaylist = await client.db.getPlaylist(ctx.author?.id!, playlistName); + if (existingPlaylist) { + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.steal.messages.playlist_exists', { playlist: playlistName }), + color: this.client.color.red, + }, + ], + }); + } + + await client.db.createPlaylistWithTracks(ctx.author?.id!, playlistName, targetSongs); + + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.steal.messages.playlist_stolen', { + playlist: playlistName, + user: targetUser.username, + }), + color: this.client.color.main, + }, + ], + }); + } catch (error) { + console.error(error); + return await ctx.sendMessage({ + embeds: [ + { + description: ctx.locale('cmd.steal.messages.error_occurred'), + color: this.client.color.red, + }, + ], + }); + } + } + + public async autocomplete(interaction: AutocompleteInteraction) { + try { + const focusedValue = interaction.options.getFocused(); + const userOptionId = interaction.options.get('user')?.value as string; + + if (!userOptionId) { + await interaction + .respond([{ name: 'Please specify a user to search their playlists.', value: 'NoUser' }]) + .catch(console.error); + return; + } + + const user = await interaction.client.users.fetch(userOptionId); + if (!user) { + await interaction.respond([{ name: 'User not found.', value: 'NoUserFound' }]).catch(console.error); + return; + } + + const playlists = await this.client.db.getUserPlaylists(user.id); + + if (!playlists || playlists.length === 0) { + await interaction + .respond([{ name: 'No playlists found for this user.', value: 'NoPlaylists' }]) + .catch(console.error); + return; + } + + const filtered = playlists.filter(playlist => playlist.name.toLowerCase().startsWith(focusedValue.toLowerCase())); + + return await interaction + .respond(filtered.map(playlist => ({ name: playlist.name, value: playlist.name }))) + .catch(console.error); + } catch (error) { + console.error('Error in autocomplete interaction:', error); + return await interaction + .respond([{ name: 'An error occurred while fetching playlists.', value: 'Error' }]) + .catch(console.error); + } + } } /** diff --git a/src/config.ts b/src/config.ts index b0456632b..792c8a04a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,46 +1,46 @@ export default { - color: { - red: 0xff0000, - green: 0x00ff00, - blue: 0x0000ff, - yellow: 0xffff00, - main: 0x2f3136, - }, - emoji: { - // You can add custom emoji with ID format (e.g., <:emojiName:123456789012345678>) - pause: "⏸️", - resume: "▶️", - stop: "⏹️", - skip: "⏭️", - previous: "⏮️", - forward: "⏩", - rewind: "⏪", - voldown: "🔉", - volup: "🔊", - shuffle: "🔀", - loop: { - none: "🔁", - track: "🔂", - }, - page: { - last: "⏩", - first: "⏪", - back: "⬅️", - next: "➡️", - cancel: "⏹️", - }, - }, - icons: { - youtube: "https://i.imgur.com/xzVHhFY.png", - spotify: "https://i.imgur.com/qvdqtsc.png", - soundcloud: "https://i.imgur.com/MVnJ7mj.png", - applemusic: "https://i.imgur.com/Wi0oyYm.png", - deezer: "https://i.imgur.com/xyZ43FG.png", - jiosaavn: "https://i.imgur.com/N9Nt80h.png", - }, - links: { - img: "https://i.imgur.com/ud3EWNh.jpg", - }, + color: { + red: 0xff0000, + green: 0x00ff00, + blue: 0x0000ff, + yellow: 0xffff00, + main: 0x2f3136, + }, + emoji: { + // You can add custom emoji with ID format (e.g., <:emojiName:123456789012345678>) + pause: '⏸️', + resume: '▶️', + stop: '⏹️', + skip: '⏭️', + previous: '⏮️', + forward: '⏩', + rewind: '⏪', + voldown: '🔉', + volup: '🔊', + shuffle: '🔀', + loop: { + none: '🔁', + track: '🔂', + }, + page: { + last: '⏩', + first: '⏪', + back: '⬅️', + next: '➡️', + cancel: '⏹️', + }, + }, + icons: { + youtube: 'https://i.imgur.com/xzVHhFY.png', + spotify: 'https://i.imgur.com/qvdqtsc.png', + soundcloud: 'https://i.imgur.com/MVnJ7mj.png', + applemusic: 'https://i.imgur.com/Wi0oyYm.png', + deezer: 'https://i.imgur.com/xyZ43FG.png', + jiosaavn: 'https://i.imgur.com/N9Nt80h.png', + } as any, + links: { + img: 'https://i.imgur.com/ud3EWNh.jpg', + }, }; /** diff --git a/src/database/server.ts b/src/database/server.ts index c84a7bc2f..a023c695c 100644 --- a/src/database/server.ts +++ b/src/database/server.ts @@ -1,267 +1,267 @@ -import { type Dj, type Guild, type Playlist, PrismaClient, type Role, type Setup, type Stay } from "@prisma/client"; -import { env } from "../env"; +import { type Dj, type Guild, type Playlist, PrismaClient, type Role, type Setup, type Stay } from '@prisma/client'; +import { env } from '../env'; export default class ServerData { - private prisma: PrismaClient; - - constructor() { - this.prisma = new PrismaClient(); - } - - public async get(guildId: string): Promise { - return (await this.prisma.guild.findUnique({ where: { guildId } })) ?? this.createGuild(guildId); - } - - private async createGuild(guildId: string): Promise { - return await this.prisma.guild.create({ - data: { - guildId, - prefix: env.PREFIX, - }, - }); - } - - public async setPrefix(guildId: string, prefix: string): Promise { - await this.prisma.guild.upsert({ - where: { guildId }, - update: { prefix }, - create: { guildId, prefix }, - }); - } - - public async getPrefix(guildId: string): Promise { - const guild = await this.get(guildId); - return guild?.prefix ?? env.PREFIX; - } - - public async updateLanguage(guildId: string, language: string): Promise { - await this.prisma.guild.update({ - where: { guildId }, - data: { language }, - }); - } - - public async getLanguage(guildId: string): Promise { - const guild = await this.get(guildId); - return guild?.language ?? env.DEFAULT_LANGUAGE; - } - - public async getSetup(guildId: string): Promise { - return await this.prisma.setup.findUnique({ where: { guildId } }); - } - - public async setSetup(guildId: string, textId: string, messageId: string): Promise { - await this.prisma.setup.upsert({ - where: { guildId }, - update: { textId, messageId }, - create: { guildId, textId, messageId }, - }); - } - - public async deleteSetup(guildId: string): Promise { - await this.prisma.setup.delete({ where: { guildId } }); - } - - public async set_247(guildId: string, textId: string, voiceId: string): Promise { - await this.prisma.stay.upsert({ - where: { guildId }, - update: { textId, voiceId }, - create: { guildId, textId, voiceId }, - }); - } - - public async delete_247(guildId: string): Promise { - await this.prisma.stay.delete({ where: { guildId } }); - } - - public async get_247(guildId?: string): Promise { - if (guildId) { - return await this.prisma.stay.findUnique({ where: { guildId } }); - } - return this.prisma.stay.findMany(); - } - - public async setDj(guildId: string, mode: boolean): Promise { - await this.prisma.dj.upsert({ - where: { guildId }, - update: { mode }, - create: { guildId, mode }, - }); - } - - public async getDj(guildId: string): Promise { - return await this.prisma.dj.findUnique({ where: { guildId } }); - } - - public async getRoles(guildId: string): Promise { - return await this.prisma.role.findMany({ where: { guildId } }); - } - - public async addRole(guildId: string, roleId: string): Promise { - await this.prisma.role.create({ data: { guildId, roleId } }); - } - - public async removeRole(guildId: string, roleId: string): Promise { - await this.prisma.role.deleteMany({ where: { guildId, roleId } }); - } - - public async clearRoles(guildId: string): Promise { - await this.prisma.role.deleteMany({ where: { guildId } }); - } - - public async getPlaylist(userId: string, name: string): Promise { - return await this.prisma.playlist.findUnique({ - where: { userId_name: { userId, name } }, - }); - } - - public async getUserPlaylists(userId: string): Promise { - return await this.prisma.playlist.findMany({ - where: { userId }, - }); - } - - public async createPlaylist(userId: string, name: string): Promise { - await this.prisma.playlist.create({ data: { userId, name } }); - } - - // createPlaylist with tracks - public async createPlaylistWithTracks(userId: string, name: string, tracks: string[]): Promise { - await this.prisma.playlist.create({ - data: { - userId, - name, - tracks: JSON.stringify(tracks), - }, - }); - } - /** - * Deletes a playlist from the database - * - * @param userId The ID of the user that owns the playlist - * @param name The name of the playlist to delete - */ - public async deletePlaylist(userId: string, name: string): Promise { - await this.prisma.playlist.delete({ - where: { userId_name: { userId, name } }, - }); - } - - public async deleteSongsFromPlaylist(userId: string, playlistName: string): Promise { - // Fetch the playlist - const playlist = await this.getPlaylist(userId, playlistName); - - if (playlist) { - // Update the playlist and reset the tracks to an empty array - await this.prisma.playlist.update({ - where: { - userId_name: { - userId, - name: playlistName, - }, - }, - data: { - tracks: JSON.stringify([]), // Set tracks to an empty array - }, - }); - } - } - - public async addTracksToPlaylist(userId: string, playlistName: string, tracks: string[]) { - // Serialize the tracks array into a JSON string - const tracksJson = JSON.stringify(tracks); - - // Check if the playlist already exists for the user - const playlist = await this.prisma.playlist.findUnique({ - where: { - userId_name: { - userId, - name: playlistName, - }, - }, - }); - - if (playlist) { - // If the playlist exists, handle existing tracks - const existingTracks = playlist.tracks ? JSON.parse(playlist.tracks) : []; // Initialize as an empty array if null - - if (Array.isArray(existingTracks)) { - // Merge new and existing tracks - const updatedTracks = [...existingTracks, ...tracks]; - - // Update the playlist with the new tracks - await this.prisma.playlist.update({ - where: { - userId_name: { - userId, - name: playlistName, - }, - }, - data: { - tracks: JSON.stringify(updatedTracks), // Store the updated tracks as a serialized JSON string - }, - }); - } else { - throw new Error("Existing tracks are not in an array format."); - } - } else { - // If no playlist exists, create a new one with the provided tracks - await this.prisma.playlist.create({ - data: { - userId, - name: playlistName, - tracks: tracksJson, // Store the serialized JSON string - }, - }); - } - } - - public async removeSong(userId: string, playlistName: string, encodedSong: string): Promise { - const playlist = await this.getPlaylist(userId, playlistName); - if (playlist) { - const tracks: string[] = JSON.parse(playlist.tracks); - - // Find the index of the song to remove - const songIndex = tracks.indexOf(encodedSong); - - if (songIndex !== -1) { - // Remove the song from the array - tracks.splice(songIndex, 1); - - // Update the playlist with the new list of tracks - await this.prisma.playlist.update({ - where: { - userId_name: { - userId, - name: playlistName, - }, - }, - data: { - tracks: JSON.stringify(tracks), // Re-serialize the updated array back to a string - }, - }); - } - } - } - - public async getTracksFromPlaylist(userId: string, playlistName: string) { - const playlist = await this.prisma.playlist.findUnique({ - where: { - userId_name: { - userId, - name: playlistName, - }, - }, - }); - - if (!playlist) { - return null; - } - - // Deserialize the tracks JSON string back into an array - const tracks = JSON.parse(playlist.tracks); - return tracks; - } + private prisma: PrismaClient; + + constructor() { + this.prisma = new PrismaClient(); + } + + public async get(guildId: string): Promise { + return (await this.prisma.guild.findUnique({ where: { guildId } })) ?? this.createGuild(guildId); + } + + private async createGuild(guildId: string): Promise { + return await this.prisma.guild.create({ + data: { + guildId, + prefix: env.PREFIX, + }, + }); + } + + public async setPrefix(guildId: string, prefix: string): Promise { + await this.prisma.guild.upsert({ + where: { guildId }, + update: { prefix }, + create: { guildId, prefix }, + }); + } + + public async getPrefix(guildId: string): Promise { + const guild = await this.get(guildId); + return guild?.prefix ?? env.PREFIX; + } + + public async updateLanguage(guildId: string, language: string): Promise { + await this.prisma.guild.update({ + where: { guildId }, + data: { language }, + }); + } + + public async getLanguage(guildId: string): Promise { + const guild = await this.get(guildId); + return guild?.language ?? env.DEFAULT_LANGUAGE; + } + + public async getSetup(guildId: string): Promise { + return await this.prisma.setup.findUnique({ where: { guildId } }); + } + + public async setSetup(guildId: string, textId: string, messageId: string): Promise { + await this.prisma.setup.upsert({ + where: { guildId }, + update: { textId, messageId }, + create: { guildId, textId, messageId }, + }); + } + + public async deleteSetup(guildId: string): Promise { + await this.prisma.setup.delete({ where: { guildId } }); + } + + public async set_247(guildId: string, textId: string, voiceId: string): Promise { + await this.prisma.stay.upsert({ + where: { guildId }, + update: { textId, voiceId }, + create: { guildId, textId, voiceId }, + }); + } + + public async delete_247(guildId: string): Promise { + await this.prisma.stay.delete({ where: { guildId } }); + } + + public async get_247(guildId?: string): Promise { + if (guildId) { + return await this.prisma.stay.findUnique({ where: { guildId } }); + } + return this.prisma.stay.findMany(); + } + + public async setDj(guildId: string, mode: boolean): Promise { + await this.prisma.dj.upsert({ + where: { guildId }, + update: { mode }, + create: { guildId, mode }, + }); + } + + public async getDj(guildId: string): Promise { + return await this.prisma.dj.findUnique({ where: { guildId } }); + } + + public async getRoles(guildId: string): Promise { + return await this.prisma.role.findMany({ where: { guildId } }); + } + + public async addRole(guildId: string, roleId: string): Promise { + await this.prisma.role.create({ data: { guildId, roleId } }); + } + + public async removeRole(guildId: string, roleId: string): Promise { + await this.prisma.role.deleteMany({ where: { guildId, roleId } }); + } + + public async clearRoles(guildId: string): Promise { + await this.prisma.role.deleteMany({ where: { guildId } }); + } + + public async getPlaylist(userId: string, name: string): Promise { + return await this.prisma.playlist.findUnique({ + where: { userId_name: { userId, name } }, + }); + } + + public async getUserPlaylists(userId: string): Promise { + return await this.prisma.playlist.findMany({ + where: { userId }, + }); + } + + public async createPlaylist(userId: string, name: string): Promise { + await this.prisma.playlist.create({ data: { userId, name } }); + } + + // createPlaylist with tracks + public async createPlaylistWithTracks(userId: string, name: string, tracks: string[]): Promise { + await this.prisma.playlist.create({ + data: { + userId, + name, + tracks: JSON.stringify(tracks), + }, + }); + } + /** + * Deletes a playlist from the database + * + * @param userId The ID of the user that owns the playlist + * @param name The name of the playlist to delete + */ + public async deletePlaylist(userId: string, name: string): Promise { + await this.prisma.playlist.delete({ + where: { userId_name: { userId, name } }, + }); + } + + public async deleteSongsFromPlaylist(userId: string, playlistName: string): Promise { + // Fetch the playlist + const playlist = await this.getPlaylist(userId, playlistName); + + if (playlist) { + // Update the playlist and reset the tracks to an empty array + await this.prisma.playlist.update({ + where: { + userId_name: { + userId, + name: playlistName, + }, + }, + data: { + tracks: JSON.stringify([]), // Set tracks to an empty array + }, + }); + } + } + + public async addTracksToPlaylist(userId: string, playlistName: string, tracks: string[]) { + // Serialize the tracks array into a JSON string + const tracksJson = JSON.stringify(tracks); + + // Check if the playlist already exists for the user + const playlist = await this.prisma.playlist.findUnique({ + where: { + userId_name: { + userId, + name: playlistName, + }, + }, + }); + + if (playlist) { + // If the playlist exists, handle existing tracks + const existingTracks = playlist.tracks ? JSON.parse(playlist.tracks) : []; // Initialize as an empty array if null + + if (Array.isArray(existingTracks)) { + // Merge new and existing tracks + const updatedTracks = [...existingTracks, ...tracks]; + + // Update the playlist with the new tracks + await this.prisma.playlist.update({ + where: { + userId_name: { + userId, + name: playlistName, + }, + }, + data: { + tracks: JSON.stringify(updatedTracks), // Store the updated tracks as a serialized JSON string + }, + }); + } else { + throw new Error('Existing tracks are not in an array format.'); + } + } else { + // If no playlist exists, create a new one with the provided tracks + await this.prisma.playlist.create({ + data: { + userId, + name: playlistName, + tracks: tracksJson, // Store the serialized JSON string + }, + }); + } + } + + public async removeSong(userId: string, playlistName: string, encodedSong: string): Promise { + const playlist = await this.getPlaylist(userId, playlistName); + if (playlist) { + const tracks: string[] = JSON.parse(playlist.tracks); + + // Find the index of the song to remove + const songIndex = tracks.indexOf(encodedSong); + + if (songIndex !== -1) { + // Remove the song from the array + tracks.splice(songIndex, 1); + + // Update the playlist with the new list of tracks + await this.prisma.playlist.update({ + where: { + userId_name: { + userId, + name: playlistName, + }, + }, + data: { + tracks: JSON.stringify(tracks), // Re-serialize the updated array back to a string + }, + }); + } + } + } + + public async getTracksFromPlaylist(userId: string, playlistName: string) { + const playlist = await this.prisma.playlist.findUnique({ + where: { + userId_name: { + userId, + name: playlistName, + }, + }, + }); + + if (!playlist) { + return null; + } + + // Deserialize the tracks JSON string back into an array + const tracks = JSON.parse(playlist.tracks); + return tracks; + } } /** diff --git a/src/env.ts b/src/env.ts index bb4bade5b..38a7e076c 100644 --- a/src/env.ts +++ b/src/env.ts @@ -1,130 +1,132 @@ -import path from "node:path"; -import { config } from "dotenv"; -import { z } from "zod"; +import path from 'node:path'; +import { config } from 'dotenv'; +import { z } from 'zod'; config({ - path: path.join(__dirname, "../.env"), + path: path.join(__dirname, '../.env'), }); const LavalinkNodeSchema = z.object({ - id: z.string(), - host: z.string(), - port: z.number(), - authorization: z.string(), - secure: z.boolean().optional(), - sessionId: z.string().optional(), - regions: z.string().array().optional(), - retryAmount: z.number().optional(), - retryDelay: z.number().optional(), - requestSignalTimeoutMS: z.number().optional(), - closeOnError: z.boolean().optional(), - heartBeatInterval: z.number().optional(), - enablePingOnStatsCheck: z.boolean().optional(), + id: z.string(), + host: z.string(), + port: z.number(), + authorization: z.string(), + secure: z.boolean().optional(), + sessionId: z.string().optional(), + regions: z.string().array().optional(), + retryAmount: z.number().optional(), + retryDelay: z.number().optional(), + requestSignalTimeoutMS: z.number().optional(), + closeOnError: z.boolean().optional(), + heartBeatInterval: z.number().optional(), + enablePingOnStatsCheck: z.boolean().optional(), }); const envSchema = z.object({ - /** - * The discord app token - */ - TOKEN: z.string(), - - /** - * The client id - */ - CLIENT_ID: z.string(), - - /** - * The default language - */ - DEFAULT_LANGUAGE: z.string().default("EnglishUS"), - - /** - * The bot prefix - */ - PREFIX: z.string().default("!"), - - /** - * The owner ids - */ - OWNER_IDS: z.preprocess((val) => (typeof val === "string" ? JSON.parse(val) : val), z.string().array().optional()), - - /** - * The guild id for devlopment purposes - */ - GUILD_ID: z.string().optional(), - - /** - * The Top.gg api key - */ - TOPGG: z.string().optional(), - - /** - * The keep alive boolean - */ - KEEP_ALIVE: z.preprocess((val) => val === "true", z.boolean().default(false)), - - /** - * The log channel id - */ - LOG_CHANNEL_ID: z.string().optional(), - - /** - * The log command id - */ - LOG_COMMANDS_ID: z.string().optional(), - - /** - * The bot status online | idle | dnd | invisible - */ - BOT_STATUS: z.preprocess( - (val) => { - if (typeof val === "string") { - return val.toLowerCase(); - } - return val; - }, - z.enum(["online", "idle", "dnd", "invisible"]).default("online"), - ), - - /** - * The bot activity - */ - BOT_ACTIVITY: z.string().default("Lavamusic"), - - /** - * The bot activity type - */ - BOT_ACTIVITY_TYPE: z.preprocess((val) => { - if (typeof val === "string") { - return parseInt(val, 10); - } - return val; - }, z.number().default(0)), - /** - * The database url - */ - DATABASE_URL: z.string().optional(), - - /** - * Search engine - */ - SEARCH_ENGINE: z.preprocess( - (val) => { - if (typeof val === "string") { - return val.toLowerCase(); - } - return val; - }, - z.enum(["youtube", "youtubemusic", "soundcloud", "spotify", "apple", "deezer", "yandex", "jiosaavn"]).default("youtube"), - ), - /** - * Node in json - */ - NODES: z.preprocess((val) => (typeof val === "string" ? JSON.parse(val) : val), z.array(LavalinkNodeSchema)), - /** - * Genius api - */ - GENIUS_API: z.string().optional(), + /** + * The discord app token + */ + TOKEN: z.string(), + + /** + * The client id + */ + CLIENT_ID: z.string(), + + /** + * The default language + */ + DEFAULT_LANGUAGE: z.string().default('EnglishUS'), + + /** + * The bot prefix + */ + PREFIX: z.string().default('!'), + + /** + * The owner ids + */ + OWNER_IDS: z.preprocess(val => (typeof val === 'string' ? JSON.parse(val) : val), z.string().array().optional()), + + /** + * The guild id for devlopment purposes + */ + GUILD_ID: z.string().optional(), + + /** + * The Top.gg api key + */ + TOPGG: z.string().optional(), + + /** + * The keep alive boolean + */ + KEEP_ALIVE: z.preprocess(val => val === 'true', z.boolean().default(false)), + + /** + * The log channel id + */ + LOG_CHANNEL_ID: z.string().optional(), + + /** + * The log command id + */ + LOG_COMMANDS_ID: z.string().optional(), + + /** + * The bot status online | idle | dnd | invisible + */ + BOT_STATUS: z.preprocess( + val => { + if (typeof val === 'string') { + return val.toLowerCase(); + } + return val; + }, + z.enum(['online', 'idle', 'dnd', 'invisible']).default('online'), + ), + + /** + * The bot activity + */ + BOT_ACTIVITY: z.string().default('Lavamusic'), + + /** + * The bot activity type + */ + BOT_ACTIVITY_TYPE: z.preprocess(val => { + if (typeof val === 'string') { + return parseInt(val, 10); + } + return val; + }, z.number().default(0)), + /** + * The database url + */ + DATABASE_URL: z.string().optional(), + + /** + * Search engine + */ + SEARCH_ENGINE: z.preprocess( + val => { + if (typeof val === 'string') { + return val.toLowerCase(); + } + return val; + }, + z + .enum(['youtube', 'youtubemusic', 'soundcloud', 'spotify', 'apple', 'deezer', 'yandex', 'jiosaavn']) + .default('youtube'), + ), + /** + * Node in json + */ + NODES: z.preprocess(val => (typeof val === 'string' ? JSON.parse(val) : val), z.array(LavalinkNodeSchema)), + /** + * Genius api + */ + GENIUS_API: z.string().optional(), }); type Env = z.infer; @@ -135,9 +137,9 @@ type Env = z.infer; export const env: Env = envSchema.parse(process.env); for (const key in env) { - if (!(key in env)) { - throw new Error(`Missing env variable: ${key}`); - } + if (!(key in env)) { + throw new Error(`Missing env variable: ${key}`); + } } /** diff --git a/src/events/client/ChannelDelete.ts b/src/events/client/ChannelDelete.ts index 5b8d42645..796064225 100644 --- a/src/events/client/ChannelDelete.ts +++ b/src/events/client/ChannelDelete.ts @@ -1,39 +1,39 @@ -import { Event, type Lavamusic } from "../../structures/index"; +import { Event, type Lavamusic } from '../../structures/index'; export default class ChannelDelete extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "channelDelete", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'channelDelete', + }); + } - public async run(channel: any): Promise { - const { guild } = channel; - const setup = await this.client.db.getSetup(guild.id); - const stay = await this.client.db.get_247(guild.id); + public async run(channel: any): Promise { + const { guild } = channel; + const setup = await this.client.db.getSetup(guild.id); + const stay = await this.client.db.get_247(guild.id); - if (Array.isArray(stay)) { - for (const s of stay) { - if (channel.type === 2 && s.voiceId === channel.id) { - await this.client.db.delete_247(guild.id); - break; - } - } - } else if (stay) { - if (channel.type === 2 && stay.voiceId === channel.id) { - await this.client.db.delete_247(guild.id); - } - } + if (Array.isArray(stay)) { + for (const s of stay) { + if (channel.type === 2 && s.voiceId === channel.id) { + await this.client.db.delete_247(guild.id); + break; + } + } + } else if (stay) { + if (channel.type === 2 && stay.voiceId === channel.id) { + await this.client.db.delete_247(guild.id); + } + } - if (setup && channel.type === 0 && setup.textId === channel.id) { - await this.client.db.deleteSetup(guild.id); - } + if (setup && channel.type === 0 && setup.textId === channel.id) { + await this.client.db.deleteSetup(guild.id); + } - const player = this.client.manager.getPlayer(guild.id); - if (player && player.voiceChannelId === channel.id) { - player.destroy(); - } - } + const player = this.client.manager.getPlayer(guild.id); + if (player && player.voiceChannelId === channel.id) { + player.destroy(); + } + } } /** diff --git a/src/events/client/GuildCreate.ts b/src/events/client/GuildCreate.ts index 5fc15e198..681cf3db7 100644 --- a/src/events/client/GuildCreate.ts +++ b/src/events/client/GuildCreate.ts @@ -1,74 +1,74 @@ -import { EmbedBuilder, type Guild, type GuildMember, type TextChannel } from "discord.js"; -import { Event, type Lavamusic } from "../../structures/index"; +import { EmbedBuilder, type Guild, type GuildMember, type TextChannel } from 'discord.js'; +import { Event, type Lavamusic } from '../../structures/index'; export default class GuildCreate extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "guildCreate", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'guildCreate', + }); + } - public async run(guild: Guild): Promise { - let owner: GuildMember | undefined; - try { - owner = await guild.members.fetch(guild.ownerId); - } catch (e) { - this.client.logger.error(`Error fetching owner for guild ${guild.id}: ${e}`); - } + public async run(guild: Guild): Promise { + let owner: GuildMember | undefined; + try { + owner = await guild.members.fetch(guild.ownerId); + } catch (e) { + this.client.logger.error(`Error fetching owner for guild ${guild.id}: ${e}`); + } - const embed = new EmbedBuilder() - .setColor(this.client.config.color.green) - .setAuthor({ - name: guild.name, - iconURL: guild.iconURL({ extension: "jpeg" }), - }) - .setDescription(`**${guild.name}** has been added to my guilds!`) - .setThumbnail(guild.iconURL({ extension: "jpeg" })) - .addFields( - { - name: "Owner", - value: owner ? owner.user.tag : "Unknown#0000", - inline: true, - }, - { - name: "Members", - value: guild.memberCount.toString(), - inline: true, - }, - { - name: "Created At", - value: ``, - inline: true, - }, - { - name: "Joined At", - value: ``, - inline: true, - }, - { name: "ID", value: guild.id, inline: true }, - ) - .setTimestamp(); + const embed = new EmbedBuilder() + .setColor(this.client.config.color.green) + .setAuthor({ + name: guild.name, + iconURL: guild.iconURL({ extension: 'jpeg' })!, + }) + .setDescription(`**${guild.name}** has been added to my guilds!`) + .setThumbnail(guild.iconURL({ extension: 'jpeg' })) + .addFields( + { + name: 'Owner', + value: owner ? owner.user.tag : 'Unknown#0000', + inline: true, + }, + { + name: 'Members', + value: guild.memberCount.toString(), + inline: true, + }, + { + name: 'Created At', + value: ``, + inline: true, + }, + { + name: 'Joined At', + value: ``, + inline: true, + }, + { name: 'ID', value: guild.id, inline: true }, + ) + .setTimestamp(); - const logChannelId = this.client.env.LOG_CHANNEL_ID; - if (!logChannelId) { - this.client.logger.error("Log channel ID not found in configuration."); - return; - } + const logChannelId = this.client.env.LOG_CHANNEL_ID; + if (!logChannelId) { + this.client.logger.error('Log channel ID not found in configuration.'); + return; + } - try { - const channel = (await this.client.channels.fetch(logChannelId)) as TextChannel; - if (!channel) { - this.client.logger.error( - `Log channel not found with ID ${logChannelId}. Please change the settings in .env or, if you have a channel, invite me to that guild.`, - ); - return; - } + try { + const channel = (await this.client.channels.fetch(logChannelId)) as TextChannel; + if (!channel) { + this.client.logger.error( + `Log channel not found with ID ${logChannelId}. Please change the settings in .env or, if you have a channel, invite me to that guild.`, + ); + return; + } - await channel.send({ embeds: [embed] }); - } catch (error) { - this.client.logger.error(`Error sending message to log channel ${logChannelId}: ${error}`); - } - } + await channel.send({ embeds: [embed] }); + } catch (error) { + this.client.logger.error(`Error sending message to log channel ${logChannelId}: ${error}`); + } + } } /** diff --git a/src/events/client/GuildDelete.ts b/src/events/client/GuildDelete.ts index e0e14b426..f5bdb0ac7 100644 --- a/src/events/client/GuildDelete.ts +++ b/src/events/client/GuildDelete.ts @@ -1,73 +1,74 @@ -import { EmbedBuilder, type Guild, type GuildMember, type TextChannel } from "discord.js"; -import { Event, type Lavamusic } from "../../structures/index"; +import { EmbedBuilder, type Guild, type GuildMember, type TextChannel } from 'discord.js'; +import { Event, type Lavamusic } from '../../structures/index'; export default class GuildDelete extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "guildDelete", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'guildDelete', + }); + } - public async run(guild: Guild): Promise { - let owner: GuildMember | undefined; - try { - owner = await guild.members.fetch(guild.ownerId); - } catch (error) { - this.client.logger.error(`Error fetching owner for guild ${guild.id}: ${error}`); - } + public async run(guild: Guild): Promise { + if (!guild) return; + let owner: GuildMember | undefined; + try { + owner = await guild.members.fetch(guild.ownerId); + } catch (error) { + this.client.logger.error(`Error fetching owner for guild ${guild.id}: ${error}`); + } - const embed = new EmbedBuilder() - .setColor(this.client.config.color.red) - .setAuthor({ - name: guild.name || "Unknown Guild", - iconURL: guild.iconURL({ extension: "jpeg" }), - }) - .setDescription(`**${guild.name}** has been removed from my guilds!`) - .setThumbnail(guild.iconURL({ extension: "jpeg" })) - .addFields( - { - name: "Owner", - value: owner ? owner.user.tag : "Unknown#0000", - inline: true, - }, - { - name: "Members", - value: guild.memberCount?.toString() || "Unknown", - inline: true, - }, - { - name: "Created At", - value: ``, - inline: true, - }, - { - name: "Removed At", - value: ``, - inline: true, - }, - { name: "ID", value: guild.id, inline: true }, - ) - .setTimestamp(); + const embed = new EmbedBuilder() + .setColor(this.client.config.color.red) + .setAuthor({ + name: guild.name || 'Unknown Guild', + iconURL: guild.iconURL({ extension: 'jpeg' })!, + }) + .setDescription(`**${guild.name}** has been removed from my guilds!`) + .setThumbnail(guild.iconURL({ extension: 'jpeg' })) + .addFields( + { + name: 'Owner', + value: owner ? owner.user.tag : 'Unknown#0000', + inline: true, + }, + { + name: 'Members', + value: guild.memberCount?.toString() || 'Unknown', + inline: true, + }, + { + name: 'Created At', + value: ``, + inline: true, + }, + { + name: 'Removed At', + value: ``, + inline: true, + }, + { name: 'ID', value: guild.id, inline: true }, + ) + .setTimestamp(); - const logChannelId = this.client.env.LOG_CHANNEL_ID; - if (!logChannelId) { - this.client.logger.error("Log channel ID not found in configuration."); - return; - } + const logChannelId = this.client.env.LOG_CHANNEL_ID; + if (!logChannelId) { + this.client.logger.error('Log channel ID not found in configuration.'); + return; + } - try { - const channel = (await this.client.channels.fetch(logChannelId)) as TextChannel; - if (!channel) { - this.client.logger.error( - `Log channel not found with ID ${logChannelId}. Please change the settings in .env or, if you have a channel, invite me to that guild.`, - ); - return; - } - await channel.send({ embeds: [embed] }); - } catch (error) { - this.client.logger.error(`Error sending message to log channel ${logChannelId}: ${error}`); - } - } + try { + const channel = (await this.client.channels.fetch(logChannelId)) as TextChannel; + if (!channel) { + this.client.logger.error( + `Log channel not found with ID ${logChannelId}. Please change the settings in .env or, if you have a channel, invite me to that guild.`, + ); + return; + } + await channel.send({ embeds: [embed] }); + } catch (error) { + this.client.logger.error(`Error sending message to log channel ${logChannelId}: ${error}`); + } + } } /** diff --git a/src/events/client/InteractionCreate.ts b/src/events/client/InteractionCreate.ts index 4e64c68c2..893d8f6ca 100644 --- a/src/events/client/InteractionCreate.ts +++ b/src/events/client/InteractionCreate.ts @@ -1,258 +1,274 @@ import { - ActionRowBuilder, - type AutocompleteInteraction, - ButtonBuilder, - ButtonStyle, - ChannelType, - Collection, - CommandInteraction, - EmbedBuilder, - type GuildMember, - InteractionType, - PermissionFlagsBits, - type TextChannel, -} from "discord.js"; -import { T } from "../../structures/I18n"; -import { Context, Event, type Lavamusic } from "../../structures/index"; + ActionRowBuilder, + type AutocompleteInteraction, + ButtonBuilder, + ButtonStyle, + ChannelType, + Collection, + CommandInteraction, + EmbedBuilder, + type GuildMember, + InteractionType, + PermissionFlagsBits, + type TextChannel, +} from 'discord.js'; +import { T } from '../../structures/I18n'; +import { Context, Event, type Lavamusic } from '../../structures/index'; export default class InteractionCreate extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "interactionCreate", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'interactionCreate', + }); + } - public async run(interaction: CommandInteraction | AutocompleteInteraction): Promise { - if (interaction instanceof CommandInteraction && interaction.isCommand()) { - const setup = await this.client.db.getSetup(interaction.guildId); - const allowedCategories = ["filters", "music", "playlist"]; - const commandInSetup = this.client.commands.get(interaction.commandName); - const locale = await this.client.db.getLanguage(interaction.guildId); + public async run(interaction: CommandInteraction | AutocompleteInteraction): Promise { + if (!(interaction.guild && interaction.guildId)) return; + if (interaction instanceof CommandInteraction && interaction.isCommand()) { + const setup = await this.client.db.getSetup(interaction.guildId); + const allowedCategories = ['filters', 'music', 'playlist']; + const commandInSetup = this.client.commands.get(interaction.commandName); + const locale = await this.client.db.getLanguage(interaction.guildId); - if ( - setup && - interaction.channelId === setup.textId && - !(commandInSetup && allowedCategories.includes(commandInSetup.category)) - ) { - return await interaction.reply({ - content: T(locale, "event.interaction.setup_channel"), - ephemeral: true, - }); - } + if ( + setup && + interaction.channelId === setup.textId && + !(commandInSetup && allowedCategories.includes(commandInSetup.category)) + ) { + return await interaction.reply({ + content: T(locale, 'event.interaction.setup_channel'), + ephemeral: true, + }); + } - const { commandName } = interaction; - await this.client.db.get(interaction.guildId); + const { commandName } = interaction; + await this.client.db.get(interaction.guildId); - const command = this.client.commands.get(commandName); - if (!command) return; + const command = this.client.commands.get(commandName); + if (!command) return; - const ctx = new Context(interaction as any, interaction.options.data as any); - ctx.setArgs(interaction.options.data as any); - ctx.guildLocale = locale; - const clientMember = interaction.guild.members.resolve(this.client.user); - if (!(interaction.inGuild() && interaction.channel.permissionsFor(clientMember)?.has(PermissionFlagsBits.ViewChannel))) return; + const ctx = new Context(interaction as any, interaction.options.data as any); + ctx.setArgs(interaction.options.data as any); + ctx.guildLocale = locale; + const clientMember = interaction.guild.members.resolve(this.client.user!)!; + if ( + !( + interaction.inGuild() && + interaction.channel?.permissionsFor(clientMember)?.has(PermissionFlagsBits.ViewChannel) + ) + ) + return; - if ( - !( - clientMember.permissions.has(PermissionFlagsBits.ViewChannel) && - clientMember.permissions.has(PermissionFlagsBits.SendMessages) && - clientMember.permissions.has(PermissionFlagsBits.EmbedLinks) && - clientMember.permissions.has(PermissionFlagsBits.ReadMessageHistory) - ) - ) { - return await (interaction.member as GuildMember) - .send({ - content: T(locale, "event.interaction.no_send_message"), - }) - .catch(() => {}); - } + if ( + !( + clientMember.permissions.has(PermissionFlagsBits.ViewChannel) && + clientMember.permissions.has(PermissionFlagsBits.SendMessages) && + clientMember.permissions.has(PermissionFlagsBits.EmbedLinks) && + clientMember.permissions.has(PermissionFlagsBits.ReadMessageHistory) + ) + ) { + return await (interaction.member as GuildMember) + .send({ + content: T(locale, 'event.interaction.no_send_message'), + }) + .catch(() => { + null; + }); + } - const logs = this.client.channels.cache.get(this.client.env.LOG_COMMANDS_ID); + const logs = this.client.channels.cache.get(this.client.env.LOG_COMMANDS_ID!); - if (command.permissions) { - if (command.permissions?.client) { - const missingClientPermissions = command.permissions.client.filter((perm) => !clientMember.permissions.has(perm)); + if (command.permissions) { + if (command.permissions?.client) { + const missingClientPermissions = command.permissions.client.filter( + (perm: any) => !clientMember.permissions.has(perm), + ); - if (missingClientPermissions.length > 0) { - return await interaction.reply({ - content: T(locale, "event.interaction.no_permission", { - permissions: missingClientPermissions.map((perm) => `\`${perm}\``).join(", "), - }), - ephemeral: true, - }); - } - } + if (missingClientPermissions.length > 0) { + return await interaction.reply({ + content: T(locale, 'event.interaction.no_permission', { + permissions: missingClientPermissions.map((perm: string) => `\`${perm}\``).join(', '), + }), + ephemeral: true, + }); + } + } - if (command.permissions?.user && !(interaction.member as GuildMember).permissions.has(command.permissions.user)) { - await interaction.reply({ - content: T(locale, "event.interaction.no_user_permission"), - ephemeral: true, - }); - return; - } + if ( + command.permissions?.user && + !(interaction.member as GuildMember).permissions.has(command.permissions.user) + ) { + await interaction.reply({ + content: T(locale, 'event.interaction.no_user_permission'), + ephemeral: true, + }); + return; + } - if (command.permissions?.dev && this.client.env.OWNER_IDS) { - const isDev = this.client.env.OWNER_IDS.includes(interaction.user.id); - if (!isDev) return; - } - } - if (command.vote && this.client.env.TOPGG) { - const voted = await this.client.topGG.hasVoted(interaction.user.id); - if (!voted) { - const voteBtn = new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setLabel(T(locale, "event.interaction.vote_button")) - .setURL(`https://top.gg/bot/${this.client.user.id}/vote`) - .setStyle(ButtonStyle.Link), - ); + if (command.permissions?.dev && this.client.env.OWNER_IDS) { + const isDev = this.client.env.OWNER_IDS.includes(interaction.user.id); + if (!isDev) return; + } + } + if (command.vote && this.client.env.TOPGG) { + const voted = await this.client.topGG.hasVoted(interaction.user.id); + if (!voted) { + const voteBtn = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setLabel(T(locale, 'event.interaction.vote_button')) + .setURL(`https://top.gg/bot/${this.client.user?.id}/vote`) + .setStyle(ButtonStyle.Link), + ); - return await interaction.reply({ - content: T(locale, "event.interaction.vote_message"), - components: [voteBtn], - ephemeral: true, - }); - } - } - if (command.player) { - if (command.player.voice) { - if (!(interaction.member as GuildMember).voice.channel) { - return await interaction.reply({ - content: T(locale, "event.interaction.no_voice_channel", { command: command.name }), - }); - } + return await interaction.reply({ + content: T(locale, 'event.interaction.vote_message'), + components: [voteBtn], + ephemeral: true, + }); + } + } + if (command.player) { + if (command.player.voice) { + if (!(interaction.member as GuildMember).voice.channel) { + return await interaction.reply({ + content: T(locale, 'event.interaction.no_voice_channel', { command: command.name }), + }); + } - if (!clientMember.permissions.has(PermissionFlagsBits.Connect)) { - return await interaction.reply({ - content: T(locale, "event.interaction.no_connect_permission", { command: command.name }), - }); - } + if (!clientMember.permissions.has(PermissionFlagsBits.Connect)) { + return await interaction.reply({ + content: T(locale, 'event.interaction.no_connect_permission', { command: command.name }), + }); + } - if (!clientMember.permissions.has(PermissionFlagsBits.Speak)) { - return await interaction.reply({ - content: T(locale, "event.interaction.no_speak_permission", { command: command.name }), - }); - } + if (!clientMember.permissions.has(PermissionFlagsBits.Speak)) { + return await interaction.reply({ + content: T(locale, 'event.interaction.no_speak_permission', { command: command.name }), + }); + } - if ( - (interaction.member as GuildMember).voice.channel.type === ChannelType.GuildStageVoice && - !clientMember.permissions.has(PermissionFlagsBits.RequestToSpeak) - ) { - return await interaction.reply({ - content: T(locale, "event.interaction.no_request_to_speak", { command: command.name }), - }); - } + if ( + (interaction.member as GuildMember).voice.channel?.type === ChannelType.GuildStageVoice && + !clientMember.permissions.has(PermissionFlagsBits.RequestToSpeak) + ) { + return await interaction.reply({ + content: T(locale, 'event.interaction.no_request_to_speak', { command: command.name }), + }); + } - if ( - clientMember.voice.channel && - clientMember.voice.channelId !== (interaction.member as GuildMember).voice.channelId - ) { - return await interaction.reply({ - content: T(locale, "event.interaction.different_voice_channel", { - channel: `<#${clientMember.voice.channelId}>`, - command: command.name, - }), - }); - } - } + if ( + clientMember.voice.channel && + clientMember.voice.channelId !== (interaction.member as GuildMember).voice.channelId + ) { + return await interaction.reply({ + content: T(locale, 'event.interaction.different_voice_channel', { + channel: `<#${clientMember.voice.channelId}>`, + command: command.name, + }), + }); + } + } - if (command.player.active) { - const queue = this.client.manager.getPlayer(interaction.guildId); - if (!queue.queue.current) { - return await interaction.reply({ - content: T(locale, "event.interaction.no_music_playing"), - }); - } - } + if (command.player.active) { + const queue = this.client.manager.getPlayer(interaction.guildId); + if (!queue.queue.current) { + return await interaction.reply({ + content: T(locale, 'event.interaction.no_music_playing'), + }); + } + } - if (command.player.dj) { - const dj = await this.client.db.getDj(interaction.guildId); - if (dj?.mode) { - const djRole = await this.client.db.getRoles(interaction.guildId); - if (!djRole) { - return await interaction.reply({ - content: T(locale, "event.interaction.no_dj_role"), - }); - } + if (command.player.dj) { + const dj = await this.client.db.getDj(interaction.guildId); + if (dj?.mode) { + const djRole = await this.client.db.getRoles(interaction.guildId); + if (!djRole) { + return await interaction.reply({ + content: T(locale, 'event.interaction.no_dj_role'), + }); + } - const hasDJRole = (interaction.member as GuildMember).roles.cache.some((role) => - djRole.map((r) => r.roleId).includes(role.id), - ); - if (!(hasDJRole && !(interaction.member as GuildMember).permissions.has(PermissionFlagsBits.ManageGuild))) { - return await interaction.reply({ - content: T(locale, "event.interaction.no_dj_permission"), - ephemeral: true, - }); - } - } - } - } + const hasDJRole = (interaction.member as GuildMember).roles.cache.some(role => + djRole.map(r => r.roleId).includes(role.id), + ); + if (!(hasDJRole && !(interaction.member as GuildMember).permissions.has(PermissionFlagsBits.ManageGuild))) { + return await interaction.reply({ + content: T(locale, 'event.interaction.no_dj_permission'), + ephemeral: true, + }); + } + } + } + } - if (!this.client.cooldown.has(commandName)) { - this.client.cooldown.set(commandName, new Collection()); - } + if (!this.client.cooldown.has(commandName)) { + this.client.cooldown.set(commandName, new Collection()); + } - const now = Date.now(); - const timestamps = this.client.cooldown.get(commandName)!; - const cooldownAmount = (command.cooldown || 5) * 1000; + const now = Date.now(); + const timestamps = this.client.cooldown.get(commandName)!; + const cooldownAmount = (command.cooldown || 5) * 1000; - if (timestamps.has(interaction.user.id)) { - const expirationTime = timestamps.get(interaction.user.id)! + cooldownAmount; - const timeLeft = (expirationTime - now) / 1000; - if (now < expirationTime && timeLeft > 0.9) { - return await interaction.reply({ - content: T(locale, "event.interaction.cooldown", { - time: timeLeft.toFixed(1), - command: commandName, - }), - }); - } - timestamps.set(interaction.user.id, now); - setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount); - } else { - timestamps.set(interaction.user.id, now); - setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount); - } + if (timestamps.has(interaction.user.id)) { + const expirationTime = timestamps.get(interaction.user.id)! + cooldownAmount; + const timeLeft = (expirationTime - now) / 1000; + if (now < expirationTime && timeLeft > 0.9) { + return await interaction.reply({ + content: T(locale, 'event.interaction.cooldown', { + time: timeLeft.toFixed(1), + command: commandName, + }), + }); + } + timestamps.set(interaction.user.id, now); + setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount); + } else { + timestamps.set(interaction.user.id, now); + setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount); + } - try { - await command.run(this.client, ctx, ctx.args); - if (setup && interaction.channelId === setup.textId && allowedCategories.includes(command.category)) { - setTimeout(() => { - interaction.deleteReply().catch(() => {}); - }, 5000); - } - if (logs) { - const embed = new EmbedBuilder() - .setAuthor({ - name: "Slash - Command Logs", - iconURL: this.client.user?.avatarURL({ - size: 2048, - }), - }) - .setColor(this.client.config.color.blue) - .setDescription( - `**\`${command.name}\`** | Used By **${interaction.user.tag} \`${interaction.user.id}\`** From **${interaction.guild.name} \`${interaction.guild.id}\`**`, - ) - .setTimestamp(); + try { + await command.run(this.client, ctx, ctx.args); + if (setup && interaction.channelId === setup.textId && allowedCategories.includes(command.category)) { + setTimeout(() => { + interaction.deleteReply().catch(() => { + null; + }); + }, 5000); + } + if (logs) { + const embed = new EmbedBuilder() + .setAuthor({ + name: 'Slash - Command Logs', + iconURL: this.client.user?.avatarURL({ + size: 2048, + })!, + }) + .setColor(this.client.config.color.blue) + .setDescription( + `**\`${command.name}\`** | Used By **${interaction.user.tag} \`${interaction.user.id}\`** From **${interaction.guild.name} \`${interaction.guild.id}\`**`, + ) + .setTimestamp(); - await (logs as TextChannel).send({ embeds: [embed] }); - } - } catch (error) { - this.client.logger.error(error); - await interaction.reply({ - content: T(locale, "event.interaction.error", { error }), - }); - } - } else if (interaction.type === InteractionType.ApplicationCommandAutocomplete) { - const command = this.client.commands.get(interaction.commandName); - if (!command) return; + await (logs as TextChannel).send({ embeds: [embed] }); + } + } catch (error) { + this.client.logger.error(error); + await interaction.reply({ + content: T(locale, 'event.interaction.error', { error }), + }); + } + } else if (interaction.type === InteractionType.ApplicationCommandAutocomplete) { + const command = this.client.commands.get(interaction.commandName); + if (!command) return; - try { - await command.autocomplete(interaction); - } catch (error) { - console.error(error); - } - } - } + try { + await command.autocomplete(interaction); + } catch (error) { + console.error(error); + } + } + } } /** diff --git a/src/events/client/MessageCreate.ts b/src/events/client/MessageCreate.ts index 7e28fc4b1..94ea9db45 100644 --- a/src/events/client/MessageCreate.ts +++ b/src/events/client/MessageCreate.ts @@ -1,261 +1,273 @@ import { - ActionRowBuilder, - ButtonBuilder, - ButtonStyle, - ChannelType, - Collection, - EmbedBuilder, - type GuildMember, - type Message, - PermissionFlagsBits, - type TextChannel, -} from "discord.js"; -import { T } from "../../structures/I18n"; -import { Context, Event, type Lavamusic } from "../../structures/index"; + ActionRowBuilder, + ButtonBuilder, + ButtonStyle, + ChannelType, + Collection, + EmbedBuilder, + type GuildMember, + type Message, + PermissionFlagsBits, + type TextChannel, +} from 'discord.js'; +import { T } from '../../structures/I18n'; +import { Context, Event, type Lavamusic } from '../../structures/index'; export default class MessageCreate extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "messageCreate", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'messageCreate', + }); + } - public async run(message: Message): Promise { - if (message.author.bot) return; + public async run(message: Message): Promise { + if (message.author.bot) return; + if (!(message.guild && message.guildId)) return; + const setup = await this.client.db.getSetup(message.guildId); + if (setup && setup.textId === message.channelId) { + return this.client.emit('setupSystem', message); + } + const locale = await this.client.db.getLanguage(message.guildId); - const setup = await this.client.db.getSetup(message.guildId); - if (setup && setup.textId === message.channelId) { - return this.client.emit("setupSystem", message); - } - const locale = await this.client.db.getLanguage(message.guildId); + const guild = await this.client.db.get(message.guildId); + const mention = new RegExp(`^<@!?${this.client.user?.id}>( |)$`); + if (mention.test(message.content)) { + await message.reply({ + content: T(locale, 'event.message.prefix_mention', { + prefix: guild.prefix, + }), + }); + return; + } - const guild = await this.client.db.get(message.guildId); - const mention = new RegExp(`^<@!?${this.client.user.id}>( |)$`); - if (mention.test(message.content)) { - await message.reply({ - content: T(locale, "event.message.prefix_mention", { - prefix: guild.prefix, - }), - }); - return; - } + const escapeRegex = (str: string): string => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const prefixRegex = new RegExp(`^(<@!?${this.client.user?.id}>|${escapeRegex(guild.prefix)})\\s*`); + if (!prefixRegex.test(message.content)) return; + const match = message.content.match(prefixRegex); + if (!match) return; + const [matchedPrefix] = match; + const args = message.content.slice(matchedPrefix.length).trim().split(/ +/g); + const cmd = args.shift()?.toLowerCase(); + if (!cmd) return; + const command = this.client.commands.get(cmd) || this.client.commands.get(this.client.aliases.get(cmd) as string); + if (!command) return; - const escapeRegex = (str: string): string => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - const prefixRegex = new RegExp(`^(<@!?${this.client.user.id}>|${escapeRegex(guild.prefix)})\\s*`); - if (!prefixRegex.test(message.content)) return; + const ctx = new Context(message, args); + ctx.setArgs(args); + ctx.guildLocale = locale; - const [matchedPrefix] = message.content.match(prefixRegex); - const args = message.content.slice(matchedPrefix.length).trim().split(/ +/g); - const cmd = args.shift()?.toLowerCase(); - const command = this.client.commands.get(cmd) || this.client.commands.get(this.client.aliases.get(cmd) as string); - if (!command) return; + const clientMember = message.guild.members.resolve(this.client.user!)!; + const isDev = this.client.env.OWNER_IDS?.includes(message.author.id); - const ctx = new Context(message, args); - ctx.setArgs(args); - ctx.guildLocale = locale; + if (!(message.inGuild() && message.channel.permissionsFor(clientMember)?.has(PermissionFlagsBits.ViewChannel))) + return; - const clientMember = message.guild.members.resolve(this.client.user); - const isDev = this.client.env.OWNER_IDS?.includes(message.author.id); + if ( + !( + clientMember.permissions.has(PermissionFlagsBits.ViewChannel) && + clientMember.permissions.has(PermissionFlagsBits.SendMessages) && + clientMember.permissions.has(PermissionFlagsBits.EmbedLinks) && + clientMember.permissions.has(PermissionFlagsBits.ReadMessageHistory) + ) + ) { + return await message.author + .send({ + content: T(locale, 'event.message.no_send_message'), + }) + .catch(() => { + null; + }); + } - if (!(message.inGuild() && message.channel.permissionsFor(clientMember)?.has(PermissionFlagsBits.ViewChannel))) return; + if (command.permissions) { + if (command.permissions?.client) { + const missingClientPermissions = command.permissions.client.filter( + (perm: any) => !clientMember.permissions.has(perm), + ); - if ( - !( - clientMember.permissions.has(PermissionFlagsBits.ViewChannel) && - clientMember.permissions.has(PermissionFlagsBits.SendMessages) && - clientMember.permissions.has(PermissionFlagsBits.EmbedLinks) && - clientMember.permissions.has(PermissionFlagsBits.ReadMessageHistory) - ) - ) { - return await message.author - .send({ - content: T(locale, "event.message.no_send_message"), - }) - .catch(() => {}); - } + if (missingClientPermissions.length > 0) { + return await message.reply({ + content: T(locale, 'event.message.no_permission', { + permissions: missingClientPermissions.map((perm: string) => `\`${perm}\``).join(', '), + }), + }); + } + } - if (command.permissions) { - if (command.permissions?.client) { - const missingClientPermissions = command.permissions.client.filter((perm) => !clientMember.permissions.has(perm)); + if (command.permissions?.user) { + if (!(isDev || (message.member as GuildMember).permissions.has(command.permissions.user))) { + return await message.reply({ + content: T(locale, 'event.message.no_user_permission'), + }); + } + } - if (missingClientPermissions.length > 0) { - return await message.reply({ - content: T(locale, "event.message.no_permission", { - permissions: missingClientPermissions.map((perm) => `\`${perm}\``).join(", "), - }), - }); - } - } + if (command.permissions?.dev && this.client.env.OWNER_IDS) { + if (!isDev) return; + } + } - if (command.permissions?.user) { - if (!(isDev || (message.member as GuildMember).permissions.has(command.permissions.user))) { - return await message.reply({ - content: T(locale, "event.message.no_user_permission"), - }); - } - } + if (command.vote && this.client.env.TOPGG) { + const voted = await this.client.topGG.hasVoted(message.author.id); + if (!(isDev || voted)) { + const voteBtn = new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setLabel(T(locale, 'event.message.vote_button')) + .setURL(`https://top.gg/bot/${this.client.user?.id}/vote`) + .setStyle(ButtonStyle.Link), + ); - if (command.permissions?.dev && this.client.env.OWNER_IDS) { - if (!isDev) return; - } - } + return await message.reply({ + content: T(locale, 'event.message.vote_message'), + components: [voteBtn], + }); + } + } - if (command.vote && this.client.env.TOPGG) { - const voted = await this.client.topGG.hasVoted(message.author.id); - if (!(isDev || voted)) { - const voteBtn = new ActionRowBuilder().addComponents( - new ButtonBuilder() - .setLabel(T(locale, "event.message.vote_button")) - .setURL(`https://top.gg/bot/${this.client.user.id}/vote`) - .setStyle(ButtonStyle.Link), - ); + if (command.player) { + if (command.player.voice) { + if (!(message.member as GuildMember).voice.channel) { + return await message.reply({ + content: T(locale, 'event.message.no_voice_channel', { command: command.name }), + }); + } - return await message.reply({ - content: T(locale, "event.message.vote_message"), - components: [voteBtn], - }); - } - } + if (!clientMember.permissions.has(PermissionFlagsBits.Connect)) { + return await message.reply({ + content: T(locale, 'event.message.no_connect_permission', { command: command.name }), + }); + } - if (command.player) { - if (command.player.voice) { - if (!(message.member as GuildMember).voice.channel) { - return await message.reply({ - content: T(locale, "event.message.no_voice_channel", { command: command.name }), - }); - } + if (!clientMember.permissions.has(PermissionFlagsBits.Speak)) { + return await message.reply({ + content: T(locale, 'event.message.no_speak_permission', { command: command.name }), + }); + } - if (!clientMember.permissions.has(PermissionFlagsBits.Connect)) { - return await message.reply({ - content: T(locale, "event.message.no_connect_permission", { command: command.name }), - }); - } + if ( + (message.member as GuildMember).voice.channel?.type === ChannelType.GuildStageVoice && + !clientMember.permissions.has(PermissionFlagsBits.RequestToSpeak) + ) { + return await message.reply({ + content: T(locale, 'event.message.no_request_to_speak', { command: command.name }), + }); + } - if (!clientMember.permissions.has(PermissionFlagsBits.Speak)) { - return await message.reply({ - content: T(locale, "event.message.no_speak_permission", { command: command.name }), - }); - } + if ( + clientMember.voice.channel && + clientMember.voice.channelId !== (message.member as GuildMember).voice.channelId + ) { + return await message.reply({ + content: T(locale, 'event.message.different_voice_channel', { + channel: `<#${clientMember.voice.channelId}>`, + command: command.name, + }), + }); + } + } - if ( - (message.member as GuildMember).voice.channel.type === ChannelType.GuildStageVoice && - !clientMember.permissions.has(PermissionFlagsBits.RequestToSpeak) - ) { - return await message.reply({ - content: T(locale, "event.message.no_request_to_speak", { command: command.name }), - }); - } + if (command.player.active) { + const queue = this.client.manager.getPlayer(message.guildId); + if (!queue?.queue.current) { + return await message.reply({ + content: T(locale, 'event.message.no_music_playing'), + }); + } + } - if (clientMember.voice.channel && clientMember.voice.channelId !== (message.member as GuildMember).voice.channelId) { - return await message.reply({ - content: T(locale, "event.message.different_voice_channel", { - channel: `<#${clientMember.voice.channelId}>`, - command: command.name, - }), - }); - } - } + if (command.player.dj) { + const dj = await this.client.db.getDj(message.guildId); + if (dj?.mode) { + const djRole = await this.client.db.getRoles(message.guildId); + if (!djRole) { + return await message.reply({ + content: T(locale, 'event.message.no_dj_role'), + }); + } - if (command.player.active) { - const queue = this.client.manager.getPlayer(message.guildId); - if (!queue?.queue.current) { - return await message.reply({ - content: T(locale, "event.message.no_music_playing"), - }); - } - } + const hasDJRole = (message.member as GuildMember).roles.cache.some(role => + djRole.map(r => r.roleId).includes(role.id), + ); + if ( + !(isDev || (hasDJRole && !(message.member as GuildMember).permissions.has(PermissionFlagsBits.ManageGuild))) + ) { + return await message.reply({ + content: T(locale, 'event.message.no_dj_permission'), + }); + } + } + } + } - if (command.player.dj) { - const dj = await this.client.db.getDj(message.guildId); - if (dj?.mode) { - const djRole = await this.client.db.getRoles(message.guildId); - if (!djRole) { - return await message.reply({ - content: T(locale, "event.message.no_dj_role"), - }); - } + if (command.args && args.length === 0) { + const embed = this.client + .embed() + .setColor(this.client.color.red) + .setTitle(T(locale, 'event.message.missing_arguments')) + .setDescription( + T(locale, 'event.message.missing_arguments_description', { + command: command.name, + examples: command.description.examples ? command.description.examples.join('\n') : 'None', + }), + ) + .setFooter({ text: T(locale, 'event.message.syntax_footer') }); + await message.reply({ embeds: [embed] }); + return; + } - const hasDJRole = (message.member as GuildMember).roles.cache.some((role) => - djRole.map((r) => r.roleId).includes(role.id), - ); - if (!(isDev || (hasDJRole && !(message.member as GuildMember).permissions.has(PermissionFlagsBits.ManageGuild)))) { - return await message.reply({ - content: T(locale, "event.message.no_dj_permission"), - }); - } - } - } - } + if (!this.client.cooldown.has(cmd)) { + this.client.cooldown.set(cmd, new Collection()); + } + const now = Date.now(); + const timestamps = this.client.cooldown.get(cmd)!; + const cooldownAmount = (command.cooldown || 5) * 1000; - if (command.args && args.length === 0) { - const embed = this.client - .embed() - .setColor(this.client.color.red) - .setTitle(T(locale, "event.message.missing_arguments")) - .setDescription( - T(locale, "event.message.missing_arguments_description", { - command: command.name, - examples: command.description.examples ? command.description.examples.join("\n") : "None", - }), - ) - .setFooter({ text: T(locale, "event.message.syntax_footer") }); - await message.reply({ embeds: [embed] }); - return; - } + if (timestamps.has(message.author.id)) { + const expirationTime = timestamps.get(message.author.id)! + cooldownAmount; + const timeLeft = (expirationTime - now) / 1000; + if (now < expirationTime && timeLeft > 0.9) { + return await message.reply({ + content: T(locale, 'event.message.cooldown', { time: timeLeft.toFixed(1), command: cmd }), + }); + } + timestamps.set(message.author.id, now); + setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); + } else { + timestamps.set(message.author.id, now); + setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); + } - if (!this.client.cooldown.has(cmd)) { - this.client.cooldown.set(cmd, new Collection()); - } - const now = Date.now(); - const timestamps = this.client.cooldown.get(cmd)!; - const cooldownAmount = (command.cooldown || 5) * 1000; + if (args.includes('@everyone') || args.includes('@here')) { + return await message.reply({ + content: T(locale, 'event.message.no_mention_everyone'), + }); + } - if (timestamps.has(message.author.id)) { - const expirationTime = timestamps.get(message.author.id)! + cooldownAmount; - const timeLeft = (expirationTime - now) / 1000; - if (now < expirationTime && timeLeft > 0.9) { - return await message.reply({ - content: T(locale, "event.message.cooldown", { time: timeLeft.toFixed(1), command: cmd }), - }); - } - timestamps.set(message.author.id, now); - setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); - } else { - timestamps.set(message.author.id, now); - setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); - } + try { + return command.run(this.client, ctx, ctx.args); + } catch (error: any) { + this.client.logger.error(error); + await message.reply({ + content: T(locale, 'event.message.error', { error: error.message || 'Unknown error' }), + }); + } finally { + const logs = this.client.channels.cache.get(this.client.env.LOG_COMMANDS_ID!); + if (logs) { + const embed = new EmbedBuilder() + .setAuthor({ + name: 'Prefix - Command Logs', + iconURL: this.client.user?.avatarURL({ size: 2048 })!, + }) + .setColor(this.client.config.color.green) + .setDescription( + `**\`${command.name}\`** | Used By **${message.author.tag} \`${message.author.id}\`** From **${message.guild.name} \`${message.guild.id}\`**`, + ) + .setTimestamp(); - if (args.includes("@everyone") || args.includes("@here")) { - return await message.reply({ - content: T(locale, "event.message.no_mention_everyone"), - }); - } - - try { - return command.run(this.client, ctx, ctx.args); - } catch (error) { - this.client.logger.error(error); - await message.reply({ - content: T(locale, "event.message.error", { error: error.message || "Unknown error" }), - }); - } finally { - const logs = this.client.channels.cache.get(this.client.env.LOG_COMMANDS_ID); - if (logs) { - const embed = new EmbedBuilder() - .setAuthor({ - name: "Prefix - Command Logs", - iconURL: this.client.user?.avatarURL({ size: 2048 }), - }) - .setColor(this.client.config.color.green) - .setDescription( - `**\`${command.name}\`** | Used By **${message.author.tag} \`${message.author.id}\`** From **${message.guild.name} \`${message.guild.id}\`**`, - ) - .setTimestamp(); - - await (logs as TextChannel).send({ embeds: [embed] }); - } - } - } + await (logs as TextChannel).send({ embeds: [embed] }); + } + } + } } /** diff --git a/src/events/client/Raw.ts b/src/events/client/Raw.ts index 41794bf43..3123c99be 100644 --- a/src/events/client/Raw.ts +++ b/src/events/client/Raw.ts @@ -1,15 +1,15 @@ -import { Event, type Lavamusic } from "../../structures/index.js"; +import { Event, type Lavamusic } from '../../structures/index.js'; export default class Raw extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "raw", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'raw', + }); + } - public async run(d): Promise { - this.client.manager.sendRawData(d); - } + public async run(d: any): Promise { + this.client.manager.sendRawData(d); + } } /** diff --git a/src/events/client/Ready.ts b/src/events/client/Ready.ts index 178e2f510..7bd1ef8f7 100644 --- a/src/events/client/Ready.ts +++ b/src/events/client/Ready.ts @@ -1,37 +1,39 @@ -import { AutoPoster } from "topgg-autoposter"; -import { env } from "../../env.js"; -import { Event, type Lavamusic } from "../../structures/index.js"; +import { AutoPoster } from 'topgg-autoposter'; +import { env } from '../../env.js'; +import { Event, type Lavamusic } from '../../structures/index.js'; export default class Ready extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "ready", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'ready', + }); + } - public async run(): Promise { - this.client.logger.success(`${this.client.user?.tag} is ready!`); + public async run(): Promise { + this.client.logger.success(`${this.client.user?.tag} is ready!`); - this.client.user?.setPresence({ - activities: [ - { - name: env.BOT_ACTIVITY, - type: env.BOT_ACTIVITY_TYPE, - }, - ], - status: env.BOT_STATUS as any, - }); + this.client.user?.setPresence({ + activities: [ + { + name: env.BOT_ACTIVITY, + type: env.BOT_ACTIVITY_TYPE, + }, + ], + status: env.BOT_STATUS as any, + }); - if (env.TOPGG) { - const autoPoster = AutoPoster(env.TOPGG, this.client); - setInterval(() => { - autoPoster.on("posted", (_stats) => {}); - }, 86400000); // 24 hours in milliseconds - } else { - this.client.logger.warn("Top.gg token not found. Skipping auto poster."); - } - await this.client.manager.init({ ...this.client.user!, shards: "auto" }); - } + if (env.TOPGG) { + const autoPoster = AutoPoster(env.TOPGG, this.client); + setInterval(() => { + autoPoster.on('posted', _stats => { + null; + }); + }, 86400000); // 24 hours in milliseconds + } else { + this.client.logger.warn('Top.gg token not found. Skipping auto poster.'); + } + await this.client.manager.init({ ...this.client.user!, shards: 'auto' }); + } } /** diff --git a/src/events/client/SetupButtons.ts b/src/events/client/SetupButtons.ts index f1766d280..7d16a541f 100644 --- a/src/events/client/SetupButtons.ts +++ b/src/events/client/SetupButtons.ts @@ -1,241 +1,264 @@ -import type { Message } from "discord.js"; -import { T } from "../../structures/I18n"; -import { Event, type Lavamusic } from "../../structures/index"; -import type { Requester } from "../../types"; -import { getButtons } from "../../utils/Buttons"; -import { buttonReply } from "../../utils/SetupSystem"; -import { checkDj } from "../player/TrackStart"; +import type { Message } from 'discord.js'; +import { T } from '../../structures/I18n'; +import { Event, type Lavamusic } from '../../structures/index'; +import type { Requester } from '../../types'; +import { getButtons } from '../../utils/Buttons'; +import { buttonReply } from '../../utils/SetupSystem'; +import { checkDj } from '../player/TrackStart'; export default class SetupButtons extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "setupButtons", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'setupButtons', + }); + } - public async run(interaction: any): Promise { - const locale = await this.client.db.getLanguage(interaction.guildId); + public async run(interaction: any): Promise { + const locale = await this.client.db.getLanguage(interaction.guildId); - if (!interaction.replied) await interaction.deferReply().catch(() => {}); - if (!interaction.member.voice.channel) { - return await buttonReply(interaction, T(locale, "event.setupButton.no_voice_channel_button"), this.client.color.red); - } - const clientMember = interaction.guild.members.cache.get(this.client.user.id); - if (clientMember.voice.channel && clientMember.voice.channelId !== interaction.member.voice.channelId) { - return await buttonReply( - interaction, - T(locale, "event.setupButton.different_voice_channel_button", { - channel: clientMember.voice.channel, - }), - this.client.color.red, - ); - } - const player = this.client.manager.getPlayer(interaction.guildId); - if (!player) return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); - if (!player.queue) return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); - if (!player.queue.current) - return await buttonReply(interaction, T(locale, "event.setupButton.no_music_playing"), this.client.color.red); - const data = await this.client.db.getSetup(interaction.guildId); - const { title, uri, duration, artworkUrl, sourceName, isStream } = player.queue.current.info; - let message: Message; - try { - message = await interaction.channel.messages.fetch(data.messageId, { - cache: true, - }); - } catch (_e) {} + if (!interaction.replied) + await interaction.deferReply().catch(() => { + null; + }); + if (!interaction.member.voice.channel) { + return await buttonReply( + interaction, + T(locale, 'event.setupButton.no_voice_channel_button'), + this.client.color.red, + ); + } + const clientMember = interaction.guild.members.cache.get(this.client.user?.id); + if (clientMember.voice.channel && clientMember.voice.channelId !== interaction.member.voice.channelId) { + return await buttonReply( + interaction, + T(locale, 'event.setupButton.different_voice_channel_button', { + channel: clientMember.voice.channel, + }), + this.client.color.red, + ); + } + const player = this.client.manager.getPlayer(interaction.guildId); + if (!player) + return await buttonReply(interaction, T(locale, 'event.setupButton.no_music_playing'), this.client.color.red); + if (!player.queue) + return await buttonReply(interaction, T(locale, 'event.setupButton.no_music_playing'), this.client.color.red); + if (!player.queue.current) + return await buttonReply(interaction, T(locale, 'event.setupButton.no_music_playing'), this.client.color.red); + const data = await this.client.db.getSetup(interaction.guildId); + const { title, uri, duration, artworkUrl, sourceName, isStream } = player.queue.current.info; + let message: Message | undefined; + try { + message = await interaction.channel.messages.fetch(data.messageId, { + cache: true, + }); + } catch (_e) { + null; + } - const iconUrl = this.client.config.icons[sourceName] || this.client.user.displayAvatarURL({ extension: "png" }); - const embed = this.client - .embed() - .setAuthor({ - name: T(locale, "event.setupButton.now_playing"), - iconURL: iconUrl, - }) - .setColor(this.client.color.main) - .setDescription( - `[${title}](${uri}) - ${isStream ? T(locale, "event.setupButton.live") : this.client.utils.formatTime(duration)} - ${T(locale, "event.setupButton.requested_by", { requester: (player.queue.current.requester as Requester).id })}`, - ) - .setImage(artworkUrl || this.client.user.displayAvatarURL({ extension: "png" })); + const iconUrl = this.client.config.icons[sourceName] || this.client.user?.displayAvatarURL({ extension: 'png' }); + const embed = this.client + .embed() + .setAuthor({ + name: T(locale, 'event.setupButton.now_playing'), + iconURL: iconUrl, + }) + .setColor(this.client.color.main) + .setDescription( + `[${title}](${uri}) - ${isStream ? T(locale, 'event.setupButton.live') : this.client.utils.formatTime(duration)} - ${T(locale, 'event.setupButton.requested_by', { requester: (player.queue.current.requester as Requester).id })}`, + ) + .setImage(artworkUrl || this.client.user?.displayAvatarURL({ extension: 'png' })!); - if (!interaction.isButton()) return; - if (!(await checkDj(this.client, interaction))) { - return await buttonReply(interaction, T(locale, "event.setupButton.no_dj_permission"), this.client.color.red); - } - if (message) { - const handleVolumeChange = async (change: number) => { - const vol = player.volume + change; - player.setVolume(vol); - await buttonReply(interaction, T(locale, "event.setupButton.volume_set", { vol }), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.volume_footer", { - vol, - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - }; - switch (interaction.customId) { - case "PREV_BUT": - if (!player.queue.previous) { - return await buttonReply(interaction, T(locale, "event.setupButton.no_previous_track"), this.client.color.main); - } - player.play({ - track: player.queue.previous[0], - }); - await buttonReply(interaction, T(locale, "event.setupButton.playing_previous"), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.previous_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - break; - case "REWIND_BUT": { - const time = player.position - 10000; - if (time < 0) { - player.seek(0); - } else { - player.seek(time); - } - await buttonReply(interaction, T(locale, "event.setupButton.rewinded"), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.rewind_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - break; - } - case "PAUSE_BUT": { - const name = player.paused ? T(locale, "event.setupButton.resumed") : T(locale, "event.setupButton.paused"); - if (player.paused) { - player.resume(); - } else { - player.pause(); - } - await buttonReply(interaction, T(locale, "event.setupButton.pause_resume", { name }), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.pause_resume_footer", { - name, - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - components: getButtons(player, this.client), - }); - break; - } - case "FORWARD_BUT": { - const time = player.position + 10000; - if (time > player.queue.current.info.duration) { - return await buttonReply(interaction, T(locale, "event.setupButton.forward_limit"), this.client.color.main); - } - player.seek(time); - await buttonReply(interaction, T(locale, "event.setupButton.forwarded"), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.forward_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - break; - } - case "SKIP_BUT": - if (player.queue.tracks.length === 0) { - return await buttonReply(interaction, T(locale, "event.setupButton.no_music_to_skip"), this.client.color.main); - } - player.skip(); - await buttonReply(interaction, T(locale, "event.setupButton.skipped"), this.client.color.main); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.skipped_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - break; - case "LOW_VOL_BUT": - await handleVolumeChange(-10); - break; - case "LOOP_BUT": { - const loopOptions: Array<"off" | "queue" | "track"> = ["off", "queue", "track"]; - const newLoop = loopOptions[(loopOptions.indexOf(player.repeatMode) + 1) % loopOptions.length]; - player.setRepeatMode(newLoop); - await buttonReply( - interaction, - T(locale, "event.setupButton.loop_set", { - loop: newLoop, - }), - this.client.color.main, - ); - await message.edit({ - embeds: [ - embed.setFooter({ - text: T(locale, "event.setupButton.loop_footer", { - loop: newLoop, - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }), - ], - }); - break; - } - case "STOP_BUT": - player.stopPlaying(true, false); - await buttonReply(interaction, T(locale, "event.setupButton.stopped"), this.client.color.main); - await message.edit({ - embeds: [ - embed - .setFooter({ - text: T(locale, "event.setupButton.stopped_footer", { - displayName: interaction.member.displayName, - }), - iconURL: interaction.member.displayAvatarURL({}), - }) - .setDescription(T(locale, "event.setupButton.nothing_playing")) - .setImage(this.client.config.links.img) - .setAuthor({ - name: this.client.user.username, - iconURL: this.client.user.displayAvatarURL({ - extension: "png", - }), - }), - ], - }); - break; - case "SHUFFLE_BUT": - player.queue.shuffle(); - await buttonReply(interaction, T(locale, "event.setupButton.shuffled"), this.client.color.main); - break; - case "HIGH_VOL_BUT": - await handleVolumeChange(10); - break; - } - } - } + if (!interaction.isButton()) return; + if (!(await checkDj(this.client, interaction))) { + return await buttonReply(interaction, T(locale, 'event.setupButton.no_dj_permission'), this.client.color.red); + } + if (message) { + const handleVolumeChange = async (change: number) => { + const vol = player.volume + change; + player.setVolume(vol); + await buttonReply(interaction, T(locale, 'event.setupButton.volume_set', { vol }), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, 'event.setupButton.volume_footer', { + vol, + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + }; + switch (interaction.customId) { + case 'PREV_BUT': { + if (!player.queue.previous) { + return await buttonReply( + interaction, + T(locale, 'event.setupButton.no_previous_track'), + this.client.color.main, + ); + } + player.play({ + track: player.queue.previous[0], + }); + await buttonReply(interaction, T(locale, 'event.setupButton.playing_previous'), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, 'event.setupButton.previous_footer', { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + break; + } + case 'REWIND_BUT': { + const time = player.position - 10000; + if (time < 0) { + player.seek(0); + } else { + player.seek(time); + } + await buttonReply(interaction, T(locale, 'event.setupButton.rewinded'), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, 'event.setupButton.rewind_footer', { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + break; + } + case 'PAUSE_BUT': { + const name = player.paused ? T(locale, 'event.setupButton.resumed') : T(locale, 'event.setupButton.paused'); + if (player.paused) { + player.resume(); + } else { + player.pause(); + } + await buttonReply(interaction, T(locale, 'event.setupButton.pause_resume', { name }), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, 'event.setupButton.pause_resume_footer', { + name, + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + components: getButtons(player, this.client), + }); + break; + } + case 'FORWARD_BUT': { + const time = player.position + 10000; + if (time > player.queue.current.info.duration) { + return await buttonReply(interaction, T(locale, 'event.setupButton.forward_limit'), this.client.color.main); + } + player.seek(time); + await buttonReply(interaction, T(locale, 'event.setupButton.forwarded'), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, 'event.setupButton.forward_footer', { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + break; + } + case 'SKIP_BUT': { + if (player.queue.tracks.length === 0) { + return await buttonReply( + interaction, + T(locale, 'event.setupButton.no_music_to_skip'), + this.client.color.main, + ); + } + player.skip(); + await buttonReply(interaction, T(locale, 'event.setupButton.skipped'), this.client.color.main); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, 'event.setupButton.skipped_footer', { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + break; + } + case 'LOW_VOL_BUT': + await handleVolumeChange(-10); + break; + case 'LOOP_BUT': { + const loopOptions: Array<'off' | 'queue' | 'track'> = ['off', 'queue', 'track']; + const newLoop = loopOptions[(loopOptions.indexOf(player.repeatMode) + 1) % loopOptions.length]; + player.setRepeatMode(newLoop); + await buttonReply( + interaction, + T(locale, 'event.setupButton.loop_set', { + loop: newLoop, + }), + this.client.color.main, + ); + await message.edit({ + embeds: [ + embed.setFooter({ + text: T(locale, 'event.setupButton.loop_footer', { + loop: newLoop, + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }), + ], + }); + break; + } + case 'STOP_BUT': { + player.stopPlaying(true, false); + await buttonReply(interaction, T(locale, 'event.setupButton.stopped'), this.client.color.main); + await message.edit({ + embeds: [ + embed + .setFooter({ + text: T(locale, 'event.setupButton.stopped_footer', { + displayName: interaction.member.displayName, + }), + iconURL: interaction.member.displayAvatarURL({}), + }) + .setDescription(T(locale, 'event.setupButton.nothing_playing')) + .setImage(this.client.config.links.img) + .setAuthor({ + name: this.client.user?.username!, + iconURL: this.client.user?.displayAvatarURL({ + extension: 'png', + })!, + }), + ], + }); + break; + } + case 'SHUFFLE_BUT': { + player.queue.shuffle(); + await buttonReply(interaction, T(locale, 'event.setupButton.shuffled'), this.client.color.main); + break; + } + case 'HIGH_VOL_BUT': + await handleVolumeChange(10); + break; + } + } + } } /** diff --git a/src/events/client/SetupSystem.ts b/src/events/client/SetupSystem.ts index ae57b3c1d..cc8fbb446 100644 --- a/src/events/client/SetupSystem.ts +++ b/src/events/client/SetupSystem.ts @@ -1,30 +1,30 @@ -import { type Message, PermissionsBitField, TextChannel } from "discord.js"; -import { T } from "../../structures/I18n"; -import { Event, type Lavamusic } from "../../structures/index"; -import { oops, setupStart } from "../../utils/SetupSystem"; +import { type Message, PermissionsBitField, TextChannel } from 'discord.js'; +import { T } from '../../structures/I18n'; +import { Event, type Lavamusic } from '../../structures/index'; +import { oops, setupStart } from '../../utils/SetupSystem'; export default class SetupSystem extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "setupSystem", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'setupSystem', + }); + } - public async run(message: Message): Promise { - const locale = await this.client.db.getLanguage(message.guildId); - const channel = message.channel as TextChannel; - if (!(channel instanceof TextChannel)) return; - if (!message.member?.voice.channel) { - await oops(channel, T(locale, "event.message.no_voice_channel_queue")); - await message.delete().catch(() => {}); - return; - } + public async run(message: Message): Promise { + const locale = await this.client.db.getLanguage(message.guildId!); + const channel = message.channel as TextChannel; + if (!(channel instanceof TextChannel)) return; + if (!message.member?.voice.channel) { + await oops(channel, T(locale, 'event.message.no_voice_channel_queue')); + await message.delete().catch(() => {}); + return; + } - const voiceChannel = message.member.voice.channel; - const clientUser = this.client.user; - const clientMember = message.guild.members.cache.get(clientUser.id); + const voiceChannel = message.member.voice.channel; + const clientUser = this.client.user; + const clientMember = message.guild?.members.cache.get(clientUser!.id); - if (!voiceChannel.permissionsFor(clientUser).has(PermissionsBitField.Flags.Connect | PermissionsBitField.Flags.Speak)) { + /* if (voiceChannel && clientUser && !voiceChannel?.permissionsFor(clientUser!).has(PermissionsBitField.Flags.Connect | PermissionsBitField.Flags.Speak)) { await oops( channel, T(locale, "event.message.no_permission_connect_speak", { @@ -33,36 +33,35 @@ export default class SetupSystem extends Event { ); await message.delete().catch(() => {}); return; - } - - if (clientMember?.voice.channel && clientMember.voice.channelId !== voiceChannel.id) { - await oops( - channel, - T(locale, "event.message.different_voice_channel_queue", { - channel: clientMember.voice.channelId, - }), - ); - await message.delete().catch(() => {}); - return; - } + } */ - let player = this.client.manager.getPlayer(message.guildId); - if (!player) { - player = this.client.manager.createPlayer({ - guildId: message.guildId, - voiceChannelId: voiceChannel.id, - textChannelId: message.channelId, - selfMute: false, - selfDeaf: true, + if (clientMember?.voice.channel && clientMember.voice.channelId !== voiceChannel.id) { + await oops( + channel, + T(locale, 'event.message.different_voice_channel_queue', { + channel: clientMember.voice.channelId, + }), + ); + await message.delete().catch(() => {}); + return; + } - vcRegion: voiceChannel.rtcRegion, - }); - if (!player.connected) await player.connect(); - } + let player = this.client.manager.getPlayer(message.guildId!); + if (!player) { + player = this.client.manager.createPlayer({ + guildId: message.guildId!, + voiceChannelId: voiceChannel.id, + textChannelId: message.channelId, + selfMute: false, + selfDeaf: true, + vcRegion: voiceChannel.rtcRegion!, + }); + if (!player.connected) await player.connect(); + } - await setupStart(this.client, message.content, player, message); - await message.delete().catch(() => {}); - } + await setupStart(this.client, message.content, player, message); + await message.delete().catch(() => {}); + } } /** diff --git a/src/events/client/VoiceStateUpdate.ts b/src/events/client/VoiceStateUpdate.ts index d8ef24c09..564f0e19a 100644 --- a/src/events/client/VoiceStateUpdate.ts +++ b/src/events/client/VoiceStateUpdate.ts @@ -1,78 +1,78 @@ -import { ChannelType, type GuildMember, type VoiceState } from "discord.js"; -import { Event, type Lavamusic } from "../../structures/index"; +import { ChannelType, type GuildMember, type VoiceState } from 'discord.js'; +import { Event, type Lavamusic } from '../../structures/index'; export default class VoiceStateUpdate extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "voiceStateUpdate", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'voiceStateUpdate', + }); + } - public async run(_oldState: VoiceState, newState: VoiceState): Promise { - const guildId = newState.guild.id; - if (!guildId) return; + public async run(_oldState: VoiceState, newState: VoiceState): Promise { + const guildId = newState.guild.id; + if (!guildId) return; - const player = this.client.manager.getPlayer(guildId); - if (!player) return; + const player = this.client.manager.getPlayer(guildId); + if (!player) return; - if (!player?.voiceChannelId) return; + if (!player?.voiceChannelId) return; - const vc = newState.guild.channels.cache.get(player.voiceChannelId); - if (!(vc && vc.members instanceof Map)) return; + const vc = newState.guild.channels.cache.get(player.voiceChannelId); + if (!(vc && vc.members instanceof Map)) return; - const is247 = await this.client.db.get_247(guildId); + const is247 = await this.client.db.get_247(guildId); - if (!(newState.guild.members.cache.get(this.client.user.id)?.voice.channelId || !is247) && player) { - return player.destroy(); - } + if (!(newState.guild.members.cache.get(this.client.user!.id)?.voice.channelId || !is247) && player) { + return player.destroy(); + } - if ( - newState.id === this.client.user.id && - newState.channelId && - newState.channel.type === ChannelType.GuildStageVoice && - newState.guild.members.me.voice.suppress - ) { - if ( - newState.guild.members.me.permissions.has(["Connect", "Speak"]) || - newState.channel.permissionsFor(newState.guild.members.me).has("MuteMembers") - ) { - await newState.guild.members.me.voice.setSuppressed(false).catch(() => {}); - } - } + if ( + newState.id === this.client.user?.id && + newState.channelId && + newState.channel?.type === ChannelType.GuildStageVoice && + newState.guild.members.me?.voice.suppress + ) { + if ( + newState.guild.members.me.permissions.has(['Connect', 'Speak']) || + newState.channel.permissionsFor(newState.guild.members.me).has('MuteMembers') + ) { + await newState.guild.members.me.voice.setSuppressed(false).catch(() => {}); + } + } - if (newState.id === this.client.user.id && !newState.serverDeaf) { - const permissions = vc.permissionsFor(newState.guild.members.me); - if (permissions?.has("DeafenMembers")) { - await newState.setDeaf(true); - } - } + if (newState.id === this.client.user?.id && !newState.serverDeaf) { + const permissions = vc.permissionsFor(newState.guild.members.me!); + if (permissions?.has('DeafenMembers')) { + await newState.setDeaf(true); + } + } - if (newState.id === this.client.user.id) { - if (newState.serverMute && !player.paused) { - player.pause(); - } else if (!newState.serverMute && player.paused) { - player.pause(); - } - } + if (newState.id === this.client.user?.id) { + if (newState.serverMute && !player.paused) { + player.pause(); + } else if (!newState.serverMute && player.paused) { + player.pause(); + } + } - if (vc.members instanceof Map && [...vc.members.values()].filter((x: GuildMember) => !x.user.bot).length <= 0) { - setTimeout(async () => { - if (!player?.voiceChannelId) return; + if (vc.members instanceof Map && [...vc.members.values()].filter((x: GuildMember) => !x.user.bot).length <= 0) { + setTimeout(async () => { + if (!player?.voiceChannelId) return; - const playerVoiceChannel = newState.guild.channels.cache.get(player?.voiceChannelId); - if ( - player && - playerVoiceChannel && - playerVoiceChannel.members instanceof Map && - [...playerVoiceChannel.members.values()].filter((x: GuildMember) => !x.user.bot).length <= 0 - ) { - if (!is247) { - player.destroy(); - } - } - }, 5000); - } - } + const playerVoiceChannel = newState.guild.channels.cache.get(player?.voiceChannelId); + if ( + player && + playerVoiceChannel && + playerVoiceChannel.members instanceof Map && + [...playerVoiceChannel.members.values()].filter((x: GuildMember) => !x.user.bot).length <= 0 + ) { + if (!is247) { + player.destroy(); + } + } + }, 5000); + } + } } /** diff --git a/src/events/node/Connect.ts b/src/events/node/Connect.ts index 75b02fe9a..40b1e4110 100644 --- a/src/events/node/Connect.ts +++ b/src/events/node/Connect.ts @@ -1,55 +1,55 @@ -import type { LavalinkNode } from "lavalink-client"; -import { Event, type Lavamusic } from "../../structures/index"; -import BotLog from "../../utils/BotLog"; +import type { LavalinkNode } from 'lavalink-client'; +import { Event, type Lavamusic } from '../../structures/index'; +import BotLog from '../../utils/BotLog'; export default class Connect extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "connect", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'connect', + }); + } - public async run(node: LavalinkNode): Promise { - this.client.logger.success(`Node ${node.id} is ready!`); + public async run(node: LavalinkNode): Promise { + this.client.logger.success(`Node ${node.id} is ready!`); - let data = await this.client.db.get_247(); - if (!data) return; + let data = await this.client.db.get_247(); + if (!data) return; - if (!Array.isArray(data)) { - data = [data]; - } + if (!Array.isArray(data)) { + data = [data]; + } - data.forEach((main, index) => { - setTimeout(async () => { - const guild = this.client.guilds.cache.get(main.guildId); - if (!guild) return; + data.forEach((main: { guildId: string; textId: string; voiceId: string }, index: number) => { + setTimeout(async () => { + const guild = this.client.guilds.cache.get(main.guildId); + if (!guild) return; - const channel = guild.channels.cache.get(main.textId); - const vc = guild.channels.cache.get(main.voiceId); + const channel = guild.channels.cache.get(main.textId); + const vc = guild.channels.cache.get(main.voiceId); - if (channel && vc) { - try { - const player = this.client.manager.createPlayer({ - guildId: guild.id, - voiceChannelId: vc.id, - textChannelId: channel.id, - selfDeaf: true, - selfMute: false, - }); - if (!player.connected) await player.connect(); - } catch (error) { - this.client.logger.error(`Failed to create queue for guild ${guild.id}: ${error.message}`); - } - } else { - this.client.logger.warn( - `Missing channels for guild ${guild.id}. Text channel: ${main.textId}, Voice channel: ${main.voiceId}`, - ); - } - }, index * 1000); - }); + if (channel && vc) { + try { + const player = this.client.manager.createPlayer({ + guildId: guild.id, + voiceChannelId: vc.id, + textChannelId: channel.id, + selfDeaf: true, + selfMute: false, + }); + if (!player.connected) await player.connect(); + } catch (error) { + this.client.logger.error(`Failed to create queue for guild ${guild.id}: ${error}`); + } + } else { + this.client.logger.warn( + `Missing channels for guild ${guild.id}. Text channel: ${main.textId}, Voice channel: ${main.voiceId}`, + ); + } + }, index * 1000); + }); - BotLog.send(this.client, `Node ${node.id} is ready!`, "success"); - } + BotLog.send(this.client, `Node ${node.id} is ready!`, 'success'); + } } /** diff --git a/src/events/node/Destroy.ts b/src/events/node/Destroy.ts index 3b5843ccc..1e80bccc9 100644 --- a/src/events/node/Destroy.ts +++ b/src/events/node/Destroy.ts @@ -1,18 +1,18 @@ -import type { DestroyReasonsType, LavalinkNode } from "lavalink-client"; -import { Event, type Lavamusic } from "../../structures/index"; -import BotLog from "../../utils/BotLog"; +import type { DestroyReasonsType, LavalinkNode } from 'lavalink-client'; +import { Event, type Lavamusic } from '../../structures/index'; +import BotLog from '../../utils/BotLog'; export default class Destroy extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "destroy", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'destroy', + }); + } - public async run(node: LavalinkNode, destroyReason?: DestroyReasonsType): Promise { - this.client.logger.success(`Node ${node.id} is destroyed!`); - BotLog.send(this.client, `Node ${node.id} is destroyed: ${destroyReason}`, "warn"); - } + public async run(node: LavalinkNode, destroyReason?: DestroyReasonsType): Promise { + this.client.logger.success(`Node ${node.id} is destroyed!`); + BotLog.send(this.client, `Node ${node.id} is destroyed: ${destroyReason}`, 'warn'); + } } /** diff --git a/src/events/player/QueueEnd.ts b/src/events/player/QueueEnd.ts index 24e26dbba..d5353bfc6 100644 --- a/src/events/player/QueueEnd.ts +++ b/src/events/player/QueueEnd.ts @@ -1,31 +1,31 @@ -import type { TextChannel } from "discord.js"; -import type { Player, Track, TrackStartEvent } from "lavalink-client"; -import { Event, type Lavamusic } from "../../structures/index"; +import type { TextChannel } from 'discord.js'; +import type { Player, Track, TrackStartEvent } from 'lavalink-client'; +import { Event, type Lavamusic } from '../../structures/index'; export default class QueueEnd extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "queueEnd", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'queueEnd', + }); + } - public async run(player: Player, _track: Track | null, _payload: TrackStartEvent): Promise { - const guild = this.client.guilds.cache.get(player.guildId); - if (!guild) return; + public async run(player: Player, _track: Track | null, _payload: TrackStartEvent): Promise { + const guild = this.client.guilds.cache.get(player.guildId); + if (!guild) return; - const messageId = player.get("messageId"); - if (!messageId) return; + const messageId = player.get('messageId'); + if (!messageId) return; - const channel = guild.channels.cache.get(player.textChannelId) as TextChannel; - if (!channel) return; + const channel = guild.channels.cache.get(player.textChannelId!) as TextChannel; + if (!channel) return; - const message = await channel.messages.fetch(messageId).catch(() => {}); - if (!message) return; + const message = await channel.messages.fetch(messageId).catch(() => {}); + if (!message) return; - if (message.editable) { - await message.edit({ components: [] }).catch(() => {}); - } - } + if (message.editable) { + await message.edit({ components: [] }).catch(() => {}); + } + } } /** diff --git a/src/events/player/TrackEnd.ts b/src/events/player/TrackEnd.ts index ddfd07469..7107b8ec7 100644 --- a/src/events/player/TrackEnd.ts +++ b/src/events/player/TrackEnd.ts @@ -1,29 +1,29 @@ -import type { TextChannel } from "discord.js"; -import type { Player, Track, TrackStartEvent } from "lavalink-client"; -import { Event, type Lavamusic } from "../../structures/index"; +import type { TextChannel } from 'discord.js'; +import type { Player, Track, TrackStartEvent } from 'lavalink-client'; +import { Event, type Lavamusic } from '../../structures/index'; export default class TrackEnd extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "trackEnd", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'trackEnd', + }); + } - public async run(player: Player, _track: Track | null, _payload: TrackStartEvent): Promise { - const guild = this.client.guilds.cache.get(player.guildId); - if (!guild) return; + public async run(player: Player, _track: Track | null, _payload: TrackStartEvent): Promise { + const guild = this.client.guilds.cache.get(player.guildId); + if (!guild) return; - const messageId = player.get("messageId"); - if (!messageId) return; + const messageId = player.get('messageId'); + if (!messageId) return; - const channel = guild.channels.cache.get(player.textChannelId) as TextChannel; - if (!channel) return; + const channel = guild.channels.cache.get(player.textChannelId!) as TextChannel; + if (!channel) return; - const message = await channel.messages.fetch(messageId).catch(() => {}); - if (!message) return; + const message = await channel.messages.fetch(messageId).catch(() => {}); + if (!message) return; - message.delete().catch(() => {}); - } + message.delete().catch(() => {}); + } } /** diff --git a/src/events/player/TrackStart.ts b/src/events/player/TrackStart.ts index 2542e41b6..aa255d505 100644 --- a/src/events/player/TrackStart.ts +++ b/src/events/player/TrackStart.ts @@ -1,265 +1,288 @@ import { - ActionRowBuilder, - ButtonBuilder, - type ButtonInteraction, - ButtonStyle, - type ChannelSelectMenuInteraction, - GuildMember, - type MentionableSelectMenuInteraction, - PermissionFlagsBits, - type RoleSelectMenuInteraction, - type StringSelectMenuInteraction, - type TextChannel, - type UserSelectMenuInteraction, -} from "discord.js"; -import type { Player, Track, TrackStartEvent } from "lavalink-client"; -import { T } from "../../structures/I18n"; -import { Event, type Lavamusic } from "../../structures/index"; -import type { Requester } from "../../types"; -import { trackStart } from "../../utils/SetupSystem"; + ActionRowBuilder, + ButtonBuilder, + type ButtonInteraction, + ButtonStyle, + type ChannelSelectMenuInteraction, + GuildMember, + type MentionableSelectMenuInteraction, + PermissionFlagsBits, + type RoleSelectMenuInteraction, + type StringSelectMenuInteraction, + type TextChannel, + type UserSelectMenuInteraction, +} from 'discord.js'; +import type { Player, Track, TrackStartEvent } from 'lavalink-client'; +import { T } from '../../structures/I18n'; +import { Event, type Lavamusic } from '../../structures/index'; +import type { Requester } from '../../types'; +import { trackStart } from '../../utils/SetupSystem'; export default class TrackStart extends Event { - constructor(client: Lavamusic, file: string) { - super(client, file, { - name: "trackStart", - }); - } + constructor(client: Lavamusic, file: string) { + super(client, file, { + name: 'trackStart', + }); + } - public async run(player: Player, track: Track | null, _payload: TrackStartEvent): Promise { - const guild = this.client.guilds.cache.get(player.guildId); - if (!guild) return; + public async run(player: Player, track: Track | null, _payload: TrackStartEvent): Promise { + const guild = this.client.guilds.cache.get(player.guildId); + if (!guild) return; + if (!player.textChannelId) return; + if (!track) return; + const channel = guild.channels.cache.get(player.textChannelId) as TextChannel; + if (!channel) return; - const channel = guild.channels.cache.get(player.textChannelId) as TextChannel; - if (!channel) return; + this.client.utils.updateStatus(this.client, guild.id); - this.client.utils.updateStatus(this.client, guild.id); + const locale = await this.client.db.getLanguage(guild.id); - const locale = await this.client.db.getLanguage(guild.id); + const embed = this.client + .embed() + .setAuthor({ + name: T(locale, 'player.trackStart.now_playing'), + iconURL: + this.client.config.icons[track.info.sourceName] ?? this.client.user?.displayAvatarURL({ extension: 'png' }), + }) + .setColor(this.client.color.main) + .setDescription(`**[${track.info.title}](${track.info.uri})**`) + .setFooter({ + text: T(locale, 'player.trackStart.requested_by', { + user: (track.requester as Requester).username, + }), + iconURL: (track.requester as Requester).avatarURL, + }) + .setThumbnail(track.info.artworkUrl) + .addFields( + { + name: T(locale, 'player.trackStart.duration'), + value: track.info.isStream ? 'LIVE' : this.client.utils.formatTime(track.info.duration), + inline: true, + }, + { + name: T(locale, 'player.trackStart.author'), + value: track.info.author, + inline: true, + }, + ) + .setTimestamp(); - const embed = this.client - .embed() - .setAuthor({ - name: T(locale, "player.trackStart.now_playing"), - iconURL: this.client.config.icons[track.info.sourceName] ?? this.client.user.displayAvatarURL({ extension: "png" }), - }) - .setColor(this.client.color.main) - .setDescription(`**[${track.info.title}](${track.info.uri})**`) - .setFooter({ - text: T(locale, "player.trackStart.requested_by", { - user: (track.requester as Requester).username, - }), - iconURL: (track.requester as Requester).avatarURL, - }) - .setThumbnail(track.info.artworkUrl) - .addFields( - { - name: T(locale, "player.trackStart.duration"), - value: track.info.isStream ? "LIVE" : this.client.utils.formatTime(track.info.duration), - inline: true, - }, - { - name: T(locale, "player.trackStart.author"), - value: track.info.author, - inline: true, - }, - ) - .setTimestamp(); + const setup = await this.client.db.getSetup(guild.id); - const setup = await this.client.db.getSetup(guild.id); + if (setup?.textId) { + const textChannel = guild.channels.cache.get(setup.textId) as TextChannel; + if (textChannel) { + await trackStart(setup.messageId, textChannel, player, track, this.client, locale); + } + } else { + const message = await channel.send({ + embeds: [embed], + components: [createButtonRow(player, this.client)], + }); - if (setup?.textId) { - const textChannel = guild.channels.cache.get(setup.textId) as TextChannel; - if (textChannel) { - await trackStart(setup.messageId, textChannel, player, track, this.client, locale); - } - } else { - const message = await channel.send({ - embeds: [embed], - components: [createButtonRow(player, this.client)], - }); - - player.set("messageId", message.id); - createCollector(message, player, track, embed, this.client, locale); - } - } + player.set('messageId', message.id); + createCollector(message, player, track, embed, this.client, locale); + } + } } function createButtonRow(player: Player, client: Lavamusic): ActionRowBuilder { - const previousButton = new ButtonBuilder() + const previousButton = new ButtonBuilder() - .setCustomId("previous") - .setEmoji(client.emoji.previous) - .setStyle(ButtonStyle.Secondary) - .setDisabled(!player.queue.previous); + .setCustomId('previous') + .setEmoji(client.emoji.previous) + .setStyle(ButtonStyle.Secondary) + .setDisabled(!player.queue.previous); - const resumeButton = new ButtonBuilder() - .setCustomId("resume") - .setEmoji(player.paused ? client.emoji.resume : client.emoji.pause) - .setStyle(player.paused ? ButtonStyle.Success : ButtonStyle.Secondary); + const resumeButton = new ButtonBuilder() + .setCustomId('resume') + .setEmoji(player.paused ? client.emoji.resume : client.emoji.pause) + .setStyle(player.paused ? ButtonStyle.Success : ButtonStyle.Secondary); - const stopButton = new ButtonBuilder().setCustomId("stop").setEmoji(client.emoji.stop).setStyle(ButtonStyle.Danger); + const stopButton = new ButtonBuilder().setCustomId('stop').setEmoji(client.emoji.stop).setStyle(ButtonStyle.Danger); - const skipButton = new ButtonBuilder().setCustomId("skip").setEmoji(client.emoji.skip).setStyle(ButtonStyle.Secondary); + const skipButton = new ButtonBuilder() + .setCustomId('skip') + .setEmoji(client.emoji.skip) + .setStyle(ButtonStyle.Secondary); - const loopButton = new ButtonBuilder() - .setCustomId("loop") - .setEmoji(player.repeatMode === "track" ? client.emoji.loop.track : client.emoji.loop.none) - .setStyle(player.repeatMode !== "off" ? ButtonStyle.Success : ButtonStyle.Secondary); + const loopButton = new ButtonBuilder() + .setCustomId('loop') + .setEmoji(player.repeatMode === 'track' ? client.emoji.loop.track : client.emoji.loop.none) + .setStyle(player.repeatMode !== 'off' ? ButtonStyle.Success : ButtonStyle.Secondary); - return new ActionRowBuilder().addComponents(resumeButton, previousButton, stopButton, skipButton, loopButton); + return new ActionRowBuilder().addComponents( + resumeButton, + previousButton, + stopButton, + skipButton, + loopButton, + ); } -function createCollector(message: any, player: Player, _track: Track, embed: any, client: Lavamusic, locale: string): void { - const collector = message.createMessageComponentCollector({ - filter: async (b: ButtonInteraction) => { - if (b.member instanceof GuildMember) { - const isSameVoiceChannel = b.guild.members.me?.voice.channelId === b.member.voice.channelId; - if (isSameVoiceChannel) return true; - } - await b.reply({ - content: T(locale, "player.trackStart.not_connected_to_voice_channel", { - channel: b.guild.members.me?.voice.channelId ?? "None", - }), - ephemeral: true, - }); - return false; - }, - }); +function createCollector( + message: any, + player: Player, + _track: Track, + embed: any, + client: Lavamusic, + locale: string, +): void { + const collector = message.createMessageComponentCollector({ + filter: async (b: ButtonInteraction) => { + if (b.member instanceof GuildMember) { + const isSameVoiceChannel = b.guild?.members.me?.voice.channelId === b.member.voice.channelId; + if (isSameVoiceChannel) return true; + } + await b.reply({ + content: T(locale, 'player.trackStart.not_connected_to_voice_channel', { + channel: b.guild?.members.me?.voice.channelId ?? 'None', + }), + ephemeral: true, + }); + return false; + }, + }); - collector.on("collect", async (interaction) => { - if (!(await checkDj(client, interaction))) { - await interaction.reply({ - content: T(locale, "player.trackStart.need_dj_role"), - ephemeral: true, - }); - return; - } + collector.on('collect', async (interaction: ButtonInteraction<'cached'>) => { + if (!(await checkDj(client, interaction))) { + await interaction.reply({ + content: T(locale, 'player.trackStart.need_dj_role'), + ephemeral: true, + }); + return; + } - const editMessage = async (text: string): Promise => { - if (message) { - await message.edit({ - embeds: [ - embed.setFooter({ - text, - iconURL: interaction.user.avatarURL({}), - }), - ], - components: [createButtonRow(player, client)], - }); - } - }; - switch (interaction.customId) { - case "previous": - if (player.queue.previous) { - await interaction.deferUpdate(); - const previousTrack = player.queue.previous[0]; - player.play({ - track: previousTrack, - }); - await editMessage( - T(locale, "player.trackStart.previous_by", { - user: interaction.user.tag, - }), - ); - } else { - await interaction.reply({ - content: T(locale, "player.trackStart.no_previous_song"), - ephemeral: true, - }); - } - break; - case "resume": - if (player.paused) { - player.resume(); - await interaction.deferUpdate(); - await editMessage( - T(locale, "player.trackStart.resumed_by", { - user: interaction.user.tag, - }), - ); - } else { - player.pause(); - await interaction.deferUpdate(); - await editMessage( - T(locale, "player.trackStart.paused_by", { - user: interaction.user.tag, - }), - ); - } - break; - case "stop": - player.stopPlaying(true, false); - await interaction.deferUpdate(); - break; - case "skip": - if (player.queue.tracks.length > 0) { - await interaction.deferUpdate(); - player.skip(); - await editMessage( - T(locale, "player.trackStart.skipped_by", { - user: interaction.user.tag, - }), - ); - } else { - await interaction.reply({ - content: T(locale, "player.trackStart.no_more_songs_in_queue"), - ephemeral: true, - }); - } - break; - case "loop": - await interaction.deferUpdate(); - switch (player.repeatMode) { - case "off": - player.setRepeatMode("track"); - await editMessage( - T(locale, "player.trackStart.looping_by", { - user: interaction.user.tag, - }), - ); - break; - case "track": - player.setRepeatMode("queue"); - await editMessage( - T(locale, "player.trackStart.looping_queue_by", { - user: interaction.user.tag, - }), - ); - break; - case "queue": - player.setRepeatMode("off"); - await editMessage( - T(locale, "player.trackStart.looping_off_by", { - user: interaction.user.tag, - }), - ); - break; - } - break; - } - }); + const editMessage = async (text: string): Promise => { + if (message) { + await message.edit({ + embeds: [ + embed.setFooter({ + text, + iconURL: interaction.user.avatarURL({}), + }), + ], + components: [createButtonRow(player, client)], + }); + } + }; + switch (interaction.customId) { + case 'previous': + if (player.queue.previous) { + await interaction.deferUpdate(); + const previousTrack = player.queue.previous[0]; + player.play({ + track: previousTrack, + }); + await editMessage( + T(locale, 'player.trackStart.previous_by', { + user: interaction.user.tag, + }), + ); + } else { + await interaction.reply({ + content: T(locale, 'player.trackStart.no_previous_song'), + ephemeral: true, + }); + } + break; + case 'resume': + if (player.paused) { + player.resume(); + await interaction.deferUpdate(); + await editMessage( + T(locale, 'player.trackStart.resumed_by', { + user: interaction.user.tag, + }), + ); + } else { + player.pause(); + await interaction.deferUpdate(); + await editMessage( + T(locale, 'player.trackStart.paused_by', { + user: interaction.user.tag, + }), + ); + } + break; + case 'stop': { + player.stopPlaying(true, false); + await interaction.deferUpdate(); + break; + } + case 'skip': + if (player.queue.tracks.length > 0) { + await interaction.deferUpdate(); + player.skip(); + await editMessage( + T(locale, 'player.trackStart.skipped_by', { + user: interaction.user.tag, + }), + ); + } else { + await interaction.reply({ + content: T(locale, 'player.trackStart.no_more_songs_in_queue'), + ephemeral: true, + }); + } + break; + case 'loop': { + await interaction.deferUpdate(); + switch (player.repeatMode) { + case 'off': { + player.setRepeatMode('track'); + await editMessage( + T(locale, 'player.trackStart.looping_by', { + user: interaction.user.tag, + }), + ); + break; + } + case 'track': { + player.setRepeatMode('queue'); + await editMessage( + T(locale, 'player.trackStart.looping_queue_by', { + user: interaction.user.tag, + }), + ); + break; + } + case 'queue': { + player.setRepeatMode('off'); + await editMessage( + T(locale, 'player.trackStart.looping_off_by', { + user: interaction.user.tag, + }), + ); + break; + } + } + break; + } + } + }); } export async function checkDj( - client: Lavamusic, - interaction: - | ButtonInteraction<"cached"> - | StringSelectMenuInteraction<"cached"> - | UserSelectMenuInteraction<"cached"> - | RoleSelectMenuInteraction<"cached"> - | MentionableSelectMenuInteraction<"cached"> - | ChannelSelectMenuInteraction<"cached">, + client: Lavamusic, + interaction: + | ButtonInteraction<'cached'> + | StringSelectMenuInteraction<'cached'> + | UserSelectMenuInteraction<'cached'> + | RoleSelectMenuInteraction<'cached'> + | MentionableSelectMenuInteraction<'cached'> + | ChannelSelectMenuInteraction<'cached'>, ): Promise { - const dj = await client.db.getDj(interaction.guildId); - if (dj?.mode) { - const djRole = await client.db.getRoles(interaction.guildId); - if (!djRole) return false; - const hasDjRole = interaction.member.roles.cache.some((role) => djRole.map((r) => r.roleId).includes(role.id)); - if (!(hasDjRole || interaction.member.permissions.has(PermissionFlagsBits.ManageGuild))) { - return false; - } - } - return true; + const dj = await client.db.getDj(interaction.guildId); + if (dj?.mode) { + const djRole = await client.db.getRoles(interaction.guildId); + if (!djRole) return false; + const hasDjRole = interaction.member.roles.cache.some(role => djRole.map(r => r.roleId).includes(role.id)); + if (!(hasDjRole || interaction.member.permissions.has(PermissionFlagsBits.ManageGuild))) { + return false; + } + } + return true; } /** diff --git a/src/index.ts b/src/index.ts index 737885c4f..0debd514d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ -import * as fs from "node:fs"; -import { shardStart } from "./shard"; -import Logger from "./structures/Logger"; -import { ThemeSelector } from "./utils/ThemeSelector"; +import * as fs from 'node:fs'; +import { shardStart } from './shard'; +import Logger from './structures/Logger'; +import { ThemeSelector } from './utils/ThemeSelector'; const logger = new Logger(); @@ -12,23 +12,23 @@ const theme = new ThemeSelector(); * @param title - The new title for the console window. */ function setConsoleTitle(title: string): void { - // Write the escape sequence to change the console title - process.stdout.write(`\x1b]0;${title}\x07`); + // Write the escape sequence to change the console title + process.stdout.write(`\x1b]0;${title}\x07`); } try { - if (!fs.existsSync("./src/utils/LavaLogo.txt")) { - logger.error("LavaLogo.txt file is missing"); - process.exit(1); - } - console.clear(); - // Set a custom title for the console window - setConsoleTitle("Lavamusic"); - const logFile = fs.readFileSync("./src/utils/LavaLogo.txt", "utf-8"); - console.log(theme.purpleNeon(logFile)); - shardStart(logger); + if (!fs.existsSync('./src/utils/LavaLogo.txt')) { + logger.error('LavaLogo.txt file is missing'); + process.exit(1); + } + console.clear(); + // Set a custom title for the console window + setConsoleTitle('Lavamusic'); + const logFile = fs.readFileSync('./src/utils/LavaLogo.txt', 'utf-8'); + console.log(theme.purpleNeon(logFile)); + shardStart(logger); } catch (err) { - logger.error("[CLIENT] An error has occurred:", err); + logger.error('[CLIENT] An error has occurred:', err); } /** diff --git a/src/plugin/index.ts b/src/plugin/index.ts index b4adc6f9c..37f4bebad 100644 --- a/src/plugin/index.ts +++ b/src/plugin/index.ts @@ -1,30 +1,30 @@ -import fs from "node:fs"; -import path from "node:path"; -import type { Lavamusic } from "../structures/index"; +import fs from 'node:fs'; +import path from 'node:path'; +import type { Lavamusic } from '../structures/index'; -const pluginsFolder = path.join(__dirname, "plugins"); +const pluginsFolder = path.join(__dirname, 'plugins'); export default async function loadPlugins(client: Lavamusic): Promise { - try { - const pluginFiles = fs.readdirSync(pluginsFolder).filter((file) => file.endsWith(".js")); - for (const file of pluginFiles) { - const pluginPath = path.join(pluginsFolder, file); - const { default: plugin } = require(pluginPath); - if (plugin.initialize) plugin.initialize(client); - client.logger.info(`Loaded plugin: ${plugin.name} v${plugin.version}`); - } - } catch (error) { - client.logger.error("Error loading plugins:", error); - } + try { + const pluginFiles = fs.readdirSync(pluginsFolder).filter(file => file.endsWith('.js')); + for (const file of pluginFiles) { + const pluginPath = path.join(pluginsFolder, file); + const { default: plugin } = require(pluginPath); + if (plugin.initialize) plugin.initialize(client); + client.logger.info(`Loaded plugin: ${plugin.name} v${plugin.version}`); + } + } catch (error) { + client.logger.error('Error loading plugins:', error); + } } export interface BotPlugin { - name: string; - version: string; - author: string; - description?: string; - initialize: (client: Lavamusic) => void; - shutdown?: (client: Lavamusic) => void; + name: string; + version: string; + author: string; + description?: string; + initialize: (client: Lavamusic) => void; + shutdown?: (client: Lavamusic) => void; } /** diff --git a/src/plugin/plugins/antiCrash.ts b/src/plugin/plugins/antiCrash.ts index 1f5bed3da..352264fbd 100644 --- a/src/plugin/plugins/antiCrash.ts +++ b/src/plugin/plugins/antiCrash.ts @@ -1,29 +1,29 @@ -import type { Lavamusic } from "../../structures/index"; -import type { BotPlugin } from "../index"; +import type { Lavamusic } from '../../structures/index'; +import type { BotPlugin } from '../index'; const antiCrash: BotPlugin = { - name: "AntiCrash Plugin", - version: "1.0.0", - author: "Appu", - initialize: (client: Lavamusic) => { - const handleExit = async (): Promise => { - if (client) { - client.logger.star("Disconnecting from Discord..."); - await client.destroy(); - client.logger.success("Successfully disconnected from Discord!"); - process.exit(); - } - }; - process.on("unhandledRejection", (reason, promise) => { - client.logger.error("Unhandled Rejection at:", promise, "reason:", reason); - }); - process.on("uncaughtException", (err) => { - client.logger.error("Uncaught Exception thrown:", err); - }); - process.on("SIGINT", handleExit); - process.on("SIGTERM", handleExit); - process.on("SIGQUIT", handleExit); - }, + name: 'AntiCrash Plugin', + version: '1.0.0', + author: 'Appu', + initialize: (client: Lavamusic) => { + const handleExit = async (): Promise => { + if (client) { + client.logger.star('Disconnecting from Discord...'); + await client.destroy(); + client.logger.success('Successfully disconnected from Discord!'); + process.exit(); + } + }; + process.on('unhandledRejection', (reason, promise) => { + client.logger.error('Unhandled Rejection at:', promise, 'reason:', reason); + }); + process.on('uncaughtException', err => { + client.logger.error('Uncaught Exception thrown:', err); + }); + process.on('SIGINT', handleExit); + process.on('SIGTERM', handleExit); + process.on('SIGQUIT', handleExit); + }, }; export default antiCrash; diff --git a/src/plugin/plugins/keepAlive.ts b/src/plugin/plugins/keepAlive.ts index 982d00df1..7ff91949c 100644 --- a/src/plugin/plugins/keepAlive.ts +++ b/src/plugin/plugins/keepAlive.ts @@ -1,23 +1,23 @@ -import http from "node:http"; -import { env } from "../../env"; -import type { Lavamusic } from "../../structures/index"; -import type { BotPlugin } from "../index"; +import http from 'node:http'; +import { env } from '../../env'; +import type { Lavamusic } from '../../structures/index'; +import type { BotPlugin } from '../index'; const keepAlive: BotPlugin = { - name: "KeepAlive Plugin", - version: "1.0.0", - author: "Appu", - initialize: (client: Lavamusic) => { - if (env.KEEP_ALIVE) { - const server = http.createServer((_req, res) => { - res.writeHead(200, { "Content-Type": "text/plain" }); - res.end(`I'm alive! Currently serving ${client.guilds.cache.size} guilds.`); - }); - server.listen(3000, () => { - client.logger.info("Keep-Alive server is running on port 3000"); - }); - } - }, + name: 'KeepAlive Plugin', + version: '1.0.0', + author: 'Appu', + initialize: (client: Lavamusic) => { + if (env.KEEP_ALIVE) { + const server = http.createServer((_req, res) => { + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end(`I'm alive! Currently serving ${client.guilds.cache.size} guilds.`); + }); + server.listen(3000, () => { + client.logger.info('Keep-Alive server is running on port 3000'); + }); + } + }, }; export default keepAlive; diff --git a/src/plugin/plugins/updateStatus.ts b/src/plugin/plugins/updateStatus.ts index 20eeb41a9..7c07dba67 100644 --- a/src/plugin/plugins/updateStatus.ts +++ b/src/plugin/plugins/updateStatus.ts @@ -1,13 +1,13 @@ -import type { Lavamusic } from "../../structures/index"; -import type { BotPlugin } from "../index"; +import type { Lavamusic } from '../../structures/index'; +import type { BotPlugin } from '../index'; const updateStatusPlugin: BotPlugin = { - name: "Update Status Plugin", - version: "1.0.0", - author: "Appu", - initialize: (client: Lavamusic) => { - client.on("ready", () => client.utils.updateStatus(client)); - }, + name: 'Update Status Plugin', + version: '1.0.0', + author: 'Appu', + initialize: (client: Lavamusic) => { + client.on('ready', () => client.utils.updateStatus(client)); + }, }; export default updateStatusPlugin; diff --git a/src/shard.ts b/src/shard.ts index 6de012e55..432e28974 100644 --- a/src/shard.ts +++ b/src/shard.ts @@ -1,24 +1,24 @@ -import { ShardingManager } from "discord.js"; -import { env } from "./env"; -import type Logger from "./structures/Logger"; +import { ShardingManager } from 'discord.js'; +import { env } from './env'; +import type Logger from './structures/Logger'; export async function shardStart(logger: Logger) { - const manager = new ShardingManager("./dist/LavaClient.js", { - respawn: true, - token: env.TOKEN, - totalShards: "auto", - shardList: "auto", - }); + const manager = new ShardingManager('./dist/LavaClient.js', { + respawn: true, + token: env.TOKEN, + totalShards: 'auto', + shardList: 'auto', + }); - manager.on("shardCreate", (shard) => { - shard.on("ready", () => { - logger.start(`[CLIENT] Shard ${shard.id} connected to Discord's Gateway.`); - }); - }); + manager.on('shardCreate', shard => { + shard.on('ready', () => { + logger.start(`[CLIENT] Shard ${shard.id} connected to Discord's Gateway.`); + }); + }); - await manager.spawn(); + await manager.spawn(); - logger.start(`[CLIENT] ${manager.totalShards} shard(s) spawned.`); + logger.start(`[CLIENT] ${manager.totalShards} shard(s) spawned.`); } /** diff --git a/src/structures/Command.ts b/src/structures/Command.ts index 69d25af82..bdcce0ff2 100644 --- a/src/structures/Command.ts +++ b/src/structures/Command.ts @@ -1,90 +1,90 @@ -import type { APIApplicationCommandOption, PermissionResolvable } from "discord.js"; -import type Lavamusic from "./Lavamusic"; +import type { APIApplicationCommandOption, PermissionResolvable } from 'discord.js'; +import type Lavamusic from './Lavamusic'; interface CommandDescription { - content: string; - usage: string; - examples: string[]; + content: string; + usage: string; + examples: string[]; } interface CommandPlayer { - voice: boolean; - dj: boolean; - active: boolean; - djPerm: string | null; + voice: boolean; + dj: boolean; + active: boolean; + djPerm: string | null; } interface CommandPermissions { - dev: boolean; - client: string[] | PermissionResolvable; - user: string[] | PermissionResolvable; + dev: boolean; + client: string[] | PermissionResolvable; + user: string[] | PermissionResolvable; } interface CommandOptions { - name: string; - name_localizations?: Record; - description?: Partial; - description_localizations?: Record; - aliases?: string[]; - cooldown?: number; - args?: boolean; - vote?: boolean; - player?: Partial; - permissions?: Partial; - slashCommand?: boolean; - options?: APIApplicationCommandOption[]; - category?: string; + name: string; + name_localizations?: Record; + description?: Partial; + description_localizations?: Record; + aliases?: string[]; + cooldown?: number; + args?: boolean; + vote?: boolean; + player?: Partial; + permissions?: Partial; + slashCommand?: boolean; + options?: APIApplicationCommandOption[]; + category?: string; } export default class Command { - public client: Lavamusic; - public name: string; - public name_localizations?: Record; - public description: CommandDescription; - public description_localizations?: Record; - public aliases: string[]; - public cooldown: number; - public args: boolean; - public vote: boolean; - public player: CommandPlayer; - public permissions: CommandPermissions; - public slashCommand: boolean; - public options: APIApplicationCommandOption[]; - public category: string; + public client: Lavamusic; + public name: string; + public name_localizations?: Record; + public description: CommandDescription; + public description_localizations?: Record; + public aliases: string[]; + public cooldown: number; + public args: boolean; + public vote: boolean; + public player: CommandPlayer; + public permissions: CommandPermissions; + public slashCommand: boolean; + public options: APIApplicationCommandOption[]; + public category: string; - constructor(client: Lavamusic, options: CommandOptions) { - this.client = client; - this.name = options.name; - this.name_localizations = options.name_localizations ?? {}; - this.description = { - content: options.description?.content ?? "No description provided", - usage: options.description?.usage ?? "No usage provided", - examples: options.description?.examples ?? ["No examples provided"], - }; - this.description_localizations = options.description_localizations ?? {}; - this.aliases = options.aliases ?? []; - this.cooldown = options.cooldown ?? 3; - this.args = options.args ?? false; - this.vote = options.vote ?? false; - this.player = { - voice: options.player?.voice ?? false, - dj: options.player?.dj ?? false, - active: options.player?.active ?? false, - djPerm: options.player?.djPerm ?? null, - }; - this.permissions = { - dev: options.permissions?.dev ?? false, - client: options.permissions?.client ?? ["SendMessages", "ViewChannel", "EmbedLinks"], - user: options.permissions?.user ?? [], - }; - this.slashCommand = options.slashCommand ?? false; - this.options = options.options ?? []; - this.category = options.category ?? "general"; - } + constructor(client: Lavamusic, options: CommandOptions) { + this.client = client; + this.name = options.name; + this.name_localizations = options.name_localizations ?? {}; + this.description = { + content: options.description?.content ?? 'No description provided', + usage: options.description?.usage ?? 'No usage provided', + examples: options.description?.examples ?? ['No examples provided'], + }; + this.description_localizations = options.description_localizations ?? {}; + this.aliases = options.aliases ?? []; + this.cooldown = options.cooldown ?? 3; + this.args = options.args ?? false; + this.vote = options.vote ?? false; + this.player = { + voice: options.player?.voice ?? false, + dj: options.player?.dj ?? false, + active: options.player?.active ?? false, + djPerm: options.player?.djPerm ?? null, + }; + this.permissions = { + dev: options.permissions?.dev ?? false, + client: options.permissions?.client ?? ['SendMessages', 'ViewChannel', 'EmbedLinks'], + user: options.permissions?.user ?? [], + }; + this.slashCommand = options.slashCommand ?? false; + this.options = options.options ?? []; + this.category = options.category ?? 'general'; + } - public async run(_client: Lavamusic, _message: any, _args: string[]): Promise { - return await Promise.resolve(); - } + public async run(_client: Lavamusic, _message: any, _args: string[]): Promise { + return await Promise.resolve(); + } } /** diff --git a/src/structures/Context.ts b/src/structures/Context.ts index 26eb2b26a..02bae690f 100644 --- a/src/structures/Context.ts +++ b/src/structures/Context.ts @@ -1,139 +1,154 @@ import { - type APIInteractionGuildMember, - ChannelType, - ChatInputCommandInteraction, - type CommandInteraction, - type DMChannel, - type Guild, - type GuildMember, - type GuildMemberResolvable, - type GuildTextBasedChannel, - type InteractionEditReplyOptions, - type InteractionReplyOptions, - Message, - type MessageCreateOptions, - type MessageEditOptions, - type MessagePayload, - type PartialDMChannel, - type TextChannel, - type User, -} from "discord.js"; -import { T } from "./I18n"; -import type { Lavamusic } from "./index"; + type APIInteractionGuildMember, + ChatInputCommandInteraction, + type CommandInteraction, + type Guild, + type GuildMember, + type GuildMemberResolvable, + type InteractionEditReplyOptions, + type InteractionReplyOptions, + Message, + type MessageCreateOptions, + type MessageEditOptions, + type MessagePayload, + type TextBasedChannel, + type TextChannel, + type User, +} from 'discord.js'; +import { T } from './I18n'; +import type { Lavamusic } from './index'; export default class Context { - public ctx: CommandInteraction | Message; - public interaction: CommandInteraction | null; - public message: Message | null; - public id: string; - public channelId: string; - public client: Lavamusic; - public author: User | null; - public channel: PartialDMChannel | GuildTextBasedChannel | TextChannel | DMChannel | null = null; - public guild: Guild | null; - public createdAt: Date; - public createdTimestamp: number; - public member: GuildMemberResolvable | GuildMember | APIInteractionGuildMember | null; - public args: any[]; - public msg: any; - public guildLocale: string; - - constructor(ctx: ChatInputCommandInteraction | Message, args: any[]) { - this.ctx = ctx; - this.interaction = ctx instanceof ChatInputCommandInteraction ? ctx : null; - this.message = ctx instanceof Message ? ctx : null; - - if (ctx.channel && ctx.channel.type !== ChannelType.GroupDM) { - this.channel = ctx.channel; - } else { - this.channel = null; - } - - this.id = ctx.id; - this.channelId = ctx.channelId; - this.client = ctx.client as Lavamusic; - this.author = ctx instanceof Message ? ctx.author : ctx.user; - this.guild = ctx.guild; - this.createdAt = ctx.createdAt; - this.createdTimestamp = ctx.createdTimestamp; - this.member = ctx.member; - this.args = args; - this.setArgs(args); - this.setUpLocale(); - } - - private async setUpLocale(): Promise { - this.guildLocale = this.guild ? await this.client.db.getLanguage(this.guild.id) : "en"; - } - - public get isInteraction(): boolean { - return this.ctx instanceof ChatInputCommandInteraction; - } - - public setArgs(args: any[]): void { - this.args = this.isInteraction ? args.map((arg: { value: any }) => arg.value) : args; - } - - public async sendMessage(content: string | MessagePayload | MessageCreateOptions | InteractionReplyOptions): Promise { - if (this.isInteraction) { - if (typeof content === "string" || isInteractionReplyOptions(content)) { - this.msg = await this.interaction.reply(content); - return this.msg; - } - } else if (typeof content === "string" || isMessagePayload(content)) { - this.msg = await (this.message.channel as TextChannel).send(content); - return this.msg; - } - return this.msg; - } - - public async editMessage(content: string | MessagePayload | InteractionEditReplyOptions | MessageEditOptions): Promise { - if (this.isInteraction && this.msg) { - this.msg = await this.interaction.editReply(content); - return this.msg; - } - if (this.msg) { - this.msg = await this.msg.edit(content); - return this.msg; - } - return this.msg; - } - - public async sendDeferMessage(content: string | MessagePayload | MessageCreateOptions): Promise { - if (this.isInteraction) { - this.msg = await this.interaction.deferReply({ fetchReply: true }); - return this.msg; - } - - this.msg = await (this.message.channel as TextChannel).send(content); - return this.msg; - } - - public locale(key: string, ...args: any) { - return T(this.guildLocale, key, ...args); - } - - public async sendFollowUp(content: string | MessagePayload | MessageCreateOptions | InteractionReplyOptions): Promise { - if (this.isInteraction) { - if (typeof content === "string" || isInteractionReplyOptions(content)) { - await this.interaction.followUp(content); - } - } else if (typeof content === "string" || isMessagePayload(content)) { - this.msg = await (this.message.channel as TextChannel).send(content); - } - } - - public get deferred(): boolean | Promise { - return this.isInteraction ? this.interaction.deferred : !!this.msg; - } + public ctx: CommandInteraction | Message; + public interaction: CommandInteraction | null; + public message: Message | null; + public id: string; + public channelId: string; + public client: Lavamusic; + public author: User | null; + public channel: TextBasedChannel; + public guild: Guild; + public createdAt: Date; + public createdTimestamp: number; + public member: GuildMemberResolvable | GuildMember | APIInteractionGuildMember | null; + public args: any[]; + public msg: any; + public guildLocale: string | undefined; + + constructor(ctx: ChatInputCommandInteraction | Message, args: any[]) { + this.ctx = ctx; + this.interaction = ctx instanceof ChatInputCommandInteraction ? ctx : null; + this.message = ctx instanceof Message ? ctx : null; + this.channel = ctx.channel!; + this.id = ctx.id; + this.channelId = ctx.channelId; + this.client = ctx.client as Lavamusic; + this.author = ctx instanceof Message ? ctx.author : ctx.user; + this.guild = ctx.guild!; + this.createdAt = ctx.createdAt; + this.createdTimestamp = ctx.createdTimestamp; + this.member = ctx.member; + this.args = args; + this.setArgs(args); + this.setUpLocale(); + } + + private async setUpLocale(): Promise { + this.guildLocale = this.guild ? await this.client.db.getLanguage(this.guild.id) : 'en'; + } + + public get isInteraction(): boolean { + return this.ctx instanceof ChatInputCommandInteraction; + } + + public setArgs(args: any[]): void { + this.args = this.isInteraction ? args.map((arg: { value: any }) => arg.value) : args; + } + + public async sendMessage( + content: string | MessagePayload | MessageCreateOptions | InteractionReplyOptions, + ): Promise { + if (this.isInteraction) { + if (typeof content === 'string' || isInteractionReplyOptions(content)) { + this.msg = await this.interaction?.reply(content); + return this.msg; + } + } else if (typeof content === 'string' || isMessagePayload(content)) { + this.msg = await (this.message?.channel as TextChannel).send(content); + return this.msg; + } + return this.msg; + } + + public async editMessage( + content: string | MessagePayload | InteractionEditReplyOptions | MessageEditOptions, + ): Promise { + if (this.isInteraction && this.msg) { + this.msg = await this.interaction?.editReply(content); + return this.msg; + } + if (this.msg) { + this.msg = await this.msg.edit(content); + return this.msg; + } + return this.msg; + } + + public async sendDeferMessage(content: string | MessagePayload | MessageCreateOptions): Promise { + if (this.isInteraction) { + this.msg = await this.interaction?.deferReply({ fetchReply: true }); + return this.msg; + } + + this.msg = await (this.message?.channel as TextChannel).send(content); + return this.msg; + } + + public locale(key: string, ...args: any) { + if (!this.guildLocale) this.guildLocale = 'EnglishUs'; + return T(this.guildLocale, key, ...args); + } + + public async sendFollowUp( + content: string | MessagePayload | MessageCreateOptions | InteractionReplyOptions, + ): Promise { + if (this.isInteraction) { + if (typeof content === 'string' || isInteractionReplyOptions(content)) { + await this.interaction?.followUp(content); + } + } else if (typeof content === 'string' || isMessagePayload(content)) { + this.msg = await (this.message?.channel as TextChannel).send(content); + } + } + + public get deferred(): boolean | undefined { + return this.isInteraction ? this.interaction?.deferred : !!this.msg; + } + options = { + getRole: (name: string, required = true) => { + return this.interaction?.options.get(name, required)?.role; + }, + getMember: (name: string, required = true) => { + return this.interaction?.options.get(name, required)?.member; + }, + get: (name: string, required = true) => { + return this.interaction?.options.get(name, required); + }, + getChannel: (name: string, required = true) => { + return this.interaction?.options.get(name, required)?.channel; + }, + getSubCommand: () => { + return this.interaction?.options.data[0].name; + }, + }; } function isInteractionReplyOptions(content: any): content is InteractionReplyOptions { - return content instanceof Object; + return content instanceof Object; } function isMessagePayload(content: any): content is MessagePayload { - return content instanceof Object; + return content instanceof Object; } /** diff --git a/src/structures/Event.ts b/src/structures/Event.ts index 452be054e..5dab8d62c 100644 --- a/src/structures/Event.ts +++ b/src/structures/Event.ts @@ -1,37 +1,37 @@ -import type { ButtonInteraction, ClientEvents, Message } from "discord.js"; -import type { LavalinkManagerEvents, NodeManagerEvents } from "lavalink-client"; -import type Lavamusic from "./Lavamusic"; +import type { ButtonInteraction, ClientEvents, Message } from 'discord.js'; +import type { LavalinkManagerEvents, NodeManagerEvents } from 'lavalink-client'; +import type Lavamusic from './Lavamusic'; // custom client events setupSystem and setupButtons interface CustomClientEvents { - setupSystem: (message: Message) => void; - setupButtons: (interaction: ButtonInteraction) => void; + setupSystem: (message: Message) => void; + setupButtons: (interaction: ButtonInteraction) => void; } export type AllEvents = LavalinkManagerEvents & NodeManagerEvents & ClientEvents & CustomClientEvents; interface EventOptions { - name: keyof AllEvents; - one?: boolean; + name: keyof AllEvents; + one?: boolean; } export default class Event { - public client: Lavamusic; - public one: boolean; - public file: string; - public name: keyof AllEvents; - public fileName: string; + public client: Lavamusic; + public one: boolean; + public file: string; + public name: keyof AllEvents; + public fileName: string; - constructor(client: Lavamusic, file: string, options: EventOptions) { - this.client = client; - this.file = file; - this.name = options.name; - this.one = options.one ?? false; - this.fileName = file.split(".")[0]; - } + constructor(client: Lavamusic, file: string, options: EventOptions) { + this.client = client; + this.file = file; + this.name = options.name; + this.one = options.one ?? false; + this.fileName = file.split('.')[0]; + } - public async run(..._args: any): Promise { - return await Promise.resolve(); - } + public async run(..._args: any): Promise { + return await Promise.resolve(); + } } /** diff --git a/src/structures/I18n.ts b/src/structures/I18n.ts index b54d6597a..00a77cf48 100644 --- a/src/structures/I18n.ts +++ b/src/structures/I18n.ts @@ -1,50 +1,58 @@ -import i18n from "i18n"; +import i18n from 'i18n'; -import { Locale } from "discord.js"; -import defaultLanguage from "../config"; -import { Language } from "../types"; -import Logger from "./Logger"; +import { Locale } from 'discord.js'; +import defaultLanguage from '../config'; +import { Language } from '../types'; +import Logger from './Logger'; const logger = new Logger(); export function initI18n() { - i18n.configure({ - locales: Object.keys(Language), - defaultLocale: typeof defaultLanguage === "string" ? defaultLanguage : "EnglishUS", - directory: `${process.cwd()}/locales`, - retryInDefaultLocale: true, - objectNotation: true, - register: global, - logWarnFn: console.warn, - logErrorFn: console.error, - missingKeyFn: (_locale, value) => { - return value; - }, - mustacheConfig: { - tags: ["{", "}"], - disable: false, - }, - }); - - logger.info("I18n has been initialized"); + i18n.configure({ + locales: Object.keys(Language), + defaultLocale: typeof defaultLanguage === 'string' ? defaultLanguage : 'EnglishUS', + directory: `${process.cwd()}/locales`, + retryInDefaultLocale: true, + objectNotation: true, + register: global, + logWarnFn: console.warn, + logErrorFn: console.error, + missingKeyFn: (_locale, value) => { + return value; + }, + mustacheConfig: { + tags: ['{', '}'], + disable: false, + }, + }); + + logger.info('I18n has been initialized'); } export { i18n }; export function T(locale: string, text: string | i18n.TranslateOptions, ...params: any) { - i18n.setLocale(locale); - return i18n.__mf(text, ...params); + i18n.setLocale(locale); + return i18n.__mf(text, ...params); } -export function localization(lan: any, name: any, desc: any) { - return { - name: [Locale[lan], name], - description: [Locale[lan], T(lan, desc)], - }; +export function localization(lan: keyof typeof Locale, name: any, desc: any) { + return { + name: [Locale[lan], name], + description: [Locale[lan], T(lan, desc)], + }; } export function descriptionLocalization(name: any, text: any) { - return i18n.getLocales().map((locale) => localization(Locale[locale] || locale, name, text)); + return i18n.getLocales().map((locale: string) => { + // Check if the locale is a valid key of the Locale enum + if (locale in Locale) { + const localeValue = Locale[locale as keyof typeof Locale]; + return localization(localeValue as any, name, text); + } + // If locale is not in the enum, handle it accordingly + return localization(locale as any, name, text); // You can choose how to handle this case + }); } /** diff --git a/src/structures/LavalinkClient.ts b/src/structures/LavalinkClient.ts index a93faf896..a05b537e5 100644 --- a/src/structures/LavalinkClient.ts +++ b/src/structures/LavalinkClient.ts @@ -1,43 +1,43 @@ -import { LavalinkManager, type LavalinkNodeOptions, type SearchPlatform, type SearchResult } from "lavalink-client"; -import { autoPlayFunction, requesterTransformer } from "../utils/functions/player"; -import type Lavamusic from "./Lavamusic"; +import { LavalinkManager, type LavalinkNodeOptions, type SearchPlatform, type SearchResult } from 'lavalink-client'; +import { autoPlayFunction, requesterTransformer } from '../utils/functions/player'; +import type Lavamusic from './Lavamusic'; export default class LavalinkClient extends LavalinkManager { - public client: Lavamusic; - constructor(client: Lavamusic) { - super({ - nodes: client.env.NODES as LavalinkNodeOptions[], - sendToShard: (guildId, payload) => client.guilds.cache.get(guildId)?.shard?.send(payload), - queueOptions: { - maxPreviousTracks: 25, - }, - playerOptions: { - defaultSearchPlatform: client.env.SEARCH_ENGINE, - onDisconnect: { - autoReconnect: true, - destroyPlayer: false, - }, - requesterTransformer: requesterTransformer, - onEmptyQueue: { - autoPlayFunction, - }, - }, - }); - this.client = client; - } - /** - * Searches for a song and returns the tracks. - * @param query The query to search for. - * @param user The user who requested the search. - * @param source The source to search in. Defaults to youtube. - * @returns An array of tracks that match the query. - */ - public async search(query: string, user: unknown, source?: SearchPlatform): Promise { - const nodes = this.nodeManager.leastUsedNodes(); - const node = nodes[Math.floor(Math.random() * nodes.length)]; - const result = await node.search({ query, source }, user, false); - return result; - } + public client: Lavamusic; + constructor(client: Lavamusic) { + super({ + nodes: client.env.NODES as LavalinkNodeOptions[], + sendToShard: (guildId, payload) => client.guilds.cache.get(guildId)?.shard?.send(payload), + queueOptions: { + maxPreviousTracks: 25, + }, + playerOptions: { + defaultSearchPlatform: client.env.SEARCH_ENGINE, + onDisconnect: { + autoReconnect: true, + destroyPlayer: false, + }, + requesterTransformer: requesterTransformer, + onEmptyQueue: { + autoPlayFunction, + }, + }, + }); + this.client = client; + } + /** + * Searches for a song and returns the tracks. + * @param query The query to search for. + * @param user The user who requested the search. + * @param source The source to search in. Defaults to youtube. + * @returns An array of tracks that match the query. + */ + public async search(query: string, user: unknown, source?: SearchPlatform): Promise { + const nodes = this.nodeManager.leastUsedNodes(); + const node = nodes[Math.floor(Math.random() * nodes.length)]; + const result = await node.search({ query, source }, user, false); + return result; + } } /** diff --git a/src/structures/Lavamusic.ts b/src/structures/Lavamusic.ts index 2e9742def..211af8b16 100644 --- a/src/structures/Lavamusic.ts +++ b/src/structures/Lavamusic.ts @@ -1,207 +1,209 @@ -import fs from "node:fs"; -import path from "node:path"; -import { Api } from "@top-gg/sdk"; +import fs from 'node:fs'; +import path from 'node:path'; +import { Api } from '@top-gg/sdk'; import { - ApplicationCommandType, - Client, - Collection, - EmbedBuilder, - Events, - type Interaction, - PermissionsBitField, - REST, - type RESTPostAPIChatInputApplicationCommandsJSONBody, - Routes, -} from "discord.js"; -import { Locale } from "discord.js"; -import config from "../config"; -import ServerData from "../database/server"; -import { env } from "../env"; -import loadPlugins from "../plugin/index"; -import { Utils } from "../utils/Utils"; -import { T, i18n, initI18n, localization } from "./I18n"; -import LavalinkClient from "./LavalinkClient"; -import Logger from "./Logger"; -import type { Command } from "./index"; + ApplicationCommandType, + Client, + Collection, + EmbedBuilder, + Events, + type Interaction, + PermissionsBitField, + REST, + type RESTPostAPIChatInputApplicationCommandsJSONBody, + Routes, +} from 'discord.js'; +import { Locale } from 'discord.js'; +import config from '../config'; +import ServerData from '../database/server'; +import { env } from '../env'; +import loadPlugins from '../plugin/index'; +import { Utils } from '../utils/Utils'; +import { T, i18n, initI18n, localization } from './I18n'; +import LavalinkClient from './LavalinkClient'; +import Logger from './Logger'; +import type { Command } from './index'; export default class Lavamusic extends Client { - public commands: Collection = new Collection(); - public aliases: Collection = new Collection(); - public db = new ServerData(); - public cooldown: Collection = new Collection(); - public config = config; - public logger: Logger = new Logger(); - public readonly emoji = config.emoji; - public readonly color = config.color; - private body: RESTPostAPIChatInputApplicationCommandsJSONBody[] = []; - public topGG!: Api; - public utils = Utils; - public env: typeof env = env; - public manager: LavalinkClient; - public embed(): EmbedBuilder { - return new EmbedBuilder(); - } - - public async start(token: string): Promise { - initI18n(); - if (env.TOPGG) { - this.topGG = new Api(env.TOPGG); - } else { - this.logger.warn("Top.gg token not found!"); - } - this.manager = new LavalinkClient(this); - await this.loadCommands(); - this.logger.info("Successfully loaded commands!"); - await this.loadEvents(); - this.logger.info("Successfully loaded events!"); - loadPlugins(this); - await this.login(token); - - this.on(Events.InteractionCreate, async (interaction: Interaction) => { - if (interaction.isButton() && interaction.guildId) { - const setup = await this.db.getSetup(interaction.guildId); - if (setup && interaction.channelId === setup.textId && interaction.message.id === setup.messageId) { - this.emit("setupButtons", interaction); - } - } - }); - } - - private async loadCommands(): Promise { - const commandsPath = fs.readdirSync(path.join(__dirname, "../commands")); - - for (const dir of commandsPath) { - const commandFiles = fs.readdirSync(path.join(__dirname, "../commands", dir)).filter((file) => file.endsWith(".js")); - - for (const file of commandFiles) { - const cmdModule = require(`../commands/${dir}/${file}`); - const command: Command = new cmdModule.default(this, file); - command.category = dir; - - this.commands.set(command.name, command); - command.aliases.forEach((alias: string) => { - this.aliases.set(alias, command.name); - }); - - if (command.slashCommand) { - const data: RESTPostAPIChatInputApplicationCommandsJSONBody = { - name: command.name, - description: T(Locale.EnglishUS, command.description.content), - type: ApplicationCommandType.ChatInput, - options: command.options || [], - default_member_permissions: - Array.isArray(command.permissions.user) && command.permissions.user.length > 0 - ? PermissionsBitField.resolve(command.permissions.user as any).toString() - : null, - name_localizations: null, - description_localizations: null, - }; - - const localizations = []; - i18n.getLocales().map((locale) => { - localizations.push(localization(locale, command.name, command.description.content)); - }); - - for (const localization of localizations) { - const [language, name] = localization.name; - const [language2, description] = localization.description; - data.name_localizations = { - ...data.name_localizations, - [language]: name, - }; - data.description_localizations = { - ...data.description_localizations, - [language2]: description, - }; - } - - if (command.options.length > 0) { - command.options.map((option) => { - const optionsLocalizations = []; - i18n.getLocales().map((locale) => { - optionsLocalizations.push(localization(locale, option.name, option.description)); - }); - - for (const localization of optionsLocalizations) { - const [language, name] = localization.name; - const [language2, description] = localization.description; - option.name_localizations = { - ...option.name_localizations, - [language]: name, - }; - option.description_localizations = { - ...option.description_localizations, - [language2]: description, - }; - } - option.description = T(Locale.EnglishUS, option.description); - }); - - data.options.map((option) => { - if ("options" in option && option.options.length > 0) { - option.options.map((subOption) => { - const subOptionsLocalizations = []; - i18n.getLocales().map((locale) => { - subOptionsLocalizations.push(localization(locale, subOption.name, subOption.description)); - }); - - for (const localization of subOptionsLocalizations) { - const [language, name] = localization.name; - const [language2, description] = localization.description; - subOption.name_localizations = { - ...subOption.name_localizations, - [language]: name, - }; - subOption.description_localizations = { - ...subOption.description_localizations, - [language2]: description, - }; - } - subOption.description = T(Locale.EnglishUS, subOption.description); - }); - } - }); - } - this.body.push(data); - } - } - } - } - - public async deployCommands(guildId?: string): Promise { - const route = guildId - ? Routes.applicationGuildCommands(this.user?.id ?? "", guildId) - : Routes.applicationCommands(this.user?.id ?? ""); - - try { - const rest = new REST({ version: "10" }).setToken(env.TOKEN ?? ""); - await rest.put(route, { body: this.body }); - this.logger.info("Successfully deployed slash commands!"); - } catch (error) { - this.logger.error(error); - } - } - - private async loadEvents(): Promise { - const eventsPath = fs.readdirSync(path.join(__dirname, "..", "events")); - - for (const dir of eventsPath) { - const eventFiles = fs.readdirSync(path.join(__dirname, "..", "events", dir)).filter((file) => file.endsWith(".js")); - - for (const file of eventFiles) { - const eventModule = require(`../events/${dir}/${file}`); - const event = new eventModule.default(this, file); - - if (dir === "player") { - this.manager.on(event.name, (...args) => event.run(...args)); - } else if (dir === "node") { - this.manager.nodeManager.on(event.name, (...args) => event.run(...args)); - } else { - this.on(event.name, (...args) => event.run(...args)); - } - } - } - } + public commands: Collection = new Collection(); + public aliases: Collection = new Collection(); + public db = new ServerData(); + public cooldown: Collection = new Collection(); + public config = config; + public logger: Logger = new Logger(); + public readonly emoji = config.emoji; + public readonly color = config.color; + private body: RESTPostAPIChatInputApplicationCommandsJSONBody[] = []; + public topGG!: Api; + public utils = Utils; + public env: typeof env = env; + public manager!: LavalinkClient; + public embed(): EmbedBuilder { + return new EmbedBuilder(); + } + + public async start(token: string): Promise { + initI18n(); + if (env.TOPGG) { + this.topGG = new Api(env.TOPGG); + } else { + this.logger.warn('Top.gg token not found!'); + } + this.manager = new LavalinkClient(this); + await this.loadCommands(); + this.logger.info('Successfully loaded commands!'); + await this.loadEvents(); + this.logger.info('Successfully loaded events!'); + loadPlugins(this); + await this.login(token); + + this.on(Events.InteractionCreate, async (interaction: Interaction) => { + if (interaction.isButton() && interaction.guildId) { + const setup = await this.db.getSetup(interaction.guildId); + if (setup && interaction.channelId === setup.textId && interaction.message.id === setup.messageId) { + this.emit('setupButtons', interaction); + } + } + }); + } + + private async loadCommands(): Promise { + const commandsPath = fs.readdirSync(path.join(__dirname, '../commands')); + + for (const dir of commandsPath) { + const commandFiles = fs + .readdirSync(path.join(__dirname, '../commands', dir)) + .filter(file => file.endsWith('.js')); + + for (const file of commandFiles) { + const cmdModule = require(`../commands/${dir}/${file}`); + const command: Command = new cmdModule.default(this, file); + command.category = dir; + + this.commands.set(command.name, command); + command.aliases.forEach((alias: string) => { + this.aliases.set(alias, command.name); + }); + + if (command.slashCommand) { + const data: RESTPostAPIChatInputApplicationCommandsJSONBody = { + name: command.name, + description: T(Locale.EnglishUS, command.description.content), + type: ApplicationCommandType.ChatInput, + options: command.options || [], + default_member_permissions: + Array.isArray(command.permissions.user) && command.permissions.user.length > 0 + ? PermissionsBitField.resolve(command.permissions.user as any).toString() + : null, + name_localizations: null, + description_localizations: null, + }; + + const localizations: { name: any[]; description: string[] }[] = []; + i18n.getLocales().map((locale: any) => { + localizations.push(localization(locale, command.name, command.description.content)); + }); + + for (const localization of localizations) { + const [language, name] = localization.name; + const [language2, description] = localization.description; + data.name_localizations = { + ...data.name_localizations, + [language]: name, + }; + data.description_localizations = { + ...data.description_localizations, + [language2]: description, + }; + } + + if (command.options.length > 0) { + command.options.map(option => { + const optionsLocalizations: { name: any[]; description: string[] }[] = []; + i18n.getLocales().map((locale: any) => { + optionsLocalizations.push(localization(locale, option.name, option.description)); + }); + + for (const localization of optionsLocalizations) { + const [language, name] = localization.name; + const [language2, description] = localization.description; + option.name_localizations = { + ...option.name_localizations, + [language]: name, + }; + option.description_localizations = { + ...option.description_localizations, + [language2]: description, + }; + } + option.description = T(Locale.EnglishUS, option.description); + }); + + data.options?.map(option => { + if ('options' in option && option.options!.length > 0) { + option.options?.map(subOption => { + const subOptionsLocalizations: { name: any[]; description: string[] }[] = []; + i18n.getLocales().map((locale: any) => { + subOptionsLocalizations.push(localization(locale, subOption.name, subOption.description)); + }); + + for (const localization of subOptionsLocalizations) { + const [language, name] = localization.name; + const [language2, description] = localization.description; + subOption.name_localizations = { + ...subOption.name_localizations, + [language]: name, + }; + subOption.description_localizations = { + ...subOption.description_localizations, + [language2]: description, + }; + } + subOption.description = T(Locale.EnglishUS, subOption.description); + }); + } + }); + } + this.body.push(data); + } + } + } + } + + public async deployCommands(guildId?: string): Promise { + const route = guildId + ? Routes.applicationGuildCommands(this.user?.id ?? '', guildId) + : Routes.applicationCommands(this.user?.id ?? ''); + + try { + const rest = new REST({ version: '10' }).setToken(env.TOKEN ?? ''); + await rest.put(route, { body: this.body }); + this.logger.info('Successfully deployed slash commands!'); + } catch (error) { + this.logger.error(error); + } + } + + private async loadEvents(): Promise { + const eventsPath = fs.readdirSync(path.join(__dirname, '..', 'events')); + + for (const dir of eventsPath) { + const eventFiles = fs.readdirSync(path.join(__dirname, '..', 'events', dir)).filter(file => file.endsWith('.js')); + + for (const file of eventFiles) { + const eventModule = require(`../events/${dir}/${file}`); + const event = new eventModule.default(this, file); + + if (dir === 'player') { + this.manager.on(event.name, (...args: any) => event.run(...args)); + } else if (dir === 'node') { + this.manager.nodeManager.on(event.name, (...args: any) => event.run(...args)); + } else { + this.on(event.name, (...args) => event.run(...args)); + } + } + } + } } /** diff --git a/src/structures/Logger.ts b/src/structures/Logger.ts index b097b9cf9..e74f39a48 100644 --- a/src/structures/Logger.ts +++ b/src/structures/Logger.ts @@ -1,59 +1,59 @@ -import pkg, { type SignaleOptions } from "signale"; +import pkg, { type SignaleOptions } from 'signale'; const { Signale } = pkg; const options: SignaleOptions = { - disabled: false, - interactive: false, - logLevel: "info", - scope: "Lavamusic", - types: { - info: { - badge: "ℹ", - color: "blue", - label: "info", - }, - warn: { - badge: "⚠", - color: "yellow", - label: "warn", - }, - error: { - badge: "✖", - color: "red", - label: "error", - }, - debug: { - badge: "🐛", - color: "magenta", - label: "debug", - }, - success: { - badge: "✔", - color: "green", - label: "success", - }, - log: { - badge: "📝", - color: "white", - label: "log", - }, - pause: { - badge: "⏸", - color: "yellow", - label: "pause", - }, - start: { - badge: "▶", - color: "green", - label: "start", - }, - }, + disabled: false, + interactive: false, + logLevel: 'info', + scope: 'Lavamusic', + types: { + info: { + badge: 'ℹ', + color: 'blue', + label: 'info', + }, + warn: { + badge: '⚠', + color: 'yellow', + label: 'warn', + }, + error: { + badge: '✖', + color: 'red', + label: 'error', + }, + debug: { + badge: '🐛', + color: 'magenta', + label: 'debug', + }, + success: { + badge: '✔', + color: 'green', + label: 'success', + }, + log: { + badge: '📝', + color: 'white', + label: 'log', + }, + pause: { + badge: '⏸', + color: 'yellow', + label: 'pause', + }, + start: { + badge: '▶', + color: 'green', + label: 'start', + }, + }, }; export default class Logger extends Signale { - constructor() { - super(options); - } + constructor() { + super(options); + } } /** diff --git a/src/structures/index.ts b/src/structures/index.ts index 3c11bc944..10179f692 100644 --- a/src/structures/index.ts +++ b/src/structures/index.ts @@ -1,7 +1,7 @@ -import Command from "./Command"; -import Context from "./Context"; -import Event from "./Event"; -import Lavamusic from "./Lavamusic"; +import Command from './Command'; +import Context from './Context'; +import Event from './Event'; +import Lavamusic from './Lavamusic'; export { Event, Command, Lavamusic, Context }; diff --git a/src/types.ts b/src/types.ts index 9ddde1a5b..f1959e169 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,86 +1,86 @@ export enum SearchEngine { - YouTube = "ytsearch", - YouTubeMusic = "ytmsearch", - Spotify = "spsearch", - Deezer = "dzsearch", - Apple = "amsearch", - SoundCloud = "scsearch", - Yandex = "ymsearch", - JioSaavn = "jssearch", + YouTube = 'ytsearch', + YouTubeMusic = 'ytmsearch', + Spotify = 'spsearch', + Deezer = 'dzsearch', + Apple = 'amsearch', + SoundCloud = 'scsearch', + Yandex = 'ymsearch', + JioSaavn = 'jssearch', } export enum Language { - // Bulgarian = "Bulgarian", - ChineseCN = "ChineseCN", - ChineseTW = "ChineseTW", - // Croatian = "Croatian", - // Czech = "Czech", - // Danish = "Danish", - // Dutch = "Dutch", - // EnglishGB = "EnglishGB", - EnglishUS = "EnglishUS", - // Finnish = "Finnish", - French = "French", - German = "German", - // Greek = "Greek", - Hindi = "Hindi", - // Hungarian = "Hungarian", - Indonesian = "Indonesian", - // Italian = "Italian", - Japanese = "Japanese", - Korean = "Korean", - // Lithuanian = "Lithuanian", - Norwegian = "Norwegian", - Polish = "Polish", - // PortugueseBR = "PortugueseBR", - // Romanian = "Romanian", - Russian = "Russian", - SpanishES = "SpanishES", - // Swedish = "Swedish", - // Thai = "Thai", - // Turkish = "Turkish", - // Ukrainian = "Ukrainian", - Vietnamese = "Vietnamese", + // Bulgarian = "Bulgarian", + ChineseCN = 'ChineseCN', + ChineseTW = 'ChineseTW', + // Croatian = "Croatian", + // Czech = "Czech", + // Danish = "Danish", + // Dutch = "Dutch", + // EnglishGB = "EnglishGB", + EnglishUS = 'EnglishUS', + // Finnish = "Finnish", + French = 'French', + German = 'German', + // Greek = "Greek", + Hindi = 'Hindi', + // Hungarian = "Hungarian", + Indonesian = 'Indonesian', + // Italian = "Italian", + Japanese = 'Japanese', + Korean = 'Korean', + // Lithuanian = "Lithuanian", + Norwegian = 'Norwegian', + Polish = 'Polish', + // PortugueseBR = "PortugueseBR", + // Romanian = "Romanian", + Russian = 'Russian', + SpanishES = 'SpanishES', + // Swedish = "Swedish", + // Thai = "Thai", + // Turkish = "Turkish", + // Ukrainian = "Ukrainian", + Vietnamese = 'Vietnamese', } export const LocaleFlags = { - // [Language.Bulgarian]: "🇧🇬", - [Language.ChineseCN]: "🇨🇳", - [Language.ChineseTW]: "🇹🇼", - // [Language.Croatian]: "🇭🇷", - // [Language.Czech]: "🇨🇿", - // [Language.Danish]: "🇩🇰", - // [Language.Dutch]: "🇳🇱", - // [Language.EnglishGB]: "🇬🇧", - [Language.EnglishUS]: "🇺🇸", - // [Language.Finnish]: "🇫🇮", - [Language.French]: "🇫🇷", - [Language.German]: "🇩🇪", - // [Language.Greek]: "🇬🇷", - [Language.Hindi]: "🇮🇳", - // [Language.Hungarian]: "🇭🇺", - [Language.Indonesian]: "🇮🇩", - // [Language.Italian]: "🇮🇹", - [Language.Japanese]: "🇯🇵", - [Language.Korean]: "🇰🇷", - // [Language.Lithuanian]: "🇱🇹", - [Language.Norwegian]: "🇳🇴", - [Language.Polish]: "🇵🇱", - // [Language.PortugueseBR]: "🇧🇷", - // [Language.Romanian]: "🇷🇴", - [Language.Russian]: "🇷🇺", - [Language.SpanishES]: "🇪🇸", - // [Language.Swedish]: "🇸🇪", - // [Language.Thai]: "🇹🇭", - // [Language.Turkish]: "🇹🇷", - // [Language.Ukrainian]: "🇺🇦", - [Language.Vietnamese]: "🇻🇳", + // [Language.Bulgarian]: "🇧🇬", + [Language.ChineseCN]: '🇨🇳', + [Language.ChineseTW]: '🇹🇼', + // [Language.Croatian]: "🇭🇷", + // [Language.Czech]: "🇨🇿", + // [Language.Danish]: "🇩🇰", + // [Language.Dutch]: "🇳🇱", + // [Language.EnglishGB]: "🇬🇧", + [Language.EnglishUS]: '🇺🇸', + // [Language.Finnish]: "🇫🇮", + [Language.French]: '🇫🇷', + [Language.German]: '🇩🇪', + // [Language.Greek]: "🇬🇷", + [Language.Hindi]: '🇮🇳', + // [Language.Hungarian]: "🇭🇺", + [Language.Indonesian]: '🇮🇩', + // [Language.Italian]: "🇮🇹", + [Language.Japanese]: '🇯🇵', + [Language.Korean]: '🇰🇷', + // [Language.Lithuanian]: "🇱🇹", + [Language.Norwegian]: '🇳🇴', + [Language.Polish]: '🇵🇱', + // [Language.PortugueseBR]: "🇧🇷", + // [Language.Romanian]: "🇷🇴", + [Language.Russian]: '🇷🇺', + [Language.SpanishES]: '🇪🇸', + // [Language.Swedish]: "🇸🇪", + // [Language.Thai]: "🇹🇭", + // [Language.Turkish]: "🇹🇷", + // [Language.Ukrainian]: "🇺🇦", + [Language.Vietnamese]: '🇻🇳', }; export interface Requester { - id: string; - username: string; - discriminator?: string; - avatarURL?: string; + id: string; + username: string; + discriminator?: string; + avatarURL?: string; } /** diff --git a/src/utils/BotLog.ts b/src/utils/BotLog.ts index 5824b381e..2add8b17e 100644 --- a/src/utils/BotLog.ts +++ b/src/utils/BotLog.ts @@ -1,25 +1,25 @@ -import type { TextChannel } from "discord.js"; -import type { Lavamusic } from "../structures/index"; +import type { TextChannel } from 'discord.js'; +import type { Lavamusic } from '../structures/index'; export default class BotLog { - public static send(client: Lavamusic, message: string, type: "error" | "warn" | "info" | "success" = "info"): void { - if (!client?.channels.cache && client.env.LOG_CHANNEL_ID) return; + public static send(client: Lavamusic, message: string, type: 'error' | 'warn' | 'info' | 'success' = 'info'): void { + if (!client?.channels.cache && client.env.LOG_CHANNEL_ID) return; - const channel = client.channels.cache.get(client.env.LOG_CHANNEL_ID!) as TextChannel; - if (!channel) return; + const channel = client.channels.cache.get(client.env.LOG_CHANNEL_ID!) as TextChannel; + if (!channel) return; - const colors = { - error: 0xff0000, - warn: 0xffff00, - info: 0x00ff00, - success: 0x00ff00, - } as const; + const colors = { + error: 0xff0000, + warn: 0xffff00, + info: 0x00ff00, + success: 0x00ff00, + } as const; - const color = colors[type]; - const embed = client.embed().setColor(color).setDescription(message).setTimestamp(); + const color = colors[type]; + const embed = client.embed().setColor(color).setDescription(message).setTimestamp(); - channel.send({ embeds: [embed] }).catch(() => {}); - } + channel.send({ embeds: [embed] }).catch(() => {}); + } } /** diff --git a/src/utils/Buttons.ts b/src/utils/Buttons.ts index c0ff18638..a8eb8335b 100644 --- a/src/utils/Buttons.ts +++ b/src/utils/Buttons.ts @@ -1,76 +1,76 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type EmojiIdentifierResolvable } from "discord.js"; -import type { Player } from "lavalink-client"; -import type { Lavamusic } from "../structures/index"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type EmojiIdentifierResolvable } from 'discord.js'; +import type { Player } from 'lavalink-client'; +import type { Lavamusic } from '../structures/index'; function getButtons(player: Player, client: Lavamusic): ActionRowBuilder[] { - const buttonData = [ - { - customId: "PREV_BUT", - emoji: client.emoji.previous, - style: ButtonStyle.Secondary, - }, - { - customId: "REWIND_BUT", - emoji: client.emoji.rewind, - style: ButtonStyle.Secondary, - }, - { - customId: "PAUSE_BUT", - emoji: player?.paused ? client.emoji.resume : client.emoji.pause, - style: player?.paused ? ButtonStyle.Success : ButtonStyle.Secondary, - }, - { - customId: "FORWARD_BUT", - emoji: client.emoji.forward, - style: ButtonStyle.Secondary, - }, - { - customId: "SKIP_BUT", - emoji: client.emoji.skip, - style: ButtonStyle.Secondary, - }, - { - customId: "LOW_VOL_BUT", - emoji: client.emoji.voldown, - style: ButtonStyle.Secondary, - }, - { - customId: "LOOP_BUT", - emoji: client.emoji.loop.none, - style: ButtonStyle.Secondary, - }, - { - customId: "STOP_BUT", - emoji: client.emoji.stop, - style: ButtonStyle.Danger, - }, - { - customId: "SHUFFLE_BUT", - emoji: client.emoji.shuffle, - style: ButtonStyle.Secondary, - }, - { - customId: "HIGH_VOL_BUT", - emoji: client.emoji.volup, - style: ButtonStyle.Secondary, - }, - ]; + const buttonData = [ + { + customId: 'PREV_BUT', + emoji: client.emoji.previous, + style: ButtonStyle.Secondary, + }, + { + customId: 'REWIND_BUT', + emoji: client.emoji.rewind, + style: ButtonStyle.Secondary, + }, + { + customId: 'PAUSE_BUT', + emoji: player?.paused ? client.emoji.resume : client.emoji.pause, + style: player?.paused ? ButtonStyle.Success : ButtonStyle.Secondary, + }, + { + customId: 'FORWARD_BUT', + emoji: client.emoji.forward, + style: ButtonStyle.Secondary, + }, + { + customId: 'SKIP_BUT', + emoji: client.emoji.skip, + style: ButtonStyle.Secondary, + }, + { + customId: 'LOW_VOL_BUT', + emoji: client.emoji.voldown, + style: ButtonStyle.Secondary, + }, + { + customId: 'LOOP_BUT', + emoji: client.emoji.loop.none, + style: ButtonStyle.Secondary, + }, + { + customId: 'STOP_BUT', + emoji: client.emoji.stop, + style: ButtonStyle.Danger, + }, + { + customId: 'SHUFFLE_BUT', + emoji: client.emoji.shuffle, + style: ButtonStyle.Secondary, + }, + { + customId: 'HIGH_VOL_BUT', + emoji: client.emoji.volup, + style: ButtonStyle.Secondary, + }, + ]; - return buttonData.reduce((rows, { customId, emoji, style }, index) => { - if (index % 5 === 0) rows.push(new ActionRowBuilder()); + return buttonData.reduce((rows, { customId, emoji, style }, index) => { + if (index % 5 === 0) rows.push(new ActionRowBuilder()); - let emojiFormat: EmojiIdentifierResolvable; - if (typeof emoji === "string" && emoji.startsWith("<:")) { - const match = emoji.match(/^<:\w+:(\d+)>$/); - emojiFormat = match ? match[1] : emoji; - } else { - emojiFormat = emoji; - } + let emojiFormat: EmojiIdentifierResolvable; + if (typeof emoji === 'string' && emoji.startsWith('<:')) { + const match = emoji.match(/^<:\w+:(\d+)>$/); + emojiFormat = match ? match[1] : emoji; + } else { + emojiFormat = emoji; + } - const button = new ButtonBuilder().setCustomId(customId).setEmoji(emojiFormat).setStyle(style); - rows[rows.length - 1].addComponents(button); - return rows; - }, [] as ActionRowBuilder[]); + const button = new ButtonBuilder().setCustomId(customId).setEmoji(emojiFormat).setStyle(style); + rows[rows.length - 1].addComponents(button); + return rows; + }, [] as ActionRowBuilder[]); } export { getButtons }; diff --git a/src/utils/SetupSystem.ts b/src/utils/SetupSystem.ts index 993ff10e5..ae6369689 100644 --- a/src/utils/SetupSystem.ts +++ b/src/utils/SetupSystem.ts @@ -1,10 +1,10 @@ -import { type ColorResolvable, EmbedBuilder, type Message, type TextChannel } from "discord.js"; +import { type ColorResolvable, EmbedBuilder, type Message, type TextChannel } from 'discord.js'; -import type { Player, Track } from "lavalink-client"; -import { T } from "../structures/I18n"; -import type { Lavamusic } from "../structures/index"; -import type { Requester } from "../types"; -import { getButtons } from "./Buttons"; +import type { Player, Track } from 'lavalink-client'; +import { T } from '../structures/I18n'; +import type { Lavamusic } from '../structures/index'; +import type { Requester } from '../types'; +import { getButtons } from './Buttons'; /** * A function that will generate an embed based on the player's current track. @@ -15,25 +15,26 @@ import { getButtons } from "./Buttons"; * @returns The modified embed. */ function neb(embed: EmbedBuilder, player: Player, client: Lavamusic, locale: string): EmbedBuilder { - if (!player?.queue.current?.info) return embed; - const iconUrl = client.config.icons[player.queue.current.info.sourceName] || client.user.displayAvatarURL({ extension: "png" }); - const icon = player.queue.current.info.artworkUrl || client.config.links.img; + if (!player?.queue.current?.info) return embed; + const iconUrl = + client.config.icons[player.queue.current.info.sourceName] || client.user!.displayAvatarURL({ extension: 'png' }); + const icon = player.queue.current.info.artworkUrl || client.config.links.img; - const description = T(locale, "player.setupStart.description", { - title: player.queue.current.info.title, - uri: player.queue.current.info.uri, - author: player.queue.current.info.author, - length: client.utils.formatTime(player.queue.current.info.duration), - requester: (player.queue.current.requester as Requester).id, - }); - return embed - .setAuthor({ - name: T(locale, "player.setupStart.now_playing"), - iconURL: iconUrl, - }) - .setDescription(description) - .setImage(icon) - .setColor(client.color.main); + const description = T(locale, 'player.setupStart.description', { + title: player.queue.current.info.title, + uri: player.queue.current.info.uri, + author: player.queue.current.info.author, + length: client.utils.formatTime(player.queue.current.info.duration), + requester: (player.queue.current.requester as Requester).id, + }); + return embed + .setAuthor({ + name: T(locale, 'player.setupStart.now_playing'), + iconURL: iconUrl, + }) + .setDescription(description) + .setImage(icon) + .setColor(client.color.main); } /** @@ -46,75 +47,79 @@ function neb(embed: EmbedBuilder, player: Player, client: Lavamusic, locale: str * @returns A promise that resolves when the function is done. */ async function setupStart(client: Lavamusic, query: string, player: Player, message: Message): Promise { - let m: Message; - const embed = client.embed(); - const n = client.embed().setColor(client.color.main); - const data = await client.db.getSetup(message.guild.id); - const locale = await client.db.getLanguage(message.guildId); - try { - if (data) - m = await message.channel.messages.fetch({ - message: data.messageId, - cache: true, - }); - } catch (error) { - client.logger.error(error); - } - if (m) { - try { - if (message.inGuild()) { - const res = await player.search(query, message.author); + let m: Message | undefined; + const embed = client.embed(); + const n = client.embed().setColor(client.color.main); + const data = await client.db.getSetup(message.guild!.id); + const locale = await client.db.getLanguage(message.guildId!); + try { + if (data) + m = await message.channel.messages.fetch({ + message: data.messageId, + cache: true, + }); + } catch (error) { + client.logger.error(error); + } + if (m) { + try { + if (message.inGuild()) { + const res = await player.search(query, message.author); - switch (res.loadType) { - case "empty": - case "error": - await message.channel - .send({ - embeds: [embed.setColor(client.color.red).setDescription(T(locale, "player.setupStart.error_searching"))], - }) - .then((msg) => setTimeout(() => msg.delete(), 5000)); - break; - case "search": - case "track": - player.queue.add(res.tracks[0]); - await message.channel - .send({ - embeds: [ - embed.setColor(client.color.main).setDescription( - T(locale, "player.setupStart.added_to_queue", { - title: res.tracks[0].info.title, - uri: res.tracks[0].info.uri, - }), - ), - ], - }) - .then((msg) => setTimeout(() => msg.delete(), 5000)); - neb(n, player, client, locale); - await m.edit({ embeds: [n] }).catch(() => {}); - break; - case "playlist": - player.queue.add(res.tracks); - await message.channel - .send({ - embeds: [ - embed - .setColor(client.color.main) - .setDescription( - T(locale, "player.setupStart.added_playlist_to_queue", { length: res.tracks.length }), - ), - ], - }) - .then((msg) => setTimeout(() => msg.delete(), 5000)); - neb(n, player, client, locale); - await m.edit({ embeds: [n] }).catch(() => {}); - break; - } - if (!player.playing) await player.play(); - } - } catch (error) { - client.logger.error(error); - } - } + switch (res.loadType) { + case 'empty': + case 'error': + await message.channel + .send({ + embeds: [ + embed.setColor(client.color.red).setDescription(T(locale, 'player.setupStart.error_searching')), + ], + }) + .then(msg => setTimeout(() => msg.delete(), 5000)); + break; + case 'search': + case 'track': { + player.queue.add(res.tracks[0]); + await message.channel + .send({ + embeds: [ + embed.setColor(client.color.main).setDescription( + T(locale, 'player.setupStart.added_to_queue', { + title: res.tracks[0].info.title, + uri: res.tracks[0].info.uri, + }), + ), + ], + }) + .then(msg => setTimeout(() => msg.delete(), 5000)); + neb(n, player, client, locale); + await m.edit({ embeds: [n] }).catch(() => {}); + break; + } + case 'playlist': { + player.queue.add(res.tracks); + await message.channel + .send({ + embeds: [ + embed + .setColor(client.color.main) + .setDescription( + T(locale, 'player.setupStart.added_playlist_to_queue', { length: res.tracks.length }), + ), + ], + }) + .then(msg => setTimeout(() => msg.delete(), 5000)); + neb(n, player, client, locale); + await m.edit({ embeds: [n] }).catch(() => {}); + break; + } + } + if (!player.playing) await player.play(); + } + } catch (error) { + client.logger.error(error); + } + } } /** @@ -128,160 +133,163 @@ async function setupStart(client: Lavamusic, query: string, player: Player, mess * @returns A promise that resolves when the function is done. */ async function trackStart( - msgId: any, - channel: TextChannel, - player: Player, - track: Track, - client: Lavamusic, - locale: string, + msgId: any, + channel: TextChannel, + player: Player, + track: Track, + client: Lavamusic, + locale: string, ): Promise { - const icon = player.queue.current ? player.queue.current.info.artworkUrl : client.config.links.img; - let m: Message; + const icon = player.queue.current ? player.queue.current.info.artworkUrl : client.config.links.img; + let m: Message | undefined; - try { - m = await channel.messages.fetch({ message: msgId, cache: true }); - } catch (error) { - client.logger.error(error); - } + try { + m = await channel.messages.fetch({ message: msgId, cache: true }); + } catch (error) { + client.logger.error(error); + } - const iconUrl = client.config.icons[player.queue.current.info.sourceName] || client.user.displayAvatarURL({ extension: "png" }); - const description = T(locale, "player.setupStart.description", { - title: track.info.title, - uri: track.info.uri, - author: track.info.author, - length: client.utils.formatTime(track.info.duration), - requester: (player.queue.current.requester as Requester).id, - }); + const iconUrl = + client.config.icons[player.queue.current!.info.sourceName] || client.user!.displayAvatarURL({ extension: 'png' }); + const description = T(locale, 'player.setupStart.description', { + title: track.info.title, + uri: track.info.uri, + author: track.info.author, + length: client.utils.formatTime(track.info.duration), + requester: (player.queue.current!.requester as Requester).id, + }); - const embed = client - .embed() - .setAuthor({ - name: T(locale, "player.setupStart.now_playing"), - iconURL: iconUrl, - }) - .setColor(client.color.main) - .setDescription(description) - .setImage(icon); + const embed = client + .embed() + .setAuthor({ + name: T(locale, 'player.setupStart.now_playing'), + iconURL: iconUrl, + }) + .setColor(client.color.main) + .setDescription(description) + .setImage(icon); - if (m) { - await m - .edit({ - embeds: [embed], - components: getButtons(player, client).map((b) => { - b.components.forEach((c) => c.setDisabled(!player?.queue.current)); - return b; - }), - }) - .catch(() => {}); - } else { - await channel - .send({ - embeds: [embed], - components: getButtons(player, client).map((b) => { - b.components.forEach((c) => c.setDisabled(!player?.queue.current)); - return b; - }), - }) - .then((msg) => { - client.db.setSetup(msg.guild.id, msg.id, msg.channel.id); - }) - .catch(() => {}); - } + if (m) { + await m + .edit({ + embeds: [embed], + components: getButtons(player, client).map(b => { + b.components.forEach(c => c.setDisabled(!player?.queue.current)); + return b; + }), + }) + .catch(() => {}); + } else { + await channel + .send({ + embeds: [embed], + components: getButtons(player, client).map(b => { + b.components.forEach(c => c.setDisabled(!player?.queue.current)); + return b; + }), + }) + .then(msg => { + client.db.setSetup(msg.guild.id, msg.id, msg.channel.id); + }) + .catch(() => {}); + } } async function updateSetup(client: Lavamusic, guild: any, locale: string): Promise { - const setup = await client.db.getSetup(guild.id); - let m: Message; - if (setup?.textId) { - const textChannel = guild.channels.cache.get(setup.textId) as TextChannel; - if (!textChannel) return; - try { - m = await textChannel.messages.fetch({ - message: setup.messageId, - cache: true, - }); - } catch (error) { - client.logger.error(error); - } - } - if (m) { - const player = client.manager.getPlayer(guild.id); - if (player?.queue.current) { - const iconUrl = client.config.icons[player.queue.current.info.sourceName] || client.user.displayAvatarURL({ extension: "png" }); - const description = T(locale, "player.setupStart.description", { - title: player.queue.current.info.title, - uri: player.queue.current.info.uri, - author: player.queue.current.info.author, - length: client.utils.formatTime(player.queue.current.info.duration), - requester: (player.queue.current.requester as Requester).id, - }); + const setup = await client.db.getSetup(guild.id); + let m: Message | undefined; + if (setup?.textId) { + const textChannel = guild.channels.cache.get(setup.textId) as TextChannel; + if (!textChannel) return; + try { + m = await textChannel.messages.fetch({ + message: setup.messageId, + cache: true, + }); + } catch (error) { + client.logger.error(error); + } + } + if (m) { + const player = client.manager.getPlayer(guild.id); + if (player?.queue.current) { + const iconUrl = + client.config.icons[player.queue.current.info.sourceName] || + client.user!.displayAvatarURL({ extension: 'png' }); + const description = T(locale, 'player.setupStart.description', { + title: player.queue.current.info.title, + uri: player.queue.current.info.uri, + author: player.queue.current.info.author, + length: client.utils.formatTime(player.queue.current.info.duration), + requester: (player.queue.current.requester as Requester).id, + }); - const embed = client - .embed() - .setAuthor({ - name: T(locale, "player.setupStart.now_playing"), - iconURL: iconUrl, - }) - .setColor(client.color.main) - .setDescription(description) - .setImage(player.queue.current.info.artworkUrl); - await m - .edit({ - embeds: [embed], - components: getButtons(player, client).map((b) => { - b.components.forEach((c) => c.setDisabled(!player?.queue.current)); - return b; - }), - }) - .catch(() => {}); - } else { - const embed = client - .embed() - .setColor(client.color.main) - .setAuthor({ - name: client.user.username, - iconURL: client.user.displayAvatarURL({ extension: "png" }), - }) - .setDescription(T(locale, "player.setupStart.nothing_playing")) - .setImage(client.config.links.img); - await m - .edit({ - embeds: [embed], - components: getButtons(player, client).map((b) => { - b.components.forEach((c) => c.setDisabled(true)); - return b; - }), - }) - .catch(() => {}); - } - } + const embed = client + .embed() + .setAuthor({ + name: T(locale, 'player.setupStart.now_playing'), + iconURL: iconUrl, + }) + .setColor(client.color.main) + .setDescription(description) + .setImage(player.queue.current.info.artworkUrl); + await m + .edit({ + embeds: [embed], + components: getButtons(player, client).map(b => { + b.components.forEach(c => c.setDisabled(!player?.queue.current)); + return b; + }), + }) + .catch(() => {}); + } else { + const embed = client + .embed() + .setColor(client.color.main) + .setAuthor({ + name: client.user!.username, + iconURL: client.user!.displayAvatarURL({ extension: 'png' }), + }) + .setDescription(T(locale, 'player.setupStart.nothing_playing')) + .setImage(client.config.links.img); + await m + .edit({ + embeds: [embed], + components: getButtons(player, client).map(b => { + b.components.forEach(c => c.setDisabled(true)); + return b; + }), + }) + .catch(() => {}); + } + } } async function buttonReply(int: any, args: string, color: ColorResolvable): Promise { - const embed = new EmbedBuilder(); - let m: Message; - if (int.replied) { - m = await int.editReply({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => {}); - } else { - m = await int.followUp({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => {}); - } - setTimeout(async () => { - if (int && !int.ephemeral) { - await m.delete().catch(() => {}); - } - }, 2000); + const embed = new EmbedBuilder(); + let m: Message; + if (int.replied) { + m = await int.editReply({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => {}); + } else { + m = await int.followUp({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => {}); + } + setTimeout(async () => { + if (int && !int.ephemeral) { + await m.delete().catch(() => {}); + } + }, 2000); } async function oops(channel: TextChannel, args: string): Promise { - try { - const embed1 = new EmbedBuilder().setColor("Red").setDescription(`${args}`); - const m = await channel.send({ - embeds: [embed1], - }); - setTimeout(async () => await m.delete().catch(() => {}), 12000); - } catch (e) { - return console.error(e); - } + try { + const embed1 = new EmbedBuilder().setColor('Red').setDescription(`${args}`); + const m = await channel.send({ + embeds: [embed1], + }); + setTimeout(async () => await m.delete().catch(() => {}), 12000); + } catch (e) { + return console.error(e); + } } export { setupStart, trackStart, buttonReply, updateSetup, oops }; diff --git a/src/utils/ThemeSelector.ts b/src/utils/ThemeSelector.ts index 6909a4fc5..638020063 100644 --- a/src/utils/ThemeSelector.ts +++ b/src/utils/ThemeSelector.ts @@ -1,79 +1,79 @@ export class ThemeSelector { - /** - * Applies a yellow fire effect to the text. - * - * @param text - The input text to apply the effect to. - * @returns The processed text with the green fire effect. - */ - fire(text: string): string { - let fade = ""; - let green = 250; + /** + * Applies a yellow fire effect to the text. + * + * @param text - The input text to apply the effect to. + * @returns The processed text with the green fire effect. + */ + fire(text: string): string { + let fade = ''; + let green = 250; - for (const line of text.split("\n")) { - fade += `\x1b[38;2;255;${green};0m${line}\x1b[0m\n`; - green = Math.max(0, green - 25); - } + for (const line of text.split('\n')) { + fade += `\x1b[38;2;255;${green};0m${line}\x1b[0m\n`; + green = Math.max(0, green - 25); + } - return fade; - } + return fade; + } - /** - * Applies a purple neon effect to the text. - * - * @param text - The input text to apply the effect to. - * @returns The processed text with the purple neon effect. - */ - purpleNeon(text: string): string { - let fade = ""; - let purple = 255; + /** + * Applies a purple neon effect to the text. + * + * @param text - The input text to apply the effect to. + * @returns The processed text with the purple neon effect. + */ + purpleNeon(text: string): string { + let fade = ''; + let purple = 255; - for (const line of text.split("\n")) { - fade += `\x1b[38;2;255;0;${purple}m${line}\x1b[0m\n`; - purple = Math.max(0, purple - 25); - } + for (const line of text.split('\n')) { + fade += `\x1b[38;2;255;0;${purple}m${line}\x1b[0m\n`; + purple = Math.max(0, purple - 25); + } - return fade; - } + return fade; + } - /** - * Applies a cyan effect to the text. - * - * @param text - The input text to apply the effect to. - * @returns The processed text with the cyan effect. - */ - cyan(text: string): string { - let fade = ""; - let blue = 100; + /** + * Applies a cyan effect to the text. + * + * @param text - The input text to apply the effect to. + * @returns The processed text with the cyan effect. + */ + cyan(text: string): string { + let fade = ''; + let blue = 100; - for (const line of text.split("\n")) { - fade += `\x1b[38;2;0;255;${blue}m${line}\x1b[0m\n`; - if (blue < 255) { - blue = Math.min(255, blue + 15); - } - } + for (const line of text.split('\n')) { + fade += `\x1b[38;2;0;255;${blue}m${line}\x1b[0m\n`; + if (blue < 255) { + blue = Math.min(255, blue + 15); + } + } - return fade; - } + return fade; + } - /** - * Applies a water effect to the text. - * - * @param text - The input text to apply the effect to. - * @returns The processed text with the water effect. - */ - water(text: string): string { - let fade = ""; - let green = 255; + /** + * Applies a water effect to the text. + * + * @param text - The input text to apply the effect to. + * @returns The processed text with the water effect. + */ + water(text: string): string { + let fade = ''; + let green = 255; - for (const line of text.split("\n")) { - fade += `\x1b[38;2;0;${green};255m${line}\x1b[0m\n`; - if (green > 30) { - green = Math.max(30, green - 40); - } - } + for (const line of text.split('\n')) { + fade += `\x1b[38;2;0;${green};255m${line}\x1b[0m\n`; + if (green > 30) { + green = Math.max(30, green - 40); + } + } - return fade; - } + return fade; + } } /** diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index c9095d9f2..8cb71a26b 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -1,166 +1,177 @@ -import { ActionRowBuilder, ActivityType, ButtonBuilder, ButtonStyle, CommandInteraction, type TextChannel } from "discord.js"; -import type { Context, Lavamusic } from "../structures/index"; - +import { + ActionRowBuilder, + ActivityType, + ButtonBuilder, + ButtonStyle, + CommandInteraction, + type TextChannel, +} from 'discord.js'; +import type { Context, Lavamusic } from '../structures/index'; + +// biome-ignore lint/complexity/noStaticOnlyClass: export class Utils { - public static formatTime(ms: number): string { - const minuteMs = 60 * 1000; - const hourMs = 60 * minuteMs; - const dayMs = 24 * hourMs; - if (ms < minuteMs) return `${ms / 1000}s`; - if (ms < hourMs) return `${Math.floor(ms / minuteMs)}m ${Math.floor((ms % minuteMs) / 1000)}s`; - if (ms < dayMs) return `${Math.floor(ms / hourMs)}h ${Math.floor((ms % hourMs) / minuteMs)}m`; - return `${Math.floor(ms / dayMs)}d ${Math.floor((ms % dayMs) / hourMs)}h`; - } - - public static updateStatus(client: Lavamusic, guildId?: string): void { - const { user } = client; - if (user && guildId === client.env.GUILD_ID) { - const player = client.manager.getPlayer(client.env.GUILD_ID); - user.setPresence({ - activities: [ - { - name: player?.queue?.current ? `🎶 | ${player.queue?.current.info.title}` : client.env.BOT_ACTIVITY, - type: player?.queue?.current ? ActivityType.Listening : client.env.BOT_ACTIVITY_TYPE, - }, - ], - status: client.env.BOT_STATUS as any, - }); - } - } - - public static chunk(array: any[], size: number) { - const chunked_arr = []; - for (let index = 0; index < array.length; index += size) { - chunked_arr.push(array.slice(index, size + index)); - } - return chunked_arr; - } - - public static formatBytes(bytes: number, decimals = 2): string { - if (bytes === 0) return "0 Bytes"; - const k = 1024; - const dm = decimals < 0 ? 0 : decimals; - const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`; - } - - public static formatNumber(number: number): string { - return number.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,"); - } - - public static parseTime(string: string): number { - const time = string.match(/([0-9]+[d,h,m,s])/g); - if (!time) return 0; - let ms = 0; - for (const t of time) { - const unit = t[t.length - 1]; - const amount = Number(t.slice(0, -1)); - if (unit === "d") ms += amount * 24 * 60 * 60 * 1000; - else if (unit === "h") ms += amount * 60 * 60 * 1000; - else if (unit === "m") ms += amount * 60 * 1000; - else if (unit === "s") ms += amount * 1000; - } - return ms; - } - - public static progressBar(current: number, total: number, size = 20): string { - const percent = Math.round((current / total) * 100); - const filledSize = Math.round((size * current) / total); - const filledBar = "▓".repeat(filledSize); - const emptyBar = "░".repeat(size - filledSize); - return `${filledBar}${emptyBar} ${percent}%`; - } - - public static async paginate(client: Lavamusic, ctx: Context, embed: any[]): Promise { - if (embed.length < 2) { - if (ctx.isInteraction) { - ctx.deferred ? ctx.interaction.followUp({ embeds: embed }) : ctx.interaction.reply({ embeds: embed }); - return; - } - - (ctx.channel as TextChannel).send({ embeds: embed }); - return; - } - - let page = 0; - const getButton = (page: number): any => { - const firstEmbed = page === 0; - const lastEmbed = page === embed.length - 1; - const pageEmbed = embed[page]; - const first = new ButtonBuilder() - .setCustomId("first") - .setEmoji(client.emoji.page.first) - .setStyle(ButtonStyle.Primary) - .setDisabled(firstEmbed); - const back = new ButtonBuilder() - .setCustomId("back") - .setEmoji(client.emoji.page.back) - .setStyle(ButtonStyle.Primary) - .setDisabled(firstEmbed); - const next = new ButtonBuilder() - .setCustomId("next") - .setEmoji(client.emoji.page.next) - .setStyle(ButtonStyle.Primary) - .setDisabled(lastEmbed); - const last = new ButtonBuilder() - .setCustomId("last") - .setEmoji(client.emoji.page.last) - .setStyle(ButtonStyle.Primary) - .setDisabled(lastEmbed); - const stop = new ButtonBuilder().setCustomId("stop").setEmoji(client.emoji.page.cancel).setStyle(ButtonStyle.Danger); - const row = new ActionRowBuilder().addComponents(first, back, stop, next, last); - return { embeds: [pageEmbed], components: [row] }; - }; - - const msgOptions = getButton(0); - const msg = ctx.isInteraction - ? await (ctx.deferred - ? ctx.interaction!.followUp({ - ...msgOptions, - fetchReply: true as boolean, - }) - : ctx.interaction!.reply({ ...msgOptions, fetchReply: true })) - : await (ctx.channel as TextChannel).send({ - ...msgOptions, - fetchReply: true, - }); - - const author = ctx instanceof CommandInteraction ? ctx.user : ctx.author; - - const filter = (int: any): any => int.user.id === author.id; - const collector = msg.createMessageComponentCollector({ - filter, - time: 60000, - }); - - collector.on("collect", async (interaction) => { - if (interaction.user.id === author.id) { - await interaction.deferUpdate(); - if (interaction.customId === "first" && page !== 0) { - page = 0; - } else if (interaction.customId === "back" && page !== 0) { - page--; - } else if (interaction.customId === "stop") { - collector.stop(); - } else if (interaction.customId === "next" && page !== embed.length - 1) { - page++; - } else if (interaction.customId === "last" && page !== embed.length - 1) { - page = embed.length - 1; - } - await interaction.editReply(getButton(page)); - } else { - await interaction.reply({ - content: ctx.locale("buttons.errors.not_author"), - ephemeral: true, - }); - } - }); - - collector.on("end", async () => { - await msg.edit({ embeds: [embed[page]], components: [] }); - }); - } + public static formatTime(ms: number): string { + const minuteMs = 60 * 1000; + const hourMs = 60 * minuteMs; + const dayMs = 24 * hourMs; + if (ms < minuteMs) return `${ms / 1000}s`; + if (ms < hourMs) return `${Math.floor(ms / minuteMs)}m ${Math.floor((ms % minuteMs) / 1000)}s`; + if (ms < dayMs) return `${Math.floor(ms / hourMs)}h ${Math.floor((ms % hourMs) / minuteMs)}m`; + return `${Math.floor(ms / dayMs)}d ${Math.floor((ms % dayMs) / hourMs)}h`; + } + + public static updateStatus(client: Lavamusic, guildId?: string): void { + const { user } = client; + if (user && client.env.GUILD_ID && guildId === client.env.GUILD_ID) { + const player = client.manager.getPlayer(client.env.GUILD_ID); + user.setPresence({ + activities: [ + { + name: player?.queue?.current ? `🎶 | ${player.queue?.current.info.title}` : client.env.BOT_ACTIVITY, + type: player?.queue?.current ? ActivityType.Listening : client.env.BOT_ACTIVITY_TYPE, + }, + ], + status: client.env.BOT_STATUS as any, + }); + } + } + + public static chunk(array: any[], size: number) { + const chunked_arr = []; + for (let index = 0; index < array.length; index += size) { + chunked_arr.push(array.slice(index, size + index)); + } + return chunked_arr; + } + + public static formatBytes(bytes: number, decimals = 2): string { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return `${Number.parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`; + } + + public static formatNumber(number: number): string { + return number.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); + } + + public static parseTime(string: string): number { + const time = string.match(/([0-9]+[d,h,m,s])/g); + if (!time) return 0; + let ms = 0; + for (const t of time) { + const unit = t[t.length - 1]; + const amount = Number(t.slice(0, -1)); + if (unit === 'd') ms += amount * 24 * 60 * 60 * 1000; + else if (unit === 'h') ms += amount * 60 * 60 * 1000; + else if (unit === 'm') ms += amount * 60 * 1000; + else if (unit === 's') ms += amount * 1000; + } + return ms; + } + + public static progressBar(current: number, total: number, size = 20): string { + const percent = Math.round((current / total) * 100); + const filledSize = Math.round((size * current) / total); + const filledBar = '▓'.repeat(filledSize); + const emptyBar = '░'.repeat(size - filledSize); + return `${filledBar}${emptyBar} ${percent}%`; + } + + public static async paginate(client: Lavamusic, ctx: Context, embed: any[]): Promise { + if (embed.length < 2) { + if (ctx.isInteraction) { + ctx.deferred ? ctx.interaction?.followUp({ embeds: embed }) : ctx.interaction?.reply({ embeds: embed }); + return; + } + + (ctx.channel as TextChannel).send({ embeds: embed }); + return; + } + + let page = 0; + const getButton = (page: number): any => { + const firstEmbed = page === 0; + const lastEmbed = page === embed.length - 1; + const pageEmbed = embed[page]; + const first = new ButtonBuilder() + .setCustomId('first') + .setEmoji(client.emoji.page.first) + .setStyle(ButtonStyle.Primary) + .setDisabled(firstEmbed); + const back = new ButtonBuilder() + .setCustomId('back') + .setEmoji(client.emoji.page.back) + .setStyle(ButtonStyle.Primary) + .setDisabled(firstEmbed); + const next = new ButtonBuilder() + .setCustomId('next') + .setEmoji(client.emoji.page.next) + .setStyle(ButtonStyle.Primary) + .setDisabled(lastEmbed); + const last = new ButtonBuilder() + .setCustomId('last') + .setEmoji(client.emoji.page.last) + .setStyle(ButtonStyle.Primary) + .setDisabled(lastEmbed); + const stop = new ButtonBuilder() + .setCustomId('stop') + .setEmoji(client.emoji.page.cancel) + .setStyle(ButtonStyle.Danger); + const row = new ActionRowBuilder().addComponents(first, back, stop, next, last); + return { embeds: [pageEmbed], components: [row] }; + }; + + const msgOptions = getButton(0); + const msg = ctx.isInteraction + ? await (ctx.deferred + ? ctx.interaction!.followUp({ + ...msgOptions, + fetchReply: true as boolean, + }) + : ctx.interaction!.reply({ ...msgOptions, fetchReply: true })) + : await (ctx.channel as TextChannel).send({ + ...msgOptions, + fetchReply: true, + }); + + const author = ctx instanceof CommandInteraction ? ctx.user : ctx.author; + + const filter = (int: any): any => int.user.id === author?.id; + const collector = msg.createMessageComponentCollector({ + filter, + time: 60000, + }); + + collector.on('collect', async interaction => { + if (interaction.user.id === author?.id) { + await interaction.deferUpdate(); + if (interaction.customId === 'first' && page !== 0) { + page = 0; + } else if (interaction.customId === 'back' && page !== 0) { + page--; + } else if (interaction.customId === 'stop') { + collector.stop(); + } else if (interaction.customId === 'next' && page !== embed.length - 1) { + page++; + } else if (interaction.customId === 'last' && page !== embed.length - 1) { + page = embed.length - 1; + } + await interaction.editReply(getButton(page)); + } else { + await interaction.reply({ + content: ctx.locale('buttons.errors.not_author'), + ephemeral: true, + }); + } + }); + + collector.on('end', async () => { + await msg.edit({ embeds: [embed[page]], components: [] }); + }); + } } /** diff --git a/src/utils/functions/player.ts b/src/utils/functions/player.ts index ced803e16..7585cac65 100644 --- a/src/utils/functions/player.ts +++ b/src/utils/functions/player.ts @@ -1,5 +1,5 @@ -import type { Player, Track } from "lavalink-client"; -import type { Requester } from "../../types"; +import type { Player, Track } from 'lavalink-client'; +import type { Requester } from '../../types'; /** * Transforms a requester into a standardized requester object. @@ -9,19 +9,20 @@ import type { Requester } from "../../types"; * @returns {Requester} The transformed requester object. */ export const requesterTransformer = (requester: any): Requester => { - // if it's already the transformed requester - if (typeof requester === "object" && "avatar" in requester && Object.keys(requester).length === 3) return requester as Requester; - // if it's still a string - if (typeof requester === "object" && "displayAvatarURL" in requester) { - // it's a user - return { - id: requester.id, - username: requester.username, - avatarURL: requester.displayAvatarURL({ extension: "png" }), - discriminator: requester.discriminator, - }; - } - return { id: requester!.toString(), username: "unknown" }; + // if it's already the transformed requester + if (typeof requester === 'object' && 'avatar' in requester && Object.keys(requester).length === 3) + return requester as Requester; + // if it's still a string + if (typeof requester === 'object' && 'displayAvatarURL' in requester) { + // it's a user + return { + id: requester.id, + username: requester.username, + avatarURL: requester.displayAvatarURL({ extension: 'png' }), + discriminator: requester.discriminator, + }; + } + return { id: requester!.toString(), username: 'unknown' }; }; /** @@ -34,71 +35,78 @@ export const requesterTransformer = (requester: any): Requester => { * @returns {Promise} A promise that resolves when the function is done. */ export async function autoPlayFunction(player: Player, lastTrack?: Track): Promise { - if (!player.get("autoplay")) return; - if (!lastTrack) return; + if (!player.get('autoplay')) return; + if (!lastTrack) return; - if (lastTrack.info.sourceName === "spotify") { - const filtered = player.queue.previous.filter((v) => v.info.sourceName === "spotify").slice(0, 5); - const ids = filtered.map( - (v) => v.info.identifier || v.info.uri.split("/")?.reverse()?.[0] || v.info.uri.split("/")?.reverse()?.[1], - ); - if (ids.length >= 2) { - const res = await player - .search( - { - query: `seed_tracks=${ids.join(",")}`, //`seed_artists=${artistIds.join(",")}&seed_genres=${genre.join(",")}&seed_tracks=${trackIds.join(",")}`; - source: "sprec", - }, - lastTrack.requester, - ) - .then((response: any) => { - response.tracks = response.tracks.filter((v) => v.info.identifier !== lastTrack.info.identifier); // remove the lastPlayed track if it's in there.. - return response; - }) - .catch(console.warn); - if (res && res.tracks.length > 0) - await player.queue.add( - res.tracks.slice(0, 5).map((track) => { - // transform the track plugininfo so you can figure out if the track is from autoplay or not. - track.pluginInfo.clientData = { ...(track.pluginInfo.clientData || {}), fromAutoplay: true }; - return track; - }), - ); - } - return; - } - if (lastTrack.info.sourceName === "youtube" || lastTrack.info.sourceName === "youtubemusic") { - const res = await player - .search( - { - query: `https://www.youtube.com/watch?v=${lastTrack.info.identifier}&list=RD${lastTrack.info.identifier}`, - source: "youtube", - }, - lastTrack.requester, - ) - .then((response: any) => { - response.tracks = response.tracks.filter((v) => v.info.identifier !== lastTrack.info.identifier); // remove the lastPlayed track if it's in there.. - return response; - }) - .catch(console.warn); - if (res && res.tracks.length > 0) - await player.queue.add( - res.tracks.slice(0, 5).map((track) => { - // transform the track plugininfo so you can figure out if the track is from autoplay or not. - track.pluginInfo.clientData = { ...(track.pluginInfo.clientData || {}), fromAutoplay: true }; - return track; - }), - ); - return; - } - if (lastTrack.info.sourceName === "jiosaavn") { - const res = await player.search({ query: `jsrec:${lastTrack.info.identifier}`, source: "jsrec" }, lastTrack.requester); - if (res.tracks.length > 0) { - const track = res.tracks.filter((v) => v.info.identifier !== lastTrack.info.identifier)[0]; - await player.queue.add(track); - } - } - return; + if (lastTrack.info.sourceName === 'spotify') { + const filtered = player.queue.previous.filter(v => v.info.sourceName === 'spotify').slice(0, 5); + const ids = filtered.map( + v => v.info.identifier || v.info.uri.split('/')?.reverse()?.[0] || v.info.uri.split('/')?.reverse()?.[1], + ); + if (ids.length >= 2) { + const res = await player + .search( + { + query: `seed_tracks=${ids.join(',')}`, //`seed_artists=${artistIds.join(",")}&seed_genres=${genre.join(",")}&seed_tracks=${trackIds.join(",")}`; + source: 'sprec', + }, + lastTrack.requester, + ) + .then((response: any) => { + response.tracks = response.tracks.filter( + (v: { info: { identifier: string } }) => v.info.identifier !== lastTrack.info.identifier, + ); // remove the lastPlayed track if it's in there.. + return response; + }) + .catch(console.warn); + if (res && res.tracks.length > 0) + await player.queue.add( + res.tracks.slice(0, 5).map((track: { pluginInfo: { clientData: any } }) => { + // transform the track plugininfo so you can figure out if the track is from autoplay or not. + track.pluginInfo.clientData = { ...(track.pluginInfo.clientData || {}), fromAutoplay: true }; + return track; + }), + ); + } + return; + } + if (lastTrack.info.sourceName === 'youtube' || lastTrack.info.sourceName === 'youtubemusic') { + const res = await player + .search( + { + query: `https://www.youtube.com/watch?v=${lastTrack.info.identifier}&list=RD${lastTrack.info.identifier}`, + source: 'youtube', + }, + lastTrack.requester, + ) + .then((response: any) => { + response.tracks = response.tracks.filter( + (v: { info: { identifier: string } }) => v.info.identifier !== lastTrack.info.identifier, + ); // remove the lastPlayed track if it's in there.. + return response; + }) + .catch(console.warn); + if (res && res.tracks.length > 0) + await player.queue.add( + res.tracks.slice(0, 5).map((track: { pluginInfo: { clientData: any } }) => { + // transform the track plugininfo so you can figure out if the track is from autoplay or not. + track.pluginInfo.clientData = { ...(track.pluginInfo.clientData || {}), fromAutoplay: true }; + return track; + }), + ); + return; + } + if (lastTrack.info.sourceName === 'jiosaavn') { + const res = await player.search( + { query: `jsrec:${lastTrack.info.identifier}`, source: 'jsrec' }, + lastTrack.requester, + ); + if (res.tracks.length > 0) { + const track = res.tracks.filter(v => v.info.identifier !== lastTrack.info.identifier)[0]; + await player.queue.add(track); + } + } + return; } /** diff --git a/tsconfig.json b/tsconfig.json index 42b245339..4a46aa3b0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +1,30 @@ { - "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { - "lib": ["dom", "esnext"], - "rootDir": "./src", - "outDir": "./dist", - "target": "esnext", - "module": "commonjs", - "declaration": true, - "sourceMap": true, - "newLine": "crlf", - "strict": false, + "module": "CommonJS", + "target": "ESNext", + "lib": ["ESNext", "WebWorker"], "moduleResolution": "node", + "declaration": true, + "sourceMap": false, + "strict": true, "esModuleInterop": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, + "preserveConstEnums": true, + /* Type Checking */ + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, "skipLibCheck": true, - "forceConsistentCasingInFileNames": true + "noErrorTruncation": true, + "outDir": "./dist", + "stripInternal": true }, - "include": ["src/"], - "exclude": ["dist/", "node_modules/"] + "exclude": ["dist/", "node_modules/"], + "include": ["src/**/*"] } From 3a0819e805c540eee4f183e303ef52f11d63f0c0 Mon Sep 17 00:00:00 2001 From: Appu Date: Tue, 24 Sep 2024 22:18:58 +0530 Subject: [PATCH 31/32] fixed some bug --- src/commands/info/Help.ts | 2 +- src/commands/music/{Lyrics.ts => Lyrics.ts.txt} | 5 +++-- src/database/server.ts | 13 ++++++++----- src/events/client/MessageCreate.ts | 2 +- src/events/client/SetupButtons.ts | 2 +- src/events/client/SetupSystem.ts | 2 +- 6 files changed, 15 insertions(+), 11 deletions(-) rename src/commands/music/{Lyrics.ts => Lyrics.ts.txt} (99%) diff --git a/src/commands/info/Help.ts b/src/commands/info/Help.ts index 499f0e7b3..65400ea36 100644 --- a/src/commands/info/Help.ts +++ b/src/commands/info/Help.ts @@ -62,7 +62,7 @@ export default class Help extends Command { .setDescription( ctx.locale('cmd.help.help_cmd', { description: ctx.locale(command.description.content), - usage: `${guild.prefix}${command.description.usage}`, + usage: `${guild?.prefix}${command.description.usage}`, examples: command.description.examples.map((example: string) => `${guild.prefix}${example}`).join(', '), aliases: command.aliases.map((alias: string) => `\`${alias}\``).join(', '), category: command.category, diff --git a/src/commands/music/Lyrics.ts b/src/commands/music/Lyrics.ts.txt similarity index 99% rename from src/commands/music/Lyrics.ts rename to src/commands/music/Lyrics.ts.txt index c4ff1cf69..4273611ae 100644 --- a/src/commands/music/Lyrics.ts +++ b/src/commands/music/Lyrics.ts.txt @@ -1,4 +1,4 @@ -import { +/* import { ActionRowBuilder, ButtonBuilder, type ButtonInteraction, @@ -173,6 +173,7 @@ export default class Lyrics extends Command { return pages; } } +*/ /** * Project: lavamusic @@ -183,4 +184,4 @@ export default class Lyrics extends Command { * This code is the property of Coder and may not be reproduced or * modified without permission. For more information, contact us at * https://discord.gg/ns8CTk9J3e - */ + */ \ No newline at end of file diff --git a/src/database/server.ts b/src/database/server.ts index a023c695c..6f3a4248e 100644 --- a/src/database/server.ts +++ b/src/database/server.ts @@ -8,7 +8,7 @@ export default class ServerData { this.prisma = new PrismaClient(); } - public async get(guildId: string): Promise { + public async get(guildId: string): Promise { return (await this.prisma.guild.findUnique({ where: { guildId } })) ?? this.createGuild(guildId); } @@ -74,9 +74,12 @@ export default class ServerData { await this.prisma.stay.delete({ where: { guildId } }); } - public async get_247(guildId?: string): Promise { + public async get_247(guildId?: string): Promise { if (guildId) { - return await this.prisma.stay.findUnique({ where: { guildId } }); + //return await this.prisma.stay.findUnique({ where: { guildId } }); + const stay = await this.prisma.stay.findUnique({ where: { guildId } }); + if (stay) return stay; + return null; } return this.prisma.stay.findMany(); } @@ -219,7 +222,7 @@ export default class ServerData { public async removeSong(userId: string, playlistName: string, encodedSong: string): Promise { const playlist = await this.getPlaylist(userId, playlistName); if (playlist) { - const tracks: string[] = JSON.parse(playlist.tracks); + const tracks: string[] = JSON.parse(playlist?.tracks!); // Find the index of the song to remove const songIndex = tracks.indexOf(encodedSong); @@ -259,7 +262,7 @@ export default class ServerData { } // Deserialize the tracks JSON string back into an array - const tracks = JSON.parse(playlist.tracks); + const tracks = JSON.parse(playlist.tracks!); return tracks; } } diff --git a/src/events/client/MessageCreate.ts b/src/events/client/MessageCreate.ts index 94ea9db45..ef36a26d4 100644 --- a/src/events/client/MessageCreate.ts +++ b/src/events/client/MessageCreate.ts @@ -34,7 +34,7 @@ export default class MessageCreate extends Event { if (mention.test(message.content)) { await message.reply({ content: T(locale, 'event.message.prefix_mention', { - prefix: guild.prefix, + prefix: guild?.prefix, }), }); return; diff --git a/src/events/client/SetupButtons.ts b/src/events/client/SetupButtons.ts index 7d16a541f..7b6c5b0b8 100644 --- a/src/events/client/SetupButtons.ts +++ b/src/events/client/SetupButtons.ts @@ -48,7 +48,7 @@ export default class SetupButtons extends Event { const { title, uri, duration, artworkUrl, sourceName, isStream } = player.queue.current.info; let message: Message | undefined; try { - message = await interaction.channel.messages.fetch(data.messageId, { + message = await interaction.channel.messages.fetch(data?.messageId, { cache: true, }); } catch (_e) { diff --git a/src/events/client/SetupSystem.ts b/src/events/client/SetupSystem.ts index cc8fbb446..047dbaf1a 100644 --- a/src/events/client/SetupSystem.ts +++ b/src/events/client/SetupSystem.ts @@ -1,4 +1,4 @@ -import { type Message, PermissionsBitField, TextChannel } from 'discord.js'; +import { type Message, TextChannel } from 'discord.js'; import { T } from '../../structures/I18n'; import { Event, type Lavamusic } from '../../structures/index'; import { oops, setupStart } from '../../utils/SetupSystem'; From 7d91283c9ef31360630b8e289ca8bfeb4219d233 Mon Sep 17 00:00:00 2001 From: LucasB25 <50886682+LucasB25@users.noreply.github.com> Date: Tue, 24 Sep 2024 22:37:21 +0200 Subject: [PATCH 32/32] update --- scripts/restart.js | 2 +- src/commands/config/Setup.ts | 5 ++- src/commands/music/Loop.ts | 9 ++++-- src/commands/music/Queue.ts | 2 +- src/commands/playlist/AddSong.ts | 2 +- src/commands/playlist/Steal.ts | 1 - src/env.ts | 2 +- src/events/client/SetupSystem.ts | 12 ++++++-- src/events/client/VoiceStateUpdate.ts | 4 ++- src/events/player/QueueEnd.ts | 8 +++-- src/events/player/TrackEnd.ts | 8 +++-- src/utils/BotLog.ts | 4 ++- src/utils/SetupSystem.ts | 44 +++++++++++++++++++++------ src/utils/Utils.ts | 2 +- 14 files changed, 76 insertions(+), 29 deletions(-) diff --git a/scripts/restart.js b/scripts/restart.js index feedb373f..9460fcaf3 100644 --- a/scripts/restart.js +++ b/scripts/restart.js @@ -3,7 +3,7 @@ const { exec } = require('node:child_process'); async function startLavamusic() { exec('npm start', (error, stdout, stderr) => { if (error) { - console.error(`Error starting Lavamusic: ${error.message}`); + console.error(`Error starting Lavamusic: ${error}`); return; } if (stderr) { diff --git a/src/commands/config/Setup.ts b/src/commands/config/Setup.ts index a88be8355..c381a3c61 100644 --- a/src/commands/config/Setup.ts +++ b/src/commands/config/Setup.ts @@ -128,7 +128,10 @@ export default class Setup extends Command { } client.db.deleteSetup(ctx.guild!.id); const textChannel = ctx.guild.channels.cache.get(data2.textId); - if (textChannel) await textChannel.delete().catch(() => {}); + if (textChannel) + await textChannel.delete().catch(() => { + null; + }); await ctx.sendMessage({ embeds: [ { diff --git a/src/commands/music/Loop.ts b/src/commands/music/Loop.ts index f3a35e3ea..079c7fa29 100644 --- a/src/commands/music/Loop.ts +++ b/src/commands/music/Loop.ts @@ -36,18 +36,21 @@ export default class Loop extends Command { let loopMessage = ''; switch (player?.repeatMode) { - case 'off': + case 'off': { player.setRepeatMode('track'); loopMessage = ctx.locale('cmd.loop.looping_song'); break; - case 'track': + } + case 'track': { player.setRepeatMode('queue'); loopMessage = ctx.locale('cmd.loop.looping_queue'); break; - case 'queue': + } + case 'queue': { player.setRepeatMode('off'); loopMessage = ctx.locale('cmd.loop.looping_off'); break; + } } return await ctx.sendMessage({ diff --git a/src/commands/music/Queue.ts b/src/commands/music/Queue.ts index ecd5b7e2f..947e75349 100644 --- a/src/commands/music/Queue.ts +++ b/src/commands/music/Queue.ts @@ -49,7 +49,7 @@ export default class Queue extends Command { ], }); } - const songStrings = []; + const songStrings: string[] = []; for (let i = 0; i < player.queue.tracks.length; i++) { const track = player.queue.tracks[i]; songStrings.push( diff --git a/src/commands/playlist/AddSong.ts b/src/commands/playlist/AddSong.ts index 237ccf48a..491bf0367 100644 --- a/src/commands/playlist/AddSong.ts +++ b/src/commands/playlist/AddSong.ts @@ -95,7 +95,7 @@ export default class AddSong extends Command { } let trackStrings: any; - let count: number = 0; + let count = 0; if (res.loadType === 'playlist') { trackStrings = res.tracks.map(track => track.encoded); count = res.tracks.length; diff --git a/src/commands/playlist/Steal.ts b/src/commands/playlist/Steal.ts index 0cedbbb6a..eef61c6ae 100644 --- a/src/commands/playlist/Steal.ts +++ b/src/commands/playlist/Steal.ts @@ -189,7 +189,6 @@ export default class StealPlaylist extends Command { .respond(filtered.map(playlist => ({ name: playlist.name, value: playlist.name }))) .catch(console.error); } catch (error) { - console.error('Error in autocomplete interaction:', error); return await interaction .respond([{ name: 'An error occurred while fetching playlists.', value: 'Error' }]) .catch(console.error); diff --git a/src/env.ts b/src/env.ts index 38a7e076c..754ca6be3 100644 --- a/src/env.ts +++ b/src/env.ts @@ -96,7 +96,7 @@ const envSchema = z.object({ */ BOT_ACTIVITY_TYPE: z.preprocess(val => { if (typeof val === 'string') { - return parseInt(val, 10); + return Number.parseInt(val, 10); } return val; }, z.number().default(0)), diff --git a/src/events/client/SetupSystem.ts b/src/events/client/SetupSystem.ts index 047dbaf1a..88b97feac 100644 --- a/src/events/client/SetupSystem.ts +++ b/src/events/client/SetupSystem.ts @@ -16,7 +16,9 @@ export default class SetupSystem extends Event { if (!(channel instanceof TextChannel)) return; if (!message.member?.voice.channel) { await oops(channel, T(locale, 'event.message.no_voice_channel_queue')); - await message.delete().catch(() => {}); + await message.delete().catch(() => { + null; + }); return; } @@ -42,7 +44,9 @@ export default class SetupSystem extends Event { channel: clientMember.voice.channelId, }), ); - await message.delete().catch(() => {}); + await message.delete().catch(() => { + null; + }); return; } @@ -60,7 +64,9 @@ export default class SetupSystem extends Event { } await setupStart(this.client, message.content, player, message); - await message.delete().catch(() => {}); + await message.delete().catch(() => { + null; + }); } } diff --git a/src/events/client/VoiceStateUpdate.ts b/src/events/client/VoiceStateUpdate.ts index 564f0e19a..c4f245db4 100644 --- a/src/events/client/VoiceStateUpdate.ts +++ b/src/events/client/VoiceStateUpdate.ts @@ -36,7 +36,9 @@ export default class VoiceStateUpdate extends Event { newState.guild.members.me.permissions.has(['Connect', 'Speak']) || newState.channel.permissionsFor(newState.guild.members.me).has('MuteMembers') ) { - await newState.guild.members.me.voice.setSuppressed(false).catch(() => {}); + await newState.guild.members.me.voice.setSuppressed(false).catch(() => { + null; + }); } } diff --git a/src/events/player/QueueEnd.ts b/src/events/player/QueueEnd.ts index d5353bfc6..94ee90dab 100644 --- a/src/events/player/QueueEnd.ts +++ b/src/events/player/QueueEnd.ts @@ -19,11 +19,15 @@ export default class QueueEnd extends Event { const channel = guild.channels.cache.get(player.textChannelId!) as TextChannel; if (!channel) return; - const message = await channel.messages.fetch(messageId).catch(() => {}); + const message = await channel.messages.fetch(messageId).catch(() => { + null; + }); if (!message) return; if (message.editable) { - await message.edit({ components: [] }).catch(() => {}); + await message.edit({ components: [] }).catch(() => { + null; + }); } } } diff --git a/src/events/player/TrackEnd.ts b/src/events/player/TrackEnd.ts index 7107b8ec7..048fc440b 100644 --- a/src/events/player/TrackEnd.ts +++ b/src/events/player/TrackEnd.ts @@ -19,10 +19,14 @@ export default class TrackEnd extends Event { const channel = guild.channels.cache.get(player.textChannelId!) as TextChannel; if (!channel) return; - const message = await channel.messages.fetch(messageId).catch(() => {}); + const message = await channel.messages.fetch(messageId).catch(() => { + null; + }); if (!message) return; - message.delete().catch(() => {}); + message.delete().catch(() => { + null; + }); } } diff --git a/src/utils/BotLog.ts b/src/utils/BotLog.ts index 2add8b17e..c5a1f1ae6 100644 --- a/src/utils/BotLog.ts +++ b/src/utils/BotLog.ts @@ -18,7 +18,9 @@ export default class BotLog { const color = colors[type]; const embed = client.embed().setColor(color).setDescription(message).setTimestamp(); - channel.send({ embeds: [embed] }).catch(() => {}); + channel.send({ embeds: [embed] }).catch(() => { + null; + }); } } diff --git a/src/utils/SetupSystem.ts b/src/utils/SetupSystem.ts index ae6369689..8f0cb3907 100644 --- a/src/utils/SetupSystem.ts +++ b/src/utils/SetupSystem.ts @@ -93,7 +93,9 @@ async function setupStart(client: Lavamusic, query: string, player: Player, mess }) .then(msg => setTimeout(() => msg.delete(), 5000)); neb(n, player, client, locale); - await m.edit({ embeds: [n] }).catch(() => {}); + await m.edit({ embeds: [n] }).catch(() => { + null; + }); break; } case 'playlist': { @@ -110,7 +112,9 @@ async function setupStart(client: Lavamusic, query: string, player: Player, mess }) .then(msg => setTimeout(() => msg.delete(), 5000)); neb(n, player, client, locale); - await m.edit({ embeds: [n] }).catch(() => {}); + await m.edit({ embeds: [n] }).catch(() => { + null; + }); break; } } @@ -178,7 +182,9 @@ async function trackStart( return b; }), }) - .catch(() => {}); + .catch(() => { + null; + }); } else { await channel .send({ @@ -191,7 +197,9 @@ async function trackStart( .then(msg => { client.db.setSetup(msg.guild.id, msg.id, msg.channel.id); }) - .catch(() => {}); + .catch(() => { + null; + }); } } @@ -241,7 +249,9 @@ async function updateSetup(client: Lavamusic, guild: any, locale: string): Promi return b; }), }) - .catch(() => {}); + .catch(() => { + null; + }); } else { const embed = client .embed() @@ -260,7 +270,9 @@ async function updateSetup(client: Lavamusic, guild: any, locale: string): Promi return b; }), }) - .catch(() => {}); + .catch(() => { + null; + }); } } } @@ -269,13 +281,19 @@ async function buttonReply(int: any, args: string, color: ColorResolvable): Prom const embed = new EmbedBuilder(); let m: Message; if (int.replied) { - m = await int.editReply({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => {}); + m = await int.editReply({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => { + null; + }); } else { - m = await int.followUp({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => {}); + m = await int.followUp({ embeds: [embed.setColor(color).setDescription(args)] }).catch(() => { + null; + }); } setTimeout(async () => { if (int && !int.ephemeral) { - await m.delete().catch(() => {}); + await m.delete().catch(() => { + null; + }); } }, 2000); } @@ -286,7 +304,13 @@ async function oops(channel: TextChannel, args: string): Promise { const m = await channel.send({ embeds: [embed1], }); - setTimeout(async () => await m.delete().catch(() => {}), 12000); + setTimeout( + async () => + await m.delete().catch(() => { + null; + }), + 12000, + ); } catch (e) { return console.error(e); } diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 8cb71a26b..a2ec572dc 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -37,7 +37,7 @@ export class Utils { } public static chunk(array: any[], size: number) { - const chunked_arr = []; + const chunked_arr: any[][] = []; for (let index = 0; index < array.length; index += size) { chunked_arr.push(array.slice(index, size + index)); }