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
DEVELOPER_IDS=DEV1_ID,DEV2_ID
PRIVATE_BOT=true
SUPPORT_URL=https://example.com/support
OSU_API_KEY=SuperSecretosu!Key
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 -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 npm i -g yarn
@@ -18,7 +18,7 @@ WORKDIR /home/node/app
RUN apt-get update -y
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 npm i -g yarn
+8 -1
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
- ``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``
- ``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)
- ``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
\*\* 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": {
"available_args": "Available arguments",
"no_permissions": "Access denied (O_O;)",
"no_permissions_description": "You need ``{{PERMISSIONS}}`` permission on this guild",
"member_no_permissions": "Access denied (O_O;)",
"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_description": "Getting it done right now! (งᓀ‸ᓂ)ง"
},
@@ -35,7 +37,9 @@
"streaming_x": "Streaming {{NAME}}",
"listening_x": "Listening to {{NAME}}",
"watching_x": "Watching {{NAME}}",
"competing_x": "Competing {{NAME}}"
"competing_x": "Competing {{NAME}}",
"since": "Since {{{TIME}}}",
"end": "Ending {{{TIME}}}"
},
"say": {
"title": "Say",
@@ -106,13 +110,19 @@
"queue_added_songs_description": "Added {{COUNT}} songs to the queue",
"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": {
"title": "Queue",
"valid_args": "``Clear`` ``Shuffle``",
"empty": "The queue is empty! (っ˘ω˘ς )",
"now_playing": "Now playing",
"upcoming_song": "Upcoming song",
"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": {
"title": "Search",
@@ -127,8 +137,8 @@
},
"settings": {
"title": "Settings",
"description": "Change how {{BOT_NAME}} behave on **{{GUILD_NAME}}**",
"valid_args": "``Prefix`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``"
"description": "Change how {{BOT_NAME}} behave on **{{{GUILD_NAME}}}**",
"valid_args": "``Prefix`` ``Language`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``"
},
"settings_prefix": {
"info": "Current prefix is {{{PREFIX}}}",
@@ -140,10 +150,23 @@
"prefix_updated": "Prefix updated",
"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": {
"title": "Network performance",
"title_processing": "Measuring network performance ( ◕▿◕ )",
"running_on": "Running on {{HOST}}",
"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": {
"available_args": "Available arguments",
"no_permissions": "許可していない (O_O;)",
"no_permissions_description": "このギルドでは ``{{PERMISSIONS}}`` の許可が必要です!",
"member_no_permissions": "許可していない (O_O;)",
"member_no_permissions_description": "このギルドでは ``{{PERMISSIONS}}`` の許可が必要です!",
"bot_no_permissions": "許可がない (O_O;)",
"bot_no_permissions_description": "そのためには、あたしの``{{PERMISSIONS}}``の権限が必要です",
"processing": "アクションの実行",
"processing_description": "今これを成し遂げています (งᓀ‸ᓂ)ง"
},
@@ -35,7 +37,9 @@
"streaming_x": "{{NAME}}をストリーミング中",
"listening_x": "{{NAME}}を再生中",
"watching_x": "{{NAME}}鑑賞中",
"competing_x": "{{NAME}}に参戦中です"
"competing_x": "{{NAME}}に参戦中です",
"since": "{{{TIME}}}から",
"end": "{{{TIME}}}で終了"
},
"say": {
"title": "言うこと",
@@ -106,13 +110,19 @@
"queue_added_songs_description": "キューに{{COUNT}}曲を追加",
"error": "曲の検索中にエラーが発生"
},
"musicplayer_playmy": {
"not_detected": "あなたの活動状況を見ると、現在、音楽を聴いていないようですね"
},
"musicplayer_queue": {
"title": "キュー",
"valid_args": "``Clear`` ``Shuffle``",
"empty": "キューは空です! (っ˘ω˘ς )",
"now_playing": "再生中",
"upcoming_song": "次の曲",
"looping_current": "(この曲の繰り返し)",
"song_x_in_queue": "{{COUNT}}曲も並んでいますよ。"
"song_x_in_queue": "{{COUNT}}曲も並んでいますよ。",
"cleared": "キューがクリアされました (。•ˇ‸ˇ•。)",
"shuffled": "キューがシャッフルされました"
},
"musicplayer_search": {
"title": "検索",
@@ -127,8 +137,8 @@
},
"settings": {
"title": "設定",
"description": "**{{GUILD_NAME}}**サーバでの{{BOT_NAME}}の動作変更",
"valid_args": "``Prefix`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``"
"description": "**{{{GUILD_NAME}}}**サーバでの{{BOT_NAME}}の動作変更",
"valid_args": "``Prefix`` ``Language`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``"
},
"settings_prefix": {
"info": "現在の接頭辞は「{{{PREFIX}}}」です。",
@@ -140,10 +150,23 @@
"prefix_updated": "プレフィックスを更新",
"prefix_updated_description": "これ以降、私は 「``{{PREFIX}}``」 を使って呼び出されることになります。"
},
"settings_language": {
"info": "現在の言語は{{LANGUAGE}}です",
"info_description": "変更する新しい言語を提供する\n**利用可能な言語**: ``{{LANGUAGES}}``",
"language_invalid": "無効な言語",
"language_invalid_description": "言語コードが無効であるか、まだサポートされていません",
"language_updated": "言語が変更されました",
"language_updated_description": "これからは{{LANGUAGE}}を使います"
},
"ping": {
"title": "ネットワークパフォーマンス",
"title_processing": "ネットワーク性能の測定 ( ◕▿◕ )",
"running_on": "{{HOST}}で実行中",
"development_environment": "開発環境"
},
"support": {
"title": "サポート",
"info": "[{{BOT_NAME}}のサポートサーバーに参加する]({{{LINK}}})",
"not_available": "{{BOT_NAME}}のサポートサーバーは現在利用できません"
}
}
+29 -6
View File
@@ -1,8 +1,10 @@
{
"common": {
"available_args": "อาร์กิวเมนต์ ตัวเลือกที่ใช้ได้",
"no_permissions": "คุณไม่มีสิทธิ์เข้าถึง (O_O;)",
"no_permissions_description": "คุณต้องมีสิทธิ์เข้าถึง ``{{PERMISSIONS}}`` ในกิลด์นี้!",
"member_no_permissions": "คุณไม่มีสิทธิ์เข้าถึง (O_O;)",
"member_no_permissions_description": "คุณต้องมีสิทธิ์เข้าถึง ``{{PERMISSIONS}}`` ในกิลด์นี้!",
"bot_no_permissions": "ฉันไม่มีสิทธิ์เข้าถึง (O_O;)",
"bot_no_permissions_description": "ฉันต้องการสิทธิ์เข้าถึง ``{{PERMISSIONS}}``",
"processing": "รอแปปนึงน้า",
"processing_description": "ฉันกำลังดำเนินการให้อยู่ (งᓀ‸ᓂ)ง"
},
@@ -35,7 +37,9 @@
"streaming_x": "กำลังสตรีม {{NAME}}",
"listening_x": "กำลังฟัง {{NAME}}",
"watching_x": "กำลังดู {{NAME}}",
"competing_x": "กำลังแข่งขัน {{NAME}}"
"competing_x": "กำลังแข่งขัน {{NAME}}",
"since": "ตั้งแต่ {{{TIME}}}",
"end": "สิ้นสุดใน {{{TIME}}}"
},
"say": {
"title": "สั่งพูด",
@@ -106,13 +110,19 @@
"queue_added_songs_description": "เพิ่ม {{COUNT}} เพลงในคิว",
"error": "เกิดข้อผิดพลาดขณะค้นหาเพลง"
},
"musicplayer_playmy": {
"not_detected": "เท่าที่ดูจากกิจกรรมของคุณ เหมือนว่าตอนนี้คุณไม่ได้เล่นเพลงอะไรอยู่นะ"
},
"musicplayer_queue": {
"title": "คิวเพลง",
"valid_args": "``Clear`` ``Shuffle``",
"empty": "ตอนนี้คิวว่างนะ (っ˘ω˘ς )",
"now_playing": "กำลังเล่น",
"upcoming_song": "เพลงต่อไป",
"looping_current": "(กำลังวนซ้ำเพลงนี้)",
"song_x_in_queue": "ตอนนี้มี {{COUNT}} เพลงอยู่ในคิว!"
"song_x_in_queue": "ตอนนี้มี {{COUNT}} เพลงอยู่ในคิว!",
"cleared": "เคลียร์คิวเรียบร้อยละ~ (。•ˇ‸ˇ•。)",
"shuffled": "สุ่มคิวเรียบร้อยละ"
},
"musicplayer_search": {
"title": "ค้นหา",
@@ -127,8 +137,8 @@
},
"settings": {
"title": "การตั้งค่า",
"description": "เปลี่ยนพฤติกรรมของ {{BOT_NAME}} บน **{{GUILD_NAME}}**",
"valid_args": "``Prefix`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``"
"description": "เปลี่ยนพฤติกรรมของ {{BOT_NAME}} บน **{{{GUILD_NAME}}}**",
"valid_args": "``Prefix`` ``Language`` ``EnableServiceAnnouncement`` ``ServiceAnnouncementChannel``"
},
"settings_prefix": {
"info": "คำนำหน้าปัจจุบันคือ {{{PREFIX}}}",
@@ -140,10 +150,23 @@
"prefix_updated": "อัปเดตคำนำหน้าแล้ว",
"prefix_updated_description": "จากนี้ไป คุณสามารถเรียกฉันโดยใช้ ``{{PREFIX}}`` นะ"
},
"settings_language": {
"info": "ภาษาปัจจุบันตอนนี้คือ {{LANGUAGE}}",
"info_description": "ระบุภาษาใหม่เพื่อเปลี่ยน\n**ภาษาที่ใช้ได้**: ``{{LANGUAGES}}``",
"language_invalid": "ภาษาไม่ถูกต้อง",
"language_invalid_description": "รหัสภาษาไม่ถูกต้องหรือยังไม่รองรับ",
"language_updated": "เปลี่ยนภาษาเรียบร้อย",
"language_updated_description": "ตั้งแต่นี้ไปฉันจะใช้ภาษา {{LANGUAGE}}"
},
"ping": {
"title": "ประสิทธิภาพของเครือข่าย",
"title_processing": "กำลังวัดประสิทธิภาพของเครือข่าย ( ◕▿◕ )",
"running_on": "ทำงานอยู่บน {{HOST}}",
"development_environment": "โหมดกำลังพัฒนา"
},
"support": {
"title": "ติดต่อฝ่ายสนับสนุน",
"info": "[เข้าร่วมเซิร์ฟเวอร์สนับสนุนของ {{BOT_NAME}}]({{{LINK}}})",
"not_available": "เซิร์ฟเวอร์สนับสนุนของ {{BOT_NAME}} ไม่พร้อมใช้งานในขณะนี้"
}
}
+8 -9
View File
@@ -1,7 +1,6 @@
{
"dependencies": {
"@discordjs/builders": "^0.15.0",
"@discordjs/opus": "^0.7.0",
"@discordjs/opus": "^0.8.0",
"@discordjs/rest": "^0.5.0",
"@discordjs/voice": "^0.10.0",
"@types/color-thief-node": "^1.0.1",
@@ -11,8 +10,8 @@
"color-thief-node": "^1.0.4",
"country-code-emoji": "^2.3.0",
"country-code-lookup": "^0.0.20",
"discord-api-types": "^0.35.0",
"discord.js": "^13.8.1",
"discord-api-types": "^0.36.2",
"discord.js": "14.0.1",
"dotenv": "^16.0.1",
"i18n": "^0.15.0",
"libsodium-wrappers": "^0.7.10",
@@ -28,14 +27,14 @@
"zlib-sync": "^0.1.7"
},
"devDependencies": {
"@prisma/client": "^3.15.2",
"@prisma/client": "^4.0.0",
"@types/bluebird": "^3.5.36",
"@types/i18n": "^0.13.3",
"@types/node": "^18.0.0",
"@types/i18n": "^0.13.4",
"@types/node": "^18.0.4",
"@types/ping": "^0.4.1",
"@types/validator": "^13.7.3",
"@types/validator": "^13.7.4",
"copyfiles": "^2.4.1",
"prisma": "^3.15.2",
"prisma": "^4.0.0",
"ts-node-dev": "^2.0.0"
},
"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 DiscordProvider from '../providers/Discord';
@@ -48,7 +48,7 @@ export default class Core extends DiscordModule {
private setActivity() {
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 {
Message,
Interaction,
ActionRowBuilder,
CommandInteraction,
MessageActionRow,
MessageButton,
ButtonInteraction
ButtonInteraction,
ButtonBuilder,
ButtonStyle
} from 'discord.js';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
@@ -162,15 +162,15 @@ export default class Debug extends DiscordModule {
});
},
invalidInteraction: async (data: HybridInteractionMessage) => {
const row = new MessageActionRow().addComponents(
new MessageButton()
const row = new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setEmoji('😥')
.setLabel(
'Make invalid interaction (Wait 7 seconds, check error in console or logs)'
)
.setCustomId('dev_make_invalid_interaction')
.setStyle('PRIMARY')
);
.setStyle(ButtonStyle.Primary)
]);
await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INVALID_TEST(data)],
components: [row]
@@ -187,7 +187,7 @@ export default class Debug extends DiscordModule {
});
query = args[0].toLowerCase();
} else if (data.isSlashCommand()) {
} else if (data.isApplicationCommand()) {
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 fs from 'fs';
import path from 'path';
@@ -21,35 +21,37 @@ import DiscordProvider from '../../providers/Discord';
import Prisma from '../../providers/Prisma';
import Users from '../../services/Users';
const hexToDecimal = (hex: string) => parseInt(hex.replace('#', ''), 16);
const EMBEDS = {
ANNOUNCEMENT_INFO: (data: Message | Interaction) => {
ANNOUNCEMENT_INFO: (data: HybridInteractionMessage) => {
return makeInfoEmbed({
title: 'Service Announcement',
description: `This module contains management tool for announcement feed\n The announcement message is located in \`\`configs/ServiceAnnouncement.json\`\``,
fields: [
{
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({
title: 'Developer 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;
payload.footer = {
text: `${user?.username}`,
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')
payload.thumbnail.url = `${user?.displayAvatarURL()}?size=4096`;
@@ -58,49 +60,52 @@ const EMBEDS = {
payload.image.url = `${user?.displayAvatarURL()}?size=4096`;
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({
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({
title: 'Unable to reload Service Announcement Configuration',
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({
title: '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({
title: '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({
title: 'Service Announcement',
description: `Broadcasted Service Announcement with errors, check console for more info`,
user: data instanceof Interaction ? data.user : data.author
user: data.getUser()
});
}
};
let Announcements = {
News: {
color: '#FAEDF0',
color: hexToDecimal('#FAEDF0'),
title: '📰 Newsletter',
description: 'Some description here',
thumbnail: {
@@ -108,7 +113,7 @@ let Announcements = {
}
},
Maintenance: {
color: '#383b80',
color: hexToDecimal('#383b80'),
title: '🔧 Maintenance',
description: 'The bot is going offline for maintenance.',
thumbnail: {
@@ -116,7 +121,7 @@ let Announcements = {
}
},
Message: {
color: '#A1DE93',
color: hexToDecimal('#A1DE93'),
title: '✉️Message',
description: 'Some description here',
thumbnail: {
@@ -124,7 +129,7 @@ let Announcements = {
}
},
Alert: {
color: '#EC255A',
color: hexToDecimal('#EC255A'),
title: '🚨 Alert',
description: 'Some description here',
thumbnail: {
@@ -162,7 +167,7 @@ export default class ServiceAnnouncement extends DiscordModule {
async run(data: HybridInteractionMessage, args: any) {
if (!Users.isDeveloper(data.getUser()!.id))
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NOT_DEVELOPER(data.getRaw())]
embeds: [EMBEDS.NOT_DEVELOPER(data)]
});
const channel = data.getChannel();
@@ -180,7 +185,7 @@ export default class ServiceAnnouncement extends DiscordModule {
} catch (err: any) {
Logger.error('Unable to load custom ServiceAnnouncement config: ' + err);
return await sendMessage(channel, undefined, {
embeds: [EMBEDS.RELOAD_ERROR(data.getRaw(), err)]
embeds: [EMBEDS.RELOAD_ERROR(data, err)]
});
}
} else {
@@ -192,7 +197,7 @@ export default class ServiceAnnouncement extends DiscordModule {
}
return await sendMessage(channel, undefined, {
embeds: [EMBEDS.RELOADED(data.getRaw())]
embeds: [EMBEDS.RELOADED(data)]
});
},
previewNews: async (data: HybridInteractionMessage) => {
@@ -247,7 +252,7 @@ export default class ServiceAnnouncement extends DiscordModule {
let _placeholder = await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.SENDING_SERVICE_ANNOUNCEMENT(data.getRaw())] },
{ embeds: [EMBEDS.SENDING_SERVICE_ANNOUNCEMENT(data)] },
true
);
if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder);
@@ -295,15 +300,15 @@ export default class ServiceAnnouncement extends DiscordModule {
.getMessage()
.edit({
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(
data,
{
embeds: [
EMBEDS.SERVICE_ANNOUNCEMENT_SENT_WITH_ERRORS(data.getRaw())
EMBEDS.SERVICE_ANNOUNCEMENT_SENT_WITH_ERRORS(data)
]
},
true
@@ -312,11 +317,11 @@ export default class ServiceAnnouncement extends DiscordModule {
if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder
.getMessage()
.edit({ embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_SENT(data.getRaw())] });
else if (data.isSlashCommand())
.edit({ embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_SENT(data)] });
else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_SENT(data.getRaw())] },
{ embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_SENT(data)] },
true
);
}
@@ -328,17 +333,17 @@ export default class ServiceAnnouncement extends DiscordModule {
if (data.isMessage()) {
if (args.length === 0)
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ANNOUNCEMENT_INFO(data.getRaw())]
embeds: [EMBEDS.ANNOUNCEMENT_INFO(data)]
});
query = args[0].toLowerCase();
} else if (data.isSlashCommand()) {
} else if (data.isApplicationCommand()) {
query = args.getSubcommand();
}
switch (query) {
case 'info':
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ANNOUNCEMENT_INFO(data.getRaw())]
embeds: [EMBEDS.ANNOUNCEMENT_INFO(data)]
});
case 'reload':
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 DiscordProvider from '../providers/Discord';
@@ -24,7 +24,7 @@ const EMBEDS = {
fields: [
{
name: `☕ ${locale.__('help.general')}`,
value: `help, ping, invite, userinfo, stats`,
value: `help, ping, invite, userinfo, stats, support`,
inline: true
},
{
@@ -34,7 +34,7 @@ const EMBEDS = {
},
{
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
},
{
+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';
@@ -18,7 +18,7 @@ import {
} from '../utils/DiscordInteraction';
const EMBEDS = {
INTERACTION_INFO: (data: Message | Interaction) => {
INTERACTION_INFO: (data: HybridInteractionMessage) => {
return makeInfoEmbed({
title: 'Interaction',
description: `This module contains management tool for interaction based contents`,
@@ -28,47 +28,47 @@ const EMBEDS = {
value: '``reloadAll`` ``unloadAll`` ``reloadglobal``'
}
],
user: data instanceof Interaction ? data.user : data.author
user: data.getUser()
});
},
PROCESSING: (data: Message | Interaction) => {
PROCESSING: (data: HybridInteractionMessage) => {
return makeProcessingEmbed({
icon: data instanceof Message ? undefined : '⌛',
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({
title: 'Developer 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({
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({
title: 'An error occurred while trying to unload interaction',
description: '```' + err + '```',
user: data instanceof Interaction ? data.user : data.author
user: data.getUser()
});
},
RELOADALL_SUCCESS: (data: Message | Interaction) => {
RELOADALL_SUCCESS: (data: HybridInteractionMessage) => {
return makeSuccessEmbed({
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({
title: 'An error occurred while trying to reload interaction',
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))
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NOT_DEVELOPER(data.getRaw())]
embeds: [EMBEDS.NOT_DEVELOPER(data)]
});
const funct = {
@@ -100,7 +100,7 @@ export default class InteractionManager extends DiscordModule {
let placeholder: HybridInteractionMessage | undefined;
let _placeholder = await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.PROCESSING(data.getRaw())]
embeds: [EMBEDS.PROCESSING(data)]
});
if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder);
@@ -110,22 +110,22 @@ export default class InteractionManager extends DiscordModule {
if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder
.getMessage()
.edit({ embeds: [EMBEDS.UNLOADALL_SUCCESS(data.getRaw())] });
else if (data.isSlashCommand())
.edit({ embeds: [EMBEDS.UNLOADALL_SUCCESS(data)] });
else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.UNLOADALL_SUCCESS(data.getRaw())] },
{ embeds: [EMBEDS.UNLOADALL_SUCCESS(data)] },
true
);
} catch (err) {
if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder
.getMessage()
.edit({ embeds: [EMBEDS.UNLOADALL_ERROR(data.getRaw(), err)] });
else if (data.isSlashCommand())
.edit({ embeds: [EMBEDS.UNLOADALL_ERROR(data, err)] });
else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.UNLOADALL_ERROR(data.getRaw(), err)] },
{ embeds: [EMBEDS.UNLOADALL_ERROR(data, err)] },
true
);
}
@@ -134,7 +134,7 @@ export default class InteractionManager extends DiscordModule {
let placeholder: HybridInteractionMessage | undefined;
let _placeholder = await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.PROCESSING(data.getRaw())]
embeds: [EMBEDS.PROCESSING(data)]
});
if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder);
@@ -145,22 +145,22 @@ export default class InteractionManager extends DiscordModule {
if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder
.getMessage()
.edit({ embeds: [EMBEDS.RELOADALL_SUCCESS(data.getRaw())] });
else if (data.isSlashCommand())
.edit({ embeds: [EMBEDS.RELOADALL_SUCCESS(data)] });
else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.RELOADALL_SUCCESS(data.getRaw())] },
{ embeds: [EMBEDS.RELOADALL_SUCCESS(data)] },
true
);
} catch (err) {
if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder
.getMessage()
.edit({ embeds: [EMBEDS.RELOADALL_ERROR(data.getRaw(), err)] });
else if (data.isSlashCommand())
.edit({ embeds: [EMBEDS.RELOADALL_ERROR(data, err)] });
else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.RELOADALL_ERROR(data.getRaw(), err)] },
{ embeds: [EMBEDS.RELOADALL_ERROR(data, err)] },
true
);
}
@@ -169,7 +169,7 @@ export default class InteractionManager extends DiscordModule {
let placeholder: HybridInteractionMessage | undefined;
let _placeholder = await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.PROCESSING(data.getRaw())]
embeds: [EMBEDS.PROCESSING(data)]
});
if (_placeholder) placeholder = new HybridInteractionMessage(_placeholder);
@@ -180,22 +180,22 @@ export default class InteractionManager extends DiscordModule {
if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder
.getMessage()
.edit({ embeds: [EMBEDS.RELOADALL_SUCCESS(data.getRaw())] });
else if (data.isSlashCommand())
.edit({ embeds: [EMBEDS.RELOADALL_SUCCESS(data)] });
else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.RELOADALL_SUCCESS(data.getRaw())] },
{ embeds: [EMBEDS.RELOADALL_SUCCESS(data)] },
true
);
} catch (err) {
if (data && data.isMessage() && placeholder && placeholder.isMessage())
return placeholder
.getMessage()
.edit({ embeds: [EMBEDS.RELOADALL_ERROR(data.getRaw(), err)] });
else if (data.isSlashCommand())
.edit({ embeds: [EMBEDS.RELOADALL_ERROR(data, err)] });
else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse(
data,
{ embeds: [EMBEDS.RELOADALL_ERROR(data.getRaw(), err)] },
{ embeds: [EMBEDS.RELOADALL_ERROR(data, err)] },
true
);
}
@@ -207,18 +207,18 @@ export default class InteractionManager extends DiscordModule {
if (data.isMessage()) {
if (args.length === 0)
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INTERACTION_INFO(data.getRaw())]
embeds: [EMBEDS.INTERACTION_INFO(data)]
});
query = args[0].toLowerCase();
} else if (data.isSlashCommand()) {
} else if (data.isApplicationCommand()) {
query = args.getSubcommand();
}
switch (query) {
case 'info':
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INTERACTION_INFO(data.getRaw())]
embeds: [EMBEDS.INTERACTION_INFO(data)]
});
case 'reloadall':
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 Environment from '../providers/Environment';
@@ -13,8 +13,8 @@ const EMBEDS = {
INVITE_INFO: (data: HybridInteractionMessage, locale: I18n) => {
const user = data.getUser();
let message;
if (!Environment.get().PRIVATE_BOT || 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`});
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=8&scope=bot%20applications.commands`});
return makeInfoEmbed({
title: `Invite`,
+109 -96
View File
@@ -3,15 +3,17 @@ import {
GuildChannel,
GuildMember,
Message,
Permissions,
TextChannel,
MessageActionRow,
MessageButton,
Interaction,
ButtonBuilder,
CommandInteraction,
Role,
ThreadChannel,
ButtonInteraction
ButtonInteraction,
BaseInteraction,
PermissionsBitField,
EmbedBuilder,
ButtonStyle,
ActionRowBuilder,
} from 'discord.js';
import DiscordProvider from '../providers/Discord';
@@ -27,44 +29,44 @@ import {
import DiscordModule, { HybridInteractionMessage } from '../utils/DiscordModule';
const EMBEDS = {
NO_PERMISSION: (data: Message | Interaction) => {
NO_PERMISSION: (data: Message | BaseInteraction) => {
return makeErrorEmbed({
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({
title: 'Missing parameter',
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({
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({
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({
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({
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({
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`,
@@ -74,58 +76,58 @@ const EMBEDS = {
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({
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({
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({
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({
title: 'Enabled Membership Screening',
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({
title: 'Disabled Membership Screening',
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({
title: 'You cannot use this role',
description:
'``' + 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({
title: 'Configured Membership Screening Role',
description:
'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({
title: 'Configured Membership Screening Channel',
description:
@@ -134,18 +136,18 @@ const EMBEDS = {
channel.name +
'``' +
' 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({
title: 'Invalid channel type, only TextChannel is supported',
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: (
data: Message | Interaction,
data: Message | BaseInteraction,
channel: GuildChannel | ThreadChannel
) => {
return makeErrorEmbed({
@@ -155,38 +157,38 @@ const EMBEDS = {
channel.name +
'``' +
' 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({
title: `I don't have permission`,
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({
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({
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({
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({
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: () => {
@@ -246,10 +248,12 @@ export default class MembershipScreening extends DiscordModule {
)
return;
embed[0].footer = {
const newEmbed = new EmbedBuilder(embed[0].data);
newEmbed.setFooter({
text: `${interaction.user.username} | v${App.version}`,
iconURL: `${interaction.user.displayAvatarURL()}?size=4096`
};
});
if (['approve', 'deny', 'ban'].includes(payload.a)) {
if (!payload.d.requester) return;
@@ -268,26 +272,35 @@ export default class MembershipScreening extends DiscordModule {
});
if (!requesterMember) {
embed[0].addField('❌ Invalid', `Unable to find the user. User left already?`);
return await message.edit({ components: [], embeds: embed });
newEmbed.addFields([
{
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)) {
embed[0].addField(
'✅ Approved',
`By: Unknown (User already obtained the role by other means)`
);
return await message.edit({ components: [], embeds: embed });
newEmbed.addFields([
{
name: '✅ Approved',
value: `By: Unknown (User already obtained the role by other means)`
}
]);
return await message.edit({ components: [], embeds: [newEmbed] });
}
if (payload.a === 'approve') {
try {
await requesterMember.roles.add(role);
embed[0].addField(
'✅ Approved',
`By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})`
);
await message.edit({ components: [], embeds: embed });
newEmbed.addFields([
{
name: '✅ Approved',
value: `By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})`
}
]);
await message.edit({ components: [], embeds: [newEmbed] });
} catch (err) {
return interaction.reply({
ephemeral: true,
@@ -297,11 +310,13 @@ export default class MembershipScreening extends DiscordModule {
} else if (payload.a === 'deny') {
try {
await requesterMember.kick();
embed[0].addField(
'❌ Denied',
`By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})`
);
await message.edit({ components: [], embeds: embed });
newEmbed.addFields([
{
name: '❌ Denied',
value: `By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})`
}
]);
await message.edit({ components: [], embeds: [newEmbed] });
} catch (err) {
return interaction.reply({
ephemeral: true,
@@ -313,11 +328,13 @@ export default class MembershipScreening extends DiscordModule {
await requesterMember.ban({
reason: `Membership Screening, action issued by ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})`
});
embed[0].addField(
'🔪 Banned',
`By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})`
);
await message.edit({ components: [], embeds: embed });
newEmbed.addFields([
{
name: '🔪 Banned',
value: `By: ${interaction.user.username}#${interaction.user.discriminator} (${interaction.user.id})`
}
]);
await message.edit({ components: [], embeds: [newEmbed] });
} catch (err) {
return interaction.reply({
ephemeral: true,
@@ -395,9 +412,9 @@ export default class MembershipScreening extends DiscordModule {
role =
data.getMessage().mentions.roles.first() ||
guild.roles.cache.find((role) => role.name === _name);
} else if (data.isSlashCommand())
} else if (data.isApplicationCommand())
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)
@@ -427,8 +444,8 @@ export default class MembershipScreening extends DiscordModule {
embeds: [EMBEDS.NO_CHANNEL_MENTIONED(data.getRaw())]
});
mentionChannel = data.getMessage().mentions.channels.first();
} else if (data.isSlashCommand())
mentionChannel = data.getSlashCommand().options.getChannel('channel');
} else if (data.isApplicationCommand())
mentionChannel = data.getSlashCommand().options.get('channel', true).channel;
if (!mentionChannel)
return await sendHybridInteractionMessageResponse(data, {
@@ -443,15 +460,14 @@ export default class MembershipScreening extends DiscordModule {
embeds: [EMBEDS.INVALID_CHANNEL_THREAD(data.getRaw(), TargetChannel)]
});
if (!TargetChannel.isText())
if (!TargetChannel.isTextBased())
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INVALID_CHANNEL(data.getRaw(), TargetChannel)]
});
if (
!guild
.me!.permissionsIn(TargetChannel)
.has([Permissions.FLAGS.SEND_MESSAGES, Permissions.FLAGS.VIEW_CHANNEL])
!guild.members.me?.permissionsIn(TargetChannel)
.has([PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.ViewChannel])
)
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.BOT_NO_PERMISSION(data.getRaw(), TargetChannel)]
@@ -470,7 +486,7 @@ export default class MembershipScreening extends DiscordModule {
return await sendMessage(channel, undefined, {
embeds: [EMBEDS.CREATE_MESSAGE()]
});
else if (data.isSlashCommand()) {
else if (data.isApplicationCommand()) {
await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.MESSAGE_CREATED(data.getRaw())]
});
@@ -483,7 +499,7 @@ export default class MembershipScreening extends DiscordModule {
let query;
if (!member.permissions.has([Permissions.FLAGS.ADMINISTRATOR]))
if (!member.permissions.has([PermissionsBitField.Flags.Administrator]))
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NO_PERMISSION(data.getRaw())]
});
@@ -495,7 +511,7 @@ export default class MembershipScreening extends DiscordModule {
});
query = args[0].toLowerCase();
} else if (data.isSlashCommand()) query = args.getSubcommand();
} else if (data.isApplicationCommand()) query = args.getSubcommand();
switch (query) {
case 'enable':
@@ -549,9 +565,9 @@ export default class MembershipScreening extends DiscordModule {
embed.setThumbnail(`${member.user.displayAvatarURL()}?size=4096`);
const row = new MessageActionRow()
.addComponents(
new MessageButton()
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents([
new ButtonBuilder()
.setCustomId(
JSON.stringify({
m: 'MembershipScreening',
@@ -563,10 +579,8 @@ export default class MembershipScreening extends DiscordModule {
)
.setEmoji('✅')
.setLabel(' Approve')
.setStyle('SUCCESS')
)
.addComponents(
new MessageButton()
.setStyle(ButtonStyle.Success),
new ButtonBuilder()
.setCustomId(
JSON.stringify({
m: 'MembershipScreening',
@@ -578,10 +592,8 @@ export default class MembershipScreening extends DiscordModule {
)
.setEmoji('⛔')
.setLabel(' Deny and kick')
.setStyle('DANGER')
)
.addComponents(
new MessageButton()
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setCustomId(
JSON.stringify({
m: 'MembershipScreening',
@@ -593,7 +605,8 @@ export default class MembershipScreening extends DiscordModule {
)
.setEmoji('🔪')
.setLabel(' Vision Hunt Decree (Ban)')
.setStyle('DANGER')
.setStyle(ButtonStyle.Danger)
]
);
await channel.send({ content: '\u200b', embeds: [embed], components: [row] });
+56 -36
View File
@@ -1,16 +1,15 @@
import {
Message,
CommandInteraction,
Interaction,
VoiceChannel,
Permissions,
GuildMember,
DMChannel,
StageChannel,
MessageActionRow,
MessageButton,
TextChannel,
VoiceBasedChannel
ActionRowBuilder,
ButtonBuilder,
PermissionsBitField,
ButtonStyle,
BaseGuildTextChannel,
BaseGuildVoiceChannel
} from 'discord.js';
import { I18n } from 'i18n';
@@ -28,11 +27,14 @@ import DiscordMusicPlayer, {
VoiceDisconnectedEvent,
ValidTracks,
DiscordMusicPlayerInstance,
DiscordMusicPlayerLoopMode
DiscordMusicPlayerLoopMode,
TrackUtils
} from '../../providers/DiscordMusicPlayer';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
import Locale from '../../services/Locale';
import { SpotifyTrack, YouTubeVideo } from 'play-dl';
import { checkBotPermissionsInChannel } from '../../utils/DiscordPermission';
const EMBEDS = {
VOICECHANNEL_JOINED: (data: HybridInteractionMessage, locale: I18n) => {
@@ -77,35 +79,37 @@ const EMBEDS = {
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({
title: locale.__('musicplayer.now_playing'),
icon: '🎵 ',
description: track.title,
description: title,
user: DiscordProvider.client.user
});
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) =>
prev.height * prev.width > current.height * current.width ? prev : current
);
if (highestResolutionThumbnail) embed.setImage(highestResolutionThumbnail.url);
if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
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({
title: locale.__('musicplayer.now_playing_repeating'),
icon: '🎵 ',
description: `${track.title}`,
description: title,
user: DiscordProvider.client.user
});
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) =>
prev.height * prev.width > current.height * current.width ? prev : current
);
if (highestResolutionThumbnail) embed.setImage(highestResolutionThumbnail.url);
if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
return embed;
}
@@ -116,7 +120,7 @@ export async function joinVoiceChannelProcedure(
instance: DiscordMusicPlayerInstance | null,
voiceChannel: VoiceChannel | StageChannel
) {
const isSlashCommand = data.isSlashCommand();
const isSlashCommand = data.isApplicationCommand();
const isAcceptableInteraction = data.isSelectMenu() || data.isButton();
const isMessage = data.isMessage();
if (!isSlashCommand && !isAcceptableInteraction && !isMessage) return;
@@ -128,16 +132,18 @@ export async function joinVoiceChannelProcedure(
if (!member || !channel || !guild) 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;
const bot = guild.me;
const bot = guild.members.me;
if (!bot) return;
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 (bot.voice.channelId) {
// But, no music instance yet (The bot might just restarted)
@@ -179,7 +185,7 @@ export async function joinVoiceChannelProcedure(
EMBEDS.VOICECHANNEL_INUSE(
data,
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
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;
previousTrack = event.instance.getPreviousTrack();
if (event.instance.queue.track[0] !== previousTrack) isLoopMessageSent = false;
else if (event.instance.queue.track[0] === previousTrack && isLoopMessageSent) return;
const row = new MessageActionRow().addComponents(
new MessageButton()
.setEmoji('▶️')
.setLabel('Open on YouTube')
.setURL(encodeURI(`https://www.youtube.com/watch?v=${event.instance.queue.track[0].id}`))
.setStyle('LINK')
);
const row = new ActionRowBuilder<ButtonBuilder>();
if(event.instance.queue.track[0] instanceof SpotifyTrack)
row.addComponents([
new ButtonBuilder()
.setEmoji('🟢')
.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.getLoopMode() === DiscordMusicPlayerLoopMode.Current) {
isLoopMessageSent = true;
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]
});
} else {
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]
});
}
+2 -2
View File
@@ -1,5 +1,5 @@
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 Locale from "../../services/Locale";
@@ -81,7 +81,7 @@ export default class Loop extends DiscordModule {
if (args.length !== 0)
query = args.join(' ');
}
else if (data.isSlashCommand())
else if (data.isApplicationCommand())
query = args.getSubcommand();
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 { SpotifyTrack, YouTubeVideo } from "play-dl";
import DiscordProvider from "../../providers/Discord";
import DiscordMusicPlayer, { ValidTracks } from "../../providers/DiscordMusicPlayer";
import DiscordMusicPlayer, { TrackUtils, ValidTracks } from "../../providers/DiscordMusicPlayer";
import Locale from "../../services/Locale";
import { makeErrorEmbed, makeInfoEmbed, sendHybridInteractionMessageResponse } from "../../utils/DiscordMessage";
import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule";
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({
title: locale.__('musicplayer.now_playing'),
icon: '🎵 ',
description: track.title,
description: title,
user: DiscordProvider.client.user
});
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) => (prev.height * prev.width > current.height * current.width) ? prev : current)
if(highestResolutionThumbnail)
embed.setImage(highestResolutionThumbnail.url);
if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
return embed;
},
@@ -55,15 +58,27 @@ export default class NowPlaying extends DiscordModule {
const instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if(!instance || !instance.queue.track[0]) return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_MUSIC_PLAYING(data, locale)] });
const row = new MessageActionRow()
.addComponents(
new MessageButton()
.setEmoji('▶️')
.setLabel('Open on YouTube')
.setURL(encodeURI(`https://www.youtube.com/watch?v=${instance.queue.track[0].id}`))
.setStyle('LINK'),
)
const row = new ActionRowBuilder<ButtonBuilder>();
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 { I18n } from "i18n";
import DiscordProvider from "../../providers/Discord";
import DiscordMusicPlayer from "../../providers/DiscordMusicPlayer";
import Locale from "../../services/Locale";
+92 -32
View File
@@ -1,20 +1,21 @@
import {
Message,
CommandInteraction,
Interaction,
VoiceChannel,
MessageActionRow,
MessageButton,
ActionRowBuilder,
ButtonBuilder,
SelectMenuInteraction,
ButtonInteraction,
StageChannel
StageChannel,
ButtonStyle,
PermissionsBitField
} from 'discord.js';
import { I18n } from 'i18n';
import { joinVoiceChannelProcedure } from './Join';
import DiscordProvider from '../../providers/Discord';
import DiscordMusicPlayer, { ValidTracks } from '../../providers/DiscordMusicPlayer';
import DiscordMusicPlayer, { TrackUtils, ValidTracks } from '../../providers/DiscordMusicPlayer';
import Locale from '../../services/Locale';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
@@ -24,6 +25,7 @@ import {
sendHybridInteractionMessageResponse,
makeInfoEmbed
} from '../../utils/DiscordMessage';
import { checkBotPermissionsInChannel } from '../../utils/DiscordPermission';
const EMBEDS = {
PLAY_INFO: (data: HybridInteractionMessage, locale: I18n) => {
@@ -33,24 +35,25 @@ const EMBEDS = {
fields: [
{
name: locale.__('common.available_args'),
value: locale.__('musicplayer_play.valid_args'),
value: locale.__('musicplayer_play.valid_args')
}
],
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({
title: locale.__('musicplayer_play.queue_added_song'),
description: track.title,
description: title,
user: data.getUser()
});
const highestResolutionThumbnail = track.thumbnails.reduce((prev, current) =>
prev.height * prev.width > current.height * current.width ? prev : current
);
if (highestResolutionThumbnail) embed.setImage(highestResolutionThumbnail.url);
if (TrackUtils.getHighestResolutionThumbnail(thumbnails))
embed.setImage(TrackUtils.getHighestResolutionThumbnail(thumbnails).url);
return embed;
},
@@ -158,6 +161,7 @@ export default class Play extends DiscordModule {
await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel);
let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if (!instance) return;
if (instance!.voiceChannel.id !== member.voice.channel.id)
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);
//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)
return await sendHybridInteractionMessageResponse(
@@ -235,11 +240,16 @@ export default class Play extends DiscordModule {
await joinVoiceChannelProcedure(new HybridInteractionMessage(interaction), null, voiceChannel);
let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if (!instance) return;
if (instance!.voiceChannel.id !== member.voice.channel.id)
return await sendHybridInteractionMessageResponse(
hybridData,
{ embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(new HybridInteractionMessage(interaction), locale)] },
{
embeds: [
EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(new HybridInteractionMessage(interaction), locale)
]
},
true
);
@@ -265,7 +275,7 @@ export default class Play extends DiscordModule {
instance.addTrackToQueue(result);
return await sendHybridInteractionMessageResponse(
hybridData,
{ embeds: [EMBEDS.ADDED_QUEUE(new HybridInteractionMessage(interaction), locale, result)] },
{ embeds: [await EMBEDS.ADDED_QUEUE(new HybridInteractionMessage(interaction), locale, result)] },
true
);
}
@@ -274,7 +284,7 @@ export default class Play extends DiscordModule {
async run(data: HybridInteractionMessage, args: any) {
let query;
const guild = data.getGuild();
const channel = data.getChannel();
const channel = data.getGuildChannel();
const member = data.getMember();
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)] });
query = args.join(' ');
} else if (data.isSlashCommand()) {
query = data.getSlashCommand().options.getString('query');
} else if (data.isApplicationCommand()) {
query = data.getSlashCommand().options.get('query', true).value?.toString();
}
if (!query) return;
@@ -304,6 +314,7 @@ export default class Play extends DiscordModule {
}
let instance = DiscordMusicPlayer.getGuildInstance(guild.id);
if (!instance) return;
if (instance!.voiceChannel.id !== member.voice.channel.id)
return await sendHybridInteractionMessageResponse(
@@ -343,15 +354,15 @@ export default class Play extends DiscordModule {
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);
}
instance.addTrackToQueue(result);
if (linkData.list) {
const row = new MessageActionRow().addComponents(
new MessageButton()
const row = new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setEmoji('✅')
.setCustomId(
JSON.stringify({
@@ -361,21 +372,70 @@ export default class Play extends DiscordModule {
})
)
.setLabel(' Add the remaining songs in the playlist')
.setStyle('PRIMARY')
.setStyle(ButtonStyle.Primary)
]
);
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result)],
embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result)],
components: [row]
});
} else
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 {
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]);
if (result.length > 1) {
@@ -387,8 +447,8 @@ export default class Play extends DiscordModule {
// The query length is too long to fit in json
if (query.length > 100 - 51) return;
const row = new MessageActionRow().addComponents(
new MessageButton()
const row = new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setEmoji('🔎')
.setCustomId(
JSON.stringify({
@@ -400,17 +460,17 @@ export default class Play extends DiscordModule {
})
)
.setLabel(' Not this? Search!')
.setStyle('PRIMARY')
);
.setStyle(ButtonStyle.Primary)
]);
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.ADDED_QUEUE(data, locale, result[0])],
embeds: [await EMBEDS.ADDED_QUEUE(data, locale, result[0])],
components: [row]
});
}
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;
}
}
+66 -30
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 DiscordMusicPlayer, {
DiscordMusicPlayerInstance,
DiscordMusicPlayerLoopMode
DiscordMusicPlayerLoopMode,
TrackUtils
} from '../../providers/DiscordMusicPlayer';
import Locale from '../../services/Locale';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
import { makeErrorEmbed, sendHybridInteractionMessageResponse, makeInfoEmbed } from '../../utils/DiscordMessage';
import {
makeErrorEmbed,
sendHybridInteractionMessageResponse,
makeSuccessEmbed,
makeInfoEmbed
} from '../../utils/DiscordMessage';
const EMBEDS = {
QUEUE: (data: HybridInteractionMessage, locale: I18n, instance: DiscordMusicPlayerInstance) => {
@@ -25,45 +31,58 @@ const EMBEDS = {
instance.getLoopMode() === DiscordMusicPlayerLoopMode.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) {
let queueString = `1. [${queue.track[0].title}](${queue.track[0].url})`;
return makeInfoEmbed({
title: locale.__('musicplayer_queue.title'),
description: `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}\n
There are ${queue.track.length} song in the queue!\n${queueString}`,
user: data.getUser()
});
let queueString = `1. [${TrackUtils.getTitle(queue.track[0])}](${queue.track[0].url})`;
description = `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}\n
There are ${queue.track.length} song in the queue!\n${queueString}`;
} 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);
let queueString = first10
.map((track, index) => `${index + 1}. [${track.title}](${track.url})`)
.map((track, index) => `${index + 1}. [${TrackUtils.getTitle(track)}](${track.url})`)
.join('\n');
return makeInfoEmbed({
title: locale.__('musicplayer_queue.title'),
description: `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}
description = `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}
${locale.__('musicplayer_queue.upcoming_song')} ${upcomingText}\n
${locale.__('musicplayer_queue.song_x_in_queue', { COUNT: queue.track.length.toString()})}
${queueString}\n${queue.track.length > 10 ? `...${queue.track.length - 10} more songs` : ''}`,
user: data.getUser()
});
${locale.__('musicplayer_queue.song_x_in_queue', { COUNT: queue.track.length.toString() })}
${queueString}\n${queue.track.length > 10 ? `...${queue.track.length - 10} more songs` : ''}`;
} 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
.map((track, index) => `${index + 1}. [${track.title}](${track.url})`)
.map((track, index) => `${index + 1}. [${TrackUtils.getTitle(track)}](${track.url})`)
.join('\n');
description = `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}
${locale.__('musicplayer_queue.upcoming_song')} ${upcomingText}\n
${locale.__('musicplayer_queue.song_x_in_queue', { COUNT: queue.track.length.toString() })}
${queueString}`;
}
return makeInfoEmbed({
title: locale.__('musicplayer_queue.title'),
description: `${locale.__('musicplayer_queue.now_playing')}${nowPlayingText}
${locale.__('musicplayer_queue.upcoming_song')} ${upcomingText}\n
${locale.__('musicplayer_queue.song_x_in_queue', { COUNT: queue.track.length.toString()})}
${queueString}`,
description: description,
fields: [
{
name: locale.__('common.available_args'),
value: locale.__('musicplayer_queue.valid_args')
}
],
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) => {
return makeErrorEmbed({
@@ -88,9 +107,9 @@ export default class QueueCommand extends DiscordModule {
async run(data: HybridInteractionMessage, args: any) {
const guild = data.getGuild();
if (!guild) return;
let query;
const locale = await Locale.getGuildLocale(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)]
});
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 { I18n } from "i18n";
import DiscordProvider from "../../providers/Discord";
import DiscordMusicPlayer from "../../providers/DiscordMusicPlayer";
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 { joinVoiceChannelProcedure } from "./Join";
import DiscordMusicPlayer, { ValidTracks } from "../../providers/DiscordMusicPlayer";
import DiscordMusicPlayer, { TrackUtils, ValidTracks } from "../../providers/DiscordMusicPlayer";
import Locale from "../../services/Locale";
import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule";
import { makeErrorEmbed, makeSuccessEmbed, sendHybridInteractionMessageResponse, makeInfoEmbed } from "../../utils/DiscordMessage";
import { makeErrorEmbed, sendHybridInteractionMessageResponse, makeInfoEmbed } from "../../utils/DiscordMessage";
const EMBEDS = {
SEARCH_INFO: (data: HybridInteractionMessage, locale: I18n) => {
@@ -28,7 +28,7 @@ const EMBEDS = {
title: locale.__('musicplayer_search.title_result'),
description: `${locale.__('musicplayer_search.x_results_found', {
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()
});
},
@@ -96,8 +96,8 @@ export default class Search extends DiscordModule {
query = args.join(' ');
}
else if (data.isSlashCommand())
query = data.getSlashCommand().options.getString('query');
else if (data.isApplicationCommand())
query = data.getSlashCommand().options.get('query', true).value?.toString();
else if (data.isButton())
query = args;
@@ -126,9 +126,9 @@ export default class Search extends DiscordModule {
let result = await DiscordMusicPlayer.searchYouTubeByQuery(query);
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
So we need to shorten our json.
@@ -157,8 +157,8 @@ export default class Search extends DiscordModule {
messageSelectMenu.addOptions(menuOptions);
const row = new MessageActionRow();
row.addComponents(messageSelectMenu);
const row = new ActionRowBuilder<SelectMenuBuilder>();
row.addComponents([messageSelectMenu]);
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 { Message, CommandInteraction, Interaction } from "discord.js";
import { Message, CommandInteraction } from "discord.js";
import { makeSuccessEmbed, makeErrorEmbed, sendHybridInteractionMessageResponse } from "../../utils/DiscordMessage";
import DiscordProvider from "../../providers/Discord";
import DiscordMusicPlayer from "../../providers/DiscordMusicPlayer";
import { I18n } from "i18n";
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 NodePing from 'ping';
import { Promise } from 'bluebird';
@@ -123,7 +123,7 @@ export default class Ping extends DiscordModule {
}`
);
if (data.isSlashCommand())
if (data.isApplicationCommand())
return await data
.getMessageComponentInteraction()
.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 DiscordModule, { HybridInteractionMessage } from '../utils/DiscordModule';
@@ -78,9 +78,9 @@ export default class Say extends DiscordModule {
if (
!message.member.permissions.has([
Permissions.FLAGS.VIEW_CHANNEL,
Permissions.FLAGS.SEND_MESSAGES,
Permissions.FLAGS.MANAGE_CHANNELS
PermissionsBitField.Flags.ViewChannel,
PermissionsBitField.Flags.SendMessages,
PermissionsBitField.Flags.ManageChannels,
])
)
return await sendHybridInteractionMessageResponse(data, {
@@ -93,22 +93,22 @@ export default class Say extends DiscordModule {
});
query = args.join(' ');
} else if (data.isSlashCommand()) {
} else if (data.isApplicationCommand()) {
const interaction = data.getSlashCommand();
if (
!data
.getGuild()!
.members.cache.get(interaction.user.id)
?.permissions.has([Permissions.FLAGS.ADMINISTRATOR])
?.permissions.has([PermissionsBitField.Flags.Administrator])
)
return await sendHybridInteractionMessageResponse(data, {
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 {
await data.getChannel()!.send({ content: query });
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 DiscordProvider from '../../providers/Discord';
@@ -14,6 +14,7 @@ import {
} from '../../utils/DiscordMessage';
import { COMMON_EMBEDS } from '.';
import { checkMemberPermissionsInGuild } from '../../utils/DiscordPermission';
const EMBEDS = {
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;
const prefix = GuildCache.prefix;
if (!member.permissions.has([Permissions.FLAGS.ADMINISTRATOR]))
return await sendHybridInteractionMessageResponse(data, {
embeds: [COMMON_EMBEDS.NO_PERMISSION(data, locale)]
});
if (!(await checkMemberPermissionsInGuild({ member, data, locale, permissions: [PermissionsBitField.Flags.ManageGuild] })))
return;
let newPrefix: string | null | undefined;
if (data.isMessage()) {
@@ -71,7 +70,7 @@ export default async (data: HybridInteractionMessage, args: any, guild: Guild, l
__name.shift();
_name = __name.join(' ');
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)
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())
return placeholder.getMessage().edit({ embeds: [EMBEDS.PREFIX_UPDATED(data, locale, newPrefix)] });
else if (data.isSlashCommand())
else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse(
data,
{ 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 DiscordProvider from '../../providers/Discord';
import Prisma from '../../providers/Prisma';
import Cache from '../../providers/Cache';
@@ -14,6 +13,7 @@ import {
} from '../../utils/DiscordMessage';
import { COMMON_EMBEDS } from '.';
import { checkMemberPermissionsInGuild } from '../../utils/DiscordPermission';
const EMBEDS = {
SERVICE_ANNOUNCEMENT_INVALID_STATUS: (data: HybridInteractionMessage, locale: I18n) => {
@@ -104,10 +104,8 @@ export const setEnableServiceAnnouncement = async (
let member = data.getMember();
if (!member) return;
if (!member.permissions.has([Permissions.FLAGS.ADMINISTRATOR]))
return await sendHybridInteractionMessageResponse(data, {
embeds: [COMMON_EMBEDS.NO_PERMISSION(data, locale)]
});
if (!(await checkMemberPermissionsInGuild({ member, data, locale, permissions: [PermissionsBitField.Flags.Administrator] })))
return;
let GuildCache = await Cache.getCachedGuild(guild.id);
if(!GuildCache) return;
@@ -124,7 +122,7 @@ export const setEnableServiceAnnouncement = async (
__name.shift();
_name = __name.join(' ');
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)
return await sendHybridInteractionMessageResponse(data, {
@@ -165,7 +163,7 @@ export const setEnableServiceAnnouncement = async (
return await (placeholder as Message).edit({
embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_STATUS_UPDATED(data, locale, newStatusBool)]
});
else if (data.isSlashCommand())
else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse(
data,
{
@@ -184,10 +182,8 @@ export const setServiceAnnouncementChannel = async (
let member = data.getMember();
if (!member) return;
if (!member.permissions.has([Permissions.FLAGS.ADMINISTRATOR]))
return await sendHybridInteractionMessageResponse(data, {
embeds: [COMMON_EMBEDS.NO_PERMISSION(data, locale)]
});
if (!(await checkMemberPermissionsInGuild({ member, data, locale, permissions: [PermissionsBitField.Flags.Administrator] })))
return;
let channel;
if (data.isMessage()) {
@@ -196,7 +192,7 @@ export const setServiceAnnouncementChannel = async (
embeds: [EMBEDS.NO_CHANNEL_MENTIONED(data, locale)]
});
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)
return await sendHybridInteractionMessageResponse(data, {
@@ -211,16 +207,14 @@ export const setServiceAnnouncementChannel = async (
embeds: [EMBEDS.INVALID_CHANNEL_THREAD(data, locale, TargetChannel)]
});
if (!TargetChannel.isText())
if (!TargetChannel.isTextBased())
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.INVALID_CHANNEL(data, locale, TargetChannel)]
});
if (
!data
.getGuild()!
.me?.permissionsIn(TargetChannel)
.has([Permissions.FLAGS.SEND_MESSAGES, Permissions.FLAGS.VIEW_CHANNEL])
!data.getGuild()?.members.me?.permissionsIn(TargetChannel)
.has([PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.ViewChannel])
)
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.BOT_NO_PERMISSION(data, locale, TargetChannel)]
@@ -239,7 +233,7 @@ export const setServiceAnnouncementChannel = async (
return await (placeholder as Message).edit({
embeds: [EMBEDS.SERVICE_ANNOUNCEMENT_CONFIGURED_CHANNEL(data, locale, TargetChannel)]
});
else if (data.isSlashCommand())
else if (data.isApplicationCommand())
return await sendHybridInteractionMessageResponse(
data,
{
+27 -12
View File
@@ -1,36 +1,49 @@
import {
Message,
Interaction,
CommandInteraction,
Permissions,
ThreadChannel,
GuildChannel,
Guild
Guild,
PermissionsBitField,
PermissionResolvable
} from 'discord.js';
import { I18n } from 'i18n';
import DiscordProvider from '../../providers/Discord';
import Prisma from '../../providers/Prisma';
import Cache from '../../providers/Cache';
import Locale from '../../services/Locale';
import DiscordModule, { HybridInteractionMessage } from '../../utils/DiscordModule';
import {
makeInfoEmbed,
makeErrorEmbed,
makeSuccessEmbed,
makeProcessingEmbed,
sendHybridInteractionMessageResponse
} from '../../utils/DiscordMessage';
import * as PrefixModule from './Prefix';
import * as LanguageModule from './Language';
import * as ServiceAnnouncementModule from './ServiceAnnouncement';
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({
title: locale.__('common.no_permissions'),
description: locale.__('common.no_permissions_description', { PERMISSIONS: 'ADMINISTRATOR'}),
title: locale.__('common.member_no_permissions'),
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()
});
},
@@ -91,7 +104,7 @@ export default class Settings extends DiscordModule {
});
query = args[0].toLowerCase();
} else if (data.isSlashCommand()) {
} else if (data.isApplicationCommand()) {
query = args.getSubcommand();
}
@@ -102,6 +115,8 @@ export default class Settings extends DiscordModule {
});
case 'prefix':
return await PrefixModule.default(data, args, guild, locale);
case 'language':
return await LanguageModule.default(data, args, guild, locale);
case 'enableserviceannouncement':
return await ServiceAnnouncementModule.setEnableServiceAnnouncement(data, args, guild, locale);
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
});
}
}
+42 -30
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 { I18n } from 'i18n';
@@ -73,10 +73,10 @@ const EMBEDS = {
if (user.presence?.activities) {
for (let activity of user.presence?.activities) {
if (activity.type === 'CUSTOM')
embed.addField(
`${locale.__('userinfo.custom_status')}`,
`${
if (activity.type === ActivityType.Custom)
embed.addFields([{
name: `${locale.__('userinfo.custom_status')}`,
value: `${
!activity.emoji
? ''
: `${
@@ -88,49 +88,61 @@ const EMBEDS = {
}`
} ${activity.state === null ? '' : activity.state}
\u200b`,
false
);
inline: false
}]);
else {
let title = '';
switch (activity.type) {
case 'PLAYING':
title = `'🕹 ${locale.__('userinfo.playing_x', {NAME: activity.name})}`;
case ActivityType.Playing:
title = `🕹 ${locale.__('userinfo.playing_x', { NAME: activity.name })}`;
break;
case 'STREAMING':
title = `'🔴 ${locale.__('userinfo.streaming_x', {NAME: activity.name})}`;
case ActivityType.Streaming:
title = `🔴 ${locale.__('userinfo.streaming_x', { NAME: activity.name })}`;
break;
case 'LISTENING':
title = `🎵 ${locale.__('userinfo.listening_x', {NAME: activity.name})}`;
case ActivityType.Listening:
title = `🎵 ${locale.__('userinfo.listening_x', { NAME: activity.name })}`;
break;
case 'WATCHING':
title = `📺 ${locale.__('userinfo.watching_x', {NAME: activity.name})}`;
case ActivityType.Watching:
title = `📺 ${locale.__('userinfo.watching_x', { NAME: activity.name })}`;
break;
case 'COMPETING':
title = `'🌠 ${locale.__('userinfo.competing_x', {NAME: activity.name})}`;
case ActivityType.Competing:
title = `🌠 ${locale.__('userinfo.competing_x', { NAME: activity.name })}`;
break;
}
embed.addField(
`${title}`,
`${activity.details === null ? '' : activity.details}
const startTime = activity.timestamps?.start;
const endTime = activity.timestamps?.end;
embed.addFields([{
name: `${title}`,
value: `${activity.details === null ? '' : activity.details}
${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`,
false
);
inline: false
}]);
}
}
}
embed.addField(
`📰 ${locale.__('userinfo.user_guild_info')}`,
`${
embed.addFields([{
name: `📰 ${locale.__('userinfo.user_guild_info')}`,
value: `${
user.joinedAt === null
? 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')}` : ''}
`
);
}]);
embed.setThumbnail(user.displayAvatarURL + '?size=4096');
embed.setAuthor({
@@ -172,7 +184,7 @@ export default class UserInfo extends DiscordModule {
if (typeof data.getMessage().mentions.users.first() !== 'undefined')
query = data.getMessage().mentions.users.first()?.id;
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
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)]
});
if (data.isSlashCommand()) await data.getMessageComponentInteraction().deferReply();
if (data.isApplicationCommand()) await data.getMessageComponentInteraction().deferReply();
let colorthief = null;
try {
+46 -49
View File
@@ -1,9 +1,10 @@
import {
Message,
MessageActionRow,
MessageButton,
Interaction,
CommandInteraction
ActionRowBuilder,
ButtonBuilder,
CommandInteraction,
BaseInteraction,
ButtonStyle
} from 'discord.js';
import validator from 'validator';
import countryLookup from 'country-code-lookup';
@@ -20,7 +21,7 @@ import {
} from '../utils/DiscordMessage';
const EMBEDS = {
osu_INFO: (data: Message | Interaction) => {
osu_INFO: (data: HybridInteractionMessage) => {
return makeInfoEmbed({
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`,
@@ -34,43 +35,43 @@ const EMBEDS = {
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({
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({
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({
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({
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({
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({
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 (typeof args[1] === 'undefined')
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NO_USER_MENTIONED(data.getRaw())]
embeds: [EMBEDS.NO_USER_MENTIONED(data)]
});
const [removed, ...newArgs] = args;
user = newArgs.join(' ');
} else if (data.isSlashCommand())
user = data.getSlashCommand().options.getString('user');
} else if (data.isApplicationCommand())
user = data.getSlashCommand().options.get('user')?.value?.toString();
if (!validator.isNumeric(user) && !this.validate_osu_username(user))
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 });
if (result instanceof Array && result.length === 0)
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();
}
@@ -258,13 +259,13 @@ export default class osu extends DiscordModule {
'https://osu.ppy.sh/images/layout/avatar-guest.png'
);
const row = new MessageActionRow().addComponents(
new MessageButton()
const row = new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setEmoji('🔗')
.setLabel(' Open Profile')
.setURL(`https://osu.ppy.sh/users/${result.id}`)
.setStyle('LINK')
);
.setStyle(ButtonStyle.Link)
]);
return await sendHybridInteractionMessageResponse(data, {
embeds: [embed],
@@ -276,26 +277,26 @@ export default class osu extends DiscordModule {
if (data.isMessage()) {
if (typeof args[1] === 'undefined')
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.NO_BEATMAP_MENTIONED(data.getRaw())]
embeds: [EMBEDS.NO_BEATMAP_MENTIONED(data)]
});
const [removed, ...newArgs] = args;
beatmap = newArgs.join(' ');
} else if (data.isSlashCommand())
beatmap = data.getSlashCommand().options.getString('beatmap');
} else if (data.isApplicationCommand())
beatmap = data.getSlashCommand().options.get('beatmap')?.value?.toString();
if (!validator.isNumeric(beatmap))
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 });
if (result instanceof Array && result.length === 0)
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];
@@ -436,33 +437,29 @@ export default class osu extends DiscordModule {
`https://assets.ppy.sh/beatmaps/${bm_result.beatmapSetId}/covers/cover.jpg`
);
const row = new MessageActionRow();
const row = new ActionRowBuilder<ButtonBuilder>();
if (bm_result.hasDownload)
row.addComponents(
new MessageButton()
row.addComponents([
new ButtonBuilder()
.setEmoji('🌎')
.setLabel(' Download (Beatconnect)')
.setURL(`https://beatconnect.io/b/${bm_result.beatmapSetId}/`)
.setStyle('LINK')
);
row.addComponents(
new MessageButton()
.setStyle(ButtonStyle.Link),
new ButtonBuilder()
.setEmoji('🔗')
.setLabel(' Open listing')
.setURL(
`https://osu.ppy.sh/beatmapsets/${bm_result.beatmapSetId}${url_mode}/${bm_result.id}`
)
.setStyle('LINK')
);
row.addComponents(
new MessageButton()
.setStyle(ButtonStyle.Link),
new ButtonBuilder()
.setEmoji('💬')
.setLabel(' Open discussion')
.setURL(
`https://osu.ppy.sh/beatmapsets/${bm_result.beatmapSetId}/discussion`
)
.setStyle('LINK')
);
.setStyle(ButtonStyle.Link)
]);
return await sendHybridInteractionMessageResponse(data, {
embeds: [embed2],
@@ -476,11 +473,11 @@ export default class osu extends DiscordModule {
if (data.isMessage()) {
if (args.length === 0) {
return await sendHybridInteractionMessageResponse(data, {
embeds: [EMBEDS.osu_INFO(data.getRaw())]
embeds: [EMBEDS.osu_INFO(data)]
});
}
query = args[0].toLowerCase();
} else if (data.isSlashCommand()) {
} else if (data.isApplicationCommand()) {
query = args.getSubcommand();
}
-1
View File
@@ -10,5 +10,4 @@ App.loadLocale();
App.loadDiscord();
App.load_osu();
export default App;
+1 -1
View File
@@ -7,7 +7,7 @@ import osu from './osuAPI';
import Logger from '../libs/Logger';
import Locale from './Locale';
class App {
public readonly versionNumber = `0.09`;
public readonly versionNumber = `0.11`;
public readonly version = `${this.versionNumber}${
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 Environment from './Environment';
@@ -14,8 +14,10 @@ import Discord_MembershipScreening from '../discord/MembershipScreening';
import Discord_osu from '../discord/osu';
import Discord_UserInfo from '../discord/UserInfo';
import Discord_Stats from '../discord/Stats';
import Discord_Support from '../discord/Support';
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_Join from '../discord/MusicPlayer/Join';
import Discord_MusicPlayer_Leave from '../discord/MusicPlayer/Leave';
@@ -38,13 +40,13 @@ class Discord {
constructor() {
this.client = new Client({
//partials: ['MESSAGE', 'CHANNEL', 'REACTION'],
intents: [
Intents.FLAGS.GUILDS,
Intents.FLAGS.GUILD_MESSAGES,
Intents.FLAGS.GUILD_MEMBERS,
Intents.FLAGS.GUILD_PRESENCES,
Intents.FLAGS.GUILD_VOICE_STATES
IntentsBitField.Flags.MessageContent,
IntentsBitField.Flags.Guilds,
IntentsBitField.Flags.GuildMessages,
IntentsBitField.Flags.GuildMembers,
IntentsBitField.Flags.GuildPresences,
IntentsBitField.Flags.GuildVoiceStates
]
});
}
@@ -64,8 +66,10 @@ class Discord {
new Discord_InteractionManager(),
new Discord_osu(),
new Discord_Settings(),
new Discord_Support(),
new Discord_MusicPlayer_Play(),
new Discord_MusicPlayer_PlayMy(),
new Discord_MusicPlayer_NowPlaying(),
new Discord_MusicPlayer_Skip(),
new Discord_MusicPlayer_Join(),
@@ -127,7 +131,7 @@ class Discord {
if (interaction.guild) {
thisModule.GuildInteractionCreate(interaction);
if (interaction.isCommand() && interaction.commandName && thisModule.commandInteractionName) {
if (interaction.type === InteractionType.ApplicationCommand && interaction.commandName && thisModule.commandInteractionName) {
thisModule.GuildCommandInteractionCreate(interaction);
if (interaction.commandName.toLowerCase() === thisModule.commandInteractionName)
thisModule.GuildModuleCommandInteractionCreate(interaction);
@@ -160,7 +164,7 @@ class Discord {
// Handling guild commands
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 (typeof message.guild?.id === 'undefined') return;
@@ -261,7 +265,7 @@ class Discord {
// Handling mentions
this.client.on('messageCreate', async (message: Message) => {
// 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 (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 {
AudioPlayer,
VoiceConnection,
@@ -7,27 +15,113 @@ import {
createAudioResource,
VoiceConnectionStatus,
AudioPlayerStatus,
AudioPlayerState,
NoSubscriberBehavior,
VoiceConnectionState,
AudioPlayerError,
DiscordGatewayAdapterCreator
} from '@discordjs/voice';
import playdl, { YouTubeVideo } from 'play-dl';
import playdl, { Spotify, SpotifyPlaylist, SpotifyTrack, YouTubeVideo } from 'play-dl';
import { EventEmitter } from 'stream';
import DiscordProvider from './Discord';
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) {
playdl.setToken({
youtube: {
tokenObject.youtube = {
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 {
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 {
public instance: DiscordMusicPlayerInstance;
@@ -75,7 +181,7 @@ export enum DiscordMusicPlayerLoopMode {
export class DiscordMusicPlayerInstance {
public queue: Queue;
public player: AudioPlayer;
public textChannel?: TextChannel;
public textChannel?: BaseGuildTextChannel | BaseGuildVoiceChannel;
public voiceChannel: VoiceChannel | StageChannel;
public voiceConnection?: VoiceConnection;
public previousTrack?: ValidTracks;
@@ -83,6 +189,8 @@ export class DiscordMusicPlayerInstance {
public paused: boolean = false;
public loopMode: DiscordMusicPlayerLoopMode = DiscordMusicPlayerLoopMode.None;
public actualPlaybackURL?: string;
public readonly events: EventEmitter;
constructor({ voiceChannel }: { voiceChannel: VoiceChannel | StageChannel }) {
@@ -98,10 +206,7 @@ export class DiscordMusicPlayerInstance {
this.player.on(AudioPlayerStatus.Idle, async (oldStage, newStage) => {
//The player stopped
if (
newStage.status === AudioPlayerStatus.Idle &&
oldStage.status !== AudioPlayerStatus.Idle
) {
if (newStage.status === AudioPlayerStatus.Idle && oldStage.status !== AudioPlayerStatus.Idle) {
// Loop mode is set to current song
if (this.loopMode === DiscordMusicPlayerLoopMode.Current) {
if (this.queue.track.length !== 0) {
@@ -132,24 +237,10 @@ export class DiscordMusicPlayerInstance {
});
}
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 joinVoiceChannel(voiceChannel: VoiceChannel | StageChannel, textChannel?: TextChannel) {
public joinVoiceChannel(voiceChannel: VoiceChannel | StageChannel, textChannel?: BaseGuildTextChannel | BaseGuildVoiceChannel) {
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');
if (textChannel) this.textChannel = textChannel;
@@ -157,31 +248,32 @@ export class DiscordMusicPlayerInstance {
this.voiceConnection = joinVoiceChannel({
channelId: this.voiceChannel.id,
guildId: this.voiceChannel.guild.id,
adapterCreator: this.voiceChannel.guild
.voiceAdapterCreator as DiscordGatewayAdapterCreator
adapterCreator: this.voiceChannel.guild.voiceAdapterCreator as DiscordGatewayAdapterCreator
});
this.voiceConnection.on(
VoiceConnectionStatus.Ready,
(oldState: VoiceConnectionState, newState: VoiceConnectionState) => {
let currentVC = DiscordProvider.client.guilds.cache.get(voiceChannel.guild.id)?.me
?.voice.channel;
async (oldState: VoiceConnectionState, newState: VoiceConnectionState) => {
let guild = DiscordProvider.client.guilds.cache.get(voiceChannel.guild.id);
if (guild) {
let currentVC = guild?.members.me?.voice.channel;
if (currentVC && currentVC.id !== this.voiceChannel.id) {
this.voiceChannel = currentVC;
}
}
}
);
this.voiceConnection.on(
VoiceConnectionStatus.Disconnected,
(oldState: VoiceConnectionState, newState: VoiceConnectionState) => {
setTimeout(() => {
if (
!DiscordProvider.client.guilds.cache.get(voiceChannel.guildId!)!.me!.voice
.channelId
) {
setTimeout(async () => {
let guild = DiscordProvider.client.guilds.cache.get(voiceChannel.guildId);
if(guild) {
if (!guild?.members.me?.voice.channelId) {
this.events.emit('disconnect', new VoiceDisconnectedEvent(this));
}
}
}, 2000);
}
);
@@ -190,9 +282,10 @@ export class DiscordMusicPlayerInstance {
public async leaveVoiceChannel() {
if (this.player) this.player.pause();
if (DiscordProvider.client.guilds.cache.get(this.voiceChannel.guild.id)?.me?.voice) {
this.voiceConnection?.disconnect();
}
const guild = DiscordProvider.client.guilds.cache.get(this.voiceChannel.guild.id);
if (!guild) return;
if (guild.members.me?.voice) this.voiceConnection?.disconnect();
}
public async pausePlayer() {
@@ -221,10 +314,25 @@ export class DiscordMusicPlayerInstance {
if (!this.voiceConnection) throw new Error('No voice connection');
try {
let resource;
if(track instanceof YouTubeVideo) {
const stream = await playdl.stream(track.url);
const resource = createAudioResource(stream.stream, {
resource = createAudioResource(stream.stream, {
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.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) {
this.loopMode = mode;
}
@@ -260,6 +393,24 @@ export class DiscordMusicPlayerInstance {
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() {
return this.paused;
}
@@ -286,10 +437,7 @@ export class DiscordMusicPlayerInstance {
const stream = 'https://fakestream:42069/fake/stream/fake/audio/fake.mp3';
const resource = createAudioResource(stream);
this.player.play(resource);
this.player.emit(
'error',
new AudioPlayerError(new Error('Music player was manually crashed'), null!)
);
this.player.emit('error', new AudioPlayerError(new Error('Music player was manually crashed'), null!));
}
}
@@ -326,12 +474,44 @@ class DiscordMusicPlayer {
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) {
// Search the url
const searched: YouTubeVideo[] = await playdl.search(
'https://www.youtube.com/watch?v=' + youtubeLink.videoId,
{ source: { youtube: 'video' } }
);
const searched: YouTubeVideo[] = await playdl.search('https://www.youtube.com/watch?v=' + youtubeLink.videoId, {
source: { youtube: 'video' }
});
for (let video of searched) {
if (video.id === youtubeLink.videoId) return video;
}
@@ -345,9 +525,7 @@ class DiscordMusicPlayer {
}
// Last resort, search the title
const videoInfo = await playdl.video_basic_info(
'https://www.youtube.com/watch?v=' + youtubeLink.videoId
);
const videoInfo = await playdl.video_basic_info('https://www.youtube.com/watch?v=' + youtubeLink.videoId);
if (videoInfo?.video_details?.title) {
const searched: YouTubeVideo[] = await playdl.search(videoInfo.video_details.title, {
source: { youtube: 'video' }
@@ -357,9 +535,7 @@ class DiscordMusicPlayer {
}
}
let yt_info = await playdl.video_info(
'https://www.youtube.com/watch?v=' + youtubeLink.videoId
);
let yt_info = await playdl.video_info('https://www.youtube.com/watch?v=' + youtubeLink.videoId);
if (yt_info) {
return new YouTubeVideo({
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 {
try {
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 {
if (
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);
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) {
let queryObject: any = {};
if (query.indexOf('?') >= 0) {
+13 -1
View File
@@ -29,19 +29,31 @@ class Environment {
const DISCORD_TOKEN = process.env.DISCORD_TOKEN;
const DEVELOPER_IDS = process.env.DEVELOPER_IDS;
const PRIVATE_BOT = process.env.PRIVATE_BOT;
const SUPPORT_URL = process.env.SUPPORT_URL;
const OSU_API_KEY = process.env.OSU_API_KEY;
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 {
NODE_ENV,
DISCORD_TOKEN,
DEVELOPER_IDS,
PRIVATE_BOT,
SUPPORT_URL,
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')
.setDescription('Search for music')
.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('pause').setDescription('Pause the current music'),
new SlashCommandBuilder().setName('resume').setDescription('Resume paused music'),
+16 -11
View File
@@ -1,5 +1,5 @@
import {
MessageEmbed,
EmbedBuilder,
User,
MessagePayload,
MessageOptions,
@@ -10,8 +10,10 @@ import {
BaseGuildTextChannel,
Message,
ColorResolvable,
Interaction,
InteractionReplyOptions
InteractionReplyOptions,
BaseInteraction,
BaseGuildVoiceChannel,
VoiceChannel
} from 'discord.js';
import App from '..';
@@ -43,7 +45,7 @@ interface EmbedDataPresets {
}
export function makeEmbed(data: EmbedData) {
const embed = new MessageEmbed();
const embed = new EmbedBuilder();
embed.setColor(data.color || '#FFFFFF');
if (!data.icon) embed.setTitle(data.title);
@@ -54,14 +56,14 @@ export function makeEmbed(data: EmbedData) {
if (data.setTimestamp) embed.setTimestamp();
if (data.user)
embed.footer = {
embed.setFooter({
text: `${data.user.username} | v${App.version}`,
iconURL: `${data.user.displayAvatarURL()}?size=4096`
};
});
else
embed.footer = {
embed.setFooter({
text: `v${App.version}`
};
});
if (data.fields) embed.addFields(data.fields);
@@ -129,13 +131,16 @@ export function makeProcessingEmbed(data: EmbedDataPresets) {
}
export async function sendMessage(
channel: TextChannel | DMChannel | BaseGuildTextChannel | GuildTextBasedChannel | PartialDMChannel,
channel: TextChannel | DMChannel | BaseGuildTextChannel | GuildTextBasedChannel | PartialDMChannel | BaseGuildTextChannel | BaseGuildVoiceChannel,
user: User | undefined,
options: string | MessagePayload | MessageOptions
) {
let message;
try {
if(channel instanceof BaseGuildVoiceChannel)
message = await (channel as VoiceChannel).send(options);
else
message = await channel.send(options);
} catch (error) {
if (typeof user === 'undefined') {
@@ -160,8 +165,8 @@ export async function sendHybridInteractionMessageResponse(
data: HybridInteractionMessage,
payload: MessageOptions | InteractionReplyOptions,
replace = false
): Promise<Message | Interaction | undefined> {
if (data.isSlashCommand() || data.isButton() || data.isSelectMenu()) {
): Promise<Message | BaseInteraction | undefined> {
if (data.isApplicationCommand() || data.isButton() || data.isSelectMenu()) {
const messageComponent = data.getMessageComponentInteraction();
if (!messageComponent.replied) {
+18 -7
View File
@@ -8,7 +8,11 @@ import {
TextBasedChannel,
MessageComponentInteraction,
User,
SelectMenuInteraction
SelectMenuInteraction,
InteractionType,
BaseInteraction,
GuildBasedChannel,
ChannelType
} from 'discord.js';
export default class DiscordModule {
@@ -56,17 +60,17 @@ export default class DiscordModule {
}
export class HybridInteractionMessage {
public data: Interaction | Message;
constructor(data: Interaction | Message) {
public data: BaseInteraction | Message;
constructor(data: BaseInteraction | Message) {
this.data = data;
}
public isInteraction(): boolean {
return this.data instanceof Interaction;
return this.data instanceof BaseInteraction;
}
public isSlashCommand(): boolean {
return this.data instanceof CommandInteraction && this.data.isCommand();
public isApplicationCommand(): boolean {
return this.data instanceof CommandInteraction && this.data.type === InteractionType.ApplicationCommand;
}
public isButton(): boolean {
@@ -85,6 +89,13 @@ export class HybridInteractionMessage {
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 {
return this.data.guild;
}
@@ -130,7 +141,7 @@ export class HybridInteractionMessage {
return this.data as Message;
}
public getRaw(): Interaction | Message {
public getRaw(): BaseInteraction | Message {
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"
kuler "^2.0.0"
"@discordjs/builders@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-0.14.0.tgz#1915af10fa1c30d738b08b1ed0ae3c224fd491d7"
integrity sha512-+fqLIqa9wN3R+kvlld8sgG0nt04BAZxdCDP4t2qZ9TJsquLWA+xMtT8Waibb3d4li4AQS+IOfjiHAznv/dhHgQ==
"@discordjs/builders@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.0.0.tgz#1ddc5a7f9d20977e7414c02989169bb7f55294ba"
integrity sha512-8y91ZfpOHubiGJu5tVyGI9tQCEyHZDTeqUWVcJd0dq7B96xIf84S0L4fwmD1k9zTe1eqEFSk0gc7BpY+FKn7Ww==
dependencies:
"@sapphire/shapeshift" "^3.1.0"
"@sindresorhus/is" "^4.6.0"
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"
"@sapphire/shapeshift" "^3.5.1"
discord-api-types "^0.36.2"
fast-deep-equal "^3.1.3"
ts-mixer "^6.0.1"
tslib "^2.4.0"
@@ -52,28 +39,33 @@
resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.7.0.tgz#1a6c00198b744ba2b73a64442145da637ac073b8"
integrity sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA==
"@discordjs/node-pre-gyp@^0.4.2":
version "0.4.2"
resolved "https://registry.yarnpkg.com/@discordjs/node-pre-gyp/-/node-pre-gyp-0.4.2.tgz#37dd27f8d353eeb87372fcce059c5249550b37ab"
integrity sha512-V239Czn+DXFGLhhuccwEDBoTdgMGrRu30dOlzm1GzrSIjwFj01ZJerNX7x+CEX1NG1Q/1gGfOOkeZFNHjycrRA==
"@discordjs/collection@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.0.0.tgz#4d777d87e56640a200200e7f5605c0fba05ac1b8"
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:
detect-libc "^1.0.3"
detect-libc "^2.0.0"
https-proxy-agent "^5.0.0"
make-dir "^3.1.0"
node-fetch "^2.6.5"
node-fetch "^2.6.7"
nopt "^5.0.0"
npmlog "^5.0.1"
rimraf "^3.0.2"
semver "^7.3.5"
tar "^6.1.11"
"@discordjs/opus@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@discordjs/opus/-/opus-0.7.0.tgz#69e9611a6412ea470f8e640f54478da89326ca3b"
integrity sha512-3Xxa3dh7taSDwBAR5fLALZ/KTxvbMmHCMxYLYve6NlPO7Ms1CLmKqp/R4ZoVzkRGQVUVWEhaB1s0v9jfa2tfDg==
"@discordjs/opus@^0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@discordjs/opus/-/opus-0.8.0.tgz#dae2d220a7942736343021f9af2b2b921d482c46"
integrity sha512-uHE7OmHEmP8YM0yvsH3iSdacdeghO0qTkF0CIkV07Tg0qdyOLUVkoZHj5Zcpge9rC4qb/JvTS2xRgttSZLM43Q==
dependencies:
"@discordjs/node-pre-gyp" "^0.4.2"
node-addon-api "^4.2.0"
"@discordjs/node-pre-gyp" "^0.4.4"
node-addon-api "^5.0.0"
"@discordjs/rest@^0.5.0":
version "0.5.0"
@@ -87,6 +79,19 @@
tslib "^2.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":
version "0.10.0"
resolved "https://registry.yarnpkg.com/@discordjs/voice/-/voice-0.10.0.tgz#5c911b5ce276233b232a98c92141e07f1f52168b"
@@ -100,14 +105,14 @@
ws "^8.7.0"
"@jridgewell/resolve-uri@^3.0.3":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe"
integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==
version "3.0.8"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz#687cc2bbf243f4e9a868ecf2262318e2658873a1"
integrity sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w==
"@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.13"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c"
integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==
version "1.4.14"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@jridgewell/trace-mapping@0.3.9":
version "0.3.9"
@@ -168,62 +173,70 @@
dependencies:
make-plural "^7.0.0"
"@prisma/client@^3.15.2":
version "3.15.2"
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.15.2.tgz#2181398147afc79bfe0d83c03a88dc45b49bd365"
integrity sha512-ErqtwhX12ubPhU4d++30uFY/rPcyvjk+mdifaZO5SeM21zS3t4jQrscy8+6IyB0GIYshl5ldTq6JSBo1d63i8w==
"@prisma/client@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.0.0.tgz#ed2f46930a1da0d8ae88d7965485973576b04270"
integrity sha512-g1h2OGoRo7anBVQ9Cw3gsbjwPtvf7i0pkGxKeZICtwkvE5CZXW+xZF4FZdmrViYkKaAShbISL0teNpu9ecpf4g==
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":
version "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz#bf5e2373ca68ce7556b967cb4965a7095e93fe53"
integrity sha512-e3k2Vd606efd1ZYy2NQKkT4C/pn31nehyLhVug6To/q8JT8FpiMrDy7zmm3KLF0L98NOQQcutaVtAPhzKhzn9w==
"@prisma/engines-version@3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11":
version "3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11"
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11.tgz#4b5efe5eee2feef12910e4627a572cd96ed83236"
integrity sha512-PiZhdD624SrYEjyLboI0X7OugNbxUzDJx9v/6ldTKuqNDVUCmRH/Z00XwDi/dgM4FlqOSO+YiUsSiSKjxxG8cw==
"@prisma/engines@3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e":
version "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e.tgz#f691893df506b93e3cb1ccc15ec6e5ac64e8e570"
integrity sha512-NHlojO1DFTsSi3FtEleL9QWXeSF/UjhCW0fgpi7bumnNZ4wj/eQ+BJJ5n2pgoOliTOGv9nX2qXvmHap7rJMNmg==
"@prisma/engines@3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11":
version "3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11"
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11.tgz#82f0018153cffa05d61422f9c0c7b0479b180f75"
integrity sha512-u/rG4lDHALolWBLr3yebZ+N2qImp3SDMcu7bHNJuRDaYvYEXy/MqfNRNEgd9GoPsXL3gofYf0VzJf2AmCG3YVw==
"@sapphire/async-queue@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.3.1.tgz#9d861e626dbffae02d808e13f823d4510e450a78"
integrity sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g==
"@sapphire/shapeshift@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.1.0.tgz#e4719fd03ab1d3ea01170f9c37b9cb64f5794759"
integrity sha512-PkxFXd3QJ1qAPS05Dy2UkVGYPm/asF1Ugt2Xyzmv4DHzO3+G7l+873C4XFFcJ9M5Je+eCMC7SSifgPTSur5QuA==
"@sapphire/async-queue@^1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.3.2.tgz#befe5f5025e2e317a9eba2d1a24ca5d2e4576f86"
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":
version "3.2.2"
resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.2.2.tgz#faacdc1b5f7c43145a71eddba917de2b707ef780"
integrity sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==
"@sindresorhus/is@^4.6.0":
version "4.6.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==
"@tokenizer/token@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276"
integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==
"@tsconfig/node10@^1.0.7":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
"@tsconfig/node12@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c"
integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==
version "1.0.11"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
"@tsconfig/node14@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2"
integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==
version "1.0.3"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
"@tsconfig/node16@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e"
integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==
version "1.0.3"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
"@types/bluebird@^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"
integrity sha512-UU2pvvjtxvugMdCpoG7If9EqbaeITyczSXymIA/h/Quzbf4HwvT1uBKDG0R42jzHlHGOsRqCRb+RP5Kj1RIBaQ==
"@types/i18n@^0.13.3":
version "0.13.3"
resolved "https://registry.yarnpkg.com/@types/i18n/-/i18n-0.13.3.tgz#88db5b33ccbbd57de54c8c2da01002e8fb48efc3"
integrity sha512-o6MlrxjGUDANSmMmQ6p/f7ea3+kJFeNWebxylKE3pLjWbIa0Npr/DT2Qf941oqovdopadAfV+PuBMTKg4p37NA==
"@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/i18n@^0.13.4":
version "0.13.4"
resolved "https://registry.yarnpkg.com/@types/i18n/-/i18n-0.13.4.tgz#fe3d27d08337f9d4a972d1f460d1471d6f79e163"
integrity sha512-PN4ZsplbpHZ2eaYixFNWkZKN51pcB02K2UKvqHVbrzq2jTO0sChPMuKKYAW1ZbElyHUvPgFeYsz9rqktChGyMw==
"@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"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a"
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":
version "0.0.1"
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"
integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==
"@types/validator@^13.7.3":
version "13.7.3"
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.3.tgz#3193c0a3c03a7d1189016c62b4fba4b149ef5e33"
integrity sha512-DNviAE5OUcZ5s+XEQHRhERLg8fOp8gSgvyJ4aaFASx5wwaObm+PBwTIMXiOFm1QrSee5oYwEAYb7LMzX2O88gA==
"@types/validator@^13.7.4":
version "13.7.4"
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.4.tgz#33cc949ee87dd47c63e35ba4ad94f6888852be04"
integrity sha512-uAaSWegu2lymY18l+s5nmcXu3sFeeTOl1zhSGoYzcr6T3wz1M+3OcW4UjfPhIhHGd13tIMRDsEpR+d8w/MexwQ==
"@types/ws@^8.5.3":
version "8.5.3"
@@ -416,9 +421,9 @@ call-bind@^1.0.0:
get-intrinsic "^1.0.2"
canvas@^2.5.0:
version "2.9.1"
resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.9.1.tgz#58ec841cba36cef0675bc7a74ebd1561f0b476b0"
integrity sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A==
version "2.9.3"
resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.9.3.tgz#8723c4f970442d4cdcedba5221579f9660a58bdb"
integrity sha512-WOUM7ghii5TV2rbhaZkh1youv/vW1/Canev6Yx6BG2W+1S07w8jKZqKkPnbiPpQEDsnJdN8ouDd7OvQEGXDcUw==
dependencies:
"@mapbox/node-pre-gyp" "^1.0.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"
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:
version "2.0.1"
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==
discord-api-types@^0.33.3:
version "0.33.3"
resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.33.3.tgz#882c39331871f619035917a1f80d486f605b3e7a"
integrity sha512-P3A1RJXKEDmGPHrFTN5+gYLsBPGUVGj+D3+fa3m0K/umc+LMfqGuEad+p7cNq7ry/icReVhS3bz9jvBvne/BRA==
version "0.33.5"
resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.33.5.tgz#6548b70520f7b944c60984dca4ab58654d664a12"
integrity sha512-dvO5M52v7m7Dy96+XUnzXNsQ/0npsYpU6dL205kAtEDueswoz3aU3bh1UMoK4cQmcGtB1YRyLKqp+DXi05lzFg==
discord-api-types@^0.35.0:
version "0.35.0"
resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.35.0.tgz#85c6ea95f27580ad13b7a08444b16cb31d41e5e2"
integrity sha512-aaIbVgPk7OA3y/HdG00tRM2nB8B3sSPH6KXgamBLRrcAJEQ8XZJNNKeRr0WVzw/lprdpKHKuTNX1DNrYnD5cOw==
discord-api-types@^0.36.2:
version "0.36.2"
resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.36.2.tgz#2362bc544837be965ec99a5919f900c9699a7028"
integrity sha512-TunPAvzwneK/m5fr4hxH3bMsrtI22nr9yjfHyo5NBGMjpsAauGNiGCmwoFf0oO3jSd2mZiKUvZwCKDaB166u2Q==
discord.js@^13.8.1:
version "13.8.1"
resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-13.8.1.tgz#37627154a887ac395ca72dd814bc7366776bb90a"
integrity sha512-jOsD+4tEZWWx0RHVyH+FBcqoTrsL+d5Mm5p+ULQOdU0qSaxhLNkWYig+yDHNZoND7nlkXX3qi+BW+gO5erWylg==
discord.js@14.0.1:
version "14.0.1"
resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.0.1.tgz#2de95035294b361689ec1c9de935f0b6c21923b1"
integrity sha512-MZ2cvvk1WCpTUCLcchX5e/mBM22JCPAYle76qEuAfQfVTFdxE2YoKgRWMwWm6s/OJdkXyzglRWj405hg4vikEg==
dependencies:
"@discordjs/builders" "^0.14.0"
"@discordjs/collection" "^0.7.0"
"@sapphire/async-queue" "^1.3.1"
"@types/node-fetch" "^2.6.1"
"@discordjs/builders" "^1.0.0"
"@discordjs/collection" "^1.0.0"
"@discordjs/rest" "^1.0.0"
"@sapphire/snowflake" "^3.2.2"
"@types/ws" "^8.5.3"
discord-api-types "^0.33.3"
form-data "^4.0.0"
node-fetch "^2.6.1"
ws "^8.7.0"
discord-api-types "^0.36.2"
fast-deep-equal "^3.1.3"
lodash.snakecase "^4.1.1"
tslib "^2.4.0"
undici "^5.7.0"
ws "^8.8.1"
dotenv@^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"
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:
version "7.0.1"
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"
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:
version "1.2.6"
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==
get-intrinsic@^1.0.2:
version "1.1.1"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
version "1.1.2"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598"
integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==
dependencies:
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.1"
has-symbols "^1.0.3"
glob-parent@~5.1.2:
version "5.1.2"
@@ -791,7 +793,7 @@ glob@^7.0.5, glob@^7.1.3:
once "^1.3.0"
path-is-absolute "^1.0.0"
has-symbols@^1.0.1:
has-symbols@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
@@ -828,6 +830,11 @@ i18n@^0.15.0:
math-interval-parser "^2.0.1"
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:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@@ -853,7 +860,7 @@ is-binary-path@~2.1.0:
dependencies:
binary-extensions "^2.0.0"
is-core-module@^2.8.1:
is-core-module@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
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"
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:
version "2.4.1"
resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.1.tgz#512c9eaef738044d1c619790ba0f806c80d9d3a9"
integrity sha512-7XB/tqc3VRbri9pRjU6E97mQ8vC27ivJ3lct4jhyT+n0JNDd4YKldFl0D75NqDp46hk8RC7Ma1Vjv/UPf67S+A==
version "2.4.2"
resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.2.tgz#a617983ac0334d0c3b942c34945380062795b47c"
integrity sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==
dependencies:
"@colors/colors" "1.5.0"
fecha "^4.2.0"
@@ -926,9 +943,9 @@ logform@^2.2.0:
triple-beam "^1.3.0"
logform@^2.3.2:
version "2.4.0"
resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe"
integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==
version "2.4.1"
resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.1.tgz#512c9eaef738044d1c619790ba0f806c80d9d3a9"
integrity sha512-7XB/tqc3VRbri9pRjU6E97mQ8vC27ivJ3lct4jhyT+n0JNDd4YKldFl0D75NqDp46hk8RC7Ma1Vjv/UPf67S+A==
dependencies:
"@colors/colors" "1.5.0"
fecha "^4.2.0"
@@ -1005,9 +1022,9 @@ minimist@^1.2.6:
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
minipass@^3.0.0:
version "3.1.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee"
integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==
version "3.3.4"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae"
integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==
dependencies:
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"
integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==
node-addon-api@^4.2.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
node-addon-api@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501"
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"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
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"
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:
version "2.3.1"
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"
integrity sha512-L6UsGHcT6i4wrQhFF1aPK+MNYgjRqR2tUoIqEY+CG1NqVkMjPRKzS37j9f8GiYPlD6wG9ruBj+q5Ax+bH8Ik1g==
prisma@^3.15.2:
version "3.15.2"
resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.15.2.tgz#4ebe32fb284da3ac60c49fbc16c75e56ecf32067"
integrity sha512-nMNSMZvtwrvoEQ/mui8L/aiCLZRCj5t6L3yujKpcDhIPk7garp8tL4nMx2+oYsN0FWBacevJhazfXAbV1kfBzA==
prisma@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.0.0.tgz#4ddb8fcd4f64d33aff8c198a6986dcce66dc8152"
integrity sha512-Dtsar03XpCBkcEb2ooGWO/WcgblDTLzGhPcustbehwlFXuTMliMDRzXsfygsgYwQoZnAUKRd1rhpvBNEUziOVw==
dependencies:
"@prisma/engines" "3.15.1-1.461d6a05159055555eb7dfb337c9fb271cbd4d7e"
"@prisma/engines" "3.16.0-49.da41d2bb3406da22087b849f0e911199ba4fbf11"
process-nextick-args@~2.0.0:
version "2.0.1"
@@ -1190,9 +1212,9 @@ q@1.x:
integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==
qs@^6.9.4:
version "6.10.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
dependencies:
side-channel "^1.0.4"
@@ -1208,7 +1230,7 @@ readable-stream@^3.4.0, readable-stream@^3.6.0:
readable-stream@~1.0.31:
version "1.0.34"
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:
core-util-is "~1.0.0"
inherits "~2.0.1"
@@ -1228,6 +1250,13 @@ readable-stream@~2.3.6:
string_decoder "~1.1.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:
version "3.6.0"
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:
version "2.1.1"
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:
version "1.22.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
dependencies:
is-core-module "^2.8.1"
is-core-module "^2.9.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
@@ -1298,7 +1327,7 @@ semver@^7.3.2, semver@^7.3.5:
set-blocking@^2.0.0:
version "2.0.0"
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:
version "1.0.4"
@@ -1331,7 +1360,7 @@ simple-get@^3.0.3:
simple-swizzle@^0.2.2:
version "0.2.2"
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:
is-arrayish "^0.3.1"
@@ -1351,7 +1380,7 @@ source-map@^0.6.0:
stack-trace@0.0.x:
version "0.0.10"
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:
version "4.2.3"
@@ -1372,7 +1401,7 @@ string_decoder@^1.1.1:
string_decoder@~0.10.x:
version "0.10.31"
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:
version "1.1.1"
@@ -1391,12 +1420,20 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
strip-bom@^3.0.0:
version "3.0.0"
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:
version "2.0.1"
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:
version "5.3.1"
@@ -1457,10 +1494,18 @@ to-regex-range@^5.0.1:
dependencies:
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:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
tree-kill@^1.2.2:
version "1.2.2"
@@ -1543,9 +1588,14 @@ underscore@^1.12.0:
integrity sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==
undici@^5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.4.0.tgz#c474fae02743d4788b96118d46008a24195024d2"
integrity sha512-A1SRXysDg7J+mVP46jF+9cKANw0kptqSFZ8tGyL+HBiv0K1spjxPX8Z4EGu+Eu6pjClJUBdnUPlxrOafR668/g==
version "5.5.1"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.5.1.tgz#baaf25844a99eaa0b22e1ef8d205bffe587c8f43"
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:
version "4.0.0"
@@ -1562,7 +1612,7 @@ utf-8-validate@^5.0.9:
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
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:
version "3.0.1"
@@ -1577,12 +1627,12 @@ validator@^13.7.0:
webidl-conversions@^3.0.0:
version "3.0.1"
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:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
@@ -1630,12 +1680,17 @@ wrap-ansi@^7.0.0:
wrappy@1:
version "1.0.2"
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:
version "8.7.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.7.0.tgz#eaf9d874b433aa00c0e0d8752532444875db3957"
integrity sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==
version "8.8.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769"
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:
version "4.0.2"