Merge pull request #3 from YuzuZensai/djs14

Update to discord.js v14, Support Voice Text Channel
This commit is contained in:
2022-07-18 15:16:56 +07:00
committed by GitHub
43 changed files with 1754 additions and 770 deletions
+5
View File
@@ -7,6 +7,11 @@ DATABASE_URL=postgresql://user:password@host:port/dbName?schema=public
DISCORD_TOKEN=SuperSecretDiscordToken DISCORD_TOKEN=SuperSecretDiscordToken
DEVELOPER_IDS=DEV1_ID,DEV2_ID DEVELOPER_IDS=DEV1_ID,DEV2_ID
PRIVATE_BOT=true PRIVATE_BOT=true
SUPPORT_URL=https://example.com/support
OSU_API_KEY=SuperSecretosu!Key OSU_API_KEY=SuperSecretosu!Key
YOUTUBE_COOKIE_BASE64=SuperSecretCookie_EncodeItInBase64_AndThen_RemoveTheLastEqualToo YOUTUBE_COOKIE_BASE64=SuperSecretCookie_EncodeItInBase64_AndThen_RemoveTheLastEqualToo
SPOTIFY_CLIENT_ID=SpotfyClientID
SPOTIFY_CLIENT_SECRET=SpotfyClientSecret
SPOTIFY_REFRESH_TOKEN=SpotfyRefreshToken
SPOTIFY_CLIENT_MARKET=CountryCodeHere
+2 -2
View File
@@ -6,7 +6,7 @@ RUN DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y RUN apt-get update -y
RUN apt-get -y install curl gnupg git python2 make g++ iputils-ping ffmpeg RUN apt-get -y install curl gnupg git python2 make g++ iputils-ping ffmpeg
RUN curl -sL https://deb.nodesource.com/setup_17.x | bash - RUN curl -sL https://deb.nodesource.com/setup_16.x | bash -
RUN apt-get -y install nodejs RUN apt-get -y install nodejs
RUN npm i -g yarn RUN npm i -g yarn
@@ -18,7 +18,7 @@ WORKDIR /home/node/app
RUN apt-get update -y RUN apt-get update -y
RUN apt-get -y install curl gnupg git python2 make g++ iputils-ping ffmpeg RUN apt-get -y install curl gnupg git python2 make g++ iputils-ping ffmpeg
RUN curl -sL https://deb.nodesource.com/setup_17.x | bash - RUN curl -sL https://deb.nodesource.com/setup_16.x | bash -
RUN apt-get -y install nodejs RUN apt-get -y install nodejs
RUN npm i -g yarn RUN npm i -g yarn
+7
View File
@@ -39,7 +39,14 @@ Take a look inside [.env.example](https://github.com/YuzuZensai/Yumi/blob/main/.
- ``DISCORD_TOKEN`` Your discord bot token - ``DISCORD_TOKEN`` Your discord bot token
- ``DEVELOPER_IDS`` List of Discord Snowflake User ID that can access developer only modules, separated by ``,`` - ``DEVELOPER_IDS`` List of Discord Snowflake User ID that can access developer only modules, separated by ``,``
- ``PRIVATE_BOT`` Should the bot invite be public? ``true`` or ``false`` - ``PRIVATE_BOT`` Should the bot invite be public? ``true`` or ``false``
- ``SUPPORT_URL`` (Optional) URL to your support server (If not set, support module will show not available message)
- ``OSU_API_KEY`` [osu! API v1 key](https://github.com/ppy/osu-api/wiki) - ``OSU_API_KEY`` [osu! API v1 key](https://github.com/ppy/osu-api/wiki)
- ``YOUTUBE_COOKIE_BASE64`` YouTube cookies encoded in base64 [How to get cookies?](https://github.com/play-dl/play-dl/tree/main/instructions#youtube-cookies=) * - ``YOUTUBE_COOKIE_BASE64`` YouTube cookies encoded in base64 [How to get cookies?](https://github.com/play-dl/play-dl/tree/main/instructions#youtube-cookies=) *
- ``SPOTIFY_CLIENT_ID`` Spotify client ID
- ``SPOTIFY_CLIENT_SECRET`` Spotify client secret
- ``SPOTIFY_REFRESH_TOKEN`` Spotify refresh token **
- ``SPOTIFY_CLIENT_MARKET`` Spotify market country code
\* Encode the cookies from the request headers base64 and put it in here instead of creating new file with ``play.authorization();`` code \* Encode the cookies from the request headers base64 and put it in here instead of creating new file with ``play.authorization();`` code
\*\* Get refresh token from ``https://accounts.spotify.com/en/authorize?client_id=<client id>&response_type=code&redirect_uri=<redirect uri>``
+29 -6
View File
@@ -1,8 +1,10 @@
{ {
"common": { "common": {
"available_args": "Available arguments", "available_args": "Available arguments",
"no_permissions": "Access denied (O_O;)", "member_no_permissions": "Access denied (O_O;)",
"no_permissions_description": "You need ``{{PERMISSIONS}}`` permission on this guild", "member_no_permissions_description": "You need ``{{PERMISSIONS}}`` permissions on this guild",
"bot_no_permissions": "I don't have permission (O_O;)",
"bot_no_permissions_description": "I need ``{{PERMISSIONS}}`` permissions",
"processing": "Please wait a moment~", "processing": "Please wait a moment~",
"processing_description": "Getting it done right now! (งᓀ‸ᓂ)ง" "processing_description": "Getting it done right now! (งᓀ‸ᓂ)ง"
}, },
@@ -35,7 +37,9 @@
"streaming_x": "Streaming {{NAME}}", "streaming_x": "Streaming {{NAME}}",
"listening_x": "Listening to {{NAME}}", "listening_x": "Listening to {{NAME}}",
"watching_x": "Watching {{NAME}}", "watching_x": "Watching {{NAME}}",
"competing_x": "Competing {{NAME}}" "competing_x": "Competing {{NAME}}",
"since": "Since {{{TIME}}}",
"end": "Ending {{{TIME}}}"
}, },
"say": { "say": {
"title": "Say", "title": "Say",
@@ -106,13 +110,19 @@
"queue_added_songs_description": "Added {{COUNT}} songs to the queue", "queue_added_songs_description": "Added {{COUNT}} songs to the queue",
"error": "Error while looking up the song" "error": "Error while looking up the song"
}, },
"musicplayer_playmy": {
"not_detected": "You don't seem to be listening to any music currently according to your activity"
},
"musicplayer_queue": { "musicplayer_queue": {
"title": "Queue", "title": "Queue",
"valid_args": "``Clear`` ``Shuffle``",
"empty": "The queue is empty! (っ˘ω˘ς )", "empty": "The queue is empty! (っ˘ω˘ς )",
"now_playing": "Now playing", "now_playing": "Now playing",
"upcoming_song": "Upcoming song", "upcoming_song": "Upcoming song",
"looping_current": "(Looping current)", "looping_current": "(Looping current)",
"song_x_in_queue": "There are {{COUNT}} song in the queue!" "song_x_in_queue": "There are {{COUNT}} song in the queue!",
"cleared": "Queue cleared~ (。•ˇ‸ˇ•。)",
"shuffled": "Queue shuffled"
}, },
"musicplayer_search": { "musicplayer_search": {
"title": "Search", "title": "Search",
@@ -127,8 +137,8 @@
}, },
"settings": { "settings": {
"title": "Settings", "title": "Settings",
"description": "Change how {{BOT_NAME}} behave on **{{GUILD_NAME}}**", "description": "Change how {{BOT_NAME}} behave on **{{{GUILD_NAME}}}**",
"valid_args": "``Prefix`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``" "valid_args": "``Prefix`` ``Language`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``"
}, },
"settings_prefix": { "settings_prefix": {
"info": "Current prefix is {{{PREFIX}}}", "info": "Current prefix is {{{PREFIX}}}",
@@ -140,10 +150,23 @@
"prefix_updated": "Prefix updated", "prefix_updated": "Prefix updated",
"prefix_updated_description": "From now onwards, I shall be called by using ``{{PREFIX}}``" "prefix_updated_description": "From now onwards, I shall be called by using ``{{PREFIX}}``"
}, },
"settings_language": {
"info": "Current language is {{LANGUAGE}}",
"info_description": "Provide a new language to change\n**Available languages**: ``{{LANGUAGES}}``",
"language_invalid": "Invalid language",
"language_invalid_description": "The language code is invalid or not supported yet",
"language_updated": "Language updated",
"language_updated_description": "I will be using {{LANGUAGE}} from now on"
},
"ping": { "ping": {
"title": "Network performance", "title": "Network performance",
"title_processing": "Measuring network performance ( ◕▿◕ )", "title_processing": "Measuring network performance ( ◕▿◕ )",
"running_on": "Running on {{HOST}}", "running_on": "Running on {{HOST}}",
"development_environment": "Development Environment" "development_environment": "Development Environment"
},
"support": {
"title": "Support",
"info": "[Join {{BOT_NAME}}'s support server]({{{LINK}}})",
"not_available": "{{BOT_NAME}}'s support server is currently unavailable"
} }
} }
+29 -6
View File
@@ -1,8 +1,10 @@
{ {
"common": { "common": {
"available_args": "Available arguments", "available_args": "Available arguments",
"no_permissions": "許可していない (O_O;)", "member_no_permissions": "許可していない (O_O;)",
"no_permissions_description": "このギルドでは ``{{PERMISSIONS}}`` の許可が必要です!", "member_no_permissions_description": "このギルドでは ``{{PERMISSIONS}}`` の許可が必要です!",
"bot_no_permissions": "許可がない (O_O;)",
"bot_no_permissions_description": "そのためには、あたしの``{{PERMISSIONS}}``の権限が必要です",
"processing": "アクションの実行", "processing": "アクションの実行",
"processing_description": "今これを成し遂げています (งᓀ‸ᓂ)ง" "processing_description": "今これを成し遂げています (งᓀ‸ᓂ)ง"
}, },
@@ -35,7 +37,9 @@
"streaming_x": "{{NAME}}をストリーミング中", "streaming_x": "{{NAME}}をストリーミング中",
"listening_x": "{{NAME}}を再生中", "listening_x": "{{NAME}}を再生中",
"watching_x": "{{NAME}}鑑賞中", "watching_x": "{{NAME}}鑑賞中",
"competing_x": "{{NAME}}に参戦中です" "competing_x": "{{NAME}}に参戦中です",
"since": "{{{TIME}}}から",
"end": "{{{TIME}}}で終了"
}, },
"say": { "say": {
"title": "言うこと", "title": "言うこと",
@@ -106,13 +110,19 @@
"queue_added_songs_description": "キューに{{COUNT}}曲を追加", "queue_added_songs_description": "キューに{{COUNT}}曲を追加",
"error": "曲の検索中にエラーが発生" "error": "曲の検索中にエラーが発生"
}, },
"musicplayer_playmy": {
"not_detected": "あなたの活動状況を見ると、現在、音楽を聴いていないようですね"
},
"musicplayer_queue": { "musicplayer_queue": {
"title": "キュー", "title": "キュー",
"valid_args": "``Clear`` ``Shuffle``",
"empty": "キューは空です! (っ˘ω˘ς )", "empty": "キューは空です! (っ˘ω˘ς )",
"now_playing": "再生中", "now_playing": "再生中",
"upcoming_song": "次の曲", "upcoming_song": "次の曲",
"looping_current": "(この曲の繰り返し)", "looping_current": "(この曲の繰り返し)",
"song_x_in_queue": "{{COUNT}}曲も並んでいますよ。" "song_x_in_queue": "{{COUNT}}曲も並んでいますよ。",
"cleared": "キューがクリアされました (。•ˇ‸ˇ•。)",
"shuffled": "キューがシャッフルされました"
}, },
"musicplayer_search": { "musicplayer_search": {
"title": "検索", "title": "検索",
@@ -127,8 +137,8 @@
}, },
"settings": { "settings": {
"title": "設定", "title": "設定",
"description": "**{{GUILD_NAME}}**サーバでの{{BOT_NAME}}の動作変更", "description": "**{{{GUILD_NAME}}}**サーバでの{{BOT_NAME}}の動作変更",
"valid_args": "``Prefix`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``" "valid_args": "``Prefix`` ``Language`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``"
}, },
"settings_prefix": { "settings_prefix": {
"info": "現在の接頭辞は「{{{PREFIX}}}」です。", "info": "現在の接頭辞は「{{{PREFIX}}}」です。",
@@ -140,10 +150,23 @@
"prefix_updated": "プレフィックスを更新", "prefix_updated": "プレフィックスを更新",
"prefix_updated_description": "これ以降、私は 「``{{PREFIX}}``」 を使って呼び出されることになります。" "prefix_updated_description": "これ以降、私は 「``{{PREFIX}}``」 を使って呼び出されることになります。"
}, },
"settings_language": {
"info": "現在の言語は{{LANGUAGE}}です",
"info_description": "変更する新しい言語を提供する\n**利用可能な言語**: ``{{LANGUAGES}}``",
"language_invalid": "無効な言語",
"language_invalid_description": "言語コードが無効であるか、まだサポートされていません",
"language_updated": "言語が変更されました",
"language_updated_description": "これからは{{LANGUAGE}}を使います"
},
"ping": { "ping": {
"title": "ネットワークパフォーマンス", "title": "ネットワークパフォーマンス",
"title_processing": "ネットワーク性能の測定 ( ◕▿◕ )", "title_processing": "ネットワーク性能の測定 ( ◕▿◕ )",
"running_on": "{{HOST}}で実行中", "running_on": "{{HOST}}で実行中",
"development_environment": "開発環境" "development_environment": "開発環境"
},
"support": {
"title": "サポート",
"info": "[{{BOT_NAME}}のサポートサーバーに参加する]({{{LINK}}})",
"not_available": "{{BOT_NAME}}のサポートサーバーは現在利用できません"
} }
} }
+29 -6
View File
@@ -1,8 +1,10 @@
{ {
"common": { "common": {
"available_args": "อาร์กิวเมนต์ ตัวเลือกที่ใช้ได้", "available_args": "อาร์กิวเมนต์ ตัวเลือกที่ใช้ได้",
"no_permissions": "คุณไม่มีสิทธิ์เข้าถึง (O_O;)", "member_no_permissions": "คุณไม่มีสิทธิ์เข้าถึง (O_O;)",
"no_permissions_description": "คุณต้องมีสิทธิ์เข้าถึง ``{{PERMISSIONS}}`` ในกิลด์นี้!", "member_no_permissions_description": "คุณต้องมีสิทธิ์เข้าถึง ``{{PERMISSIONS}}`` ในกิลด์นี้!",
"bot_no_permissions": "ฉันไม่มีสิทธิ์เข้าถึง (O_O;)",
"bot_no_permissions_description": "ฉันต้องการสิทธิ์เข้าถึง ``{{PERMISSIONS}}``",
"processing": "รอแปปนึงน้า", "processing": "รอแปปนึงน้า",
"processing_description": "ฉันกำลังดำเนินการให้อยู่ (งᓀ‸ᓂ)ง" "processing_description": "ฉันกำลังดำเนินการให้อยู่ (งᓀ‸ᓂ)ง"
}, },
@@ -35,7 +37,9 @@
"streaming_x": "กำลังสตรีม {{NAME}}", "streaming_x": "กำลังสตรีม {{NAME}}",
"listening_x": "กำลังฟัง {{NAME}}", "listening_x": "กำลังฟัง {{NAME}}",
"watching_x": "กำลังดู {{NAME}}", "watching_x": "กำลังดู {{NAME}}",
"competing_x": "กำลังแข่งขัน {{NAME}}" "competing_x": "กำลังแข่งขัน {{NAME}}",
"since": "ตั้งแต่ {{{TIME}}}",
"end": "สิ้นสุดใน {{{TIME}}}"
}, },
"say": { "say": {
"title": "สั่งพูด", "title": "สั่งพูด",
@@ -106,13 +110,19 @@
"queue_added_songs_description": "เพิ่ม {{COUNT}} เพลงในคิว", "queue_added_songs_description": "เพิ่ม {{COUNT}} เพลงในคิว",
"error": "เกิดข้อผิดพลาดขณะค้นหาเพลง" "error": "เกิดข้อผิดพลาดขณะค้นหาเพลง"
}, },
"musicplayer_playmy": {
"not_detected": "เท่าที่ดูจากกิจกรรมของคุณ เหมือนว่าตอนนี้คุณไม่ได้เล่นเพลงอะไรอยู่นะ"
},
"musicplayer_queue": { "musicplayer_queue": {
"title": "คิวเพลง", "title": "คิวเพลง",
"valid_args": "``Clear`` ``Shuffle``",
"empty": "ตอนนี้คิวว่างนะ (っ˘ω˘ς )", "empty": "ตอนนี้คิวว่างนะ (っ˘ω˘ς )",
"now_playing": "กำลังเล่น", "now_playing": "กำลังเล่น",
"upcoming_song": "เพลงต่อไป", "upcoming_song": "เพลงต่อไป",
"looping_current": "(กำลังวนซ้ำเพลงนี้)", "looping_current": "(กำลังวนซ้ำเพลงนี้)",
"song_x_in_queue": "ตอนนี้มี {{COUNT}} เพลงอยู่ในคิว!" "song_x_in_queue": "ตอนนี้มี {{COUNT}} เพลงอยู่ในคิว!",
"cleared": "เคลียร์คิวเรียบร้อยละ~ (。•ˇ‸ˇ•。)",
"shuffled": "สุ่มคิวเรียบร้อยละ"
}, },
"musicplayer_search": { "musicplayer_search": {
"title": "ค้นหา", "title": "ค้นหา",
@@ -127,8 +137,8 @@
}, },
"settings": { "settings": {
"title": "การตั้งค่า", "title": "การตั้งค่า",
"description": "เปลี่ยนพฤติกรรมของ {{BOT_NAME}} บน **{{GUILD_NAME}}**", "description": "เปลี่ยนพฤติกรรมของ {{BOT_NAME}} บน **{{{GUILD_NAME}}}**",
"valid_args": "``Prefix`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``" "valid_args": "``Prefix`` ``Language`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``"
}, },
"settings_prefix": { "settings_prefix": {
"info": "คำนำหน้าปัจจุบันคือ {{{PREFIX}}}", "info": "คำนำหน้าปัจจุบันคือ {{{PREFIX}}}",
@@ -140,10 +150,23 @@
"prefix_updated": "อัปเดตคำนำหน้าแล้ว", "prefix_updated": "อัปเดตคำนำหน้าแล้ว",
"prefix_updated_description": "จากนี้ไป คุณสามารถเรียกฉันโดยใช้ ``{{PREFIX}}`` นะ" "prefix_updated_description": "จากนี้ไป คุณสามารถเรียกฉันโดยใช้ ``{{PREFIX}}`` นะ"
}, },
"settings_language": {
"info": "ภาษาปัจจุบันตอนนี้คือ {{LANGUAGE}}",
"info_description": "ระบุภาษาใหม่เพื่อเปลี่ยน\n**ภาษาที่ใช้ได้**: ``{{LANGUAGES}}``",
"language_invalid": "ภาษาไม่ถูกต้อง",
"language_invalid_description": "รหัสภาษาไม่ถูกต้องหรือยังไม่รองรับ",
"language_updated": "เปลี่ยนภาษาเรียบร้อย",
"language_updated_description": "ตั้งแต่นี้ไปฉันจะใช้ภาษา {{LANGUAGE}}"
},
"ping": { "ping": {
"title": "ประสิทธิภาพของเครือข่าย", "title": "ประสิทธิภาพของเครือข่าย",
"title_processing": "กำลังวัดประสิทธิภาพของเครือข่าย ( ◕▿◕ )", "title_processing": "กำลังวัดประสิทธิภาพของเครือข่าย ( ◕▿◕ )",
"running_on": "ทำงานอยู่บน {{HOST}}", "running_on": "ทำงานอยู่บน {{HOST}}",
"development_environment": "โหมดกำลังพัฒนา" "development_environment": "โหมดกำลังพัฒนา"
},
"support": {
"title": "ติดต่อฝ่ายสนับสนุน",
"info": "[เข้าร่วมเซิร์ฟเวอร์สนับสนุนของ {{BOT_NAME}}]({{{LINK}}})",
"not_available": "เซิร์ฟเวอร์สนับสนุนของ {{BOT_NAME}} ไม่พร้อมใช้งานในขณะนี้"
} }
} }
+8 -9
View File
@@ -1,7 +1,6 @@
{ {
"dependencies": { "dependencies": {
"@discordjs/builders": "^0.15.0", "@discordjs/opus": "^0.8.0",
"@discordjs/opus": "^0.7.0",
"@discordjs/rest": "^0.5.0", "@discordjs/rest": "^0.5.0",
"@discordjs/voice": "^0.10.0", "@discordjs/voice": "^0.10.0",
"@types/color-thief-node": "^1.0.1", "@types/color-thief-node": "^1.0.1",
@@ -11,8 +10,8 @@
"color-thief-node": "^1.0.4", "color-thief-node": "^1.0.4",
"country-code-emoji": "^2.3.0", "country-code-emoji": "^2.3.0",
"country-code-lookup": "^0.0.20", "country-code-lookup": "^0.0.20",
"discord-api-types": "^0.35.0", "discord-api-types": "^0.36.2",
"discord.js": "^13.8.1", "discord.js": "14.0.1",
"dotenv": "^16.0.1", "dotenv": "^16.0.1",
"i18n": "^0.15.0", "i18n": "^0.15.0",
"libsodium-wrappers": "^0.7.10", "libsodium-wrappers": "^0.7.10",
@@ -28,14 +27,14 @@
"zlib-sync": "^0.1.7" "zlib-sync": "^0.1.7"
}, },
"devDependencies": { "devDependencies": {
"@prisma/client": "^3.15.2", "@prisma/client": "^4.0.0",
"@types/bluebird": "^3.5.36", "@types/bluebird": "^3.5.36",
"@types/i18n": "^0.13.3", "@types/i18n": "^0.13.4",
"@types/node": "^18.0.0", "@types/node": "^18.0.4",
"@types/ping": "^0.4.1", "@types/ping": "^0.4.1",
"@types/validator": "^13.7.3", "@types/validator": "^13.7.4",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
"prisma": "^3.15.2", "prisma": "^4.0.0",
"ts-node-dev": "^2.0.0" "ts-node-dev": "^2.0.0"
}, },
"scripts": { "scripts": {
+2 -2
View File
@@ -1,4 +1,4 @@
import { Guild } from 'discord.js'; import { ActivityType, Guild } from 'discord.js';
import DiscordModule from '../utils/DiscordModule'; import DiscordModule from '../utils/DiscordModule';
import DiscordProvider from '../providers/Discord'; import DiscordProvider from '../providers/Discord';
@@ -48,7 +48,7 @@ export default class Core extends DiscordModule {
private setActivity() { private setActivity() {
DiscordProvider.client.user!.setActivity('for your heart 💖', { DiscordProvider.client.user!.setActivity('for your heart 💖', {
type: 'COMPETING' type: ActivityType.Competing
}); });
} }
} }
+9 -9
View File
@@ -1,11 +1,11 @@
import { User as PrismaUser, Guild as PrismaGuild } from '@prisma/client'; import { User as PrismaUser, Guild as PrismaGuild } from '@prisma/client';
import { import {
Message, Message,
Interaction, ActionRowBuilder,
CommandInteraction, CommandInteraction,
MessageActionRow, ButtonInteraction,
MessageButton, ButtonBuilder,
ButtonInteraction ButtonStyle
} from 'discord.js'; } from 'discord.js';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule'; import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
@@ -162,15 +162,15 @@ export default class Debug extends DiscordModule {
}); });
}, },
invalidInteraction: async (data: HybridInteractionMessage) => { invalidInteraction: async (data: HybridInteractionMessage) => {
const row = new MessageActionRow().addComponents( const row = new ActionRowBuilder<ButtonBuilder>().addComponents([
new MessageButton() new ButtonBuilder()
.setEmoji('😥') .setEmoji('😥')
.setLabel( .setLabel(
'Make invalid interaction (Wait 7 seconds, check error in console or logs)' 'Make invalid interaction (Wait 7 seconds, check error in console or logs)'
) )
.setCustomId('dev_make_invalid_interaction') .setCustomId('dev_make_invalid_interaction')
.setStyle('PRIMARY') .setStyle(ButtonStyle.Primary)
); ]);
await sendHybridInteractionMessageResponse(data, { await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INVALID_TEST(data)], embeds: [EMBEDS.INVALID_TEST(data)],
components: [row] components: [row]
@@ -187,7 +187,7 @@ export default class Debug extends DiscordModule {
}); });
query = args[0].toLowerCase(); query = args[0].toLowerCase();
} else if (data.isSlashCommand()) { } else if (data.isApplicationCommand()) {
query = args.getSubcommand(); query = args.getSubcommand();
} }
+42 -37
View File
@@ -1,4 +1,4 @@
import { Message, MessageEmbed, Interaction, CommandInteraction, TextChannel } from 'discord.js'; import { Message, EmbedBuilder, CommandInteraction, TextChannel, EmbedData } from 'discord.js';
import { Promise } from 'bluebird'; import { Promise } from 'bluebird';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
@@ -21,35 +21,37 @@ import DiscordProvider from '../../providers/Discord';
import Prisma from '../../providers/Prisma'; import Prisma from '../../providers/Prisma';
import Users from '../../services/Users'; import Users from '../../services/Users';
const hexToDecimal = (hex: string) => parseInt(hex.replace('#', ''), 16);
const EMBEDS = { const EMBEDS = {
ANNOUNCEMENT_INFO: (data: Message | Interaction) => { ANNOUNCEMENT_INFO: (data: HybridInteractionMessage) => {
return makeInfoEmbed({ return makeInfoEmbed({
title: 'Service Announcement', title: 'Service Announcement',
description: `This module contains management tool for announcement feed\n The announcement message is located in \`\`configs/ServiceAnnouncement.json\`\``, description: `This module contains management tool for announcement feed\n The announcement message is located in \`\`configs/ServiceAnnouncement.json\`\``,
fields: [ fields: [
{ {
name: 'Available arguments', name: 'Available arguments',
value: '``reload`` ``previewNews`` ``sendNews`` ``previewMaintenance`` ``sendMaintenance`` ``previewMessage`` ``sendMessage`` ``previewAlert`` ``sendAlert``' value: '``Reload`` ``PreviewNews`` ``SendNews`` ``PreviewMaintenance`` ``SendMaintenance`` ``PreviewMessage`` ``SendMessage`` ``PreviewAlert`` ``SendAlert``'
} }
], ],
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
NOT_DEVELOPER: (data: Message | Interaction) => { NOT_DEVELOPER: (data: HybridInteractionMessage) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'Developer only', title: 'Developer only',
description: `This command is restricted to the developers only`, description: `This command is restricted to the developers only`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
MAKE_PAYLOAD: (payload: any) => { MAKE_PAYLOAD: (payload: EmbedData) => {
const user = DiscordProvider.client.user; const user = DiscordProvider.client.user;
payload.footer = { payload.footer = {
text: `${user?.username}`, text: `${user?.username}`,
iconURL: `${user?.displayAvatarURL()}?size=4096` iconURL: `${user?.displayAvatarURL()}?size=4096`
}; };
if (!payload.timestamp) payload.timestamp = new Date(); if (!payload.timestamp) payload.timestamp = Date.now();
if (payload.thumbnail?.url === 'bot_avatar') if (payload.thumbnail?.url === 'bot_avatar')
payload.thumbnail.url = `${user?.displayAvatarURL()}?size=4096`; payload.thumbnail.url = `${user?.displayAvatarURL()}?size=4096`;
@@ -58,49 +60,52 @@ const EMBEDS = {
payload.image.url = `${user?.displayAvatarURL()}?size=4096`; payload.image.url = `${user?.displayAvatarURL()}?size=4096`;
if (payload.description) if (payload.description)
payload.description = payload.description.replaceAll('{bot_username}', user?.username); payload.description = payload.description.replaceAll('{bot_username}', user?.username!);
return new MessageEmbed(payload); if (payload.color && isNaN(payload.color))
payload.color = hexToDecimal(payload.color.toString());
return new EmbedBuilder(payload);
}, },
RELOADED: (data: Message | Interaction) => { RELOADED: (data: HybridInteractionMessage) => {
return makeSuccessEmbed({ return makeSuccessEmbed({
title: 'Service Announcement Configuration Reloaded', title: 'Service Announcement Configuration Reloaded',
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
RELOAD_ERROR: (data: Message | Interaction, error: string) => { RELOAD_ERROR: (data: HybridInteractionMessage, error: string) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'Unable to reload Service Announcement Configuration', title: 'Unable to reload Service Announcement Configuration',
description: `\`\`\`${error}\`\`\``, description: `\`\`\`${error}\`\`\``,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
SENDING_SERVICE_ANNOUNCEMENT: (data: Message | Interaction) => { SENDING_SERVICE_ANNOUNCEMENT: (data: HybridInteractionMessage) => {
return makeProcessingEmbed({ return makeProcessingEmbed({
title: 'Service Announcement', title: 'Service Announcement',
description: `Broadcasting Service Announcement`, description: `Broadcasting Service Announcement`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
SERVICE_ANNOUNCEMENT_SENT: (data: Message | Interaction) => { SERVICE_ANNOUNCEMENT_SENT: (data: HybridInteractionMessage) => {
return makeSuccessEmbed({ return makeSuccessEmbed({
title: 'Service Announcement', title: 'Service Announcement',
description: `Broadcasted Service Announcement`, description: `Broadcasted Service Announcement`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
SERVICE_ANNOUNCEMENT_SENT_WITH_ERRORS: (data: Message | Interaction) => { SERVICE_ANNOUNCEMENT_SENT_WITH_ERRORS: (data: HybridInteractionMessage) => {
return makeWarningEmbed({ return makeWarningEmbed({
title: 'Service Announcement', title: 'Service Announcement',
description: `Broadcasted Service Announcement with errors, check console for more info`, description: `Broadcasted Service Announcement with errors, check console for more info`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
} }
}; };
let Announcements = { let Announcements = {
News: { News: {
color: '#FAEDF0', color: hexToDecimal('#FAEDF0'),
title: '📰 Newsletter', title: '📰 Newsletter',
description: 'Some description here', description: 'Some description here',
thumbnail: { thumbnail: {
@@ -108,7 +113,7 @@ let Announcements = {
} }
}, },
Maintenance: { Maintenance: {
color: '#383b80', color: hexToDecimal('#383b80'),
title: '🔧 Maintenance', title: '🔧 Maintenance',
description: 'The bot is going offline for maintenance.', description: 'The bot is going offline for maintenance.',
thumbnail: { thumbnail: {
@@ -116,7 +121,7 @@ let Announcements = {
} }
}, },
Message: { Message: {
color: '#A1DE93', color: hexToDecimal('#A1DE93'),
title: '✉️Message', title: '✉️Message',
description: 'Some description here', description: 'Some description here',
thumbnail: { thumbnail: {
@@ -124,7 +129,7 @@ let Announcements = {
} }
}, },
Alert: { Alert: {
color: '#EC255A', color: hexToDecimal('#EC255A'),
title: '🚨 Alert', title: '🚨 Alert',
description: 'Some description here', description: 'Some description here',
thumbnail: { thumbnail: {
@@ -162,7 +167,7 @@ export default class ServiceAnnouncement extends DiscordModule {
async run(data: HybridInteractionMessage, args: any) { async run(data: HybridInteractionMessage, args: any) {
if (!Users.isDeveloper(data.getUser()!.id)) if (!Users.isDeveloper(data.getUser()!.id))
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NOT_DEVELOPER(data.getRaw())] embeds: [EMBEDS.NOT_DEVELOPER(data)]
}); });
const channel = data.getChannel(); const channel = data.getChannel();
@@ -180,7 +185,7 @@ export default class ServiceAnnouncement extends DiscordModule {
} catch (err: any) { } catch (err: any) {
Logger.error('Unable to load custom ServiceAnnouncement config: ' + err); Logger.error('Unable to load custom ServiceAnnouncement config: ' + err);
return await sendMessage(channel, undefined, { return await sendMessage(channel, undefined, {
embeds: [EMBEDS.RELOAD_ERROR(data.getRaw(), err)] embeds: [EMBEDS.RELOAD_ERROR(data, err)]
}); });
} }
} else { } else {
@@ -192,7 +197,7 @@ export default class ServiceAnnouncement extends DiscordModule {
} }
return await sendMessage(channel, undefined, { return await sendMessage(channel, undefined, {
embeds: [EMBEDS.RELOADED(data.getRaw())] embeds: [EMBEDS.RELOADED(data)]
}); });
}, },
previewNews: async (data: HybridInteractionMessage) => { previewNews: async (data: HybridInteractionMessage) => {
@@ -247,7 +252,7 @@ export default class ServiceAnnouncement extends DiscordModule {
let _placeholder = await sendHybridInteractionMessageResponse( let _placeholder = await sendHybridInteractionMessageResponse(
data, data,
{ embeds: [EMBEDS.SENDING_SERVICE_ANNOUNCEMENT(data.getRaw())] }, { embeds: [EMBEDS.SENDING_SERVICE_ANNOUNCEMENT(data)] },
true true
); );
if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder); if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder);
@@ -295,15 +300,15 @@ export default class ServiceAnnouncement extends DiscordModule {
.getMessage() .getMessage()
.edit({ .edit({
embeds: [ embeds: [
EMBEDS.SERVICE_ANNOUNCEMENT_SENT_WITH_ERRORS(data.getRaw()) EMBEDS.SERVICE_ANNOUNCEMENT_SENT_WITH_ERRORS(data)
] ]
}); });
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
data, data,
{ {
embeds: [ embeds: [
EMBEDS.SERVICE_ANNOUNCEMENT_SENT_WITH_ERRORS(data.getRaw()) EMBEDS.SERVICE_ANNOUNCEMENT_SENT_WITH_ERRORS(data)
] ]
}, },
true true
@@ -312,11 +317,11 @@ export default class ServiceAnnouncement extends DiscordModule {
if (data && data.isMessage() && placeholder && placeholder.isMessage()) if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder return placeholder
.getMessage() .getMessage()
.edit({ embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_SENT(data.getRaw())] }); .edit({ embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_SENT(data)] });
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
data, data,
{ embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_SENT(data.getRaw())] }, { embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_SENT(data)] },
true true
); );
} }
@@ -328,17 +333,17 @@ export default class ServiceAnnouncement extends DiscordModule {
if (data.isMessage()) { if (data.isMessage()) {
if (args.length === 0) if (args.length === 0)
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ANNOUNCEMENT_INFO(data.getRaw())] embeds: [EMBEDS.ANNOUNCEMENT_INFO(data)]
}); });
query = args[0].toLowerCase(); query = args[0].toLowerCase();
} else if (data.isSlashCommand()) { } else if (data.isApplicationCommand()) {
query = args.getSubcommand(); query = args.getSubcommand();
} }
switch (query) { switch (query) {
case 'info': case 'info':
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ANNOUNCEMENT_INFO(data.getRaw())] embeds: [EMBEDS.ANNOUNCEMENT_INFO(data)]
}); });
case 'reload': case 'reload':
return await funct.reload(data); return await funct.reload(data);
+3 -3
View File
@@ -1,4 +1,4 @@
import { CommandInteraction, Interaction, Message } from 'discord.js'; import { CommandInteraction, Message } from 'discord.js';
import { I18n } from 'i18n'; import { I18n } from 'i18n';
import DiscordProvider from '../providers/Discord'; import DiscordProvider from '../providers/Discord';
@@ -24,7 +24,7 @@ const EMBEDS = {
fields: [ fields: [
{ {
name: `☕ ${locale.__('help.general')}`, name: `☕ ${locale.__('help.general')}`,
value: `help, ping, invite, userinfo, stats`, value: `help, ping, invite, userinfo, stats, support`,
inline: true inline: true
}, },
{ {
@@ -34,7 +34,7 @@ const EMBEDS = {
}, },
{ {
name: `🎵 ${locale.__('help.music')}`, name: `🎵 ${locale.__('help.music')}`,
value: `play (p), search, skip, pause, resume, queue (q), nowplaying (np), repeat (loop), summon (join), disconnect (leave, dc)`, value: `play (p), playmy, search, skip, pause, resume, queue (q), nowplaying (np), repeat (loop), summon (join), disconnect (leave, dc)`,
inline: true inline: true
}, },
{ {
+40 -40
View File
@@ -1,4 +1,4 @@
import { Message, Interaction, CommandInteraction } from 'discord.js'; import { Message, CommandInteraction, BaseInteraction } from 'discord.js';
import Users from '../services/Users'; import Users from '../services/Users';
@@ -18,7 +18,7 @@ import {
} from '../utils/DiscordInteraction'; } from '../utils/DiscordInteraction';
const EMBEDS = { const EMBEDS = {
INTERACTION_INFO: (data: Message | Interaction) => { INTERACTION_INFO: (data: HybridInteractionMessage) => {
return makeInfoEmbed({ return makeInfoEmbed({
title: 'Interaction', title: 'Interaction',
description: `This module contains management tool for interaction based contents`, description: `This module contains management tool for interaction based contents`,
@@ -28,47 +28,47 @@ const EMBEDS = {
value: '``reloadAll`` ``unloadAll`` ``reloadglobal``' value: '``reloadAll`` ``unloadAll`` ``reloadglobal``'
} }
], ],
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
PROCESSING: (data: Message | Interaction) => { PROCESSING: (data: HybridInteractionMessage) => {
return makeProcessingEmbed({ return makeProcessingEmbed({
icon: data instanceof Message ? undefined : '⌛', icon: data instanceof Message ? undefined : '⌛',
title: `Performing actions`, title: `Performing actions`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
NOT_DEVELOPER: (data: Message | Interaction) => { NOT_DEVELOPER: (data: HybridInteractionMessage) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'Developer only', title: 'Developer only',
description: `This command is restricted to the developers only`, description: `This command is restricted to the developers only`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
UNLOADALL_SUCCESS: (data: Message | Interaction) => { UNLOADALL_SUCCESS: (data: HybridInteractionMessage) => {
return makeSuccessEmbed({ return makeSuccessEmbed({
title: 'Unloaded all Interaction', title: 'Unloaded all Interaction',
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
UNLOADALL_ERROR: (data: Message | Interaction, err: any) => { UNLOADALL_ERROR: (data: HybridInteractionMessage, err: any) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'An error occurred while trying to unload interaction', title: 'An error occurred while trying to unload interaction',
description: '```' + err + '```', description: '```' + err + '```',
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
RELOADALL_SUCCESS: (data: Message | Interaction) => { RELOADALL_SUCCESS: (data: HybridInteractionMessage) => {
return makeSuccessEmbed({ return makeSuccessEmbed({
title: 'Reloaded all Interaction', title: 'Reloaded all Interaction',
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
RELOADALL_ERROR: (data: Message | Interaction, err: any) => { RELOADALL_ERROR: (data: HybridInteractionMessage, err: any) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'An error occurred while trying to reload interaction', title: 'An error occurred while trying to reload interaction',
description: '```' + err + '```', description: '```' + err + '```',
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
} }
}; };
@@ -92,7 +92,7 @@ export default class InteractionManager extends DiscordModule {
if (!Users.isDeveloper(user.id)) if (!Users.isDeveloper(user.id))
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NOT_DEVELOPER(data.getRaw())] embeds: [EMBEDS.NOT_DEVELOPER(data)]
}); });
const funct = { const funct = {
@@ -100,7 +100,7 @@ export default class InteractionManager extends DiscordModule {
let placeholder: HybridInteractionMessage | undefined; let placeholder: HybridInteractionMessage | undefined;
let _placeholder = await sendHybridInteractionMessageResponse(data, { let _placeholder = await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.PROCESSING(data.getRaw())] embeds: [EMBEDS.PROCESSING(data)]
}); });
if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder); if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder);
@@ -110,22 +110,22 @@ export default class InteractionManager extends DiscordModule {
if (data && data.isMessage() && placeholder && placeholder.isMessage()) if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder return placeholder
.getMessage() .getMessage()
.edit({ embeds: [EMBEDS.UNLOADALL_SUCCESS(data.getRaw())] }); .edit({ embeds: [EMBEDS.UNLOADALL_SUCCESS(data)] });
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
data, data,
{ embeds: [EMBEDS.UNLOADALL_SUCCESS(data.getRaw())] }, { embeds: [EMBEDS.UNLOADALL_SUCCESS(data)] },
true true
); );
} catch (err) { } catch (err) {
if (data && data.isMessage() && placeholder && placeholder.isMessage()) if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder return placeholder
.getMessage() .getMessage()
.edit({ embeds: [EMBEDS.UNLOADALL_ERROR(data.getRaw(), err)] }); .edit({ embeds: [EMBEDS.UNLOADALL_ERROR(data, err)] });
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
data, data,
{ embeds: [EMBEDS.UNLOADALL_ERROR(data.getRaw(), err)] }, { embeds: [EMBEDS.UNLOADALL_ERROR(data, err)] },
true true
); );
} }
@@ -134,7 +134,7 @@ export default class InteractionManager extends DiscordModule {
let placeholder: HybridInteractionMessage | undefined; let placeholder: HybridInteractionMessage | undefined;
let _placeholder = await sendHybridInteractionMessageResponse(data, { let _placeholder = await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.PROCESSING(data.getRaw())] embeds: [EMBEDS.PROCESSING(data)]
}); });
if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder); if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder);
@@ -145,22 +145,22 @@ export default class InteractionManager extends DiscordModule {
if (data && data.isMessage() && placeholder && placeholder.isMessage()) if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder return placeholder
.getMessage() .getMessage()
.edit({ embeds: [EMBEDS.RELOADALL_SUCCESS(data.getRaw())] }); .edit({ embeds: [EMBEDS.RELOADALL_SUCCESS(data)] });
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
data, data,
{ embeds: [EMBEDS.RELOADALL_SUCCESS(data.getRaw())] }, { embeds: [EMBEDS.RELOADALL_SUCCESS(data)] },
true true
); );
} catch (err) { } catch (err) {
if (data && data.isMessage() && placeholder && placeholder.isMessage()) if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder return placeholder
.getMessage() .getMessage()
.edit({ embeds: [EMBEDS.RELOADALL_ERROR(data.getRaw(), err)] }); .edit({ embeds: [EMBEDS.RELOADALL_ERROR(data, err)] });
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
data, data,
{ embeds: [EMBEDS.RELOADALL_ERROR(data.getRaw(), err)] }, { embeds: [EMBEDS.RELOADALL_ERROR(data, err)] },
true true
); );
} }
@@ -169,7 +169,7 @@ export default class InteractionManager extends DiscordModule {
let placeholder: HybridInteractionMessage | undefined; let placeholder: HybridInteractionMessage | undefined;
let _placeholder = await sendHybridInteractionMessageResponse(data, { let _placeholder = await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.PROCESSING(data.getRaw())] embeds: [EMBEDS.PROCESSING(data)]
}); });
if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder); if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder);
@@ -180,22 +180,22 @@ export default class InteractionManager extends DiscordModule {
if (data && data.isMessage() && placeholder && placeholder.isMessage()) if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder return placeholder
.getMessage() .getMessage()
.edit({ embeds: [EMBEDS.RELOADALL_SUCCESS(data.getRaw())] }); .edit({ embeds: [EMBEDS.RELOADALL_SUCCESS(data)] });
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
data, data,
{ embeds: [EMBEDS.RELOADALL_SUCCESS(data.getRaw())] }, { embeds: [EMBEDS.RELOADALL_SUCCESS(data)] },
true true
); );
} catch (err) { } catch (err) {
if (data && data.isMessage() && placeholder && placeholder.isMessage()) if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder return placeholder
.getMessage() .getMessage()
.edit({ embeds: [EMBEDS.RELOADALL_ERROR(data.getRaw(), err)] }); .edit({ embeds: [EMBEDS.RELOADALL_ERROR(data, err)] });
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
data, data,
{ embeds: [EMBEDS.RELOADALL_ERROR(data.getRaw(), err)] }, { embeds: [EMBEDS.RELOADALL_ERROR(data, err)] },
true true
); );
} }
@@ -207,18 +207,18 @@ export default class InteractionManager extends DiscordModule {
if (data.isMessage()) { if (data.isMessage()) {
if (args.length === 0) if (args.length === 0)
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INTERACTION_INFO(data.getRaw())] embeds: [EMBEDS.INTERACTION_INFO(data)]
}); });
query = args[0].toLowerCase(); query = args[0].toLowerCase();
} else if (data.isSlashCommand()) { } else if (data.isApplicationCommand()) {
query = args.getSubcommand(); query = args.getSubcommand();
} }
switch (query) { switch (query) {
case 'info': case 'info':
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INTERACTION_INFO(data.getRaw())] embeds: [EMBEDS.INTERACTION_INFO(data)]
}); });
case 'reloadall': case 'reloadall':
return await funct.reloadAll(data); return await funct.reloadAll(data);
+3 -3
View File
@@ -1,4 +1,4 @@
import { Message, CommandInteraction, Interaction } from 'discord.js'; import { Message, CommandInteraction } from 'discord.js';
import { I18n } from 'i18n'; import { I18n } from 'i18n';
import Environment from '../providers/Environment'; import Environment from '../providers/Environment';
@@ -13,8 +13,8 @@ const EMBEDS = {
INVITE_INFO: (data: HybridInteractionMessage, locale: I18n) => { INVITE_INFO: (data: HybridInteractionMessage, locale: I18n) => {
const user = data.getUser(); const user = data.getUser();
let message; let message;
if (!Environment.get().PRIVATE_BOT || user != null && Users.isDeveloper(user.id)) if (!(Environment.get().PRIVATE_BOT === 'true') || user != null && Users.isDeveloper(user.id))
message = locale.__('invite.info', { BOT_NAME: DiscordProvider.client.user!.username, LINK: `https://discord.com/api/oauth2/authorize?client_id=${DiscordProvider.client.user?.id}&permissions=0&scope=bot%20applications.commands`}); message = locale.__('invite.info', { BOT_NAME: DiscordProvider.client.user!.username, LINK: `https://discord.com/api/oauth2/authorize?client_id=${DiscordProvider.client.user?.id}&permissions=8&scope=bot%20applications.commands`});
return makeInfoEmbed({ return makeInfoEmbed({
title: `Invite`, title: `Invite`,
+109 -96
View File
@@ -3,15 +3,17 @@ import {
GuildChannel, GuildChannel,
GuildMember, GuildMember,
Message, Message,
Permissions,
TextChannel, TextChannel,
MessageActionRow, ButtonBuilder,
MessageButton,
Interaction,
CommandInteraction, CommandInteraction,
Role, Role,
ThreadChannel, ThreadChannel,
ButtonInteraction ButtonInteraction,
BaseInteraction,
PermissionsBitField,
EmbedBuilder,
ButtonStyle,
ActionRowBuilder,
} from 'discord.js'; } from 'discord.js';
import DiscordProvider from '../providers/Discord'; import DiscordProvider from '../providers/Discord';
@@ -27,44 +29,44 @@ import {
import DiscordModule, { HybridInteractionMessage } from '../utils/DiscordModule'; import DiscordModule, { HybridInteractionMessage } from '../utils/DiscordModule';
const EMBEDS = { const EMBEDS = {
NO_PERMISSION: (data: Message | Interaction) => { NO_PERMISSION: (data: Message | BaseInteraction) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'You need ``ADMINISTRATOR`` permission on this guild!', title: 'You need ``ADMINISTRATOR`` permission on this guild!',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
NO_PARAMETER: (data: Message | Interaction) => { NO_PARAMETER: (data: Message | BaseInteraction) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'Missing parameter', title: 'Missing parameter',
description: `You must define membership screening channel and role to enable this feature`, description: `You must define membership screening channel and role to enable this feature`,
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
NO_ROLE_MENTIONED: (data: Message | Interaction) => { NO_ROLE_MENTIONED: (data: Message | BaseInteraction) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'No role mentioned', title: 'No role mentioned',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
NO_ROLE_FOUND: (data: Message | Interaction) => { NO_ROLE_FOUND: (data: Message | BaseInteraction) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'Cannot find that role', title: 'Cannot find that role',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
NO_CHANNEL_MENTIONED: (data: Message | Interaction) => { NO_CHANNEL_MENTIONED: (data: Message | BaseInteraction) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'No channel mentioned', title: 'No channel mentioned',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
NO_CHANNEL_FOUND: (data: Message | Interaction) => { NO_CHANNEL_FOUND: (data: Message | BaseInteraction) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'Cannot find that channel', title: 'Cannot find that channel',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
MSINFO: (data: Message | Interaction) => { MSINFO: (data: Message | BaseInteraction) => {
return makeInfoEmbed({ return makeInfoEmbed({
title: 'Membership Screening', title: 'Membership Screening',
description: `Membership Screening is a feature to prevent unwanted people to join your guild, similar to whitelist feature. Moderators can approve or deny join request`, description: `Membership Screening is a feature to prevent unwanted people to join your guild, similar to whitelist feature. Moderators can approve or deny join request`,
@@ -74,58 +76,58 @@ const EMBEDS = {
value: '``setRole`` ``setChannel`` ``createMessage``' value: '``setRole`` ``setChannel`` ``createMessage``'
} }
], ],
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
ALREADY_ENABLED: (data: Message | Interaction) => { ALREADY_ENABLED: (data: Message | BaseInteraction) => {
return makeInfoEmbed({ return makeInfoEmbed({
title: 'Membership Screening is already enabled', title: 'Membership Screening is already enabled',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
MESSAGE_CREATED: (data: Message | Interaction) => { MESSAGE_CREATED: (data: Message | BaseInteraction) => {
return makeSuccessEmbed({ return makeSuccessEmbed({
title: 'Membership Screening message created', title: 'Membership Screening message created',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
ALREADY_DISABLED: (data: Message | Interaction) => { ALREADY_DISABLED: (data: Message | BaseInteraction) => {
return makeInfoEmbed({ return makeInfoEmbed({
title: 'Membership Screening is already disabled', title: 'Membership Screening is already disabled',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
ENABLED: (data: Message | Interaction) => { ENABLED: (data: Message | BaseInteraction) => {
return makeSuccessEmbed({ return makeSuccessEmbed({
title: 'Enabled Membership Screening', title: 'Enabled Membership Screening',
description: `All new member join request will be sent in your defined channel`, description: `All new member join request will be sent in your defined channel`,
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
DISABLED: (data: Message | Interaction) => { DISABLED: (data: Message | BaseInteraction) => {
return makeSuccessEmbed({ return makeSuccessEmbed({
title: 'Disabled Membership Screening', title: 'Disabled Membership Screening',
description: `No longer accepting request, all new member can join directly`, description: `No longer accepting request, all new member can join directly`,
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
MANAGED_ROLE: (data: Message | Interaction, role: Role) => { MANAGED_ROLE: (data: Message | BaseInteraction, role: Role) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'You cannot use this role', title: 'You cannot use this role',
description: description:
'``' + role.name + '``' + ' is managed by external service and cannot be used', '``' + role.name + '``' + ' is managed by external service and cannot be used',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
CONFIGURED_ROLE: (data: Message | Interaction, role: Role) => { CONFIGURED_ROLE: (data: Message | BaseInteraction, role: Role) => {
return makeSuccessEmbed({ return makeSuccessEmbed({
title: 'Configured Membership Screening Role', title: 'Configured Membership Screening Role',
description: description:
'New member will be given a ' + '``' + role.name + '``' + ' role after approval', 'New member will be given a ' + '``' + role.name + '``' + ' role after approval',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
CONFIGURED_CHANNEL: (data: Message | Interaction, channel: GuildChannel) => { CONFIGURED_CHANNEL: (data: Message | BaseInteraction, channel: GuildChannel) => {
return makeSuccessEmbed({ return makeSuccessEmbed({
title: 'Configured Membership Screening Channel', title: 'Configured Membership Screening Channel',
description: description:
@@ -134,18 +136,18 @@ const EMBEDS = {
channel.name + channel.name +
'``' + '``' +
' can approve or deny request', ' can approve or deny request',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
INVALID_CHANNEL: (data: Message | Interaction, channel: GuildChannel) => { INVALID_CHANNEL: (data: Message | BaseInteraction, channel: GuildChannel) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'Invalid channel type, only TextChannel is supported', title: 'Invalid channel type, only TextChannel is supported',
description: '``' + channel.name + '``' + ' is not a text channel', description: '``' + channel.name + '``' + ' is not a text channel',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
INVALID_CHANNEL_THREAD: ( INVALID_CHANNEL_THREAD: (
data: Message | Interaction, data: Message | BaseInteraction,
channel: GuildChannel | ThreadChannel channel: GuildChannel | ThreadChannel
) => { ) => {
return makeErrorEmbed({ return makeErrorEmbed({
@@ -155,38 +157,38 @@ const EMBEDS = {
channel.name + channel.name +
'``' + '``' +
' is a thread channel. Please use a regular text channel', ' is a thread channel. Please use a regular text channel',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
BOT_NO_PERMISSION: (data: Message | Interaction, channel: GuildChannel) => { BOT_NO_PERMISSION: (data: Message | BaseInteraction, channel: GuildChannel) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: `I don't have permission`, title: `I don't have permission`,
description: 'I cannot access/send message in ' + '``' + channel.name + '``', description: 'I cannot access/send message in ' + '``' + channel.name + '``',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
NO_LONGER_VALID_ROLE: (data: Message | Interaction) => { NO_LONGER_VALID_ROLE: (data: Message | BaseInteraction) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'The configured role is no longer valid. Please update the role in configuration', title: 'The configured role is no longer valid. Please update the role in configuration',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
CANNOT_PERFORM_ASSIGN_ROLE: (data: Message | Interaction) => { CANNOT_PERFORM_ASSIGN_ROLE: (data: Message | BaseInteraction) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'Cannot grant the role to user, make sure I have permission to do that', title: 'Cannot grant the role to user, make sure I have permission to do that',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
CANNOT_PERFORM_ASSIGN_KICK: (data: Message | Interaction) => { CANNOT_PERFORM_ASSIGN_KICK: (data: Message | BaseInteraction) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'Cannot kick the user, make sure I have permission to do that', title: 'Cannot kick the user, make sure I have permission to do that',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
CANNOT_PERFORM_ASSIGN_BAN: (data: Message | Interaction) => { CANNOT_PERFORM_ASSIGN_BAN: (data: Message | BaseInteraction) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: 'Cannot ban the user, make sure I have permission to do that', title: 'Cannot ban the user, make sure I have permission to do that',
user: data instanceof Interaction ? data.user : data.author user: data instanceof BaseInteraction ? data.user : data.author
}); });
}, },
CREATE_MESSAGE: () => { CREATE_MESSAGE: () => {
@@ -246,10 +248,12 @@ export default class MembershipScreening extends DiscordModule {
) )
return; return;
embed[0].footer = { const newEmbed = new EmbedBuilder(embed[0].data);
newEmbed.setFooter({
text: `${interaction.user.username} | v${App.version}`, text: `${interaction.user.username} | v${App.version}`,
iconURL: `${interaction.user.displayAvatarURL()}?size=4096` iconURL: `${interaction.user.displayAvatarURL()}?size=4096`
}; });
if (['approve', 'deny', 'ban'].includes(payload.a)) { if (['approve', 'deny', 'ban'].includes(payload.a)) {
if (!payload.d.requester) return; if (!payload.d.requester) return;
@@ -268,26 +272,35 @@ export default class MembershipScreening extends DiscordModule {
}); });
if (!requesterMember) { if (!requesterMember) {
embed[0].addField('❌ Invalid', `Unable to find the user. User left already?`); newEmbed.addFields([
return await message.edit({ components: [], embeds: embed }); {
name: '❌ Invalid',
value: `Unable to find the user. User left already?`
}
]);
return await message.edit({ components: [], embeds: [newEmbed] });
} }
if (requesterMember.roles.cache.has(PrismaGuild.MembershipScreening_GivenRole)) { if (requesterMember.roles.cache.has(PrismaGuild.MembershipScreening_GivenRole)) {
embed[0].addField( newEmbed.addFields([
'✅ Approved', {
`By: Unknown (User already obtained the role by other means)` name: '✅ Approved',
); value: `By: Unknown (User already obtained the role by other means)`
return await message.edit({ components: [], embeds: embed }); }
]);
return await message.edit({ components: [], embeds: [newEmbed] });
} }
if (payload.a === 'approve') { if (payload.a === 'approve') {
try { try {
await requesterMember.roles.add(role); await requesterMember.roles.add(role);
embed[0].addField( newEmbed.addFields([
'✅ Approved', {
`By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})` name: '✅ Approved',
); value: `By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})`
await message.edit({ components: [], embeds: embed }); }
]);
await message.edit({ components: [], embeds: [newEmbed] });
} catch (err) { } catch (err) {
return interaction.reply({ return interaction.reply({
ephemeral: true, ephemeral: true,
@@ -297,11 +310,13 @@ export default class MembershipScreening extends DiscordModule {
} else if (payload.a === 'deny') { } else if (payload.a === 'deny') {
try { try {
await requesterMember.kick(); await requesterMember.kick();
embed[0].addField( newEmbed.addFields([
'❌ Denied', {
`By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})` name: '❌ Denied',
); value: `By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})`
await message.edit({ components: [], embeds: embed }); }
]);
await message.edit({ components: [], embeds: [newEmbed] });
} catch (err) { } catch (err) {
return interaction.reply({ return interaction.reply({
ephemeral: true, ephemeral: true,
@@ -313,11 +328,13 @@ export default class MembershipScreening extends DiscordModule {
await requesterMember.ban({ await requesterMember.ban({
reason: `Membership Screening, action issued by ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})` reason: `Membership Screening, action issued by ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})`
}); });
embed[0].addField( newEmbed.addFields([
'🔪 Banned', {
`By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})` name: '🔪 Banned',
); value: `By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})`
await message.edit({ components: [], embeds: embed }); }
]);
await message.edit({ components: [], embeds: [newEmbed] });
} catch (err) { } catch (err) {
return interaction.reply({ return interaction.reply({
ephemeral: true, ephemeral: true,
@@ -395,9 +412,9 @@ export default class MembershipScreening extends DiscordModule {
role = role =
data.getMessage().mentions.roles.first() || data.getMessage().mentions.roles.first() ||
guild.roles.cache.find((role) => role.name === _name); guild.roles.cache.find((role) => role.name === _name);
} else if (data.isSlashCommand()) } else if (data.isApplicationCommand())
role = guild.roles.cache.find( role = guild.roles.cache.find(
(role) => role.id === data.getSlashCommand().options.getRole('role')?.id (role) => role.id === data.getSlashCommand().options.get('role')?.role?.id
); );
if (!role) if (!role)
@@ -427,8 +444,8 @@ export default class MembershipScreening extends DiscordModule {
embeds: [EMBEDS.NO_CHANNEL_MENTIONED(data.getRaw())] embeds: [EMBEDS.NO_CHANNEL_MENTIONED(data.getRaw())]
}); });
mentionChannel = data.getMessage().mentions.channels.first(); mentionChannel = data.getMessage().mentions.channels.first();
} else if (data.isSlashCommand()) } else if (data.isApplicationCommand())
mentionChannel = data.getSlashCommand().options.getChannel('channel'); mentionChannel = data.getSlashCommand().options.get('channel', true).channel;
if (!mentionChannel) if (!mentionChannel)
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
@@ -443,15 +460,14 @@ export default class MembershipScreening extends DiscordModule {
embeds: [EMBEDS.INVALID_CHANNEL_THREAD(data.getRaw(), TargetChannel)] embeds: [EMBEDS.INVALID_CHANNEL_THREAD(data.getRaw(), TargetChannel)]
}); });
if (!TargetChannel.isText()) if (!TargetChannel.isTextBased())
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INVALID_CHANNEL(data.getRaw(), TargetChannel)] embeds: [EMBEDS.INVALID_CHANNEL(data.getRaw(), TargetChannel)]
}); });
if ( if (
!guild !guild.members.me?.permissionsIn(TargetChannel)
.me!.permissionsIn(TargetChannel) .has([PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.ViewChannel])
.has([Permissions.FLAGS.SEND_MESSAGES, Permissions.FLAGS.VIEW_CHANNEL])
) )
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.BOT_NO_PERMISSION(data.getRaw(), TargetChannel)] embeds: [EMBEDS.BOT_NO_PERMISSION(data.getRaw(), TargetChannel)]
@@ -470,7 +486,7 @@ export default class MembershipScreening extends DiscordModule {
return await sendMessage(channel, undefined, { return await sendMessage(channel, undefined, {
embeds: [EMBEDS.CREATE_MESSAGE()] embeds: [EMBEDS.CREATE_MESSAGE()]
}); });
else if (data.isSlashCommand()) { else if (data.isApplicationCommand()) {
await sendHybridInteractionMessageResponse(data, { await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.MESSAGE_CREATED(data.getRaw())] embeds: [EMBEDS.MESSAGE_CREATED(data.getRaw())]
}); });
@@ -483,7 +499,7 @@ export default class MembershipScreening extends DiscordModule {
let query; let query;
if (!member.permissions.has([Permissions.FLAGS.ADMINISTRATOR])) if (!member.permissions.has([PermissionsBitField.Flags.Administrator]))
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NO_PERMISSION(data.getRaw())] embeds: [EMBEDS.NO_PERMISSION(data.getRaw())]
}); });
@@ -495,7 +511,7 @@ export default class MembershipScreening extends DiscordModule {
}); });
query = args[0].toLowerCase(); query = args[0].toLowerCase();
} else if (data.isSlashCommand()) query = args.getSubcommand(); } else if (data.isApplicationCommand()) query = args.getSubcommand();
switch (query) { switch (query) {
case 'enable': case 'enable':
@@ -549,9 +565,9 @@ export default class MembershipScreening extends DiscordModule {
embed.setThumbnail(`${member.user.displayAvatarURL()}?size=4096`); embed.setThumbnail(`${member.user.displayAvatarURL()}?size=4096`);
const row = new MessageActionRow() const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents( .addComponents([
new MessageButton() new ButtonBuilder()
.setCustomId( .setCustomId(
JSON.stringify({ JSON.stringify({
m: 'MembershipScreening', m: 'MembershipScreening',
@@ -563,10 +579,8 @@ export default class MembershipScreening extends DiscordModule {
) )
.setEmoji('✅') .setEmoji('✅')
.setLabel(' Approve') .setLabel(' Approve')
.setStyle('SUCCESS') .setStyle(ButtonStyle.Success),
) new ButtonBuilder()
.addComponents(
new MessageButton()
.setCustomId( .setCustomId(
JSON.stringify({ JSON.stringify({
m: 'MembershipScreening', m: 'MembershipScreening',
@@ -578,10 +592,8 @@ export default class MembershipScreening extends DiscordModule {
) )
.setEmoji('⛔') .setEmoji('⛔')
.setLabel(' Deny and kick') .setLabel(' Deny and kick')
.setStyle('DANGER') .setStyle(ButtonStyle.Danger),
) new ButtonBuilder()
.addComponents(
new MessageButton()
.setCustomId( .setCustomId(
JSON.stringify({ JSON.stringify({
m: 'MembershipScreening', m: 'MembershipScreening',
@@ -593,7 +605,8 @@ export default class MembershipScreening extends DiscordModule {
) )
.setEmoji('🔪') .setEmoji('🔪')
.setLabel(' Vision Hunt Decree (Ban)') .setLabel(' Vision Hunt Decree (Ban)')
.setStyle('DANGER') .setStyle(ButtonStyle.Danger)
]
); );
await channel.send({ content: '\u200b', embeds: [embed], components: [row] }); await channel.send({ content: '\u200b', embeds: [embed], components: [row] });
+56 -36
View File
@@ -1,16 +1,15 @@
import { import {
Message, Message,
CommandInteraction, CommandInteraction,
Interaction,
VoiceChannel, VoiceChannel,
Permissions,
GuildMember,
DMChannel, DMChannel,
StageChannel, StageChannel,
MessageActionRow, ActionRowBuilder,
MessageButton, ButtonBuilder,
TextChannel, PermissionsBitField,
VoiceBasedChannel ButtonStyle,
BaseGuildTextChannel,
BaseGuildVoiceChannel
} from 'discord.js'; } from 'discord.js';
import { I18n } from 'i18n'; import { I18n } from 'i18n';
@@ -28,11 +27,14 @@ import DiscordMusicPlayer, {
VoiceDisconnectedEvent, VoiceDisconnectedEvent,
ValidTracks, ValidTracks,
DiscordMusicPlayerInstance, DiscordMusicPlayerInstance,
DiscordMusicPlayerLoopMode DiscordMusicPlayerLoopMode,
TrackUtils
} from '../../providers/DiscordMusicPlayer'; } from '../../providers/DiscordMusicPlayer';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule'; import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
import Locale from '../../services/Locale'; import Locale from '../../services/Locale';
import { SpotifyTrack, YouTubeVideo } from 'play-dl';
import { checkBotPermissionsInChannel } from '../../utils/DiscordPermission';
const EMBEDS = { const EMBEDS = {
VOICECHANNEL_JOINED: (data: HybridInteractionMessage, locale: I18n) => { VOICECHANNEL_JOINED: (data: HybridInteractionMessage, locale: I18n) => {
@@ -77,35 +79,37 @@ const EMBEDS = {
user: data.getUser() user: data.getUser()
}); });
}, },
NOW_PLAYING: (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => { NOW_PLAYING: async (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => {
const title = TrackUtils.getTitle(track);
const thumbnails = await TrackUtils.getThumbnails(track);
const embed = makeInfoEmbed({ const embed = makeInfoEmbed({
title: locale.__('musicplayer.now_playing'), title: locale.__('musicplayer.now_playing'),
icon: '🎵 ', icon: '🎵 ',
description: track.title, description: title,
user: DiscordProvider.client.user user: DiscordProvider.client.user
}); });
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) => if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
prev.height * prev.width > current.height * current.width ? prev : current embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
);
if (highestResolutionThumbnail) embed.setImage(highestResolutionThumbnail.url);
return embed; return embed;
}, },
NOW_REPEATING: (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => { NOW_REPEATING: async (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => {
const title = TrackUtils.getTitle(track);
const thumbnails = await TrackUtils.getThumbnails(track);
const embed = makeInfoEmbed({ const embed = makeInfoEmbed({
title: locale.__('musicplayer.now_playing_repeating'), title: locale.__('musicplayer.now_playing_repeating'),
icon: '🎵 ', icon: '🎵 ',
description: `${track.title}`, description: title,
user: DiscordProvider.client.user user: DiscordProvider.client.user
}); });
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) => if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
prev.height * prev.width > current.height * current.width ? prev : current embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
);
if (highestResolutionThumbnail) embed.setImage(highestResolutionThumbnail.url);
return embed; return embed;
} }
@@ -116,7 +120,7 @@ export async function joinVoiceChannelProcedure(
instance: DiscordMusicPlayerInstance | null, instance: DiscordMusicPlayerInstance | null,
voiceChannel: VoiceChannel | StageChannel voiceChannel: VoiceChannel | StageChannel
) { ) {
const isSlashCommand = data.isSlashCommand(); const isSlashCommand = data.isApplicationCommand();
const isAcceptableInteraction = data.isSelectMenu() || data.isButton(); const isAcceptableInteraction = data.isSelectMenu() || data.isButton();
const isMessage = data.isMessage(); const isMessage = data.isMessage();
if (!isSlashCommand && !isAcceptableInteraction && !isMessage) return; if (!isSlashCommand && !isAcceptableInteraction && !isMessage) return;
@@ -128,16 +132,18 @@ export async function joinVoiceChannelProcedure(
if (!member || !channel || !guild) return; if (!member || !channel || !guild) return;
if (channel instanceof DMChannel) return; if (channel instanceof DMChannel) return;
if (!(channel instanceof TextChannel)) return; if (!(channel instanceof BaseGuildTextChannel || channel instanceof BaseGuildVoiceChannel)) return;
const memberVoiceChannel = member.voice.channel; //isMessage ? member.voice.channel : DiscordProvider.client.guilds.cache.get(guild.id)!.members.cache.get((data as Interaction).user.id)?.voice.channel; const memberVoiceChannel = member.voice.channel;
if (!memberVoiceChannel) return; if (!memberVoiceChannel) return;
const bot = guild.me; const bot = guild.members.me;
if (!bot) return; if (!bot) return;
const locale = await Locale.getGuildLocale(guild.id); const locale = await Locale.getGuildLocale(guild.id);
if(!await checkBotPermissionsInChannel({ guild, data, locale, channel: memberVoiceChannel, permissions: [PermissionsBitField.Flags.ViewChannel, PermissionsBitField.Flags.Connect]})) return;
// If already in VoiceChannel // If already in VoiceChannel
if (bot.voice.channelId) { if (bot.voice.channelId) {
// But, no music instance yet (The bot might just restarted) // But, no music instance yet (The bot might just restarted)
@@ -179,7 +185,7 @@ export async function joinVoiceChannelProcedure(
EMBEDS.VOICECHANNEL_INUSE( EMBEDS.VOICECHANNEL_INUSE(
data, data,
locale, locale,
member.permissions.has([Permissions.FLAGS.MOVE_MEMBERS]) member.permissions.has([PermissionsBitField.Flags.MoveMembers])
) )
] ]
}); });
@@ -217,30 +223,44 @@ export async function joinVoiceChannelProcedure(
// Register Event Listeners // Register Event Listeners
instance.events.on('playing', async (event: PlayerPlayingEvent) => { instance.events.on('playing', async (event: PlayerPlayingEvent) => {
const locale = await Locale.getGuildLocale(guild.id);
if (!event.instance.queue || !event.instance.queue.track || event.instance.queue.track.length === 0) return; if (!event.instance.queue || !event.instance.queue.track || event.instance.queue.track.length === 0) return;
previousTrack = event.instance.getPreviousTrack(); previousTrack = event.instance.getPreviousTrack();
if (event.instance.queue.track[0] !== previousTrack) isLoopMessageSent = false; if (event.instance.queue.track[0] !== previousTrack) isLoopMessageSent = false;
else if (event.instance.queue.track[0] === previousTrack && isLoopMessageSent) return; else if (event.instance.queue.track[0] === previousTrack && isLoopMessageSent) return;
const row = new MessageActionRow().addComponents( const row = new ActionRowBuilder<ButtonBuilder>();
new MessageButton() if(event.instance.queue.track[0] instanceof SpotifyTrack)
.setEmoji('▶️') row.addComponents([
.setLabel('Open on YouTube') new ButtonBuilder()
.setURL(encodeURI(`https://www.youtube.com/watch?v=${event.instance.queue.track[0].id}`)) .setEmoji('🟢')
.setStyle('LINK') .setLabel('Open in Spotify')
); .setURL(encodeURI(`https://open.spotify.com/track/${event.instance.queue.track[0].id}`))
.setStyle(ButtonStyle.Link)
]);
if(event.instance.queue.track[0] instanceof YouTubeVideo || event.instance.queue.track[0] instanceof SpotifyTrack) {
const actualPlaybackURL = event.instance.getActualPlaybackURL();
if(event.instance.queue.track[0] instanceof SpotifyTrack && !actualPlaybackURL) return;
row.addComponents([
new ButtonBuilder()
.setEmoji('🔴')
.setLabel('Open in YouTube')
.setURL(event.instance.queue.track[0] instanceof YouTubeVideo ? encodeURI(`https://www.youtube.com/watch?v=${event.instance.queue.track[0].id}`) : encodeURI(event.instance.getActualPlaybackURL()!))
.setStyle(ButtonStyle.Link)
]);
}
if (event.instance.textChannel) { if (event.instance.textChannel) {
if (event.instance.getLoopMode() === DiscordMusicPlayerLoopMode.Current) { if (event.instance.getLoopMode() === DiscordMusicPlayerLoopMode.Current) {
isLoopMessageSent = true; isLoopMessageSent = true;
await sendMessage(event.instance.textChannel, undefined, { await sendMessage(event.instance.textChannel, undefined, {
embeds: [EMBEDS.NOW_REPEATING(data, locale, event.instance.queue.track[0])], embeds: [await EMBEDS.NOW_REPEATING(data, locale, event.instance.queue.track[0])],
components: [row] components: [row]
}); });
} else { } else {
await sendMessage(event.instance.textChannel, undefined, { await sendMessage(event.instance.textChannel, undefined, {
embeds: [EMBEDS.NOW_PLAYING(data, locale, event.instance.queue.track[0])], embeds: [await EMBEDS.NOW_PLAYING(data, locale, event.instance.queue.track[0])],
components: [row] components: [row]
}); });
} }
+2 -2
View File
@@ -1,5 +1,5 @@
import { I18n } from "i18n"; import { I18n } from "i18n";
import { Message, CommandInteraction, Interaction, MessageActionRow, ButtonInteraction, MessageSelectMenu, MessageSelectOptionData } from "discord.js"; import { Message, CommandInteraction } from "discord.js";
import DiscordMusicPlayer, { DiscordMusicPlayerLoopMode } from "../../providers/DiscordMusicPlayer"; import DiscordMusicPlayer, { DiscordMusicPlayerLoopMode } from "../../providers/DiscordMusicPlayer";
import Locale from "../../services/Locale"; import Locale from "../../services/Locale";
@@ -81,7 +81,7 @@ export default class Loop extends DiscordModule {
if (args.length !== 0) if (args.length !== 0)
query = args.join(' '); query = args.join(' ');
} }
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
query = args.getSubcommand(); query = args.getSubcommand();
const voiceChannel = member.voice.channel; const voiceChannel = member.voice.channel;
+32 -17
View File
@@ -1,26 +1,29 @@
import { Message, CommandInteraction, MessageActionRow, MessageButton } from "discord.js"; import { Message, CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
import { I18n } from "i18n"; import { I18n } from "i18n";
import { SpotifyTrack, YouTubeVideo } from "play-dl";
import DiscordProvider from "../../providers/Discord"; import DiscordProvider from "../../providers/Discord";
import DiscordMusicPlayer, { ValidTracks } from "../../providers/DiscordMusicPlayer"; import DiscordMusicPlayer, { TrackUtils, ValidTracks } from "../../providers/DiscordMusicPlayer";
import Locale from "../../services/Locale"; import Locale from "../../services/Locale";
import { makeErrorEmbed, makeInfoEmbed, sendHybridInteractionMessageResponse } from "../../utils/DiscordMessage"; import { makeErrorEmbed, makeInfoEmbed, sendHybridInteractionMessageResponse } from "../../utils/DiscordMessage";
import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule"; import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule";
const EMBEDS = { const EMBEDS = {
NOW_PLAYING: (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => { NOW_PLAYING: async (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => {
const title = TrackUtils.getTitle(track);
const thumbnails = await TrackUtils.getThumbnails(track);
const embed = makeInfoEmbed({ const embed = makeInfoEmbed({
title: locale.__('musicplayer.now_playing'), title: locale.__('musicplayer.now_playing'),
icon: '🎵 ', icon: '🎵 ',
description: track.title, description: title,
user: DiscordProvider.client.user user: DiscordProvider.client.user
}); });
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) => (prev.height * prev.width > current.height * current.width) ? prev : current) if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
if(highestResolutionThumbnail)
embed.setImage(highestResolutionThumbnail.url);
return embed; return embed;
}, },
@@ -55,15 +58,27 @@ export default class NowPlaying extends DiscordModule {
const instance = DiscordMusicPlayer.getGuildInstance(guild.id); const instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if(!instance || !instance.queue.track[0]) return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_MUSIC_PLAYING(data, locale)] }); if(!instance || !instance.queue.track[0]) return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_MUSIC_PLAYING(data, locale)] });
const row = new MessageActionRow() const row = new ActionRowBuilder<ButtonBuilder>();
.addComponents(
new MessageButton()
.setEmoji('▶️')
.setLabel('Open on YouTube')
.setURL(encodeURI(`https://www.youtube.com/watch?v=${instance.queue.track[0].id}`))
.setStyle('LINK'),
)
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NOW_PLAYING(data, locale, instance.queue.track[0])], components: [row] }); if(instance.queue.track[0] instanceof SpotifyTrack)
row.addComponents([
new ButtonBuilder()
.setEmoji('🟢')
.setLabel('Open in Spotify')
.setURL(encodeURI(`https://open.spotify.com/track/${instance.queue.track[0].id}`))
.setStyle(ButtonStyle.Link),
]);
if(instance.queue.track[0] instanceof YouTubeVideo || instance.queue.track[0] instanceof SpotifyTrack) {
const actualPlaybackURL = instance.getActualPlaybackURL();
if(instance.queue.track[0] instanceof SpotifyTrack && !actualPlaybackURL) return;
row.addComponents([
new ButtonBuilder()
.setEmoji('🔴')
.setLabel('Open in YouTube')
.setURL(instance.queue.track[0] instanceof YouTubeVideo ? encodeURI(`https://www.youtube.com/watch?v=${instance.queue.track[0].id}`) : encodeURI(instance.getActualPlaybackURL()!))
.setStyle(ButtonStyle.Link),
]);
}
return await sendHybridInteractionMessageResponse(data, { embeds: [await EMBEDS.NOW_PLAYING(data, locale, instance.queue.track[0])], components: [row] });
} }
} }
-1
View File
@@ -1,7 +1,6 @@
import { Message, CommandInteraction, Interaction } from "discord.js"; import { Message, CommandInteraction, Interaction } from "discord.js";
import { I18n } from "i18n"; import { I18n } from "i18n";
import DiscordProvider from "../../providers/Discord";
import DiscordMusicPlayer from "../../providers/DiscordMusicPlayer"; import DiscordMusicPlayer from "../../providers/DiscordMusicPlayer";
import Locale from "../../services/Locale"; import Locale from "../../services/Locale";
+92 -32
View File
@@ -1,20 +1,21 @@
import { import {
Message, Message,
CommandInteraction, CommandInteraction,
Interaction,
VoiceChannel, VoiceChannel,
MessageActionRow, ActionRowBuilder,
MessageButton, ButtonBuilder,
SelectMenuInteraction, SelectMenuInteraction,
ButtonInteraction, ButtonInteraction,
StageChannel StageChannel,
ButtonStyle,
PermissionsBitField
} from 'discord.js'; } from 'discord.js';
import { I18n } from 'i18n'; import { I18n } from 'i18n';
import { joinVoiceChannelProcedure } from './Join'; import { joinVoiceChannelProcedure } from './Join';
import DiscordProvider from '../../providers/Discord'; import DiscordProvider from '../../providers/Discord';
import DiscordMusicPlayer, { ValidTracks } from '../../providers/DiscordMusicPlayer'; import DiscordMusicPlayer, { TrackUtils, ValidTracks } from '../../providers/DiscordMusicPlayer';
import Locale from '../../services/Locale'; import Locale from '../../services/Locale';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule'; import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
@@ -24,6 +25,7 @@ import {
sendHybridInteractionMessageResponse, sendHybridInteractionMessageResponse,
makeInfoEmbed makeInfoEmbed
} from '../../utils/DiscordMessage'; } from '../../utils/DiscordMessage';
import { checkBotPermissionsInChannel } from '../../utils/DiscordPermission';
const EMBEDS = { const EMBEDS = {
PLAY_INFO: (data: HybridInteractionMessage, locale: I18n) => { PLAY_INFO: (data: HybridInteractionMessage, locale: I18n) => {
@@ -33,24 +35,25 @@ const EMBEDS = {
fields: [ fields: [
{ {
name: locale.__('common.available_args'), name: locale.__('common.available_args'),
value: locale.__('musicplayer_play.valid_args'), value: locale.__('musicplayer_play.valid_args')
} }
], ],
user: data.getUser() user: data.getUser()
}); });
}, },
ADDED_QUEUE: (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => { ADDED_QUEUE: async (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => {
const title = TrackUtils.getTitle(track);
const thumbnails = await TrackUtils.getThumbnails(track);
let embed = makeSuccessEmbed({ let embed = makeSuccessEmbed({
title: locale.__('musicplayer_play.queue_added_song'), title: locale.__('musicplayer_play.queue_added_song'),
description: track.title, description: title,
user: data.getUser() user: data.getUser()
}); });
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) => if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
prev.height * prev.width > current.height * current.width ? prev : current embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
);
if (highestResolutionThumbnail) embed.setImage(highestResolutionThumbnail.url);
return embed; return embed;
}, },
@@ -158,6 +161,7 @@ export default class Play extends DiscordModule {
await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel); await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel);
let instance = DiscordMusicPlayer.getGuildInstance(guild.id); let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if (!instance) return;
if (instance!.voiceChannel.id !== member.voice.channel.id) if (instance!.voiceChannel.id !== member.voice.channel.id)
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
@@ -222,7 +226,8 @@ export default class Play extends DiscordModule {
const voiceChannel = DiscordProvider.client.guilds.cache.get(guild.id)?.channels.cache.get(payload.d.v); const voiceChannel = DiscordProvider.client.guilds.cache.get(guild.id)?.channels.cache.get(payload.d.v);
//let member = DiscordProvider.client.guilds.cache.get(guild.id)?.members.cache.get(user.id); //let member = DiscordProvider.client.guilds.cache.get(guild.id)?.members.cache.get(user.id);
if (!voiceChannel || !(voiceChannel instanceof VoiceChannel || voiceChannel instanceof StageChannel)) return; if (!voiceChannel || !(voiceChannel instanceof VoiceChannel || voiceChannel instanceof StageChannel))
return;
if (!member.voice.channel) if (!member.voice.channel)
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
@@ -235,11 +240,16 @@ export default class Play extends DiscordModule {
await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel); await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel);
let instance = DiscordMusicPlayer.getGuildInstance(guild.id); let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if (!instance) return;
if (instance!.voiceChannel.id !== member.voice.channel.id) if (instance!.voiceChannel.id !== member.voice.channel.id)
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
hybridData, hybridData,
{ embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(new HybridInteractionMessage(interaction), locale)] }, {
embeds: [
EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(new HybridInteractionMessage(interaction), locale)
]
},
true true
); );
@@ -265,7 +275,7 @@ export default class Play extends DiscordModule {
instance.addTrackToQueue(result); instance.addTrackToQueue(result);
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
hybridData, hybridData,
{ embeds: [EMBEDS.ADDED_QUEUE(new HybridInteractionMessage(interaction), locale, result)] }, { embeds: [await EMBEDS.ADDED_QUEUE(new HybridInteractionMessage(interaction), locale, result)] },
true true
); );
} }
@@ -274,7 +284,7 @@ export default class Play extends DiscordModule {
async run(data: HybridInteractionMessage, args: any) { async run(data: HybridInteractionMessage, args: any) {
let query; let query;
const guild = data.getGuild(); const guild = data.getGuild();
const channel = data.getChannel(); const channel = data.getGuildChannel();
const member = data.getMember(); const member = data.getMember();
if (!guild || !channel || !member) return; if (!guild || !channel || !member) return;
@@ -286,8 +296,8 @@ export default class Play extends DiscordModule {
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.PLAY_INFO(data, locale)] }); return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.PLAY_INFO(data, locale)] });
query = args.join(' '); query = args.join(' ');
} else if (data.isSlashCommand()) { } else if (data.isApplicationCommand()) {
query = data.getSlashCommand().options.getString('query'); query = data.getSlashCommand().options.get('query', true).value?.toString();
} }
if (!query) return; if (!query) return;
@@ -304,6 +314,7 @@ export default class Play extends DiscordModule {
} }
let instance = DiscordMusicPlayer.getGuildInstance(guild.id); let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if (!instance) return;
if (instance!.voiceChannel.id !== member.voice.channel.id) if (instance!.voiceChannel.id !== member.voice.channel.id)
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
@@ -343,15 +354,15 @@ export default class Play extends DiscordModule {
if (!result) return; if (!result) return;
if (data.isMessage() && data.getMessage().embeds.length > 0) { if (data.isMessage() && data.getMessage().embeds.length > 0 && await checkBotPermissionsInChannel({ guild, channel, permissions: [PermissionsBitField.Flags.ManageMessages]})) {
data.getMessage().suppressEmbeds(true); data.getMessage().suppressEmbeds(true);
} }
instance.addTrackToQueue(result); instance.addTrackToQueue(result);
if (linkData.list) { if (linkData.list) {
const row = new MessageActionRow().addComponents( const row = new ActionRowBuilder<ButtonBuilder>().addComponents([
new MessageButton() new ButtonBuilder()
.setEmoji('✅') .setEmoji('✅')
.setCustomId( .setCustomId(
JSON.stringify({ JSON.stringify({
@@ -361,21 +372,70 @@ export default class Play extends DiscordModule {
}) })
) )
.setLabel(' Add the remaining songs in the playlist') .setLabel(' Add the remaining songs in the playlist')
.setStyle('PRIMARY') .setStyle(ButtonStyle.Primary)
]
); );
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result)], embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result)],
components: [row] components: [row]
}); });
} else } else
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result)] embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result)]
}); });
} else if (DiscordMusicPlayer.isSpotifyLink(query)) {
let linkData = DiscordMusicPlayer.parseSpotifyLink(query);
if (linkData.type === 'playlist' || linkData.type === 'album') {
let playlist = await DiscordMusicPlayer.getSpotifySongsInPlayList(query).catch((err) => {
sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.LOOKUP_ERROR(data, locale, err)] }, true);
return null;
});
if(!playlist) return;
const songs = await playlist.all_tracks();
for (const song of songs) {
instance.addTrackToQueue(song);
}
return await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.ADDED_SONGS_QUEUE(data, locale, songs)] },
true
);
}
let result = await DiscordMusicPlayer.searchSpotifyBySpotifyLink(linkData).catch((err) => {
sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.LOOKUP_ERROR(data, locale, err)] }, true);
return null;
});
if (!result) return;
if (data.isMessage() && data.getMessage().embeds.length > 0 && await checkBotPermissionsInChannel({ guild, channel, permissions: [PermissionsBitField.Flags.ManageMessages]})) {
data.getMessage().suppressEmbeds(true);
}
instance.addTrackToQueue(result);
return await sendHybridInteractionMessageResponse(data, {
embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result)]
});
} else { } else {
let result = await DiscordMusicPlayer.searchYouTubeByQuery(query); let result = await DiscordMusicPlayer.searchYouTubeByQuery(query);
if (!result) return; if (!result) {
// TODO: Send not found embed
await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.LOOKUP_ERROR(data, locale, new Error('Cannot find that song'))] },
true
);
return;
}
instance.addTrackToQueue(result[0]); instance.addTrackToQueue(result[0]);
if (result.length > 1) { if (result.length > 1) {
@@ -387,8 +447,8 @@ export default class Play extends DiscordModule {
// The query length is too long to fit in json // The query length is too long to fit in json
if (query.length > 100 - 51) return; if (query.length > 100 - 51) return;
const row = new MessageActionRow().addComponents( const row = new ActionRowBuilder<ButtonBuilder>().addComponents([
new MessageButton() new ButtonBuilder()
.setEmoji('🔎') .setEmoji('🔎')
.setCustomId( .setCustomId(
JSON.stringify({ JSON.stringify({
@@ -400,17 +460,17 @@ export default class Play extends DiscordModule {
}) })
) )
.setLabel(' Not this? Search!') .setLabel(' Not this? Search!')
.setStyle('PRIMARY') .setStyle(ButtonStyle.Primary)
); ]);
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result[0])], embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result[0])],
components: [row] components: [row]
}); });
} }
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result[0])] embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result[0])]
}); });
} }
} }
+161
View File
@@ -0,0 +1,161 @@
import { I18n } from "i18n";
import { Message, CommandInteraction, ActivityType, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
import DiscordMusicPlayer, { TrackUtils, ValidTracks } from "../../providers/DiscordMusicPlayer";
import Locale from "../../services/Locale";
import { makeSuccessEmbed, makeErrorEmbed, sendHybridInteractionMessageResponse } from "../../utils/DiscordMessage";
import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule";
import { joinVoiceChannelProcedure } from "./Join";
const EMBEDS = {
NOT_DETECTED: (data: HybridInteractionMessage, locale: I18n) => {
return makeErrorEmbed({
title: locale.__('musicplayer_playmy.not_detected'),
user: data.getUser()
});
},
USER_NOT_IN_VOICECHANNEL: (data: HybridInteractionMessage, locale: I18n) => {
return makeErrorEmbed({
title: locale.__('musicplayer.not_in_voice'),
user: data.getUser()
});
},
USER_NOT_IN_SAME_VOICECHANNEL: (data: HybridInteractionMessage, locale: I18n) => {
return makeErrorEmbed({
title: locale.__('musicplayer.different_voice_channel'),
user: data.getUser()
});
},
ADDED_QUEUE: async (data: HybridInteractionMessage, locale: I18n, track: ValidTracks) => {
const title = TrackUtils.getTitle(track);
const thumbnails = await TrackUtils.getThumbnails(track);
let embed = makeSuccessEmbed({
title: locale.__('musicplayer_play.queue_added_song'),
description: title,
user: data.getUser()
});
if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
return embed;
}
}
export default class PlayMy extends DiscordModule {
public id = "Discord_MusicPlayer_PlayMy";
public commands = ["playmy"];
public commandInteractionName = "playmy";
async GuildOnModuleCommand(args: any, message: Message) {
await this.run(new HybridInteractionMessage(message), args);
}
async GuildModuleCommandInteractionCreate(interaction: CommandInteraction) {
await this.run(new HybridInteractionMessage(interaction), interaction.options);
}
async run(data: HybridInteractionMessage, args: any) {
const guild = data.getGuild();
const member = data.getMember();
if(!guild || !member) return;
const locale = await Locale.getGuildLocale(guild.id);
if (!member.voice.channel)
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.USER_NOT_IN_VOICECHANNEL(data, locale)]
});
const voiceChannel = member.voice.channel;
if (!DiscordMusicPlayer.isGuildInstanceExists(guild.id)) {
await joinVoiceChannelProcedure(data, null, voiceChannel);
}
let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if (!instance) return;
if (instance!.voiceChannel.id !== member.voice.channel.id)
return await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(data, locale)] },
true
);
if (!instance!.isConnected()) {
await joinVoiceChannelProcedure(data, instance!, voiceChannel);
instance = DiscordMusicPlayer.getGuildInstance(guild.id);
}
if(!instance) return;
if(!member.presence) return;
if(!member.presence.activities) return;
let query;
let found = false;
for (const activity of member.presence.activities) {
if(activity.type === ActivityType.Listening) {
if(!activity.details) continue;
found = true;
if(activity.name == "Spotify")
query = `${activity.state} - ${activity.details}`
else
query = `${activity.state} - ${activity.details}` // TODO: Find better query formula
const result = await DiscordMusicPlayer.searchYouTubeByQuery(query);
if (!result) continue;
instance.addTrackToQueue(result[0]);
let row;
if (result.length > 1) {
// TODO: Find a better logic than this
// If the first result title is exact match with the query or first title contains half the space of the query, it's probably a sentence
// then we don't need to show the search results
// if(result[0].title === query || result[0].title && (Math.ceil((result[0].title.split(" ").length - 1) / 2) === Math.ceil((query.split(" ").length - 1) / 2))) return;
// The query length is too long to fit in json
if (query.length > 100 - 51) return;
row = new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setEmoji('🔎')
.setCustomId(
JSON.stringify({
m: 'MP_S',
a: 'search',
d: {
q: query
}
})
)
.setLabel(' Not this? Search!')
.setStyle(ButtonStyle.Primary)
]);
}
if(!row)
await sendHybridInteractionMessageResponse(data, {
embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result[0])]
});
else
await sendHybridInteractionMessageResponse(data, {
embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result[0])],
components: [row]
});
}
}
if(!found)
await sendHybridInteractionMessageResponse(data, { embeds:[EMBEDS.NOT_DETECTED(data, locale)] });
return;
}
}
+65 -29
View File
@@ -1,14 +1,20 @@
import { Message, CommandInteraction, Interaction, TextChannel } from 'discord.js'; import { Message, CommandInteraction } from 'discord.js';
import { I18n } from 'i18n'; import { I18n } from 'i18n';
import DiscordMusicPlayer, { import DiscordMusicPlayer, {
DiscordMusicPlayerInstance, DiscordMusicPlayerInstance,
DiscordMusicPlayerLoopMode DiscordMusicPlayerLoopMode,
TrackUtils
} from '../../providers/DiscordMusicPlayer'; } from '../../providers/DiscordMusicPlayer';
import Locale from '../../services/Locale'; import Locale from '../../services/Locale';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule'; import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
import { makeErrorEmbed, sendHybridInteractionMessageResponse, makeInfoEmbed } from '../../utils/DiscordMessage'; import {
makeErrorEmbed,
sendHybridInteractionMessageResponse,
makeSuccessEmbed,
makeInfoEmbed
} from '../../utils/DiscordMessage';
const EMBEDS = { const EMBEDS = {
QUEUE: (data: HybridInteractionMessage, locale: I18n, instance: DiscordMusicPlayerInstance) => { QUEUE: (data: HybridInteractionMessage, locale: I18n, instance: DiscordMusicPlayerInstance) => {
@@ -25,45 +31,58 @@ const EMBEDS = {
instance.getLoopMode() === DiscordMusicPlayerLoopMode.Current instance.getLoopMode() === DiscordMusicPlayerLoopMode.Current
? ` ${locale.__('musicplayer_queue.looping_current')}` ? ` ${locale.__('musicplayer_queue.looping_current')}`
: '' : ''
}: [${queue.track[0].title}](${queue.track[0].url})`; }: [${TrackUtils.getTitle(queue.track[0])}](${queue.track[0].url})`;
let description;
if (queue.track.length == 1) { if (queue.track.length == 1) {
let queueString = `1. [${queue.track[0].title}](${queue.track[0].url})`; let queueString = `1. [${TrackUtils.getTitle(queue.track[0])}](${queue.track[0].url})`;
return makeInfoEmbed({ description = `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}\n
title: locale.__('musicplayer_queue.title'), There are ${queue.track.length} song in the queue!\n${queueString}`;
description: `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}\n
There are ${queue.track.length} song in the queue!\n${queueString}`,
user: data.getUser()
});
} else if (queue.track.length >= 10) { } else if (queue.track.length >= 10) {
const upcomingText = `[${queue.track[1].title}](${queue.track[1].url})`; const upcomingText = `[${TrackUtils.getTitle(queue.track[1])}](${queue.track[1].url})`;
const first10 = queue.track.slice(0, 10); const first10 = queue.track.slice(0, 10);
let queueString = first10 let queueString = first10
.map((track, index) => `${index + 1}. [${track.title}](${track.url})`) .map((track, index) => `${index + 1}. [${TrackUtils.getTitle(track)}](${track.url})`)
.join('\n'); .join('\n');
return makeInfoEmbed({ description = `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}
title: locale.__('musicplayer_queue.title'),
description: `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}
${locale.__('musicplayer_queue.upcoming_song')} ${upcomingText}\n ${locale.__('musicplayer_queue.upcoming_song')} ${upcomingText}\n
${locale.__('musicplayer_queue.song_x_in_queue', { COUNT: queue.track.length.toString() })} ${locale.__('musicplayer_queue.song_x_in_queue', { COUNT: queue.track.length.toString() })}
${queueString}\n${queue.track.length > 10 ? `...${queue.track.length - 10} more songs` : ''}`, ${queueString}\n${queue.track.length > 10 ? `...${queue.track.length - 10} more songs` : ''}`;
user: data.getUser()
});
} else { } else {
const upcomingText = `[${queue.track[1].title}](${queue.track[1].url})`; const upcomingText = `[${TrackUtils.getTitle(queue.track[1])}](${queue.track[1].url})`;
const queueString = queue.track const queueString = queue.track
.map((track, index) => `${index + 1}. [${track.title}](${track.url})`) .map((track, index) => `${index + 1}. [${TrackUtils.getTitle(track)}](${track.url})`)
.join('\n'); .join('\n');
return makeInfoEmbed({ description = `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}
title: locale.__('musicplayer_queue.title'),
description: `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}
${locale.__('musicplayer_queue.upcoming_song')} ${upcomingText}\n ${locale.__('musicplayer_queue.upcoming_song')} ${upcomingText}\n
${locale.__('musicplayer_queue.song_x_in_queue', { COUNT: queue.track.length.toString() })} ${locale.__('musicplayer_queue.song_x_in_queue', { COUNT: queue.track.length.toString() })}
${queueString}`, ${queueString}`;
}
return makeInfoEmbed({
title: locale.__('musicplayer_queue.title'),
description: description,
fields: [
{
name: locale.__('common.available_args'),
value: locale.__('musicplayer_queue.valid_args')
}
],
user: data.getUser() user: data.getUser()
}); });
} }
} },
QUEUE_CLEARED: (data: HybridInteractionMessage, locale: I18n) => {
return makeSuccessEmbed({
title: locale.__('musicplayer_queue.cleared'),
user: data.getUser()
});
},
QUEUE_SHUFFLED: (data: HybridInteractionMessage, locale: I18n) => {
return makeSuccessEmbed({
title: locale.__('musicplayer_queue.shuffled'),
user: data.getUser()
});
}, },
NO_MUSIC_PLAYING: (data: HybridInteractionMessage, locale: I18n) => { NO_MUSIC_PLAYING: (data: HybridInteractionMessage, locale: I18n) => {
return makeErrorEmbed({ return makeErrorEmbed({
@@ -88,9 +107,9 @@ export default class QueueCommand extends DiscordModule {
async run(data: HybridInteractionMessage, args: any) { async run(data: HybridInteractionMessage, args: any) {
const guild = data.getGuild(); const guild = data.getGuild();
if (!guild) return; if (!guild) return;
let query;
const locale = await Locale.getGuildLocale(guild.id); const locale = await Locale.getGuildLocale(guild.id);
const instance = DiscordMusicPlayer.getGuildInstance(guild.id); const instance = DiscordMusicPlayer.getGuildInstance(guild.id);
@@ -99,8 +118,25 @@ export default class QueueCommand extends DiscordModule {
embeds: [EMBEDS.NO_MUSIC_PLAYING(data, locale)] embeds: [EMBEDS.NO_MUSIC_PLAYING(data, locale)]
}); });
await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.QUEUE(data, locale, instance)] }); if (data.isMessage()) {
if (args.length === 0)
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.QUEUE(data, locale, instance)]
});
return; query = args.join(' ');
} else if (data.isApplicationCommand()) {
//query = data.getSlashCommand().options.get('query', true).value?.toString();
}
if (!query) return;
switch (query.toLowerCase()) {
case 'clear':
instance.clearQueue();
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.QUEUE_CLEARED(data, locale)] });
case 'shuffle':
instance.shuffleQueue();
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.QUEUE_SHUFFLED(data, locale)] });
}
} }
} }
-1
View File
@@ -1,7 +1,6 @@
import { Message, CommandInteraction, Interaction } from "discord.js"; import { Message, CommandInteraction, Interaction } from "discord.js";
import { I18n } from "i18n"; import { I18n } from "i18n";
import DiscordProvider from "../../providers/Discord";
import DiscordMusicPlayer from "../../providers/DiscordMusicPlayer"; import DiscordMusicPlayer from "../../providers/DiscordMusicPlayer";
import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule"; import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule";
+10 -10
View File
@@ -1,13 +1,13 @@
import { Message, CommandInteraction, Interaction, MessageActionRow, ButtonInteraction, MessageSelectMenu, MessageSelectOptionData } from "discord.js"; import { Message, CommandInteraction, ActionRowBuilder, ButtonInteraction, SelectMenuBuilder, SelectMenuComponentOptionData } from "discord.js";
import { I18n } from "i18n"; import { I18n } from "i18n";
import { joinVoiceChannelProcedure } from "./Join"; import { joinVoiceChannelProcedure } from "./Join";
import DiscordMusicPlayer, { ValidTracks } from "../../providers/DiscordMusicPlayer"; import DiscordMusicPlayer, { TrackUtils, ValidTracks } from "../../providers/DiscordMusicPlayer";
import Locale from "../../services/Locale"; import Locale from "../../services/Locale";
import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule"; import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule";
import { makeErrorEmbed, makeSuccessEmbed, sendHybridInteractionMessageResponse, makeInfoEmbed } from "../../utils/DiscordMessage"; import { makeErrorEmbed, sendHybridInteractionMessageResponse, makeInfoEmbed } from "../../utils/DiscordMessage";
const EMBEDS = { const EMBEDS = {
SEARCH_INFO: (data: HybridInteractionMessage, locale: I18n) => { SEARCH_INFO: (data: HybridInteractionMessage, locale: I18n) => {
@@ -28,7 +28,7 @@ const EMBEDS = {
title: locale.__('musicplayer_search.title_result'), title: locale.__('musicplayer_search.title_result'),
description: `${locale.__('musicplayer_search.x_results_found', { description: `${locale.__('musicplayer_search.x_results_found', {
COUNT: result.length.toString() COUNT: result.length.toString()
})}\n\n${result.map(track => `- [${track.title}](${track.url})`).join('\n')}`, })}\n\n${result.map(track => `- [${TrackUtils.getTitle(track)}](${track.url})`).join('\n')}`,
user: data.getUser() user: data.getUser()
}); });
}, },
@@ -96,8 +96,8 @@ export default class Search extends DiscordModule {
query = args.join(' '); query = args.join(' ');
} }
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
query = data.getSlashCommand().options.getString('query'); query = data.getSlashCommand().options.get('query', true).value?.toString();
else if (data.isButton()) else if (data.isButton())
query = args; query = args;
@@ -126,9 +126,9 @@ export default class Search extends DiscordModule {
let result = await DiscordMusicPlayer.searchYouTubeByQuery(query); let result = await DiscordMusicPlayer.searchYouTubeByQuery(query);
if (!result) return; // TODO: Handle when search returned nothing if (!result) return; // TODO: Handle when search returned nothing
const menuOptions: MessageSelectOptionData[] = []; const menuOptions: SelectMenuComponentOptionData[] = [];
const messageSelectMenu = new MessageSelectMenu(); const messageSelectMenu = new SelectMenuBuilder();
/* /*
Discord have 100 char custom id char limit Discord have 100 char custom id char limit
So we need to shorten our json. So we need to shorten our json.
@@ -157,8 +157,8 @@ export default class Search extends DiscordModule {
messageSelectMenu.addOptions(menuOptions); messageSelectMenu.addOptions(menuOptions);
const row = new MessageActionRow(); const row = new ActionRowBuilder<SelectMenuBuilder>();
row.addComponents(messageSelectMenu); row.addComponents([messageSelectMenu]);
return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.SEARCH_RESULT(data, locale, result)], components: [row] }); return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.SEARCH_RESULT(data, locale, result)], components: [row] });
} }
+2 -2
View File
@@ -1,8 +1,8 @@
import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule"; import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule";
import { Message, CommandInteraction, Interaction } from "discord.js"; import { Message, CommandInteraction } from "discord.js";
import { makeSuccessEmbed, makeErrorEmbed, sendHybridInteractionMessageResponse } from "../../utils/DiscordMessage"; import { makeSuccessEmbed, makeErrorEmbed, sendHybridInteractionMessageResponse } from "../../utils/DiscordMessage";
import DiscordProvider from "../../providers/Discord";
import DiscordMusicPlayer from "../../providers/DiscordMusicPlayer"; import DiscordMusicPlayer from "../../providers/DiscordMusicPlayer";
import { I18n } from "i18n"; import { I18n } from "i18n";
import Locale from "../../services/Locale"; import Locale from "../../services/Locale";
+2 -2
View File
@@ -1,4 +1,4 @@
import { CommandInteraction, Message, Interaction } from 'discord.js'; import { CommandInteraction, Message } from 'discord.js';
import os from 'os'; import os from 'os';
import NodePing from 'ping'; import NodePing from 'ping';
import { Promise } from 'bluebird'; import { Promise } from 'bluebird';
@@ -123,7 +123,7 @@ export default class Ping extends DiscordModule {
}` }`
); );
if (data.isSlashCommand()) if (data.isApplicationCommand())
return await data return await data
.getMessageComponentInteraction() .getMessageComponentInteraction()
.editReply({ embeds: [EMBEDS.PING_INFO(data, locale, finalString.join('\n'))] }); .editReply({ embeds: [EMBEDS.PING_INFO(data, locale, finalString.join('\n'))] });
+8 -8
View File
@@ -1,4 +1,4 @@
import { Message, Permissions, Interaction, CommandInteraction } from 'discord.js'; import { Message, CommandInteraction, PermissionsBitField } from 'discord.js';
import { I18n } from 'i18n'; import { I18n } from 'i18n';
import DiscordModule, { HybridInteractionMessage } from '../utils/DiscordModule'; import DiscordModule, { HybridInteractionMessage } from '../utils/DiscordModule';
@@ -78,9 +78,9 @@ export default class Say extends DiscordModule {
if ( if (
!message.member.permissions.has([ !message.member.permissions.has([
Permissions.FLAGS.VIEW_CHANNEL, PermissionsBitField.Flags.ViewChannel,
Permissions.FLAGS.SEND_MESSAGES, PermissionsBitField.Flags.SendMessages,
Permissions.FLAGS.MANAGE_CHANNELS PermissionsBitField.Flags.ManageChannels,
]) ])
) )
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
@@ -93,22 +93,22 @@ export default class Say extends DiscordModule {
}); });
query = args.join(' '); query = args.join(' ');
} else if (data.isSlashCommand()) { } else if (data.isApplicationCommand()) {
const interaction = data.getSlashCommand(); const interaction = data.getSlashCommand();
if ( if (
!data !data
.getGuild()! .getGuild()!
.members.cache.get(interaction.user.id) .members.cache.get(interaction.user.id)
?.permissions.has([Permissions.FLAGS.ADMINISTRATOR]) ?.permissions.has([PermissionsBitField.Flags.Administrator])
) )
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NO_PERMISSION(data, locale)] embeds: [EMBEDS.NO_PERMISSION(data, locale)]
}); });
query = interaction.options.getString('message'); query = interaction.options.get('message', true).value?.toString();
} }
if (data.isSlashCommand() && data.getChannel()) { if (data.isApplicationCommand() && data.getChannel()) {
try { try {
await data.getChannel()!.send({ content: query }); await data.getChannel()!.send({ content: query });
await data await data
+107
View File
@@ -0,0 +1,107 @@
import { Guild, PermissionsBitField } from 'discord.js';
import { I18n } from 'i18n';
import Prisma from '../../providers/Prisma';
import Cache from '../../providers/Cache';
import { HybridInteractionMessage } from '../../utils/DiscordModule';
import {
makeInfoEmbed,
makeErrorEmbed,
makeSuccessEmbed,
sendHybridInteractionMessageResponse
} from '../../utils/DiscordMessage';
import { COMMON_EMBEDS } from '.';
import Locale from '../../providers/Locale';
import { checkMemberPermissionsInGuild } from '../../utils/DiscordPermission';
const EMBEDS = {
LANGUAGE_INFO: (data: HybridInteractionMessage, locale: I18n, currentLocale: string) => {
return makeInfoEmbed({
title: locale.__('settings_language.info', { LANGUAGE: currentLocale }),
description: locale.__('settings_language.info_description', {
LANGUAGES: Locale.getLocaleProvider('en').getLocales().join(', ')
}),
user: data.getUser()
});
},
LANGUAGE_INVALID: (data: HybridInteractionMessage, locale: I18n) => {
return makeErrorEmbed({
title: locale.__('settings_language.language_invalid'),
description: locale.__('settings_language.language_invalid_description'),
user: data.getUser()
});
},
LANGUAGE_UPDATED: (data: HybridInteractionMessage, locale: I18n, newLanguage: string) => {
return makeSuccessEmbed({
title: locale.__({ phrase: 'settings_language.language_updated', locale: newLanguage }),
description: locale.__(
{ phrase: 'settings_language.language_updated_description', locale: newLanguage },
{ LANGUAGE: newLanguage }
),
user: data.getUser()
});
}
};
export default async (data: HybridInteractionMessage, args: any, guild: Guild, locale: I18n) => {
let member = data.getMember();
if (!member) return;
const GuildCache = await Cache.getCachedGuild(guild.id);
if (!GuildCache) return;
const language = GuildCache.locale;
if (!(await checkMemberPermissionsInGuild({ member, data, locale, permissions: [PermissionsBitField.Flags.ManageGuild] })))
return;
let newLanguage: string | null | undefined;
if (data.isMessage()) {
let _name: string;
if (typeof args[1] === 'undefined')
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.LANGUAGE_INFO(data, locale, language)]
});
let __name = args;
__name.shift();
_name = __name.join(' ');
newLanguage = _name;
} else if (data.isApplicationCommand())
newLanguage = data.getSlashCommand().options.get('language', true).value?.toString();
if (!newLanguage)
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.LANGUAGE_INFO(data, locale, language)]
});
newLanguage = newLanguage.toLowerCase();
if (!Locale.getLocaleProvider('en').getLocales().includes(newLanguage))
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.LANGUAGE_INVALID(data, locale)]
});
let placeholder: HybridInteractionMessage | undefined;
let _placeholder = await sendHybridInteractionMessageResponse(data, {
embeds: [COMMON_EMBEDS.PROCESSING(data, locale)]
});
if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder);
await Prisma.client.guild.update({
where: { id: guild.id },
data: { locale: newLanguage }
});
Cache.updateGuildCache(guild.id);
if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder.getMessage().edit({ embeds: [EMBEDS.LANGUAGE_UPDATED(data, locale, newLanguage)] });
else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.LANGUAGE_UPDATED(data, locale, newLanguage)] },
true
);
};
+6 -7
View File
@@ -1,4 +1,4 @@
import { Permissions, Guild } from 'discord.js'; import { Guild, PermissionsBitField } from 'discord.js';
import { I18n } from 'i18n'; import { I18n } from 'i18n';
import DiscordProvider from '../../providers/Discord'; import DiscordProvider from '../../providers/Discord';
@@ -14,6 +14,7 @@ import {
} from '../../utils/DiscordMessage'; } from '../../utils/DiscordMessage';
import { COMMON_EMBEDS } from '.'; import { COMMON_EMBEDS } from '.';
import { checkMemberPermissionsInGuild } from '../../utils/DiscordPermission';
const EMBEDS = { const EMBEDS = {
PREFIX_INFO: (data: HybridInteractionMessage, locale: I18n, currentPrefix: string) => { PREFIX_INFO: (data: HybridInteractionMessage, locale: I18n, currentPrefix: string) => {
@@ -54,10 +55,8 @@ export default async (data: HybridInteractionMessage, args: any, guild: Guild, l
if (!GuildCache) return; if (!GuildCache) return;
const prefix = GuildCache.prefix; const prefix = GuildCache.prefix;
if (!member.permissions.has([Permissions.FLAGS.ADMINISTRATOR])) if (!(await checkMemberPermissionsInGuild({ member, data, locale, permissions: [PermissionsBitField.Flags.ManageGuild] })))
return await sendHybridInteractionMessageResponse(data, { return;
embeds: [COMMON_EMBEDS.NO_PERMISSION(data, locale)]
});
let newPrefix: string | null | undefined; let newPrefix: string | null | undefined;
if (data.isMessage()) { if (data.isMessage()) {
@@ -71,7 +70,7 @@ export default async (data: HybridInteractionMessage, args: any, guild: Guild, l
__name.shift(); __name.shift();
_name = __name.join(' '); _name = __name.join(' ');
newPrefix = _name; newPrefix = _name;
} else if (data.isSlashCommand()) newPrefix = data.getSlashCommand().options.getString('prefix'); } else if (data.isApplicationCommand()) newPrefix = data.getSlashCommand().options.get('prefix', true).value?.toString();
if (!newPrefix) if (!newPrefix)
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
@@ -103,7 +102,7 @@ export default async (data: HybridInteractionMessage, args: any, guild: Guild, l
if (data && data.isMessage() && placeholder && placeholder.isMessage()) if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder.getMessage().edit({ embeds: [EMBEDS.PREFIX_UPDATED(data, locale, newPrefix)] }); return placeholder.getMessage().edit({ embeds: [EMBEDS.PREFIX_UPDATED(data, locale, newPrefix)] });
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
data, data,
{ embeds: [EMBEDS.PREFIX_UPDATED(data, locale, newPrefix)] }, { embeds: [EMBEDS.PREFIX_UPDATED(data, locale, newPrefix)] },
+13 -19
View File
@@ -1,7 +1,6 @@
import { Permissions, Guild, GuildChannel, ThreadChannel, Message } from 'discord.js'; import { Guild, GuildChannel, ThreadChannel, Message, PermissionsBitField } from 'discord.js';
import { I18n } from 'i18n'; import { I18n } from 'i18n';
import DiscordProvider from '../../providers/Discord';
import Prisma from '../../providers/Prisma'; import Prisma from '../../providers/Prisma';
import Cache from '../../providers/Cache'; import Cache from '../../providers/Cache';
@@ -14,6 +13,7 @@ import {
} from '../../utils/DiscordMessage'; } from '../../utils/DiscordMessage';
import { COMMON_EMBEDS } from '.'; import { COMMON_EMBEDS } from '.';
import { checkMemberPermissionsInGuild } from '../../utils/DiscordPermission';
const EMBEDS = { const EMBEDS = {
SERVICE_ANNOUNCEMENT_INVALID_STATUS: (data: HybridInteractionMessage, locale: I18n) => { SERVICE_ANNOUNCEMENT_INVALID_STATUS: (data: HybridInteractionMessage, locale: I18n) => {
@@ -104,10 +104,8 @@ export const setEnableServiceAnnouncement = async (
let member = data.getMember(); let member = data.getMember();
if (!member) return; if (!member) return;
if (!member.permissions.has([Permissions.FLAGS.ADMINISTRATOR])) if (!(await checkMemberPermissionsInGuild({ member, data, locale, permissions: [PermissionsBitField.Flags.Administrator] })))
return await sendHybridInteractionMessageResponse(data, { return;
embeds: [COMMON_EMBEDS.NO_PERMISSION(data, locale)]
});
let GuildCache = await Cache.getCachedGuild(guild.id); let GuildCache = await Cache.getCachedGuild(guild.id);
if(!GuildCache) return; if(!GuildCache) return;
@@ -124,7 +122,7 @@ export const setEnableServiceAnnouncement = async (
__name.shift(); __name.shift();
_name = __name.join(' '); _name = __name.join(' ');
newStatus = _name; newStatus = _name;
} else if (data.isSlashCommand()) newStatus = data.getSlashCommand().options.getString('status')!; } else if (data.isApplicationCommand()) newStatus = data.getSlashCommand().options.get('status', true).value?.toString();
if (!newStatus) if (!newStatus)
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
@@ -165,7 +163,7 @@ export const setEnableServiceAnnouncement = async (
return await (placeholder as Message).edit({ return await (placeholder as Message).edit({
embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_STATUS_UPDATED(data, locale, newStatusBool)] embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_STATUS_UPDATED(data, locale, newStatusBool)]
}); });
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
data, data,
{ {
@@ -184,10 +182,8 @@ export const setServiceAnnouncementChannel = async (
let member = data.getMember(); let member = data.getMember();
if (!member) return; if (!member) return;
if (!member.permissions.has([Permissions.FLAGS.ADMINISTRATOR])) if (!(await checkMemberPermissionsInGuild({ member, data, locale, permissions: [PermissionsBitField.Flags.Administrator] })))
return await sendHybridInteractionMessageResponse(data, { return;
embeds: [COMMON_EMBEDS.NO_PERMISSION(data, locale)]
});
let channel; let channel;
if (data.isMessage()) { if (data.isMessage()) {
@@ -196,7 +192,7 @@ export const setServiceAnnouncementChannel = async (
embeds: [EMBEDS.NO_CHANNEL_MENTIONED(data, locale)] embeds: [EMBEDS.NO_CHANNEL_MENTIONED(data, locale)]
}); });
channel = data.getMessage().mentions.channels.first(); channel = data.getMessage().mentions.channels.first();
} else if (data.isSlashCommand()) channel = data.getSlashCommand().options.getChannel('channel'); } else if (data.isApplicationCommand()) channel = data.getSlashCommand().options.get('channel', true).channel;
if (!channel) if (!channel)
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
@@ -211,16 +207,14 @@ export const setServiceAnnouncementChannel = async (
embeds: [EMBEDS.INVALID_CHANNEL_THREAD(data, locale, TargetChannel)] embeds: [EMBEDS.INVALID_CHANNEL_THREAD(data, locale, TargetChannel)]
}); });
if (!TargetChannel.isText()) if (!TargetChannel.isTextBased())
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INVALID_CHANNEL(data, locale, TargetChannel)] embeds: [EMBEDS.INVALID_CHANNEL(data, locale, TargetChannel)]
}); });
if ( if (
!data !data.getGuild()?.members.me?.permissionsIn(TargetChannel)
.getGuild()! .has([PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.ViewChannel])
.me?.permissionsIn(TargetChannel)
.has([Permissions.FLAGS.SEND_MESSAGES, Permissions.FLAGS.VIEW_CHANNEL])
) )
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.BOT_NO_PERMISSION(data, locale, TargetChannel)] embeds: [EMBEDS.BOT_NO_PERMISSION(data, locale, TargetChannel)]
@@ -239,7 +233,7 @@ export const setServiceAnnouncementChannel = async (
return await (placeholder as Message).edit({ return await (placeholder as Message).edit({
embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_CONFIGURED_CHANNEL(data, locale, TargetChannel)] embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_CONFIGURED_CHANNEL(data, locale, TargetChannel)]
}); });
else if (data.isSlashCommand()) else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse( return await sendHybridInteractionMessageResponse(
data, data,
{ {
+27 -12
View File
@@ -1,36 +1,49 @@
import { import {
Message, Message,
Interaction,
CommandInteraction, CommandInteraction,
Permissions, Guild,
ThreadChannel, PermissionsBitField,
GuildChannel, PermissionResolvable
Guild
} from 'discord.js'; } from 'discord.js';
import { I18n } from 'i18n'; import { I18n } from 'i18n';
import DiscordProvider from '../../providers/Discord'; import DiscordProvider from '../../providers/Discord';
import Prisma from '../../providers/Prisma';
import Cache from '../../providers/Cache';
import Locale from '../../services/Locale'; import Locale from '../../services/Locale';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule'; import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
import { import {
makeInfoEmbed, makeInfoEmbed,
makeErrorEmbed, makeErrorEmbed,
makeSuccessEmbed,
makeProcessingEmbed, makeProcessingEmbed,
sendHybridInteractionMessageResponse sendHybridInteractionMessageResponse
} from '../../utils/DiscordMessage'; } from '../../utils/DiscordMessage';
import * as PrefixModule from './Prefix'; import * as PrefixModule from './Prefix';
import * as LanguageModule from './Language';
import * as ServiceAnnouncementModule from './ServiceAnnouncement'; import * as ServiceAnnouncementModule from './ServiceAnnouncement';
export const COMMON_EMBEDS = { export const COMMON_EMBEDS = {
NO_PERMISSION: (data: HybridInteractionMessage, locale: I18n) => { MEMBER_NO_PERMISSION: (data: HybridInteractionMessage, locale: I18n, permissions: PermissionResolvable[]) => {
let allPermissions = [];
for(const value of permissions) {
allPermissions.push(new PermissionsBitField(value).toArray());
}
return makeErrorEmbed({ return makeErrorEmbed({
title: locale.__('common.no_permissions'), title: locale.__('common.member_no_permissions'),
description: locale.__('common.no_permissions_description', { PERMISSIONS: 'ADMINISTRATOR'}), description: locale.__('common.member_no_permissions_description', { PERMISSIONS: allPermissions.join(', ')}),
user: data.getUser()
});
},
BOT_NO_PERMISSION: (data: HybridInteractionMessage, locale: I18n, permissions: PermissionResolvable[]) => {
let allPermissions = [];
for(const value of permissions) {
allPermissions.push(new PermissionsBitField(value).toArray());
}
return makeErrorEmbed({
title: locale.__('common.bot_no_permissions'),
description: locale.__('common.bot_no_permissions_description', { PERMISSIONS: allPermissions.join(', ')}),
user: data.getUser() user: data.getUser()
}); });
}, },
@@ -91,7 +104,7 @@ export default class Settings extends DiscordModule {
}); });
query = args[0].toLowerCase(); query = args[0].toLowerCase();
} else if (data.isSlashCommand()) { } else if (data.isApplicationCommand()) {
query = args.getSubcommand(); query = args.getSubcommand();
} }
@@ -102,6 +115,8 @@ export default class Settings extends DiscordModule {
}); });
case 'prefix': case 'prefix':
return await PrefixModule.default(data, args, guild, locale); return await PrefixModule.default(data, args, guild, locale);
case 'language':
return await LanguageModule.default(data, args, guild, locale);
case 'enableserviceannouncement': case 'enableserviceannouncement':
return await ServiceAnnouncementModule.setEnableServiceAnnouncement(data, args, guild, locale); return await ServiceAnnouncementModule.setEnableServiceAnnouncement(data, args, guild, locale);
case 'serviceannouncementchannel': case 'serviceannouncementchannel':
+54
View File
@@ -0,0 +1,54 @@
import { Message, CommandInteraction } from 'discord.js';
import { I18n } from 'i18n';
import Environment from '../providers/Environment';
import DiscordProvider from '../providers/Discord';
import Locale from '../services/Locale';
import DiscordModule, { HybridInteractionMessage } from '../utils/DiscordModule';
import { sendHybridInteractionMessageResponse, makeInfoEmbed } from '../utils/DiscordMessage';
const EMBEDS = {
SUPPORT_INFO: (data: HybridInteractionMessage, locale: I18n) => {
const user = data.getUser();
let message;
if (Environment.get().SUPPORT_URL)
message = locale.__('support.info', { BOT_NAME: DiscordProvider.client.user!.username, LINK: Environment.get().SUPPORT_URL});
return makeInfoEmbed({
title: locale.__('support.title'),
description: message || locale.__('support.not_available', { BOT_NAME: DiscordProvider.client.user!.username}),
user
});
}
};
export default class Support extends DiscordModule {
public id: string = 'Discord_Support';
public commands = ['support'];
public commandInteractionName = 'support';
async GuildOnModuleCommand(args: any, message: Message) {
await this.run(new HybridInteractionMessage(message), args);
}
async GuildModuleCommandInteractionCreate(interaction: CommandInteraction) {
await this.run(new HybridInteractionMessage(interaction), interaction.options);
}
async run(data: HybridInteractionMessage, args: any) {
const guild = data.getGuild();
if (!guild) return;
const locale = await Locale.getGuildLocale(guild.id);
await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.SUPPORT_INFO(data, locale)]
});
if(Environment.get().SUPPORT_URL)
await sendHybridInteractionMessageResponse(data, {
content: Environment.get().SUPPORT_URL
});
}
}
+40 -28
View File
@@ -1,4 +1,4 @@
import { Message, CommandInteraction, Presence, PresenceStatus } from 'discord.js'; import { Message, CommandInteraction, Presence, PresenceStatus, ActivityType } from 'discord.js';
import { getColorFromURL, Palette } from 'color-thief-node'; import { getColorFromURL, Palette } from 'color-thief-node';
import { I18n } from 'i18n'; import { I18n } from 'i18n';
@@ -73,10 +73,10 @@ const EMBEDS = {
if (user.presence?.activities) { if (user.presence?.activities) {
for (let activity of user.presence?.activities) { for (let activity of user.presence?.activities) {
if (activity.type === 'CUSTOM') if (activity.type === ActivityType.Custom)
embed.addField( embed.addFields([{
`${locale.__('userinfo.custom_status')}`, name: `${locale.__('userinfo.custom_status')}`,
`${ value: `${
!activity.emoji !activity.emoji
? '' ? ''
: `${ : `${
@@ -88,49 +88,61 @@ const EMBEDS = {
}` }`
} ${activity.state === null ? '' : activity.state} } ${activity.state === null ? '' : activity.state}
\u200b`, \u200b`,
false inline: false
); }]);
else { else {
let title = ''; let title = '';
switch (activity.type) { switch (activity.type) {
case 'PLAYING': case ActivityType.Playing:
title = `'🕹 ${locale.__('userinfo.playing_x', {NAME: activity.name})}`; title = `🕹 ${locale.__('userinfo.playing_x', { NAME: activity.name })}`;
break; break;
case 'STREAMING': case ActivityType.Streaming:
title = `'🔴 ${locale.__('userinfo.streaming_x', {NAME: activity.name})}`; title = `🔴 ${locale.__('userinfo.streaming_x', { NAME: activity.name })}`;
break; break;
case 'LISTENING': case ActivityType.Listening:
title = `🎵 ${locale.__('userinfo.listening_x', { NAME: activity.name })}`; title = `🎵 ${locale.__('userinfo.listening_x', { NAME: activity.name })}`;
break; break;
case 'WATCHING': case ActivityType.Watching:
title = `📺 ${locale.__('userinfo.watching_x', { NAME: activity.name })}`; title = `📺 ${locale.__('userinfo.watching_x', { NAME: activity.name })}`;
break; break;
case 'COMPETING': case ActivityType.Competing:
title = `'🌠 ${locale.__('userinfo.competing_x', {NAME: activity.name})}`; title = `🌠 ${locale.__('userinfo.competing_x', { NAME: activity.name })}`;
break; break;
} }
embed.addField( const startTime = activity.timestamps?.start;
`${title}`, const endTime = activity.timestamps?.end;
`${activity.details === null ? '' : activity.details} embed.addFields([{
name: `${title}`,
value: `${activity.details === null ? '' : activity.details}
${activity.state === null ? '' : activity.state} ${activity.state === null ? '' : activity.state}
Since <t:${Math.round(new Date(activity.createdAt).getTime() / 1000)}:R> ${
startTime
? `${locale.__('userinfo.since', {TIME: `<t:${Math.round(startTime.getTime() / 1000)}:R>` })}`
: ''
}${
endTime
? `\n${locale.__('userinfo.end', {TIME: `<t:${Math.round(endTime.getTime() / 1000)}:R>` })}`
: ''
}
\u200b`, \u200b`,
false inline: false
); }]);
} }
} }
} }
embed.addField( embed.addFields([{
`📰 ${locale.__('userinfo.user_guild_info')}`, name: `📰 ${locale.__('userinfo.user_guild_info')}`,
`${ value: `${
user.joinedAt === null user.joinedAt === null
? locale.__('userinfo.join_date_unknown') ? locale.__('userinfo.join_date_unknown')
: locale.__('userinfo.join_date', {TIME: `<t:${Math.round(user.joinedAt.getTime() / 1000).toString()}:R>`}) : locale.__('userinfo.join_date', {
TIME: `<t:${Math.round(user.joinedAt.getTime() / 1000).toString()}:R>`
})
} }
${user.isGuildOwner ? `${locale.__('userinfo.guild_owner')}` : ''} ${user.isGuildOwner ? `${locale.__('userinfo.guild_owner')}` : ''}
` `
); }]);
embed.setThumbnail(user.displayAvatarURL + '?size=4096'); embed.setThumbnail(user.displayAvatarURL + '?size=4096');
embed.setAuthor({ embed.setAuthor({
@@ -172,7 +184,7 @@ export default class UserInfo extends DiscordModule {
if (typeof data.getMessage().mentions.users.first() !== 'undefined') if (typeof data.getMessage().mentions.users.first() !== 'undefined')
query = data.getMessage().mentions.users.first()?.id; query = data.getMessage().mentions.users.first()?.id;
else query = args[0]; else query = args[0];
} else if (data.isSlashCommand()) query = data.getSlashCommand().options.getUser('user')?.id; } else if (data.isApplicationCommand()) query = data.getSlashCommand().options.getUser('user')?.id;
// Find the user want to look up // Find the user want to look up
let TargetMember = (await guild.members.fetch()).get(query); let TargetMember = (await guild.members.fetch()).get(query);
@@ -181,7 +193,7 @@ export default class UserInfo extends DiscordModule {
embeds: [EMBEDS.USER_NOT_FOUND(data, locale)] embeds: [EMBEDS.USER_NOT_FOUND(data, locale)]
}); });
if (data.isSlashCommand()) await data.getMessageComponentInteraction().deferReply(); if (data.isApplicationCommand()) await data.getMessageComponentInteraction().deferReply();
let colorthief = null; let colorthief = null;
try { try {
+46 -49
View File
@@ -1,9 +1,10 @@
import { import {
Message, Message,
MessageActionRow, ActionRowBuilder,
MessageButton, ButtonBuilder,
Interaction, CommandInteraction,
CommandInteraction BaseInteraction,
ButtonStyle
} from 'discord.js'; } from 'discord.js';
import validator from 'validator'; import validator from 'validator';
import countryLookup from 'country-code-lookup'; import countryLookup from 'country-code-lookup';
@@ -20,7 +21,7 @@ import {
} from '../utils/DiscordMessage'; } from '../utils/DiscordMessage';
const EMBEDS = { const EMBEDS = {
osu_INFO: (data: Message | Interaction) => { osu_INFO: (data: HybridInteractionMessage) => {
return makeInfoEmbed({ return makeInfoEmbed({
title: 'osu!', title: 'osu!',
description: `[osu!](https://osu.ppy.sh/home) is a free-to-play rhythm game primarily developed, published, and created by Dean "peppy" Herbert`, description: `[osu!](https://osu.ppy.sh/home) is a free-to-play rhythm game primarily developed, published, and created by Dean "peppy" Herbert`,
@@ -34,43 +35,43 @@ const EMBEDS = {
value: '``user``' value: '``user``'
} }
], ],
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
NO_USER_FOUND: (data: Message | Interaction) => { NO_USER_FOUND: (data: HybridInteractionMessage) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: `That user doesn't exists on osu!`, title: `That user doesn't exists on osu!`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
NO_USER_MENTIONED: (data: Message | Interaction) => { NO_USER_MENTIONED: (data: HybridInteractionMessage) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: `No osu! username or user id provided`, title: `No osu! username or user id provided`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
INVALID_USER_MENTIONED: (data: Message | Interaction) => { INVALID_USER_MENTIONED: (data: HybridInteractionMessage) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: `Not a valid osu username or id`, title: `Not a valid osu username or id`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
INVALID_BEATMAP_ID_MENTIONED: (data: Message | Interaction) => { INVALID_BEATMAP_ID_MENTIONED: (data: HybridInteractionMessage) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: `Not a valid osu beatmap id`, title: `Not a valid osu beatmap id`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
NO_BEATMAP_FOUND: (data: Message | Interaction) => { NO_BEATMAP_FOUND: (data: HybridInteractionMessage) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: `That beatmap doesn't exists on osu!`, title: `That beatmap doesn't exists on osu!`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
}, },
NO_BEATMAP_MENTIONED: (data: Message | Interaction) => { NO_BEATMAP_MENTIONED: (data: HybridInteractionMessage) => {
return makeErrorEmbed({ return makeErrorEmbed({
title: `No osu! beatmap id provided`, title: `No osu! beatmap id provided`,
user: data instanceof Interaction ? data.user : data.author user: data.getUser()
}); });
} }
}; };
@@ -98,26 +99,26 @@ export default class osu extends DiscordModule {
if (data.isMessage()) { if (data.isMessage()) {
if (typeof args[1] === 'undefined') if (typeof args[1] === 'undefined')
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NO_USER_MENTIONED(data.getRaw())] embeds: [EMBEDS.NO_USER_MENTIONED(data)]
}); });
const [removed, ...newArgs] = args; const [removed, ...newArgs] = args;
user = newArgs.join(' '); user = newArgs.join(' ');
} else if (data.isSlashCommand()) } else if (data.isApplicationCommand())
user = data.getSlashCommand().options.getString('user'); user = data.getSlashCommand().options.get('user')?.value?.toString();
if (!validator.isNumeric(user) && !this.validate_osu_username(user)) if (!validator.isNumeric(user) && !this.validate_osu_username(user))
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INVALID_USER_MENTIONED(data.getRaw())] embeds: [EMBEDS.INVALID_USER_MENTIONED(data)]
}); });
let result = await osuAPI.client.getUser({ u: user }); let result = await osuAPI.client.getUser({ u: user });
if (result instanceof Array && result.length === 0) if (result instanceof Array && result.length === 0)
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NO_USER_FOUND(data.getRaw())] embeds: [EMBEDS.NO_USER_FOUND(data)]
}); });
if (data.isSlashCommand()) { if (data.isApplicationCommand()) {
await data.getSlashCommand().deferReply(); await data.getSlashCommand().deferReply();
} }
@@ -258,13 +259,13 @@ export default class osu extends DiscordModule {
'https://osu.ppy.sh/images/layout/avatar-guest.png' 'https://osu.ppy.sh/images/layout/avatar-guest.png'
); );
const row = new MessageActionRow().addComponents( const row = new ActionRowBuilder<ButtonBuilder>().addComponents([
new MessageButton() new ButtonBuilder()
.setEmoji('🔗') .setEmoji('🔗')
.setLabel(' Open Profile') .setLabel(' Open Profile')
.setURL(`https://osu.ppy.sh/users/${result.id}`) .setURL(`https://osu.ppy.sh/users/${result.id}`)
.setStyle('LINK') .setStyle(ButtonStyle.Link)
); ]);
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [embed], embeds: [embed],
@@ -276,26 +277,26 @@ export default class osu extends DiscordModule {
if (data.isMessage()) { if (data.isMessage()) {
if (typeof args[1] === 'undefined') if (typeof args[1] === 'undefined')
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NO_BEATMAP_MENTIONED(data.getRaw())] embeds: [EMBEDS.NO_BEATMAP_MENTIONED(data)]
}); });
const [removed, ...newArgs] = args; const [removed, ...newArgs] = args;
beatmap = newArgs.join(' '); beatmap = newArgs.join(' ');
} else if (data.isSlashCommand()) } else if (data.isApplicationCommand())
beatmap = data.getSlashCommand().options.getString('beatmap'); beatmap = data.getSlashCommand().options.get('beatmap')?.value?.toString();
if (!validator.isNumeric(beatmap)) if (!validator.isNumeric(beatmap))
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INVALID_BEATMAP_ID_MENTIONED(data.getRaw())] embeds: [EMBEDS.INVALID_BEATMAP_ID_MENTIONED(data)]
}); });
let result = await osuAPI.client.getBeatmaps({ b: beatmap }); let result = await osuAPI.client.getBeatmaps({ b: beatmap });
if (result instanceof Array && result.length === 0) if (result instanceof Array && result.length === 0)
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NO_BEATMAP_FOUND(data.getRaw())] embeds: [EMBEDS.NO_BEATMAP_FOUND(data)]
}); });
if (data.isSlashCommand()) await data.getSlashCommand().deferReply(); if (data.isApplicationCommand()) await data.getSlashCommand().deferReply();
const bm_result = result[0]; const bm_result = result[0];
@@ -436,33 +437,29 @@ export default class osu extends DiscordModule {
`https://assets.ppy.sh/beatmaps/${bm_result.beatmapSetId}/covers/cover.jpg` `https://assets.ppy.sh/beatmaps/${bm_result.beatmapSetId}/covers/cover.jpg`
); );
const row = new MessageActionRow(); const row = new ActionRowBuilder<ButtonBuilder>();
if (bm_result.hasDownload) if (bm_result.hasDownload)
row.addComponents( row.addComponents([
new MessageButton() new ButtonBuilder()
.setEmoji('🌎') .setEmoji('🌎')
.setLabel(' Download (Beatconnect)') .setLabel(' Download (Beatconnect)')
.setURL(`https://beatconnect.io/b/${bm_result.beatmapSetId}/`) .setURL(`https://beatconnect.io/b/${bm_result.beatmapSetId}/`)
.setStyle('LINK') .setStyle(ButtonStyle.Link),
); new ButtonBuilder()
row.addComponents(
new MessageButton()
.setEmoji('🔗') .setEmoji('🔗')
.setLabel(' Open listing') .setLabel(' Open listing')
.setURL( .setURL(
`https://osu.ppy.sh/beatmapsets/${bm_result.beatmapSetId}${url_mode}/${bm_result.id}` `https://osu.ppy.sh/beatmapsets/${bm_result.beatmapSetId}${url_mode}/${bm_result.id}`
) )
.setStyle('LINK') .setStyle(ButtonStyle.Link),
); new ButtonBuilder()
row.addComponents(
new MessageButton()
.setEmoji('💬') .setEmoji('💬')
.setLabel(' Open discussion') .setLabel(' Open discussion')
.setURL( .setURL(
`https://osu.ppy.sh/beatmapsets/${bm_result.beatmapSetId}/discussion` `https://osu.ppy.sh/beatmapsets/${bm_result.beatmapSetId}/discussion`
) )
.setStyle('LINK') .setStyle(ButtonStyle.Link)
); ]);
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [embed2], embeds: [embed2],
@@ -476,11 +473,11 @@ export default class osu extends DiscordModule {
if (data.isMessage()) { if (data.isMessage()) {
if (args.length === 0) { if (args.length === 0) {
return await sendHybridInteractionMessageResponse(data, { return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.osu_INFO(data.getRaw())] embeds: [EMBEDS.osu_INFO(data)]
}); });
} }
query = args[0].toLowerCase(); query = args[0].toLowerCase();
} else if (data.isSlashCommand()) { } else if (data.isApplicationCommand()) {
query = args.getSubcommand(); query = args.getSubcommand();
} }
-1
View File
@@ -10,5 +10,4 @@ App.loadLocale();
App.loadDiscord(); App.loadDiscord();
App.load_osu(); App.load_osu();
export default App; export default App;
+1 -1
View File
@@ -7,7 +7,7 @@ import osu from './osuAPI';
import Logger from '../libs/Logger'; import Logger from '../libs/Logger';
import Locale from './Locale'; import Locale from './Locale';
class App { class App {
public readonly versionNumber = `0.09`; public readonly versionNumber = `0.11`;
public readonly version = `${this.versionNumber}${ public readonly version = `${this.versionNumber}${
Environment.get().NODE_ENV === 'development' ? ' / Development Build' : '' Environment.get().NODE_ENV === 'development' ? ' / Development Build' : ''
}`; }`;
+14 -10
View File
@@ -1,4 +1,4 @@
import { Guild, Client, GuildMember, Intents, Interaction, Message, TextChannel, BaseGuildTextChannel } from 'discord.js'; import { Guild, Client, GuildMember, IntentsBitField, Interaction, Message, TextChannel, BaseGuildTextChannel, TextBasedChannelMixin, ChannelType, BaseGuildVoiceChannel, InteractionType } from 'discord.js';
import Logger from '../libs/Logger'; import Logger from '../libs/Logger';
import Environment from './Environment'; import Environment from './Environment';
@@ -14,8 +14,10 @@ import Discord_MembershipScreening from '../discord/MembershipScreening';
import Discord_osu from '../discord/osu'; import Discord_osu from '../discord/osu';
import Discord_UserInfo from '../discord/UserInfo'; import Discord_UserInfo from '../discord/UserInfo';
import Discord_Stats from '../discord/Stats'; import Discord_Stats from '../discord/Stats';
import Discord_Support from '../discord/Support';
import Discord_MusicPlayer_Play from '../discord/MusicPlayer/Play'; import Discord_MusicPlayer_Play from '../discord/MusicPlayer/Play';
import Discord_MusicPlayer_PlayMy from '../discord/MusicPlayer/PlayMy';
import Discord_MusicPlayer_Skip from '../discord/MusicPlayer/Skip'; import Discord_MusicPlayer_Skip from '../discord/MusicPlayer/Skip';
import Discord_MusicPlayer_Join from '../discord/MusicPlayer/Join'; import Discord_MusicPlayer_Join from '../discord/MusicPlayer/Join';
import Discord_MusicPlayer_Leave from '../discord/MusicPlayer/Leave'; import Discord_MusicPlayer_Leave from '../discord/MusicPlayer/Leave';
@@ -38,13 +40,13 @@ class Discord {
constructor() { constructor() {
this.client = new Client({ this.client = new Client({
//partials: ['MESSAGE', 'CHANNEL', 'REACTION'],
intents: [ intents: [
Intents.FLAGS.GUILDS, IntentsBitField.Flags.MessageContent,
Intents.FLAGS.GUILD_MESSAGES, IntentsBitField.Flags.Guilds,
Intents.FLAGS.GUILD_MEMBERS, IntentsBitField.Flags.GuildMessages,
Intents.FLAGS.GUILD_PRESENCES, IntentsBitField.Flags.GuildMembers,
Intents.FLAGS.GUILD_VOICE_STATES IntentsBitField.Flags.GuildPresences,
IntentsBitField.Flags.GuildVoiceStates
] ]
}); });
} }
@@ -64,8 +66,10 @@ class Discord {
new Discord_InteractionManager(), new Discord_InteractionManager(),
new Discord_osu(), new Discord_osu(),
new Discord_Settings(), new Discord_Settings(),
new Discord_Support(),
new Discord_MusicPlayer_Play(), new Discord_MusicPlayer_Play(),
new Discord_MusicPlayer_PlayMy(),
new Discord_MusicPlayer_NowPlaying(), new Discord_MusicPlayer_NowPlaying(),
new Discord_MusicPlayer_Skip(), new Discord_MusicPlayer_Skip(),
new Discord_MusicPlayer_Join(), new Discord_MusicPlayer_Join(),
@@ -127,7 +131,7 @@ class Discord {
if (interaction.guild) { if (interaction.guild) {
thisModule.GuildInteractionCreate(interaction); thisModule.GuildInteractionCreate(interaction);
if (interaction.isCommand() && interaction.commandName && thisModule.commandInteractionName) { if (interaction.type === InteractionType.ApplicationCommand && interaction.commandName && thisModule.commandInteractionName) {
thisModule.GuildCommandInteractionCreate(interaction); thisModule.GuildCommandInteractionCreate(interaction);
if (interaction.commandName.toLowerCase() === thisModule.commandInteractionName) if (interaction.commandName.toLowerCase() === thisModule.commandInteractionName)
thisModule.GuildModuleCommandInteractionCreate(interaction); thisModule.GuildModuleCommandInteractionCreate(interaction);
@@ -160,7 +164,7 @@ class Discord {
// Handling guild commands // Handling guild commands
this.client.on('messageCreate', async (message: Message) => { this.client.on('messageCreate', async (message: Message) => {
if (!(message.channel instanceof BaseGuildTextChannel)) return; if (!(message.channel instanceof BaseGuildTextChannel || message.channel instanceof BaseGuildVoiceChannel)) return;
if (message.author.bot) return; if (message.author.bot) return;
if (typeof message.guild?.id === 'undefined') return; if (typeof message.guild?.id === 'undefined') return;
@@ -261,7 +265,7 @@ class Discord {
// Handling mentions // Handling mentions
this.client.on('messageCreate', async (message: Message) => { this.client.on('messageCreate', async (message: Message) => {
// TODO: Handle DMs commands soon // TODO: Handle DMs commands soon
if (!(message.channel instanceof TextChannel)) return; if (!(message.channel instanceof BaseGuildTextChannel || message.channel instanceof BaseGuildVoiceChannel)) return;
if (message.author.bot) return; if (message.author.bot) return;
if (typeof message.guild?.id === 'undefined') return; if (typeof message.guild?.id === 'undefined') return;
+289 -57
View File
@@ -1,4 +1,12 @@
import { VoiceChannel, Snowflake, TextChannel, StageChannel, Guild } from 'discord.js'; import {
VoiceChannel,
Snowflake,
StageChannel,
Guild,
PermissionsBitField,
BaseGuildVoiceChannel,
BaseGuildTextChannel
} from 'discord.js';
import { import {
AudioPlayer, AudioPlayer,
VoiceConnection, VoiceConnection,
@@ -7,27 +15,113 @@ import {
createAudioResource, createAudioResource,
VoiceConnectionStatus, VoiceConnectionStatus,
AudioPlayerStatus, AudioPlayerStatus,
AudioPlayerState,
NoSubscriberBehavior, NoSubscriberBehavior,
VoiceConnectionState, VoiceConnectionState,
AudioPlayerError, AudioPlayerError,
DiscordGatewayAdapterCreator DiscordGatewayAdapterCreator
} from '@discordjs/voice'; } from '@discordjs/voice';
import playdl, { YouTubeVideo } from 'play-dl'; import playdl, { Spotify, SpotifyPlaylist, SpotifyTrack, YouTubeVideo } from 'play-dl';
import { EventEmitter } from 'stream'; import { EventEmitter } from 'stream';
import DiscordProvider from './Discord'; import DiscordProvider from './Discord';
import Environment from './Environment'; import Environment from './Environment';
import Logger from '../libs/Logger';
export type ValidTracks = YouTubeVideo; export type ValidTracks = YouTubeVideo | SpotifyTrack;
declare class YouTubeThumbnail {
url: string;
width: number;
height: number;
constructor(data: any);
toJSON(): {
url: string;
width: number;
height: number;
};
}
interface SpotifyThumbnail {
height: number;
width: number;
url: string;
}
interface TokenOptions {
spotify?: {
client_id: string;
client_secret: string;
refresh_token: string;
market: string;
};
soundcloud?: {
client_id: string;
};
youtube?: {
cookie: string;
};
useragent?: string[];
}
let tokenObject: TokenOptions = {};
if (Environment.get().YOUTUBE_COOKIE_BASE64) { if (Environment.get().YOUTUBE_COOKIE_BASE64) {
playdl.setToken({ tokenObject.youtube = {
youtube: {
cookie: Buffer.from(Environment.get().YOUTUBE_COOKIE_BASE64, 'base64').toString() cookie: Buffer.from(Environment.get().YOUTUBE_COOKIE_BASE64, 'base64').toString()
} }
});
} }
if (Environment.get().SPOTIFY_CLIENT_ID && Environment.get().SPOTIFY_CLIENT_SECRET && Environment.get().SPOTIFY_REFRESH_TOKEN && Environment.get().SPOTIFY_CLIENT_MARKET) {
tokenObject.spotify = {
client_id: Environment.get().SPOTIFY_CLIENT_ID,
client_secret: Environment.get().SPOTIFY_CLIENT_SECRET,
refresh_token: Environment.get().SPOTIFY_REFRESH_TOKEN,
market: Environment.get().SPOTIFY_CLIENT_MARKET
}
}
playdl.setToken(tokenObject);
export class TrackUtils {
public static getTitle(track: ValidTracks) {
if (track instanceof YouTubeVideo) {
return track.title;
} else if (track instanceof SpotifyTrack) {
let artistNames = "";
for(let artist of track.artists)
artistNames += artist.name + " ";
return `${artistNames} - ${track.name}`;;
} else {
throw new Error('Invalid track type');
}
}
public static async getThumbnails(track: ValidTracks) {
if (track instanceof YouTubeVideo) {
return track.thumbnails;
} else if (track instanceof SpotifyTrack) {
if (track.thumbnail)
return [track.thumbnail];
else {
// Try to find the thumbnail again
const result = await DiscordMusicPlayer_Instance.searchSpotifyBySpotifyLink(track);
if(!result) return null;
if(result.thumbnail)
return [result.thumbnail];
return null;
}
} else {
throw new Error('Invalid track type');
}
}
public static getHighestResolutionThumbnail(thumbnails: (YouTubeThumbnail[] | SpotifyThumbnail[] | null)) {
if(!thumbnails) return null;
return (thumbnails as any[]).reduce((prev: any, current: any) =>
prev.height * prev.width > current.height * current.width ? prev : current
);
}
}
export class Queue { export class Queue {
public track: ValidTracks[] = []; public track: ValidTracks[] = [];
} }
@@ -42,6 +136,18 @@ export class YouTubeLink {
} }
} }
export class SpotifyLink {
public id: string;
public type: 'track' | 'playlist' | 'album';
public url: string;
constructor(id: string, type: ('track' | 'playlist' | 'album'), url: string) {
this.id = id;
this.type = type;
this.url = url;
}
}
export class PlayerPlayingEvent { export class PlayerPlayingEvent {
public instance: DiscordMusicPlayerInstance; public instance: DiscordMusicPlayerInstance;
@@ -75,7 +181,7 @@ export enum DiscordMusicPlayerLoopMode {
export class DiscordMusicPlayerInstance { export class DiscordMusicPlayerInstance {
public queue: Queue; public queue: Queue;
public player: AudioPlayer; public player: AudioPlayer;
public textChannel?: TextChannel; public textChannel?: BaseGuildTextChannel | BaseGuildVoiceChannel;
public voiceChannel: VoiceChannel | StageChannel; public voiceChannel: VoiceChannel | StageChannel;
public voiceConnection?: VoiceConnection; public voiceConnection?: VoiceConnection;
public previousTrack?: ValidTracks; public previousTrack?: ValidTracks;
@@ -83,6 +189,8 @@ export class DiscordMusicPlayerInstance {
public paused: boolean = false; public paused: boolean = false;
public loopMode: DiscordMusicPlayerLoopMode = DiscordMusicPlayerLoopMode.None; public loopMode: DiscordMusicPlayerLoopMode = DiscordMusicPlayerLoopMode.None;
public actualPlaybackURL?: string;
public readonly events: EventEmitter; public readonly events: EventEmitter;
constructor({ voiceChannel }: { voiceChannel: VoiceChannel | StageChannel }) { constructor({ voiceChannel }: { voiceChannel: VoiceChannel | StageChannel }) {
@@ -98,10 +206,7 @@ export class DiscordMusicPlayerInstance {
this.player.on(AudioPlayerStatus.Idle, async (oldStage, newStage) => { this.player.on(AudioPlayerStatus.Idle, async (oldStage, newStage) => {
//The player stopped //The player stopped
if ( if (newStage.status === AudioPlayerStatus.Idle && oldStage.status !== AudioPlayerStatus.Idle) {
newStage.status === AudioPlayerStatus.Idle &&
oldStage.status !== AudioPlayerStatus.Idle
) {
// Loop mode is set to current song // Loop mode is set to current song
if (this.loopMode === DiscordMusicPlayerLoopMode.Current) { if (this.loopMode === DiscordMusicPlayerLoopMode.Current) {
if (this.queue.track.length !== 0) { if (this.queue.track.length !== 0) {
@@ -132,24 +237,10 @@ export class DiscordMusicPlayerInstance {
}); });
} }
public isReady() { public joinVoiceChannel(voiceChannel: VoiceChannel | StageChannel, textChannel?: BaseGuildTextChannel | BaseGuildVoiceChannel) {
if (!this.voiceConnection) return false;
return this.voiceConnection.state.status === VoiceConnectionStatus.Ready;
}
public isConnected() {
if (!this.voiceConnection) return false;
if (this.voiceConnection.state.status === VoiceConnectionStatus.Destroyed) return false;
if (this.voiceConnection.state.status === VoiceConnectionStatus.Disconnected) return false;
return true;
}
public joinVoiceChannel(voiceChannel: VoiceChannel | StageChannel, textChannel?: TextChannel) {
const permissions = voiceChannel.permissionsFor(DiscordProvider.client.user!); const permissions = voiceChannel.permissionsFor(DiscordProvider.client.user!);
if (!permissions || !voiceChannel.joinable || !permissions.has('CONNECT')) if (!permissions || !voiceChannel.joinable || !permissions.has(PermissionsBitField.Flags.Connect))
throw new Error('No permissions'); throw new Error('No permissions');
if (textChannel) this.textChannel = textChannel; if (textChannel) this.textChannel = textChannel;
@@ -157,31 +248,32 @@ export class DiscordMusicPlayerInstance {
this.voiceConnection = joinVoiceChannel({ this.voiceConnection = joinVoiceChannel({
channelId: this.voiceChannel.id, channelId: this.voiceChannel.id,
guildId: this.voiceChannel.guild.id, guildId: this.voiceChannel.guild.id,
adapterCreator: this.voiceChannel.guild adapterCreator: this.voiceChannel.guild.voiceAdapterCreator as DiscordGatewayAdapterCreator
.voiceAdapterCreator as DiscordGatewayAdapterCreator
}); });
this.voiceConnection.on( this.voiceConnection.on(
VoiceConnectionStatus.Ready, VoiceConnectionStatus.Ready,
(oldState: VoiceConnectionState, newState: VoiceConnectionState) => { async (oldState: VoiceConnectionState, newState: VoiceConnectionState) => {
let currentVC = DiscordProvider.client.guilds.cache.get(voiceChannel.guild.id)?.me let guild = DiscordProvider.client.guilds.cache.get(voiceChannel.guild.id);
?.voice.channel; if (guild) {
let currentVC = guild?.members.me?.voice.channel;
if (currentVC && currentVC.id !== this.voiceChannel.id) { if (currentVC && currentVC.id !== this.voiceChannel.id) {
this.voiceChannel = currentVC; this.voiceChannel = currentVC;
} }
} }
}
); );
this.voiceConnection.on( this.voiceConnection.on(
VoiceConnectionStatus.Disconnected, VoiceConnectionStatus.Disconnected,
(oldState: VoiceConnectionState, newState: VoiceConnectionState) => { (oldState: VoiceConnectionState, newState: VoiceConnectionState) => {
setTimeout(() => { setTimeout(async () => {
if ( let guild = DiscordProvider.client.guilds.cache.get(voiceChannel.guildId);
!DiscordProvider.client.guilds.cache.get(voiceChannel.guildId!)!.me!.voice if(guild) {
.channelId if (!guild?.members.me?.voice.channelId) {
) {
this.events.emit('disconnect', new VoiceDisconnectedEvent(this)); this.events.emit('disconnect', new VoiceDisconnectedEvent(this));
} }
}
}, 2000); }, 2000);
} }
); );
@@ -190,9 +282,10 @@ export class DiscordMusicPlayerInstance {
public async leaveVoiceChannel() { public async leaveVoiceChannel() {
if (this.player) this.player.pause(); if (this.player) this.player.pause();
if (DiscordProvider.client.guilds.cache.get(this.voiceChannel.guild.id)?.me?.voice) { const guild = DiscordProvider.client.guilds.cache.get(this.voiceChannel.guild.id);
this.voiceConnection?.disconnect(); if (!guild) return;
}
if (guild.members.me?.voice) this.voiceConnection?.disconnect();
} }
public async pausePlayer() { public async pausePlayer() {
@@ -221,10 +314,25 @@ export class DiscordMusicPlayerInstance {
if (!this.voiceConnection) throw new Error('No voice connection'); if (!this.voiceConnection) throw new Error('No voice connection');
try { try {
let resource;
if(track instanceof YouTubeVideo) {
const stream = await playdl.stream(track.url); const stream = await playdl.stream(track.url);
const resource = createAudioResource(stream.stream, { resource = createAudioResource(stream.stream, {
inputType: stream.type inputType: stream.type
}); });
this.actualPlaybackURL = track.url;
} else {
const search = await DiscordMusicPlayer_Instance.searchYouTubeBySpotifyLink(track);
if(!search)
throw new Error('Unable to find Spotify track on YouTube');
const stream = await playdl.stream(search.url);
resource = createAudioResource(stream.stream, {
inputType: stream.type
});
this.actualPlaybackURL = search.url;
}
this.player.play(resource); this.player.play(resource);
this.voiceConnection.subscribe(this.player); this.voiceConnection.subscribe(this.player);
@@ -248,6 +356,31 @@ export class DiscordMusicPlayerInstance {
} }
} }
public clearQueue() {
if (!this.voiceConnection) throw new Error('No voice connection');
if(!this.queue.track || this.queue.track.length === 0) return;
this.queue.track = [this.queue.track[0]];
}
public shuffleQueue() {
if (!this.voiceConnection) throw new Error('No voice connection');
const shuffleFixedFirst = (queue: ValidTracks[]) => {
if(queue.length <= 2) return queue;
const fixedFirst = queue.shift();
if(!fixedFirst) return queue;
queue.sort(() => Math.random() - 0.5);
queue.unshift(fixedFirst);
return queue;
}
if(!this.queue.track || this.queue.track.length === 0) return;
this.queue.track = shuffleFixedFirst(this.queue.track);
}
public setLoopMode(mode: DiscordMusicPlayerLoopMode) { public setLoopMode(mode: DiscordMusicPlayerLoopMode) {
this.loopMode = mode; this.loopMode = mode;
} }
@@ -260,6 +393,24 @@ export class DiscordMusicPlayerInstance {
return this.previousTrack; return this.previousTrack;
} }
public getActualPlaybackURL() {
return this.actualPlaybackURL;
}
public isReady() {
if (!this.voiceConnection) return false;
return this.voiceConnection.state.status === VoiceConnectionStatus.Ready;
}
public isConnected() {
if (!this.voiceConnection) return false;
if (this.voiceConnection.state.status === VoiceConnectionStatus.Destroyed) return false;
if (this.voiceConnection.state.status === VoiceConnectionStatus.Disconnected) return false;
return true;
}
public isPaused() { public isPaused() {
return this.paused; return this.paused;
} }
@@ -286,10 +437,7 @@ export class DiscordMusicPlayerInstance {
const stream = 'https://fakestream:42069/fake/stream/fake/audio/fake.mp3'; const stream = 'https://fakestream:42069/fake/stream/fake/audio/fake.mp3';
const resource = createAudioResource(stream); const resource = createAudioResource(stream);
this.player.play(resource); this.player.play(resource);
this.player.emit( this.player.emit('error', new AudioPlayerError(new Error('Music player was manually crashed'), null!));
'error',
new AudioPlayerError(new Error('Music player was manually crashed'), null!)
);
} }
} }
@@ -326,12 +474,44 @@ class DiscordMusicPlayer {
return searched; return searched;
} }
public async searchYouTubeBySpotifyLink(spotifyLink: SpotifyLink) {
const track = await this.searchSpotifyBySpotifyLink(spotifyLink);
if(!track) return;
let artistNames = "";
for(let artist of track.artists)
artistNames += artist.name + " ";
const ytSearchResult = await this.searchYouTubeByQuery(`${artistNames} ${track.name}`);
if(!ytSearchResult) return null;
return ytSearchResult[0];
}
public async searchSpotifyBySpotifyLink(spotifyLink: SpotifyLink) {
if(playdl.is_expired())
await playdl.refreshToken();
if(spotifyLink.type != 'track') return;
// Fetch data from spotify
const searched: Spotify = await playdl.spotify("https://open.spotify.com/track/" + spotifyLink.id).catch((err) => {
Logger.error(err.message);
throw new Error('Error while searching on Spotify');
});
if(!(searched instanceof SpotifyTrack)) return;
if (!searched) return null;
const track = searched as unknown as SpotifyTrack;
return track;
}
public async searchYouTubeByYouTubeLink(youtubeLink: YouTubeLink) { public async searchYouTubeByYouTubeLink(youtubeLink: YouTubeLink) {
// Search the url // Search the url
const searched: YouTubeVideo[] = await playdl.search( const searched: YouTubeVideo[] = await playdl.search('https://www.youtube.com/watch?v=' + youtubeLink.videoId, {
'https://www.youtube.com/watch?v=' + youtubeLink.videoId, source: { youtube: 'video' }
{ source: { youtube: 'video' } } });
);
for (let video of searched) { for (let video of searched) {
if (video.id === youtubeLink.videoId) return video; if (video.id === youtubeLink.videoId) return video;
} }
@@ -345,9 +525,7 @@ class DiscordMusicPlayer {
} }
// Last resort, search the title // Last resort, search the title
const videoInfo = await playdl.video_basic_info( const videoInfo = await playdl.video_basic_info('https://www.youtube.com/watch?v=' + youtubeLink.videoId);
'https://www.youtube.com/watch?v=' + youtubeLink.videoId
);
if (videoInfo?.video_details?.title) { if (videoInfo?.video_details?.title) {
const searched: YouTubeVideo[] = await playdl.search(videoInfo.video_details.title, { const searched: YouTubeVideo[] = await playdl.search(videoInfo.video_details.title, {
source: { youtube: 'video' } source: { youtube: 'video' }
@@ -357,9 +535,7 @@ class DiscordMusicPlayer {
} }
} }
let yt_info = await playdl.video_info( let yt_info = await playdl.video_info('https://www.youtube.com/watch?v=' + youtubeLink.videoId);
'https://www.youtube.com/watch?v=' + youtubeLink.videoId
);
if (yt_info) { if (yt_info) {
return new YouTubeVideo({ return new YouTubeVideo({
id: yt_info.video_details.id, id: yt_info.video_details.id,
@@ -393,6 +569,21 @@ class DiscordMusicPlayer {
}); });
} }
public async getSpotifySongsInPlayList(spotifyLink: string) {
if(playdl.is_expired())
await playdl.refreshToken();
const result = await playdl.spotify(spotifyLink).catch((err) => {
Logger.error(err.message);
throw new Error('Error while searching on Spotify');
});
if(!(result.type == 'playlist' || result.type == 'album'))
throw new Error("Not a spotify playlist");
return result as unknown as SpotifyPlaylist;
}
public isYouTubeLink(link: string): boolean { public isYouTubeLink(link: string): boolean {
try { try {
this.parseYouTubeLink(link); this.parseYouTubeLink(link);
@@ -402,10 +593,21 @@ class DiscordMusicPlayer {
} }
} }
public isSpotifyLink(link: string): boolean {
try {
this.parseSpotifyLink(link);
return true;
} catch (err) {
return false;
}
}
public parseYouTubeLink(query: string): YouTubeLink { public parseYouTubeLink(query: string): YouTubeLink {
if ( if (
query.startsWith('https://www.youtube.com/watch?v=') || query.startsWith('https://www.youtube.com/watch?v=') ||
query.startsWith('http://www.youtube.com/watch?v=') query.startsWith('http://www.youtube.com/watch?v=') ||
query.startsWith('https://music.youtube.com/watch?v=') ||
query.startsWith('https://music.youtube.com/watch?v=')
) { ) {
let data = this.parseURLQuery(query); let data = this.parseURLQuery(query);
if (!data.v) throw new Error('YouTube link is invalid'); if (!data.v) throw new Error('YouTube link is invalid');
@@ -437,6 +639,36 @@ class DiscordMusicPlayer {
} }
} }
public parseSpotifyLink(query: string): SpotifyLink {
if (query.startsWith('https://open.spotify.com/track/')) {
let id = query.split('/')[4].split(/[?#]/)[0];
return {
id: id,
type: 'track',
url: query.split(/[?#]/)[0]
};
}
else if (query.startsWith('https://open.spotify.com/album/')) {
let id = query.split('/')[4].split(/[?#]/)[0];
return {
id: id,
type: 'album',
url: query.split(/[?#]/)[0]
};
}
else if (query.startsWith('https://open.spotify.com/playlist/')) {
let id = query.split('/')[4].split(/[?#]/)[0];
return {
id: id,
type: 'playlist',
url: query.split(/[?#]/)[0]
};
}
else {
throw new Error('Spotify link is invalid');
}
}
private parseURLQuery(query: string) { private parseURLQuery(query: string) {
let queryObject: any = {}; let queryObject: any = {};
if (query.indexOf('?') >= 0) { if (query.indexOf('?') >= 0) {
+13 -1
View File
@@ -29,19 +29,31 @@ class Environment {
const DISCORD_TOKEN = process.env.DISCORD_TOKEN; const DISCORD_TOKEN = process.env.DISCORD_TOKEN;
const DEVELOPER_IDS = process.env.DEVELOPER_IDS; const DEVELOPER_IDS = process.env.DEVELOPER_IDS;
const PRIVATE_BOT = process.env.PRIVATE_BOT; const PRIVATE_BOT = process.env.PRIVATE_BOT;
const SUPPORT_URL = process.env.SUPPORT_URL;
const OSU_API_KEY = process.env.OSU_API_KEY; const OSU_API_KEY = process.env.OSU_API_KEY;
const YOUTUBE_COOKIE_BASE64 = process.env.YOUTUBE_COOKIE_BASE64; const YOUTUBE_COOKIE_BASE64 = process.env.YOUTUBE_COOKIE_BASE64;
const SPOTIFY_CLIENT_ID = process.env.SPOTIFY_CLIENT_ID;
const SPOTIFY_CLIENT_SECRET= process.env.SPOTIFY_CLIENT_SECRET;
const SPOTIFY_REFRESH_TOKEN= process.env.SPOTIFY_REFRESH_TOKEN;
const SPOTIFY_CLIENT_MARKET= process.env.SPOTIFY_CLIENT_MARKET;
return { return {
NODE_ENV, NODE_ENV,
DISCORD_TOKEN, DISCORD_TOKEN,
DEVELOPER_IDS, DEVELOPER_IDS,
PRIVATE_BOT, PRIVATE_BOT,
SUPPORT_URL,
OSU_API_KEY, OSU_API_KEY,
YOUTUBE_COOKIE_BASE64 YOUTUBE_COOKIE_BASE64,
SPOTIFY_CLIENT_ID,
SPOTIFY_CLIENT_SECRET,
SPOTIFY_REFRESH_TOKEN,
SPOTIFY_CLIENT_MARKET
}; };
} }
+1
View File
@@ -28,6 +28,7 @@ export const GLOBAL_COMMANDS: Object[] = [
.setName('search') .setName('search')
.setDescription('Search for music') .setDescription('Search for music')
.addStringOption((option) => option.setName('query').setDescription('Search query').setRequired(true)), .addStringOption((option) => option.setName('query').setDescription('Search query').setRequired(true)),
new SlashCommandBuilder().setName('playmy').setDescription('Play the detected songs you are listening to'),
new SlashCommandBuilder().setName('skip').setDescription('Skip the current song'), new SlashCommandBuilder().setName('skip').setDescription('Skip the current song'),
new SlashCommandBuilder().setName('pause').setDescription('Pause the current music'), new SlashCommandBuilder().setName('pause').setDescription('Pause the current music'),
new SlashCommandBuilder().setName('resume').setDescription('Resume paused music'), new SlashCommandBuilder().setName('resume').setDescription('Resume paused music'),
+16 -11
View File
@@ -1,5 +1,5 @@
import { import {
MessageEmbed, EmbedBuilder,
User, User,
MessagePayload, MessagePayload,
MessageOptions, MessageOptions,
@@ -10,8 +10,10 @@ import {
BaseGuildTextChannel, BaseGuildTextChannel,
Message, Message,
ColorResolvable, ColorResolvable,
Interaction, InteractionReplyOptions,
InteractionReplyOptions BaseInteraction,
BaseGuildVoiceChannel,
VoiceChannel
} from 'discord.js'; } from 'discord.js';
import App from '..'; import App from '..';
@@ -43,7 +45,7 @@ interface EmbedDataPresets {
} }
export function makeEmbed(data: EmbedData) { export function makeEmbed(data: EmbedData) {
const embed = new MessageEmbed(); const embed = new EmbedBuilder();
embed.setColor(data.color || '#FFFFFF'); embed.setColor(data.color || '#FFFFFF');
if (!data.icon) embed.setTitle(data.title); if (!data.icon) embed.setTitle(data.title);
@@ -54,14 +56,14 @@ export function makeEmbed(data: EmbedData) {
if (data.setTimestamp) embed.setTimestamp(); if (data.setTimestamp) embed.setTimestamp();
if (data.user) if (data.user)
embed.footer = { embed.setFooter({
text: `${data.user.username} | v${App.version}`, text: `${data.user.username} | v${App.version}`,
iconURL: `${data.user.displayAvatarURL()}?size=4096` iconURL: `${data.user.displayAvatarURL()}?size=4096`
}; });
else else
embed.footer = { embed.setFooter({
text: `v${App.version}` text: `v${App.version}`
}; });
if (data.fields) embed.addFields(data.fields); if (data.fields) embed.addFields(data.fields);
@@ -129,13 +131,16 @@ export function makeProcessingEmbed(data: EmbedDataPresets) {
} }
export async function sendMessage( export async function sendMessage(
channel: TextChannel | DMChannel | BaseGuildTextChannel | GuildTextBasedChannel | PartialDMChannel, channel: TextChannel | DMChannel | BaseGuildTextChannel | GuildTextBasedChannel | PartialDMChannel | BaseGuildTextChannel | BaseGuildVoiceChannel,
user: User | undefined, user: User | undefined,
options: string | MessagePayload | MessageOptions options: string | MessagePayload | MessageOptions
) { ) {
let message; let message;
try { try {
if(channel instanceof BaseGuildVoiceChannel)
message = await (channel as VoiceChannel).send(options);
else
message = await channel.send(options); message = await channel.send(options);
} catch (error) { } catch (error) {
if (typeof user === 'undefined') { if (typeof user === 'undefined') {
@@ -160,8 +165,8 @@ export async function sendHybridInteractionMessageResponse(
data: HybridInteractionMessage, data: HybridInteractionMessage,
payload: MessageOptions | InteractionReplyOptions, payload: MessageOptions | InteractionReplyOptions,
replace = false replace = false
): Promise<Message | Interaction | undefined> { ): Promise<Message | BaseInteraction | undefined> {
if (data.isSlashCommand() || data.isButton() || data.isSelectMenu()) { if (data.isApplicationCommand() || data.isButton() || data.isSelectMenu()) {
const messageComponent = data.getMessageComponentInteraction(); const messageComponent = data.getMessageComponentInteraction();
if (!messageComponent.replied) { if (!messageComponent.replied) {
+18 -7
View File
@@ -8,7 +8,11 @@ import {
TextBasedChannel, TextBasedChannel,
MessageComponentInteraction, MessageComponentInteraction,
User, User,
SelectMenuInteraction SelectMenuInteraction,
InteractionType,
BaseInteraction,
GuildBasedChannel,
ChannelType
} from 'discord.js'; } from 'discord.js';
export default class DiscordModule { export default class DiscordModule {
@@ -56,17 +60,17 @@ export default class DiscordModule {
} }
export class HybridInteractionMessage { export class HybridInteractionMessage {
public data: Interaction | Message; public data: BaseInteraction | Message;
constructor(data: Interaction | Message) { constructor(data: BaseInteraction | Message) {
this.data = data; this.data = data;
} }
public isInteraction(): boolean { public isInteraction(): boolean {
return this.data instanceof Interaction; return this.data instanceof BaseInteraction;
} }
public isSlashCommand(): boolean { public isApplicationCommand(): boolean {
return this.data instanceof CommandInteraction && this.data.isCommand(); return this.data instanceof CommandInteraction && this.data.type === InteractionType.ApplicationCommand;
} }
public isButton(): boolean { public isButton(): boolean {
@@ -85,6 +89,13 @@ export class HybridInteractionMessage {
return this.data.channel; return this.data.channel;
} }
public getGuildChannel(): GuildBasedChannel | null {
if (this.data.channel && this.data.channel.type !== ChannelType.DM) {
return this.getChannel() as GuildBasedChannel;
}
return null;
}
public getGuild(): Guild | null { public getGuild(): Guild | null {
return this.data.guild; return this.data.guild;
} }
@@ -130,7 +141,7 @@ export class HybridInteractionMessage {
return this.data as Message; return this.data as Message;
} }
public getRaw(): Interaction | Message { public getRaw(): BaseInteraction | Message {
return this.data; return this.data;
} }
} }
+99
View File
@@ -0,0 +1,99 @@
import { Guild, GuildChannelResolvable, GuildMember } from 'discord.js';
import { I18n } from 'i18n';
import { COMMON_EMBEDS } from '../discord/Settings';
import { sendHybridInteractionMessageResponse } from './DiscordMessage';
import { HybridInteractionMessage } from './DiscordModule';
export async function checkMemberPermissionsInGuild({
member,
permissions,
data,
locale
}: {
member: GuildMember;
permissions: bigint[];
data?: HybridInteractionMessage;
locale?: I18n;
}): Promise<boolean> {
const hasPermissions = member.permissions.has(permissions);
if (!hasPermissions && data && locale)
await sendHybridInteractionMessageResponse(data, {
embeds: [COMMON_EMBEDS.MEMBER_NO_PERMISSION(data, locale, permissions)]
});
return hasPermissions;
}
export async function checkMemberPermissionsInChannel({
member,
channel,
permissions,
data,
locale
}: {
member: GuildMember;
channel: GuildChannelResolvable;
permissions: bigint[];
data?: HybridInteractionMessage;
locale?: I18n;
}): Promise<boolean> {
const hasPermissions = member.permissionsIn(channel).has(permissions);
if (!hasPermissions && data && locale)
await sendHybridInteractionMessageResponse(data, {
embeds: [COMMON_EMBEDS.MEMBER_NO_PERMISSION(data, locale, permissions)]
});
return hasPermissions;
}
export async function checkBotPermissionsInGuild({
guild,
permissions,
data,
locale
}: {
guild: Guild;
permissions: bigint[];
data?: HybridInteractionMessage;
locale?: I18n;
}): Promise<boolean> {
const member = guild.members.me;
if(!member) return false;
const hasPermissions = member.permissions.has(permissions);
if (!hasPermissions && data && locale)
await sendHybridInteractionMessageResponse(data, {
embeds: [COMMON_EMBEDS.BOT_NO_PERMISSION(data, locale, permissions)]
});
return hasPermissions;
}
export async function checkBotPermissionsInChannel({
guild,
channel,
permissions,
data,
locale
}: {
guild: Guild;
channel: GuildChannelResolvable;
permissions: bigint[];
data?: HybridInteractionMessage;
locale?: I18n;
}): Promise<boolean> {
const member = guild.members.me;
if(!member) return false;
const hasPermissions = member.permissionsIn(channel).has(permissions);
if (!hasPermissions && data && locale)
await sendHybridInteractionMessageResponse(data, {
embeds: [COMMON_EMBEDS.BOT_NO_PERMISSION(data, locale, permissions)]
});
return hasPermissions;
}
+233 -178
View File
@@ -23,26 +23,13 @@
enabled "2.0.x" enabled "2.0.x"
kuler "^2.0.0" kuler "^2.0.0"
"@discordjs/builders@^0.14.0": "@discordjs/builders@^1.0.0":
version "0.14.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-0.14.0.tgz#1915af10fa1c30d738b08b1ed0ae3c224fd491d7" resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.0.0.tgz#1ddc5a7f9d20977e7414c02989169bb7f55294ba"
integrity sha512-+fqLIqa9wN3R+kvlld8sgG0nt04BAZxdCDP4t2qZ9TJsquLWA+xMtT8Waibb3d4li4AQS+IOfjiHAznv/dhHgQ== integrity sha512-8y91ZfpOHubiGJu5tVyGI9tQCEyHZDTeqUWVcJd0dq7B96xIf84S0L4fwmD1k9zTe1eqEFSk0gc7BpY+FKn7Ww==
dependencies: dependencies:
"@sapphire/shapeshift" "^3.1.0" "@sapphire/shapeshift" "^3.5.1"
"@sindresorhus/is" "^4.6.0" discord-api-types "^0.36.2"
discord-api-types "^0.33.3"
fast-deep-equal "^3.1.3"
ts-mixer "^6.0.1"
tslib "^2.4.0"
"@discordjs/builders@^0.15.0":
version "0.15.0"
resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-0.15.0.tgz#f76318f2de0b057c21bb25bb010ef2317039793d"
integrity sha512-w1UfCPzx2iKycn6qh/f0c+PcDpcTHzHr7TXALXu/a4gKHGamiSg3lP8GhYswweSJk/Q5cbFLHZUsnoY3MVKNAg==
dependencies:
"@sapphire/shapeshift" "^3.1.0"
"@sindresorhus/is" "^4.6.0"
discord-api-types "^0.33.3"
fast-deep-equal "^3.1.3" fast-deep-equal "^3.1.3"
ts-mixer "^6.0.1" ts-mixer "^6.0.1"
tslib "^2.4.0" tslib "^2.4.0"
@@ -52,28 +39,33 @@
resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.7.0.tgz#1a6c00198b744ba2b73a64442145da637ac073b8" resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.7.0.tgz#1a6c00198b744ba2b73a64442145da637ac073b8"
integrity sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA== integrity sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA==
"@discordjs/node-pre-gyp@^0.4.2": "@discordjs/collection@^1.0.0":
version "0.4.2" version "1.0.0"
resolved "https://registry.yarnpkg.com/@discordjs/node-pre-gyp/-/node-pre-gyp-0.4.2.tgz#37dd27f8d353eeb87372fcce059c5249550b37ab" resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.0.0.tgz#4d777d87e56640a200200e7f5605c0fba05ac1b8"
integrity sha512-V239Czn+DXFGLhhuccwEDBoTdgMGrRu30dOlzm1GzrSIjwFj01ZJerNX7x+CEX1NG1Q/1gGfOOkeZFNHjycrRA== integrity sha512-nAxDQYE5dNAzEGQ7HU20sujDsG5vLowUKCEqZkKUIlrXERZFTt/60zKUj/g4+AVCGeq+pXC5hivMaNtiC+PY5Q==
"@discordjs/node-pre-gyp@^0.4.4":
version "0.4.4"
resolved "https://registry.yarnpkg.com/@discordjs/node-pre-gyp/-/node-pre-gyp-0.4.4.tgz#33eea1038784ffc5715ef775e4f9d6cffaa96c73"
integrity sha512-x569MMtdk6jdGo2S58iiZoyv4p/N2Ju8Nh6vvzZb1wyouV7IE3VuU0hg2kqUmTfD0z6r4uD6acvMTuc+iA3f8g==
dependencies: dependencies:
detect-libc "^1.0.3" detect-libc "^2.0.0"
https-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0"
make-dir "^3.1.0" make-dir "^3.1.0"
node-fetch "^2.6.5" node-fetch "^2.6.7"
nopt "^5.0.0" nopt "^5.0.0"
npmlog "^5.0.1" npmlog "^5.0.1"
rimraf "^3.0.2" rimraf "^3.0.2"
semver "^7.3.5" semver "^7.3.5"
tar "^6.1.11" tar "^6.1.11"
"@discordjs/opus@^0.7.0": "@discordjs/opus@^0.8.0":
version "0.7.0" version "0.8.0"
resolved "https://registry.yarnpkg.com/@discordjs/opus/-/opus-0.7.0.tgz#69e9611a6412ea470f8e640f54478da89326ca3b" resolved "https://registry.yarnpkg.com/@discordjs/opus/-/opus-0.8.0.tgz#dae2d220a7942736343021f9af2b2b921d482c46"
integrity sha512-3Xxa3dh7taSDwBAR5fLALZ/KTxvbMmHCMxYLYve6NlPO7Ms1CLmKqp/R4ZoVzkRGQVUVWEhaB1s0v9jfa2tfDg== integrity sha512-uHE7OmHEmP8YM0yvsH3iSdacdeghO0qTkF0CIkV07Tg0qdyOLUVkoZHj5Zcpge9rC4qb/JvTS2xRgttSZLM43Q==
dependencies: dependencies:
"@discordjs/node-pre-gyp" "^0.4.2" "@discordjs/node-pre-gyp" "^0.4.4"
node-addon-api "^4.2.0" node-addon-api "^5.0.0"
"@discordjs/rest@^0.5.0": "@discordjs/rest@^0.5.0":
version "0.5.0" version "0.5.0"
@@ -87,6 +79,19 @@
tslib "^2.4.0" tslib "^2.4.0"
undici "^5.4.0" undici "^5.4.0"
"@discordjs/rest@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.0.0.tgz#624ac48cf8f66f46d47371323963a0c0617ddd63"
integrity sha512-uDAvnE0P2a8axMdD4C51EGjvCRQ2HZk2Yxf6vHWZgIqG87D8DGKMPwmquIxrrB07MjV+rwci2ObU+mGhGP+bJg==
dependencies:
"@discordjs/collection" "^1.0.0"
"@sapphire/async-queue" "^1.3.2"
"@sapphire/snowflake" "^3.2.2"
discord-api-types "^0.36.2"
file-type "^17.1.2"
tslib "^2.4.0"
undici "^5.7.0"
"@discordjs/voice@^0.10.0": "@discordjs/voice@^0.10.0":
version "0.10.0" version "0.10.0"
resolved "https://registry.yarnpkg.com/@discordjs/voice/-/voice-0.10.0.tgz#5c911b5ce276233b232a98c92141e07f1f52168b" resolved "https://registry.yarnpkg.com/@discordjs/voice/-/voice-0.10.0.tgz#5c911b5ce276233b232a98c92141e07f1f52168b"
@@ -100,14 +105,14 @@
ws "^8.7.0" ws "^8.7.0"
"@jridgewell/resolve-uri@^3.0.3": "@jridgewell/resolve-uri@^3.0.3":
version "3.0.7" version "3.0.8"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz#687cc2bbf243f4e9a868ecf2262318e2658873a1"
integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== integrity sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w==
"@jridgewell/sourcemap-codec@^1.4.10": "@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.13" version "1.4.14"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@jridgewell/trace-mapping@0.3.9": "@jridgewell/trace-mapping@0.3.9":
version "0.3.9" version "0.3.9"
@@ -168,62 +173,70 @@
dependencies: dependencies:
make-plural "^7.0.0" make-plural "^7.0.0"
"@prisma/client@^3.15.2": "@prisma/client@^4.0.0":
version "3.15.2" version "4.0.0"
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.15.2.tgz#2181398147afc79bfe0d83c03a88dc45b49bd365" resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.0.0.tgz#ed2f46930a1da0d8ae88d7965485973576b04270"
integrity sha512-ErqtwhX12ubPhU4d++30uFY/rPcyvjk+mdifaZO5SeM21zS3t4jQrscy8+6IyB0GIYshl5ldTq6JSBo1d63i8w== integrity sha512-g1h2OGoRo7anBVQ9Cw3gsbjwPtvf7i0pkGxKeZICtwkvE5CZXW+xZF4FZdmrViYkKaAShbISL0teNpu9ecpf4g==
dependencies: dependencies:
"@prisma/engines-version" "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" "@prisma/engines-version" "3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11"
"@prisma/engines-version@3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e": "@prisma/engines-version@3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11":
version "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" version "3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11"
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz#bf5e2373ca68ce7556b967cb4965a7095e93fe53" resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11.tgz#4b5efe5eee2feef12910e4627a572cd96ed83236"
integrity sha512-e3k2Vd606efd1ZYy2NQKkT4C/pn31nehyLhVug6To/q8JT8FpiMrDy7zmm3KLF0L98NOQQcutaVtAPhzKhzn9w== integrity sha512-PiZhdD624SrYEjyLboI0X7OugNbxUzDJx9v/6ldTKuqNDVUCmRH/Z00XwDi/dgM4FlqOSO+YiUsSiSKjxxG8cw==
"@prisma/engines@3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e": "@prisma/engines@3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11":
version "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" version "3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11"
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz#f691893df506b93e3cb1ccc15ec6e5ac64e8e570" resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11.tgz#82f0018153cffa05d61422f9c0c7b0479b180f75"
integrity sha512-NHlojO1DFTsSi3FtEleL9QWXeSF/UjhCW0fgpi7bumnNZ4wj/eQ+BJJ5n2pgoOliTOGv9nX2qXvmHap7rJMNmg== integrity sha512-u/rG4lDHALolWBLr3yebZ+N2qImp3SDMcu7bHNJuRDaYvYEXy/MqfNRNEgd9GoPsXL3gofYf0VzJf2AmCG3YVw==
"@sapphire/async-queue@^1.3.1": "@sapphire/async-queue@^1.3.1":
version "1.3.1" version "1.3.1"
resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.3.1.tgz#9d861e626dbffae02d808e13f823d4510e450a78" resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.3.1.tgz#9d861e626dbffae02d808e13f823d4510e450a78"
integrity sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g== integrity sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g==
"@sapphire/shapeshift@^3.1.0": "@sapphire/async-queue@^1.3.2":
version "3.1.0" version "1.3.2"
resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.1.0.tgz#e4719fd03ab1d3ea01170f9c37b9cb64f5794759" resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.3.2.tgz#befe5f5025e2e317a9eba2d1a24ca5d2e4576f86"
integrity sha512-PkxFXd3QJ1qAPS05Dy2UkVGYPm/asF1Ugt2Xyzmv4DHzO3+G7l+873C4XFFcJ9M5Je+eCMC7SSifgPTSur5QuA== integrity sha512-rUpMLATsoAMnlN3gecAcr9Ecnw1vG7zi5Xr+IX22YzRzi1k9PF9vKzoT8RuEJbiIszjcimu3rveqUnvwDopz8g==
"@sapphire/shapeshift@^3.5.1":
version "3.5.1"
resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.5.1.tgz#3bfd0e6bcfdced4ac26a6f450b5f7b8e49f4f2cc"
integrity sha512-7JFsW5IglyOIUQI1eE0g6h06D/Far6HqpcowRScgCiLSqTf3hhkPWCWotVTtVycnDCMYIwPeaw6IEPBomKC8pA==
dependencies:
fast-deep-equal "^3.1.3"
lodash.uniqwith "^4.5.0"
"@sapphire/snowflake@^3.2.2": "@sapphire/snowflake@^3.2.2":
version "3.2.2" version "3.2.2"
resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.2.2.tgz#faacdc1b5f7c43145a71eddba917de2b707ef780" resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.2.2.tgz#faacdc1b5f7c43145a71eddba917de2b707ef780"
integrity sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ== integrity sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==
"@sindresorhus/is@^4.6.0": "@tokenizer/token@^0.3.0":
version "4.6.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276"
integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==
"@tsconfig/node10@^1.0.7": "@tsconfig/node10@^1.0.7":
version "1.0.8" version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
"@tsconfig/node12@^1.0.7": "@tsconfig/node12@^1.0.7":
version "1.0.9" version "1.0.11"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
"@tsconfig/node14@^1.0.0": "@tsconfig/node14@^1.0.0":
version "1.0.1" version "1.0.3"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
"@tsconfig/node16@^1.0.2": "@tsconfig/node16@^1.0.2":
version "1.0.2" version "1.0.3"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
"@types/bluebird@^3.5.36": "@types/bluebird@^3.5.36":
version "3.5.36" version "3.5.36"
@@ -235,29 +248,21 @@
resolved "https://registry.yarnpkg.com/@types/color-thief-node/-/color-thief-node-1.0.1.tgz#851a82b48a5560e4d2c051324e42c21a25e5802c" resolved "https://registry.yarnpkg.com/@types/color-thief-node/-/color-thief-node-1.0.1.tgz#851a82b48a5560e4d2c051324e42c21a25e5802c"
integrity sha512-UU2pvvjtxvugMdCpoG7If9EqbaeITyczSXymIA/h/Quzbf4HwvT1uBKDG0R42jzHlHGOsRqCRb+RP5Kj1RIBaQ== integrity sha512-UU2pvvjtxvugMdCpoG7If9EqbaeITyczSXymIA/h/Quzbf4HwvT1uBKDG0R42jzHlHGOsRqCRb+RP5Kj1RIBaQ==
"@types/i18n@^0.13.3": "@types/i18n@^0.13.4":
version "0.13.3" version "0.13.4"
resolved "https://registry.yarnpkg.com/@types/i18n/-/i18n-0.13.3.tgz#88db5b33ccbbd57de54c8c2da01002e8fb48efc3" resolved "https://registry.yarnpkg.com/@types/i18n/-/i18n-0.13.4.tgz#fe3d27d08337f9d4a972d1f460d1471d6f79e163"
integrity sha512-o6MlrxjGUDANSmMmQ6p/f7ea3+kJFeNWebxylKE3pLjWbIa0Npr/DT2Qf941oqovdopadAfV+PuBMTKg4p37NA== integrity sha512-PN4ZsplbpHZ2eaYixFNWkZKN51pcB02K2UKvqHVbrzq2jTO0sChPMuKKYAW1ZbElyHUvPgFeYsz9rqktChGyMw==
"@types/node-fetch@^2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975"
integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==
dependencies:
"@types/node" "*"
form-data "^3.0.0"
"@types/node@*": "@types/node@*":
version "17.0.40"
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.40.tgz#76ee88ae03650de8064a6cf75b8d95f9f4a16090"
integrity sha512-UXdBxNGqTMtm7hCwh9HtncFVLrXoqA3oJW30j6XWp5BH/wu3mVeaxo7cq5benFdBw34HB3XDT2TRPI7rXZ+mDg==
"@types/node@^18.0.0":
version "18.0.0" version "18.0.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a"
integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA== integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==
"@types/node@^18.0.4":
version "18.0.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.4.tgz#48aedbf35efb3af1248e4cd4d792c730290cd5d6"
integrity sha512-M0+G6V0Y4YV8cqzHssZpaNCqvYwlCiulmm0PwpNLF55r/+cT8Ol42CHRU1SEaYFH2rTwiiE1aYg/2g2rrtGdPA==
"@types/os-utils@^0.0.1": "@types/os-utils@^0.0.1":
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/@types/os-utils/-/os-utils-0.0.1.tgz#f1ec7830c60be563d6e9566a0ae3b23ea5497493" resolved "https://registry.yarnpkg.com/@types/os-utils/-/os-utils-0.0.1.tgz#f1ec7830c60be563d6e9566a0ae3b23ea5497493"
@@ -278,10 +283,10 @@
resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1"
integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==
"@types/validator@^13.7.3": "@types/validator@^13.7.4":
version "13.7.3" version "13.7.4"
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.3.tgz#3193c0a3c03a7d1189016c62b4fba4b149ef5e33" resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.4.tgz#33cc949ee87dd47c63e35ba4ad94f6888852be04"
integrity sha512-DNviAE5OUcZ5s+XEQHRhERLg8fOp8gSgvyJ4aaFASx5wwaObm+PBwTIMXiOFm1QrSee5oYwEAYb7LMzX2O88gA== integrity sha512-uAaSWegu2lymY18l+s5nmcXu3sFeeTOl1zhSGoYzcr6T3wz1M+3OcW4UjfPhIhHGd13tIMRDsEpR+d8w/MexwQ==
"@types/ws@^8.5.3": "@types/ws@^8.5.3":
version "8.5.3" version "8.5.3"
@@ -416,9 +421,9 @@ call-bind@^1.0.0:
get-intrinsic "^1.0.2" get-intrinsic "^1.0.2"
canvas@^2.5.0: canvas@^2.5.0:
version "2.9.1" version "2.9.3"
resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.9.1.tgz#58ec841cba36cef0675bc7a74ebd1561f0b476b0" resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.9.3.tgz#8723c4f970442d4cdcedba5221579f9660a58bdb"
integrity sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A== integrity sha512-WOUM7ghii5TV2rbhaZkh1youv/vW1/Canev6Yx6BG2W+1S07w8jKZqKkPnbiPpQEDsnJdN8ouDd7OvQEGXDcUw==
dependencies: dependencies:
"@mapbox/node-pre-gyp" "^1.0.0" "@mapbox/node-pre-gyp" "^1.0.0"
nan "^2.15.0" nan "^2.15.0"
@@ -597,11 +602,6 @@ delegates@^1.0.0:
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
detect-libc@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
detect-libc@^2.0.0: detect-libc@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
@@ -613,29 +613,31 @@ diff@^4.0.1:
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
discord-api-types@^0.33.3: discord-api-types@^0.33.3:
version "0.33.3" version "0.33.5"
resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.33.3.tgz#882c39331871f619035917a1f80d486f605b3e7a" resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.33.5.tgz#6548b70520f7b944c60984dca4ab58654d664a12"
integrity sha512-P3A1RJXKEDmGPHrFTN5+gYLsBPGUVGj+D3+fa3m0K/umc+LMfqGuEad+p7cNq7ry/icReVhS3bz9jvBvne/BRA== integrity sha512-dvO5M52v7m7Dy96+XUnzXNsQ/0npsYpU6dL205kAtEDueswoz3aU3bh1UMoK4cQmcGtB1YRyLKqp+DXi05lzFg==
discord-api-types@^0.35.0: discord-api-types@^0.36.2:
version "0.35.0" version "0.36.2"
resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.35.0.tgz#85c6ea95f27580ad13b7a08444b16cb31d41e5e2" resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.36.2.tgz#2362bc544837be965ec99a5919f900c9699a7028"
integrity sha512-aaIbVgPk7OA3y/HdG00tRM2nB8B3sSPH6KXgamBLRrcAJEQ8XZJNNKeRr0WVzw/lprdpKHKuTNX1DNrYnD5cOw== integrity sha512-TunPAvzwneK/m5fr4hxH3bMsrtI22nr9yjfHyo5NBGMjpsAauGNiGCmwoFf0oO3jSd2mZiKUvZwCKDaB166u2Q==
discord.js@^13.8.1: discord.js@14.0.1:
version "13.8.1" version "14.0.1"
resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-13.8.1.tgz#37627154a887ac395ca72dd814bc7366776bb90a" resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.0.1.tgz#2de95035294b361689ec1c9de935f0b6c21923b1"
integrity sha512-jOsD+4tEZWWx0RHVyH+FBcqoTrsL+d5Mm5p+ULQOdU0qSaxhLNkWYig+yDHNZoND7nlkXX3qi+BW+gO5erWylg== integrity sha512-MZ2cvvk1WCpTUCLcchX5e/mBM22JCPAYle76qEuAfQfVTFdxE2YoKgRWMwWm6s/OJdkXyzglRWj405hg4vikEg==
dependencies: dependencies:
"@discordjs/builders" "^0.14.0" "@discordjs/builders" "^1.0.0"
"@discordjs/collection" "^0.7.0" "@discordjs/collection" "^1.0.0"
"@sapphire/async-queue" "^1.3.1" "@discordjs/rest" "^1.0.0"
"@types/node-fetch" "^2.6.1" "@sapphire/snowflake" "^3.2.2"
"@types/ws" "^8.5.3" "@types/ws" "^8.5.3"
discord-api-types "^0.33.3" discord-api-types "^0.36.2"
form-data "^4.0.0" fast-deep-equal "^3.1.3"
node-fetch "^2.6.1" lodash.snakecase "^4.1.1"
ws "^8.7.0" tslib "^2.4.0"
undici "^5.7.0"
ws "^8.8.1"
dotenv@^16.0.1: dotenv@^16.0.1:
version "16.0.1" version "16.0.1"
@@ -686,6 +688,15 @@ fecha@^4.2.0:
resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd"
integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==
file-type@^17.1.2:
version "17.1.2"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-17.1.2.tgz#9257437a64e0c3623f70d9f27430522d978b1384"
integrity sha512-3thBUSfa9YEUEGO/NAAiQGvjujZxZiJTF6xNwyDn6kB0NcEtwMn5ttkGG9jGwm/Nt/t8U1bpBNqyBNZCz4F4ig==
dependencies:
readable-web-to-node-stream "^3.0.2"
strtok3 "^7.0.0-alpha.7"
token-types "^5.0.0-alpha.2"
fill-range@^7.0.1: fill-range@^7.0.1:
version "7.0.1" version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@@ -707,15 +718,6 @@ form-data@^3.0.0:
combined-stream "^1.0.8" combined-stream "^1.0.8"
mime-types "^2.1.12" mime-types "^2.1.12"
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
formidable@^1.2.2: formidable@^1.2.2:
version "1.2.6" version "1.2.6"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168" resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168"
@@ -764,13 +766,13 @@ get-caller-file@^2.0.5:
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-intrinsic@^1.0.2: get-intrinsic@^1.0.2:
version "1.1.1" version "1.1.2"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598"
integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==
dependencies: dependencies:
function-bind "^1.1.1" function-bind "^1.1.1"
has "^1.0.3" has "^1.0.3"
has-symbols "^1.0.1" has-symbols "^1.0.3"
glob-parent@~5.1.2: glob-parent@~5.1.2:
version "5.1.2" version "5.1.2"
@@ -791,7 +793,7 @@ glob@^7.0.5, glob@^7.1.3:
once "^1.3.0" once "^1.3.0"
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
has-symbols@^1.0.1: has-symbols@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
@@ -828,6 +830,11 @@ i18n@^0.15.0:
math-interval-parser "^2.0.1" math-interval-parser "^2.0.1"
mustache "^4.2.0" mustache "^4.2.0"
ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
inflight@^1.0.4: inflight@^1.0.4:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@@ -853,7 +860,7 @@ is-binary-path@~2.1.0:
dependencies: dependencies:
binary-extensions "^2.0.0" binary-extensions "^2.0.0"
is-core-module@^2.8.1: is-core-module@^2.9.0:
version "2.9.0" version "2.9.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
@@ -914,10 +921,20 @@ libsodium@^0.7.0:
resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.10.tgz#c2429a7e4c0836f879d701fec2c8a208af024159" resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.10.tgz#c2429a7e4c0836f879d701fec2c8a208af024159"
integrity sha512-eY+z7hDrDKxkAK+QKZVNv92A5KYkxfvIshtBJkmg5TSiCnYqZP3i9OO9whE79Pwgm4jGaoHgkM4ao/b9Cyu4zQ== integrity sha512-eY+z7hDrDKxkAK+QKZVNv92A5KYkxfvIshtBJkmg5TSiCnYqZP3i9OO9whE79Pwgm4jGaoHgkM4ao/b9Cyu4zQ==
lodash.snakecase@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==
lodash.uniqwith@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniqwith/-/lodash.uniqwith-4.5.0.tgz#7a0cbf65f43b5928625a9d4d0dc54b18cadc7ef3"
integrity sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q==
logform@^2.2.0: logform@^2.2.0:
version "2.4.1" version "2.4.2"
resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.1.tgz#512c9eaef738044d1c619790ba0f806c80d9d3a9" resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.2.tgz#a617983ac0334d0c3b942c34945380062795b47c"
integrity sha512-7XB/tqc3VRbri9pRjU6E97mQ8vC27ivJ3lct4jhyT+n0JNDd4YKldFl0D75NqDp46hk8RC7Ma1Vjv/UPf67S+A== integrity sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==
dependencies: dependencies:
"@colors/colors" "1.5.0" "@colors/colors" "1.5.0"
fecha "^4.2.0" fecha "^4.2.0"
@@ -926,9 +943,9 @@ logform@^2.2.0:
triple-beam "^1.3.0" triple-beam "^1.3.0"
logform@^2.3.2: logform@^2.3.2:
version "2.4.0" version "2.4.1"
resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe" resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.1.tgz#512c9eaef738044d1c619790ba0f806c80d9d3a9"
integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw== integrity sha512-7XB/tqc3VRbri9pRjU6E97mQ8vC27ivJ3lct4jhyT+n0JNDd4YKldFl0D75NqDp46hk8RC7Ma1Vjv/UPf67S+A==
dependencies: dependencies:
"@colors/colors" "1.5.0" "@colors/colors" "1.5.0"
fecha "^4.2.0" fecha "^4.2.0"
@@ -1005,9 +1022,9 @@ minimist@^1.2.6:
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
minipass@^3.0.0: minipass@^3.0.0:
version "3.1.6" version "3.3.4"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae"
integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
@@ -1049,12 +1066,12 @@ nan@^2.14.0, nan@^2.15.0:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916"
integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==
node-addon-api@^4.2.0: node-addon-api@^5.0.0:
version "4.3.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501"
integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== integrity sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==
node-fetch@^2.6.1, node-fetch@^2.6.5, node-fetch@^2.6.7: node-fetch@^2.6.7:
version "2.6.7" version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@@ -1142,6 +1159,11 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
peek-readable@^5.0.0-alpha.5:
version "5.0.0-alpha.5"
resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0-alpha.5.tgz#ace5dfedf7bc33f17c9b5170b9d54f69a4fba79b"
integrity sha512-pJohF/tDwV3ntnT5+EkUo4E700q/j/OCDuPxtM+5/kFGjyOai/sK4/We4Cy1MB2OiTQliWU5DxPvYIKQAdPqAA==
picomatch@^2.0.4, picomatch@^2.2.1: picomatch@^2.0.4, picomatch@^2.2.1:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
@@ -1172,12 +1194,12 @@ prism-media@^1.3.2:
resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.3.2.tgz#a1f04423ec15d22f3d62b1987b6a25dc49aad13b" resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.3.2.tgz#a1f04423ec15d22f3d62b1987b6a25dc49aad13b"
integrity sha512-L6UsGHcT6i4wrQhFF1aPK+MNYgjRqR2tUoIqEY+CG1NqVkMjPRKzS37j9f8GiYPlD6wG9ruBj+q5Ax+bH8Ik1g== integrity sha512-L6UsGHcT6i4wrQhFF1aPK+MNYgjRqR2tUoIqEY+CG1NqVkMjPRKzS37j9f8GiYPlD6wG9ruBj+q5Ax+bH8Ik1g==
prisma@^3.15.2: prisma@^4.0.0:
version "3.15.2" version "4.0.0"
resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.15.2.tgz#4ebe32fb284da3ac60c49fbc16c75e56ecf32067" resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.0.0.tgz#4ddb8fcd4f64d33aff8c198a6986dcce66dc8152"
integrity sha512-nMNSMZvtwrvoEQ/mui8L/aiCLZRCj5t6L3yujKpcDhIPk7garp8tL4nMx2+oYsN0FWBacevJhazfXAbV1kfBzA== integrity sha512-Dtsar03XpCBkcEb2ooGWO/WcgblDTLzGhPcustbehwlFXuTMliMDRzXsfygsgYwQoZnAUKRd1rhpvBNEUziOVw==
dependencies: dependencies:
"@prisma/engines" "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e" "@prisma/engines" "3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11"
process-nextick-args@~2.0.0: process-nextick-args@~2.0.0:
version "2.0.1" version "2.0.1"
@@ -1190,9 +1212,9 @@ q@1.x:
integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==
qs@^6.9.4: qs@^6.9.4:
version "6.10.3" version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
dependencies: dependencies:
side-channel "^1.0.4" side-channel "^1.0.4"
@@ -1208,7 +1230,7 @@ readable-stream@^3.4.0, readable-stream@^3.6.0:
readable-stream@~1.0.31: readable-stream@~1.0.31:
version "1.0.34" version "1.0.34"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= integrity sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==
dependencies: dependencies:
core-util-is "~1.0.0" core-util-is "~1.0.0"
inherits "~2.0.1" inherits "~2.0.1"
@@ -1228,6 +1250,13 @@ readable-stream@~2.3.6:
string_decoder "~1.1.1" string_decoder "~1.1.1"
util-deprecate "~1.0.1" util-deprecate "~1.0.1"
readable-web-to-node-stream@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb"
integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==
dependencies:
readable-stream "^3.6.0"
readdirp@~3.6.0: readdirp@~3.6.0:
version "3.6.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@@ -1238,14 +1267,14 @@ readdirp@~3.6.0:
require-directory@^2.1.1: require-directory@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
resolve@^1.0.0: resolve@^1.0.0:
version "1.22.0" version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
dependencies: dependencies:
is-core-module "^2.8.1" is-core-module "^2.9.0"
path-parse "^1.0.7" path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0" supports-preserve-symlinks-flag "^1.0.0"
@@ -1298,7 +1327,7 @@ semver@^7.3.2, semver@^7.3.5:
set-blocking@^2.0.0: set-blocking@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
side-channel@^1.0.4: side-channel@^1.0.4:
version "1.0.4" version "1.0.4"
@@ -1331,7 +1360,7 @@ simple-get@^3.0.3:
simple-swizzle@^0.2.2: simple-swizzle@^0.2.2:
version "0.2.2" version "0.2.2"
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==
dependencies: dependencies:
is-arrayish "^0.3.1" is-arrayish "^0.3.1"
@@ -1351,7 +1380,7 @@ source-map@^0.6.0:
stack-trace@0.0.x: stack-trace@0.0.x:
version "0.0.10" version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3" version "4.2.3"
@@ -1372,7 +1401,7 @@ string_decoder@^1.1.1:
string_decoder@~0.10.x: string_decoder@~0.10.x:
version "0.10.31" version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==
string_decoder@~1.1.1: string_decoder@~1.1.1:
version "1.1.1" version "1.1.1"
@@ -1391,12 +1420,20 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
strip-bom@^3.0.0: strip-bom@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
strip-json-comments@^2.0.0: strip-json-comments@^2.0.0:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
strtok3@^7.0.0-alpha.7:
version "7.0.0-alpha.8"
resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0-alpha.8.tgz#23a7870974e0494b58b14af6dd1c2c67cf13314d"
integrity sha512-u+k19v+rTxBjGYxncRQjGvZYwYvEd0uP3D+uHKe/s4WB1eXS5ZwpZsTlBu5xSS4zEd89mTXECXg6WW3FSeV8cA==
dependencies:
"@tokenizer/token" "^0.3.0"
peek-readable "^5.0.0-alpha.5"
superagent@^5.2.1: superagent@^5.2.1:
version "5.3.1" version "5.3.1"
@@ -1457,10 +1494,18 @@ to-regex-range@^5.0.1:
dependencies: dependencies:
is-number "^7.0.0" is-number "^7.0.0"
token-types@^5.0.0-alpha.2:
version "5.0.0-alpha.2"
resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.0-alpha.2.tgz#e43d63b2a8223a593d1c782a5149bec18f1abf97"
integrity sha512-EsG9UxAW4M6VATrEEjhPFTKEUi1OiJqTUMIZOGBN49fGxYjZB36k0p7to3HZSmWRoHm1QfZgrg3e02fpqAt5fQ==
dependencies:
"@tokenizer/token" "^0.3.0"
ieee754 "^1.2.1"
tr46@~0.0.3: tr46@~0.0.3:
version "0.0.3" version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
tree-kill@^1.2.2: tree-kill@^1.2.2:
version "1.2.2" version "1.2.2"
@@ -1543,9 +1588,14 @@ underscore@^1.12.0:
integrity sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ== integrity sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==
undici@^5.4.0: undici@^5.4.0:
version "5.4.0" version "5.5.1"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.4.0.tgz#c474fae02743d4788b96118d46008a24195024d2" resolved "https://registry.yarnpkg.com/undici/-/undici-5.5.1.tgz#baaf25844a99eaa0b22e1ef8d205bffe587c8f43"
integrity sha512-A1SRXysDg7J+mVP46jF+9cKANw0kptqSFZ8tGyL+HBiv0K1spjxPX8Z4EGu+Eu6pjClJUBdnUPlxrOafR668/g== integrity sha512-MEvryPLf18HvlCbLSzCW0U00IMftKGI5udnjrQbC5D4P0Hodwffhv+iGfWuJwg16Y/TK11ZFK8i+BPVW2z/eAw==
undici@^5.7.0:
version "5.7.0"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.7.0.tgz#979f89229c01505573cb274d0e11ea8d82b4004f"
integrity sha512-ORgxwDkiPS+gK2VxE7iyVeR7JliVn5DqhZ4LgQqYLBXsuK+lwOEmnJ66dhvlpLM0tC3fC7eYF1Bti2frbw2eAA==
untildify@^4.0.0: untildify@^4.0.0:
version "4.0.0" version "4.0.0"
@@ -1562,7 +1612,7 @@ utf-8-validate@^5.0.9:
util-deprecate@^1.0.1, util-deprecate@~1.0.1: util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
v8-compile-cache-lib@^3.0.1: v8-compile-cache-lib@^3.0.1:
version "3.0.1" version "3.0.1"
@@ -1577,12 +1627,12 @@ validator@^13.7.0:
webidl-conversions@^3.0.0: webidl-conversions@^3.0.0:
version "3.0.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
whatwg-url@^5.0.0: whatwg-url@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies: dependencies:
tr46 "~0.0.3" tr46 "~0.0.3"
webidl-conversions "^3.0.0" webidl-conversions "^3.0.0"
@@ -1630,12 +1680,17 @@ wrap-ansi@^7.0.0:
wrappy@1: wrappy@1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
ws@^8.7.0: ws@^8.7.0:
version "8.7.0" version "8.8.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.7.0.tgz#eaf9d874b433aa00c0e0d8752532444875db3957" resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769"
integrity sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg== integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==
ws@^8.8.1:
version "8.8.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0"
integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==
xtend@^4.0.0, xtend@~4.0.1: xtend@^4.0.0, xtend@~4.0.1:
version "4.0.2" version "4.0.2"