Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ace8170323 | ||
|
|
1374bcc25d | ||
|
|
757d6e3501 | ||
|
|
b023f7134d | ||
|
|
9f191e6e80 | ||
|
|
7ca5041725 | ||
|
|
a045be3f2f | ||
|
|
c569661481 | ||
|
|
e8cd52d315 | ||
|
|
d4d2930f58 | ||
|
|
0f6ad29a3c | ||
|
|
e4397c3f54 |
No files matched your search
@@ -48,19 +48,19 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
with:
|
||||
images: unknowableshade/quetza-bot
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
||||
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
"printWidth": 100,
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"endOfLine": "crlf",
|
||||
"endOfLine": "lf",
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.json, **/*.yml"],
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
FROM node:18-alpine as builder
|
||||
|
||||
RUN apk add --no-cache python3
|
||||
RUN apk add --no-cache make
|
||||
RUN apk add --no-cache gcc
|
||||
RUN apk add --no-cache build-base
|
||||
|
||||
RUN npm install -g pnpm
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
@@ -19,6 +24,9 @@ RUN apk add --no-cache bash
|
||||
RUN apk add --no-cache curl
|
||||
RUN apk add --no-cache ffmpeg
|
||||
RUN apk add --no-cache python3
|
||||
RUN apk add --no-cache make
|
||||
RUN apk add --no-cache gcc
|
||||
RUN apk add --no-cache build-base
|
||||
|
||||
RUN npm install -g pnpm
|
||||
|
||||
|
||||
+35
-28
@@ -1,54 +1,61 @@
|
||||
{
|
||||
"name": "quetza-bot",
|
||||
"engines": {
|
||||
"node": "18.x.x",
|
||||
"npm": "*"
|
||||
},
|
||||
"version": "4.2.2",
|
||||
"description": "Discord multipurpose bot",
|
||||
"type": "module",
|
||||
"version": "4.0.0",
|
||||
"description": "Discord multifunctional bot",
|
||||
"main": "./dist/index.js",
|
||||
"scripts": {
|
||||
"start": "node ./dist/index.js",
|
||||
"build": "pnpm exec tsc --build",
|
||||
"clean": "rm -rf ./dist && pnpm run build",
|
||||
"lint": "pnpm exec eslint . && pnpm exec prettier --check .",
|
||||
"format": "pnpm exec eslint --fix . && pnpm exec prettier --write .",
|
||||
"preinstall": "pnpm dlx only-allow pnpm"
|
||||
},
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"discord",
|
||||
"bot",
|
||||
"music"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/unknowableshade/quetza-bot.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "unknowableshade",
|
||||
"email": "60275123+unknowableshade@users.noreply.github.com",
|
||||
"url": "https://unknowableshade.ru"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/unknowableshade/quetza-bot.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/unknowableshade/quetza-bot/issues"
|
||||
},
|
||||
"readme": "https://github.com/unknowableshade/quetza-bot#readme",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"start": "node dist/index.js",
|
||||
"build": "pnpm exec tsc --build",
|
||||
"clean": "pnpm exec tsc --build --clean",
|
||||
"lint": "pnpm exec eslint --fix . && pnpm exec prettier --write .",
|
||||
"preinstall": "pnpx only-allow pnpm"
|
||||
"engines": {
|
||||
"node": "18.x.x",
|
||||
"npm": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@discordjs/opus": "^0.9.0",
|
||||
"@discordjs/voice": "^0.16.0",
|
||||
"@prisma/client": "4.13.0",
|
||||
"discord-ytdl-core": "^5.0.4",
|
||||
"@prisma/client": "5.0.0",
|
||||
"discord.js": "^14.11.0",
|
||||
"prism-media": "^1.3.5",
|
||||
"tweetnacl": "^1.0.3",
|
||||
"winston": "^3.8.2",
|
||||
"winston": "^3.10.0",
|
||||
"ytdl-core": "^4.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.16.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.5",
|
||||
"@typescript-eslint/parser": "^5.59.5",
|
||||
"eslint": "^8.40.0",
|
||||
"@types/node": "^20.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"eslint": "^8.44.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"prettier": "^2.8.8",
|
||||
"prisma": "^4.13.0",
|
||||
"typescript": "^4.9.5"
|
||||
"mocha": "^10.2.0",
|
||||
"prettier": "^3.0.0",
|
||||
"prisma": "^5.0.0",
|
||||
"typescript": "^5.1.6"
|
||||
}
|
||||
}
|
||||
Generated
+634
-295
File diff suppressed because it is too large.
Load diff
+4
-1
@@ -58,7 +58,10 @@ export default class Client extends DiscordClient {
|
||||
});
|
||||
|
||||
importModule(path.join(data.rootDir, MODULE_EVENTS), (event: Event) => {
|
||||
this.on(event.name, (...eventee: unknown[]) => event.execute(this, eventee));
|
||||
this.on(
|
||||
event.name,
|
||||
async (...eventee: unknown[]) => await event.execute(this, eventee)
|
||||
);
|
||||
events.push(event.name);
|
||||
});
|
||||
|
||||
|
||||
+1
-8
@@ -4,17 +4,10 @@ const logger = winston.createLogger({
|
||||
level: "info",
|
||||
format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
|
||||
transports: [
|
||||
new winston.transports.Console({ format: winston.format.simple() }),
|
||||
new winston.transports.File({ filename: "logs/error.log", level: "error" }),
|
||||
new winston.transports.File({ filename: "logs/combined.log" })
|
||||
]
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
logger.add(
|
||||
new winston.transports.Console({
|
||||
format: winston.format.simple()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export default logger;
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction): Promise<void> {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction): Promise<void> {
|
||||
if (!interaction.isChatInputCommand()) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction): Promise<void> {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction): Promise<void> {
|
||||
if (!interaction.isChatInputCommand()) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -6,13 +6,23 @@ import logger from "../../../lib/logger.js";
|
||||
async function execute(client: Client, eventee: [Interaction]): Promise<void> {
|
||||
const [interaction] = eventee;
|
||||
|
||||
logger.info("Interaction created.", interaction);
|
||||
|
||||
if (interaction.isCommand()) {
|
||||
if (interaction.isChatInputCommand()) {
|
||||
const command = client.commands.get(interaction.commandName);
|
||||
|
||||
if (command) {
|
||||
command.execute(client, interaction);
|
||||
if (!command) {
|
||||
logger.error(`No command matching ${interaction.commandName} was found.`, {
|
||||
interaction
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`Interaction ${interaction.commandName} was created.`, interaction);
|
||||
|
||||
await command.execute(client, interaction);
|
||||
} catch (error) {
|
||||
logger.error("Interaction error occured. ", { error, interaction });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { fileURLToPath } from "url";
|
||||
const name = "core";
|
||||
const description = "Quetza core functionality";
|
||||
const author = "unknowableshade";
|
||||
const tag = "4.2.0";
|
||||
const tag = "4.2.2";
|
||||
|
||||
const rootDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction): Promise<void> {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction): Promise<void> {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction): Promise<void> {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction): Promise<void> {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
@@ -6,7 +6,7 @@ import Filter from "../lib/filter.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const pos = interaction.options.getNumber("position", true);
|
||||
const query = interaction.options.getString("query", true);
|
||||
const pos = interaction.options.getInteger("position", true);
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
@@ -6,7 +6,7 @@ import replies from "../lib/replies.js";
|
||||
import { LoopOption } from "../lib/types.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
@@ -43,7 +43,7 @@ async function execute(client: Client, interaction: Interaction) {
|
||||
}
|
||||
|
||||
if (!player.resource) {
|
||||
player.play();
|
||||
await player.play();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
ButtonBuilder,
|
||||
ButtonInteraction,
|
||||
ButtonStyle,
|
||||
Interaction,
|
||||
ChatInputCommandInteraction,
|
||||
MessageActionRowComponentBuilder,
|
||||
SlashCommandBuilder
|
||||
} from "discord.js";
|
||||
@@ -13,7 +13,7 @@ import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interaction, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
import { controller } from "../module.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction) {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild() || !interaction.channel) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
|
||||
+160
-17
@@ -1,13 +1,29 @@
|
||||
import { execFile } from "child_process";
|
||||
import path from "path";
|
||||
import prismMedia from "prism-media";
|
||||
import { Duplex, Readable } from "stream";
|
||||
import { promisify } from "util";
|
||||
import ytdl from "ytdl-core";
|
||||
|
||||
import config from "../../../config.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import { StreamOptions, YtdlDump, YtdlResult, YtdlStreamOptions } from "./types.js";
|
||||
|
||||
const { opus: Opus, FFmpeg } = prismMedia;
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
const SEARCH_DEPTH = 5;
|
||||
const YTDL_EVENTS = [
|
||||
"info",
|
||||
"progress",
|
||||
"abort",
|
||||
"request",
|
||||
"response",
|
||||
"error",
|
||||
"redirect",
|
||||
"retry",
|
||||
"reconnect"
|
||||
];
|
||||
|
||||
function isURL(url: string): boolean {
|
||||
return /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)$/.test(
|
||||
@@ -15,23 +31,6 @@ function isURL(url: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
interface YtdlDump {
|
||||
id: string;
|
||||
original_url: string;
|
||||
title?: string;
|
||||
uploader?: string;
|
||||
duration?: number;
|
||||
extractor_key: string;
|
||||
thumbnail?: string;
|
||||
}
|
||||
|
||||
interface YtdlResult {
|
||||
url: string;
|
||||
title: string;
|
||||
thumbnail: string;
|
||||
duration: number;
|
||||
}
|
||||
|
||||
async function fetchInfoRecursive(query: string, depth = 0): Promise<YtdlResult[]> {
|
||||
if (depth === SEARCH_DEPTH) {
|
||||
logger.warn("yt-dlp failed to find anything.", { query });
|
||||
@@ -100,3 +99,147 @@ export async function fetchInfo(query: string): Promise<YtdlResult[]> {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function ytdlStream(url: string, options?: YtdlStreamOptions) {
|
||||
logger.info("Created ytdl stream.", { url, options });
|
||||
|
||||
let ffmpegArgs: string[] = [
|
||||
"-analyzeduration",
|
||||
"0",
|
||||
"-loglevel",
|
||||
"0",
|
||||
"-f",
|
||||
`${options?.fmt ? options.fmt : "s16le"}`,
|
||||
"-ar",
|
||||
"48000",
|
||||
"-ac",
|
||||
"2"
|
||||
];
|
||||
|
||||
if (options?.seek) {
|
||||
ffmpegArgs.unshift("-ss", options.seek.toString());
|
||||
}
|
||||
|
||||
if (options?.encoderArgs) {
|
||||
ffmpegArgs = ffmpegArgs.concat(options.encoderArgs);
|
||||
}
|
||||
|
||||
const transcoder = new FFmpeg({
|
||||
args: ffmpegArgs
|
||||
});
|
||||
|
||||
const inputStream = ytdl(url, options);
|
||||
const transcoderStream = inputStream.pipe(transcoder);
|
||||
|
||||
if (options && !options.opusEncoded) {
|
||||
YTDL_EVENTS.forEach((event) =>
|
||||
inputStream.on(event, (...args) => transcoderStream.emit(event, ...args))
|
||||
);
|
||||
inputStream.on("error", () => transcoder.destroy());
|
||||
transcoderStream.on("close", () => transcoder.destroy());
|
||||
|
||||
return transcoderStream;
|
||||
}
|
||||
|
||||
const opus = new Opus.Encoder({
|
||||
rate: 48000,
|
||||
channels: 2,
|
||||
frameSize: 960
|
||||
});
|
||||
|
||||
const outputStream = transcoderStream.pipe(opus);
|
||||
|
||||
transcoderStream.on("error", (e) => outputStream.emit("error", e));
|
||||
|
||||
YTDL_EVENTS.forEach((event) =>
|
||||
inputStream.on(event, (...args) => outputStream.emit(event, ...args))
|
||||
);
|
||||
|
||||
outputStream.on("close", () => {
|
||||
transcoder.destroy();
|
||||
opus.destroy();
|
||||
});
|
||||
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
export function arbitraryStream(stream: string | Readable | Duplex, options?: StreamOptions) {
|
||||
logger.info("Created arbitrary stream.", { stream, options });
|
||||
|
||||
let ffmpegArgs: string[];
|
||||
|
||||
if (typeof stream === "string") {
|
||||
ffmpegArgs = [
|
||||
"-reconnect",
|
||||
"1",
|
||||
"-reconnect_streamed",
|
||||
"1",
|
||||
"-reconnect_delay_max",
|
||||
"5",
|
||||
"-i",
|
||||
stream,
|
||||
"-analyzeduration",
|
||||
"0",
|
||||
"-loglevel",
|
||||
"0",
|
||||
"-f",
|
||||
`${options?.fmt ? options.fmt : "s16le"}`,
|
||||
"-ar",
|
||||
"48000",
|
||||
"-ac",
|
||||
"2"
|
||||
];
|
||||
} else {
|
||||
ffmpegArgs = [
|
||||
"-analyzeduration",
|
||||
"0",
|
||||
"-loglevel",
|
||||
"0",
|
||||
"-f",
|
||||
`${options?.fmt ? options.fmt : "s16le"}`,
|
||||
"-ar",
|
||||
"48000",
|
||||
"-ac",
|
||||
"2"
|
||||
];
|
||||
}
|
||||
|
||||
if (options?.seek) {
|
||||
ffmpegArgs.unshift("-ss", options.seek.toString());
|
||||
}
|
||||
|
||||
if (options?.encoderArgs) {
|
||||
ffmpegArgs = ffmpegArgs.concat(options.encoderArgs);
|
||||
}
|
||||
|
||||
let transcoder = new FFmpeg({
|
||||
args: ffmpegArgs
|
||||
});
|
||||
|
||||
if (typeof stream !== "string") {
|
||||
transcoder = stream.pipe(transcoder);
|
||||
|
||||
stream.on("error", () => transcoder.destroy());
|
||||
}
|
||||
|
||||
if (options && !options.opusEncoded) {
|
||||
transcoder.on("close", () => transcoder.destroy());
|
||||
|
||||
return transcoder;
|
||||
}
|
||||
|
||||
const opus = new Opus.Encoder({
|
||||
rate: 48000,
|
||||
channels: 2,
|
||||
frameSize: 960
|
||||
});
|
||||
|
||||
const outputStream = transcoder.pipe(opus);
|
||||
|
||||
outputStream.on("close", () => {
|
||||
transcoder.destroy();
|
||||
opus.destroy();
|
||||
});
|
||||
|
||||
return outputStream;
|
||||
}
|
||||
@@ -6,7 +6,7 @@ const STATUSBAR_LENGTH = 15;
|
||||
|
||||
export function statusBarGenerator(resource?: AudioResource<Track>): string {
|
||||
const duration = resource?.metadata.duration || 1;
|
||||
const playback = Math.min(resource?.playbackDuration || 0 / 1000, duration);
|
||||
const playback = Math.min((resource?.playbackDuration || 0) / 1000, duration);
|
||||
|
||||
const filled = Array<string>(Math.floor((STATUSBAR_LENGTH * playback) / duration)).fill("🔹");
|
||||
|
||||
|
||||
@@ -9,9 +9,8 @@ import {
|
||||
VoiceConnection
|
||||
} from "@discordjs/voice";
|
||||
import { Guild, GuildTextBasedChannel, Message, User, VoiceBasedChannel } from "discord.js";
|
||||
import ytdl from "discord-ytdl-core";
|
||||
|
||||
import { fetchInfo } from "./fetch.js";
|
||||
import { fetchInfo, ytdlStream } from "./fetch.js";
|
||||
import Filter from "./filter.js";
|
||||
import { largestCommonSequence } from "./misc.js";
|
||||
import Music from "./music.js";
|
||||
@@ -77,7 +76,7 @@ export default class Player {
|
||||
}
|
||||
|
||||
private resourceGenerator(track: Track, seek?: number) {
|
||||
const stream = ytdl(track.url, {
|
||||
const stream = ytdlStream(track.url, {
|
||||
opusEncoded: true,
|
||||
filter: "audioonly",
|
||||
highWaterMark: 1 << 25,
|
||||
@@ -134,7 +133,7 @@ export default class Player {
|
||||
});
|
||||
}
|
||||
|
||||
public play(seek?: number): void {
|
||||
public async play(seek?: number): Promise<void> {
|
||||
const track = this.queue.current();
|
||||
|
||||
if (!this.connection_ || !track) {
|
||||
@@ -145,7 +144,7 @@ export default class Player {
|
||||
|
||||
this.connection_.subscribe(this.player_);
|
||||
|
||||
this.messageResolvable(track);
|
||||
await this.messageResolvable(track);
|
||||
}
|
||||
|
||||
public connect(channel: VoiceBasedChannel): void {
|
||||
|
||||
@@ -63,20 +63,22 @@ export default class Queue {
|
||||
}
|
||||
|
||||
public push(tracks: Track[], index?: number): Track {
|
||||
if (!index || index >= this.tracks_.length) {
|
||||
this.tracks_ = this.tracks_.concat(tracks);
|
||||
} else {
|
||||
this.tracks_.splice(Math.max(0, index), 0, ...tracks);
|
||||
if (this.position_ >= index) {
|
||||
this.position_ += tracks.length;
|
||||
}
|
||||
}
|
||||
|
||||
for (const track of tracks) {
|
||||
this.duration_ += track.duration;
|
||||
}
|
||||
|
||||
return this.tracks_[index || this.tracks_.length - 1];
|
||||
if (index === undefined || index >= this.tracks_.length) {
|
||||
this.tracks_ = this.tracks_.concat(tracks);
|
||||
|
||||
return this.tracks_[this.tracks_.length - 1];
|
||||
}
|
||||
|
||||
this.tracks_.splice(Math.max(0, index), 0, ...tracks);
|
||||
if (this.position_ >= index) {
|
||||
this.position_ += tracks.length;
|
||||
}
|
||||
|
||||
return this.tracks_[Math.max(0, index)];
|
||||
}
|
||||
|
||||
public pop(query: number): Track | undefined {
|
||||
|
||||
@@ -38,7 +38,7 @@ const replies = {
|
||||
.setColor(config.colors.default)
|
||||
.setAuthor({
|
||||
iconURL: track.requester.avatarURL() ?? undefined,
|
||||
name: track.requester.tag
|
||||
name: track.requester.username
|
||||
})
|
||||
.setTitle(track.title)
|
||||
.setURL(track.url)
|
||||
@@ -202,7 +202,7 @@ const replies = {
|
||||
.setColor(config.colors.success)
|
||||
.setAuthor({
|
||||
iconURL: track.requester.avatarURL() ?? undefined,
|
||||
name: track.requester.tag
|
||||
name: track.requester.username
|
||||
})
|
||||
.setTitle(track.title)
|
||||
.setURL(track.url)
|
||||
@@ -261,7 +261,7 @@ const replies = {
|
||||
|
||||
embed
|
||||
.setColor(config.colors.success)
|
||||
.setDescription("Playback can experience some lags during intial transformation.");
|
||||
.setDescription("Playback can experience some lags during initial transformation.");
|
||||
|
||||
return { embeds: [embed] };
|
||||
},
|
||||
@@ -290,7 +290,7 @@ const replies = {
|
||||
.setTitle(`${queue.position + 1}. ${resource.metadata.title}`)
|
||||
.setAuthor({
|
||||
iconURL: resource.metadata.requester.avatarURL() ?? undefined,
|
||||
name: resource.metadata.requester.tag
|
||||
name: resource.metadata.requester.username
|
||||
})
|
||||
.setURL(resource.metadata.url)
|
||||
.setDescription(statusBarGenerator(resource))
|
||||
@@ -306,7 +306,7 @@ const replies = {
|
||||
embed.setColor(config.colors.info).setFields(
|
||||
queue.tracks.slice(position, position + 10).map((value, index) => ({
|
||||
name: `${position + index + 1}. ${value.title.padEnd(35)}`,
|
||||
value: `[${toISOTime(value.duration)}] | ${value.requester.tag}`
|
||||
value: `[${toISOTime(value.duration)}] | ${value.requester.username}`
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -320,7 +320,7 @@ const replies = {
|
||||
.setTitle(`${player.queue.position + 1}. ${player.resource.metadata.title}`)
|
||||
.setAuthor({
|
||||
iconURL: player.resource.metadata.requester.avatarURL() ?? undefined,
|
||||
name: player.resource.metadata.requester.tag
|
||||
name: player.resource.metadata.requester.username
|
||||
})
|
||||
.setURL(player.resource.metadata.url)
|
||||
.setDescription(statusBarGenerator(player.resource))
|
||||
|
||||
+27
-245
@@ -1,4 +1,5 @@
|
||||
import { User } from "discord.js";
|
||||
import { downloadOptions } from "ytdl-core";
|
||||
|
||||
export interface Track {
|
||||
url: string;
|
||||
@@ -10,252 +11,33 @@ export interface Track {
|
||||
|
||||
export type LoopOption = "LOOP" | "SONG" | "NONE" | "AUTO";
|
||||
|
||||
export interface YtdlArgs {
|
||||
help?: boolean;
|
||||
version?: boolean;
|
||||
update?: boolean;
|
||||
ignoreErrors?: boolean;
|
||||
abortOnError?: boolean;
|
||||
dumpUserAgent?: boolean;
|
||||
listExtractors?: boolean;
|
||||
extractorDescriptions?: boolean;
|
||||
forceGenericExtractor?: boolean;
|
||||
defaultSearch?: string;
|
||||
igonreConfig?: boolean;
|
||||
configLocation?: string;
|
||||
flatPlaylist?: boolean;
|
||||
markWatched?: boolean;
|
||||
noColor?: boolean;
|
||||
proxy?: string;
|
||||
socketTimeout?: number;
|
||||
sourceAddress?: string;
|
||||
forceIpv4?: boolean;
|
||||
forceIpv6?: boolean;
|
||||
geoVerificationProxy?: string;
|
||||
geoBypass?: boolean;
|
||||
geoBypassCountry?: string;
|
||||
geoBypassIpBlock?: string;
|
||||
playlistStart?: number;
|
||||
playlistEnd?: number | "last";
|
||||
playlistItems?: string;
|
||||
matchTitle?: string;
|
||||
rejectTitle?: string;
|
||||
maxDownloads?: number;
|
||||
minFilesize?: string;
|
||||
maxFilesize?: string;
|
||||
date?: string;
|
||||
datebefore?: string;
|
||||
dateafter?: string;
|
||||
minViews?: number;
|
||||
maxViews?: number;
|
||||
matchFilter?: string;
|
||||
noPlaylist?: boolean;
|
||||
yesPlaylist?: boolean;
|
||||
ageLimit?: number;
|
||||
downloadArchive?: string;
|
||||
includeAds?: boolean;
|
||||
limitRate?: string;
|
||||
retries?: number | "infinite";
|
||||
skipUnavailableFragments?: boolean;
|
||||
abortOnUnavailableFragment?: boolean;
|
||||
keepFragments?: boolean;
|
||||
bufferSize?: string;
|
||||
noResizeBuffer?: boolean;
|
||||
httpChunkSize?: string;
|
||||
playlistReverse?: boolean;
|
||||
playlistRandom?: boolean;
|
||||
xattrSetFilesize?: boolean;
|
||||
hlsPreferNative?: boolean;
|
||||
hlsPreferFfmpeg?: boolean;
|
||||
hlsUseMpegts?: boolean;
|
||||
externalDownloader?: string;
|
||||
externalDownloaderArgs?: string;
|
||||
batchFile?: string;
|
||||
id?: boolean;
|
||||
output?: string;
|
||||
outputNaPlaceholder?: string;
|
||||
autonumberStart?: number;
|
||||
restrictFilenames?: boolean;
|
||||
noOverwrites?: boolean;
|
||||
continue?: boolean;
|
||||
noPart?: boolean;
|
||||
noMtime?: boolean;
|
||||
writeDescription?: boolean;
|
||||
writeInfoJson?: boolean;
|
||||
writeAnnotations?: boolean;
|
||||
loadInfoJson?: string;
|
||||
cookies?: string;
|
||||
cacheDir?: string;
|
||||
noCacheDir?: boolean;
|
||||
rmCacheDir?: boolean;
|
||||
writeThumbnail?: boolean;
|
||||
writeAllThumbnails?: boolean;
|
||||
listThumbnails?: boolean;
|
||||
quiet?: boolean;
|
||||
noWarnings?: boolean;
|
||||
simulate?: boolean;
|
||||
skipDownload?: boolean;
|
||||
getUrl?: boolean;
|
||||
getTitle?: boolean;
|
||||
getId?: boolean;
|
||||
getThumbnail?: boolean;
|
||||
getDuration?: boolean;
|
||||
getFilename?: boolean;
|
||||
getFormat?: boolean;
|
||||
dumpJson?: boolean;
|
||||
dumpSingleJson?: boolean;
|
||||
printJson?: boolean;
|
||||
newline?: boolean;
|
||||
noProgress?: boolean;
|
||||
consoleTitle?: boolean;
|
||||
verbose?: boolean;
|
||||
dumpPages?: boolean;
|
||||
writePages?: boolean;
|
||||
printTraffic?: boolean;
|
||||
callHome?: boolean;
|
||||
encoding?: string;
|
||||
noCheckCertificate?: boolean;
|
||||
preferInsecure?: boolean;
|
||||
userAgent?: string;
|
||||
referer?: string;
|
||||
addHeader?: string;
|
||||
bidiWorkaround?: boolean;
|
||||
sleepInterval?: number;
|
||||
maxSleepInterval?: number;
|
||||
format?: string;
|
||||
allFormats?: boolean;
|
||||
preferFreeFormats?: boolean;
|
||||
listFormats?: boolean;
|
||||
youtubeSkipDashManifest?: boolean;
|
||||
mergeOutputFormat?: string;
|
||||
writeSub?: boolean;
|
||||
writeAutoSub?: boolean;
|
||||
allSubs?: boolean;
|
||||
listSubs?: boolean;
|
||||
subFormat?: string;
|
||||
subLang?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
twofactor?: string;
|
||||
netrc?: boolean;
|
||||
videoPassword?: string;
|
||||
apMso?: string;
|
||||
apUsername?: string;
|
||||
apPassword?: string;
|
||||
apListMso?: boolean;
|
||||
extractAudio?: boolean;
|
||||
audioFormat?: string;
|
||||
audioQuality?: number;
|
||||
recodeVideo?: string;
|
||||
postprocessorArgs?: string;
|
||||
keepVideo?: boolean;
|
||||
noPostOverwrites?: boolean;
|
||||
embedSubs?: boolean;
|
||||
embedThumbnail?: boolean;
|
||||
addMetadata?: boolean;
|
||||
metadataFromFile?: string;
|
||||
xattrs?: boolean;
|
||||
fixup?: string;
|
||||
preferAvconv?: boolean;
|
||||
preferFfmpeg?: boolean;
|
||||
ffmpegLocation?: string;
|
||||
exec?: string;
|
||||
convertSubs?: string;
|
||||
}
|
||||
|
||||
interface YtdlFormat {
|
||||
asr: number;
|
||||
filesize: number;
|
||||
format_id: string;
|
||||
format_note: string;
|
||||
fps: number;
|
||||
height: number;
|
||||
quality: number;
|
||||
tbr: number;
|
||||
url: string;
|
||||
width: number;
|
||||
ext: string;
|
||||
vcodec: string;
|
||||
acodec: string;
|
||||
abr: number;
|
||||
downloader_options: unknown;
|
||||
container: string;
|
||||
format: string;
|
||||
protocol: string;
|
||||
http_headers: unknown;
|
||||
}
|
||||
|
||||
interface YtdlThumbnail {
|
||||
height: number;
|
||||
url: string;
|
||||
width: number;
|
||||
resolution: string;
|
||||
export interface YtdlDump {
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface YtdlEntry {
|
||||
uploader: string;
|
||||
duration: number;
|
||||
id: string;
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface YtdlResponse {
|
||||
id: string;
|
||||
title: string;
|
||||
formats: YtdlFormat[];
|
||||
thumbnails: YtdlThumbnail[];
|
||||
description: string;
|
||||
upload_date: string;
|
||||
uploader: string;
|
||||
uploader_id: string;
|
||||
uploader_url: string;
|
||||
channel_id: string;
|
||||
channel_url: string;
|
||||
duration: number;
|
||||
view_count: number;
|
||||
average_rating: number;
|
||||
age_limit: number;
|
||||
webpage_url: string;
|
||||
categories: string[];
|
||||
tags: string[];
|
||||
is_live: boolean;
|
||||
like_count: number;
|
||||
dislike_count: number;
|
||||
channel: string;
|
||||
track: string;
|
||||
artist: string;
|
||||
creator: string;
|
||||
alt_title: string;
|
||||
extractor: string;
|
||||
webpage_url_basename: string;
|
||||
original_url: string;
|
||||
title?: string;
|
||||
uploader?: string;
|
||||
duration?: number;
|
||||
extractor_key: string;
|
||||
playlist: string;
|
||||
playlist_index: number;
|
||||
thumbnail?: string;
|
||||
}
|
||||
|
||||
export interface YtdlResult {
|
||||
url: string;
|
||||
title: string;
|
||||
thumbnail: string;
|
||||
display_id: string;
|
||||
requested_subtitles: unknown;
|
||||
asr: number;
|
||||
filesize: number;
|
||||
format_id: string;
|
||||
format_note: string;
|
||||
fps: number;
|
||||
height: number;
|
||||
quality: number;
|
||||
tbr: number;
|
||||
url: string;
|
||||
width: number;
|
||||
ext: string;
|
||||
vcodec: string;
|
||||
acodec: string;
|
||||
abr: number;
|
||||
downloader_options: { http_chunk_size: number };
|
||||
container: string;
|
||||
format: string;
|
||||
protocol: string;
|
||||
http_headers: unknown;
|
||||
fulltitle: string;
|
||||
_filename: string;
|
||||
entries: YtdlEntry[];
|
||||
duration: number;
|
||||
}
|
||||
|
||||
export interface YtdlStreamOptions extends downloadOptions {
|
||||
seek?: number;
|
||||
encoderArgs?: string[];
|
||||
fmt?: string;
|
||||
opusEncoded?: boolean;
|
||||
}
|
||||
|
||||
export interface StreamOptions {
|
||||
seek?: number;
|
||||
encoderArgs?: string[];
|
||||
fmt?: string;
|
||||
opusEncoded?: boolean;
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
import { Events, Interaction, PermissionFlagsBits, SlashCommandBuilder } from "discord.js";
|
||||
import {
|
||||
ChatInputCommandInteraction,
|
||||
Events,
|
||||
PermissionFlagsBits,
|
||||
SlashCommandBuilder
|
||||
} from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction): Promise<void> {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild()) {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction): Promise<void> {
|
||||
if (!interaction.inCachedGuild()) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
return;
|
||||
@@ -49,7 +54,7 @@ async function execute(client: Client, interaction: Interaction): Promise<void>
|
||||
|
||||
await interaction.editReply(replies.auto("add role to a member", event));
|
||||
} catch (error) {
|
||||
logger.error("Error occured while quering db.", { interaction });
|
||||
logger.error("Error occured while quering db.", { error });
|
||||
} finally {
|
||||
await client.db.$disconnect();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Interaction, PermissionFlagsBits, SlashCommandBuilder } from "discord.js";
|
||||
import { ChatInputCommandInteraction, PermissionFlagsBits, SlashCommandBuilder } from "discord.js";
|
||||
|
||||
import Client from "../../../lib/client.js";
|
||||
import logger from "../../../lib/logger.js";
|
||||
import replies from "../lib/replies.js";
|
||||
|
||||
async function execute(client: Client, interaction: Interaction): Promise<void> {
|
||||
async function execute(client: Client, interaction: ChatInputCommandInteraction): Promise<void> {
|
||||
if (!interaction.isChatInputCommand() || !interaction.inCachedGuild()) {
|
||||
logger.warn("Interaction rejected.", { interaction });
|
||||
|
||||
@@ -20,7 +20,7 @@ async function execute(client: Client, interaction: Interaction): Promise<void>
|
||||
const member = await interaction.guild.members.fetch(user).catch(() => undefined);
|
||||
|
||||
if (member) {
|
||||
await interaction.reply(replies.roleList(user.tag, member.roles.cache));
|
||||
await interaction.reply(replies.roleList(user.username, member.roles.cache));
|
||||
} else {
|
||||
await interaction.reply(replies.notMember("edited", user));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ const replies = {
|
||||
notMember: (action: string, user: User): InteractionReplyOptions => {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor("Blue")
|
||||
.setTitle(`${user.tag} cannot be ${action}`)
|
||||
.setTitle(`${user.username} cannot be ${action}`)
|
||||
.setDescription("They're not a guild member.")
|
||||
.setTimestamp();
|
||||
|
||||
@@ -39,7 +39,7 @@ const replies = {
|
||||
): InteractionReplyOptions => {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(config.colors.success)
|
||||
.setTitle(`Role ${roleName} was ${action} user ${user.tag}`)
|
||||
.setTitle(`Role ${roleName} was ${action} user ${user.username}`)
|
||||
.setDescription(reason ?? "No reason provided.")
|
||||
.setTimestamp();
|
||||
|
||||
|
||||
Reference in new issue
Block a user