feat: rewrite config command

chore: updated depends

fix: deprecated warnings
use new i18n from client
This commit is contained in:
Jonny_Bro 2025-03-06 10:58:01 +05:00
parent 7f5a42b159
commit 1ec5fc66c7
No known key found for this signature in database
GPG key ID: A18808B38428F7C4
24 changed files with 877 additions and 1596 deletions

View file

@ -1,22 +0,0 @@
/* eslint-disable no-unused-vars */
import { sep } from "path";
class BaseCommand {
constructor(options, client) {
/**
* @type {import("discord.js").SlashCommandBuilder | import("discord.js").ContextMenuCommandBuilder | import("discord.js").ApplicationCommandData}
*/
this.command = options.command;
/**
* @type {Boolean}
*/
this.ownerOnly = (options.ownerOnly === true ? true : false) || false;
this.dirname = options.dirname || false;
/**
* @type {String}
*/
this.category = this.dirname ? this.dirname.split(sep)[parseInt(this.dirname.split(sep).length - 1, 10)] : "Other";
}
}
export default BaseCommand;

View file

@ -1,14 +0,0 @@
class BaseEvent {
constructor(options) {
/**
* @type {String}
*/
this.name = options.name;
/**
* @type {Boolean}
*/
this.once = options.once;
}
}
export default BaseEvent;

View file

@ -1,388 +0,0 @@
import { Client, Collection, SlashCommandBuilder, ContextMenuCommandBuilder, EmbedBuilder, PermissionsBitField, ChannelType } from "discord.js";
import { GiveawaysManager } from "discord-giveaways";
import { REST } from "@discordjs/rest";
import { Player } from "discord-player";
import { SpotifyExtractor } from "@discord-player/extractor";
import { YoutubeiExtractor } from "discord-player-youtubei";
import { Routes } from "discord-api-types/v10";
import { join, sep } from "path";
import { promises as fs } from "fs";
import { setTimeout } from "timers/promises";
import mongoose from "mongoose";
import * as emojis from "../../emojis.json";
import langs from "../languages/language-meta.js";
import logger from "../helpers/logger.js";
import * as funcs from "../helpers/functions.js";
import BaseEvent from "./BaseEvent.js";
import BaseCommand from "./BaseCommand.js";
import guild from "./Guild.js";
import user from "./User.js";
import member from "./Member.js";
class JaBaClient extends Client {
constructor(options) {
super(options);
this.config = config;
this.customEmojis = emojis;
this.languages = langs;
this.commands = new Collection();
this.logger = logger;
this.wait = setTimeout;
this.functions = funcs;
this.guildsData = guild.default;
this.usersData = user.default;
this.membersData = member.default;
this.databaseCache = {};
this.databaseCache.users = new Collection();
this.databaseCache.guilds = new Collection();
this.databaseCache.members = new Collection();
this.databaseCache.usersReminds = new Collection();
}
/**
* Initializes the client by logging in with the provided token and connecting to the MongoDB database.
*
* This method is called during the client's startup process to set up the necessary connections and resources.
*
* @returns {Promise<void>} A Promise that resolves when the client is fully initialized.
*/
async init() {
this.player = new Player(this);
await this.player.extractors.register(YoutubeiExtractor, {
authentication: this.config.youtubeCookie,
streamOptions: {
useClient: "IOS",
highWaterMark: 2 * 1024 * 1024, // 2MB, default is 512 KB (512 * 1024)
},
});
await this.player.extractors.register(SpotifyExtractor, {
clientId: this.config.spotify.clientId,
clientSecret: this.config.spotify.clientSecret,
});
await this.player.extractors.loadDefault(ext => !["YouTubeExtractor", "SpotifyExtractor"].includes(ext));
this.player.events.on("playerStart", async (queue, track) => {
const m = (
await queue.metadata.channel.send({
content: this.translate(
"music/play:NOW_PLAYING",
{
songName: `${track.title} - ${track.author}`,
songURL: track.url,
},
queue.metadata.data.guild.language,
),
})
).id;
if (track.durationMS > 1) {
setTimeout(() => {
const message = queue.metadata.channel.messages.cache.get(m);
if (message && message.deletable) message.delete();
}, track.durationMS);
} else {
setTimeout(
() => {
const message = queue.metadata.channel.messages.cache.get(m);
if (message && message.deletable) message.delete();
},
5 * 60 * 1000,
);
}
});
this.player.events.on("emptyQueue", queue => queue.metadata.channel.send(this.translate("music/play:QUEUE_ENDED", null, queue.metadata.data.guild.language)));
this.player.events.on("emptyChannel", queue => queue.metadata.channel.send(this.translate("music/play:STOP_EMPTY", null, queue.metadata.data.guild.language)));
this.player.events.on("playerError", (queue, e) => {
queue.metadata.channel.send({ content: this.translate("music/play:ERR_OCCURRED", { error: e.message }, queue.metadata.data.guild.language) });
console.log(e);
});
this.player.events.on("error", (queue, e) => {
queue.metadata.channel.send({ content: this.translate("music/play:ERR_OCCURRED", { error: e.message }, queue.metadata.data.guild.language) });
console.log(e);
});
this.giveawaysManager = new GiveawaysManager(this, {
storage: "./giveaways.json",
default: {
botsCanWin: false,
embedColor: this.config.embed.color,
embedColorEnd: "#FF0000",
reaction: "🎉",
},
});
mongoose
.connect(this.config.mongoDB)
.then(this.logger.log("Connected to the MongoDB database."))
.catch(e => {
this.logger.error(`Unable to connect to the MongoDB database.\nError: ${e.message}\n${e.stack}`);
});
this.login(this.config.token);
}
/**
* Loads all the commands from the specified directory and registers them with the Discord API.
*
* This method dynamically loads all command files from the specified directory,
* creates instances of the corresponding command classes, and registers them with the Discord API.
*
* @param {string} dir - The directory path where the command files are located.
* @returns {Promise<void>} A Promise that resolves when all the commands have been loaded and registered.
*/
async loadCommands(dir) {
const rest = new REST().setToken(this.config.token),
filePath = join(__dirname, dir),
folders = (await fs.readdir(filePath)).map(file => join(filePath, file));
const commands = [];
for (const folder of folders) {
const files = await fs.readdir(folder);
for (const file of files) {
if (!file.endsWith(".js")) continue;
const Command = require(join(folder, file));
if (!(Command.prototype instanceof BaseCommand)) continue;
const command = new Command(this);
this.commands.set(command.command.name, command);
if (typeof command.onLoad === "function") await command.onLoad(this);
commands.push(command.command instanceof SlashCommandBuilder || command.command instanceof ContextMenuCommandBuilder ? command.command.toJSON() : command.command);
this.logger.log(`Successfully loaded "${file}" command. (Command: ${command.command.name})`);
}
}
try {
const route = this.config.production ? Routes.applicationCommands(this.config.userId) : Routes.applicationGuildCommands(this.config.userId, this.config.support.id);
await rest.put(route, { body: commands });
this.logger.log("Successfully registered application commands.");
} catch (e) {
this.logger.error("Error registering application commands:", e);
}
}
/**
* Loads a command from the specified directory and file.
* @param {string} dir - The directory containing the command file.
* @param {string} file - The name of the command file (without the .js extension).
* @returns {Promise<void>} This method does not return a value.
*/
async loadCommand(dir, file) {
try {
const Command = require(join(dir, `${file}.js`));
if (!(Command.prototype instanceof BaseCommand)) {
return this.logger.error(`Tried to load a non-command file: "${file}.js"`);
}
const command = new Command(this);
this.commands.set(command.command.name, command);
if (typeof command.onLoad === "function") await command.onLoad(this);
this.logger.log(`Successfully loaded "${file}" command file. (Command: ${command.command.name})`);
} catch (e) {
this.logger.error(`Error loading command "${file}":`, e);
}
}
/**
* Unloads a command from the specified directory and file.
* @param {string} dir - The directory containing the command file.
* @param {string} name - The name of the command file (without the .js extension).
* @returns {void} This method does not return a value.
*/
unloadCommand(dir, name) {
delete require.cache[require.resolve(`${dir}${sep}${name}.js`)];
return;
}
/**
* Loads all event files from the specified directory and its subdirectories.
* @param {string} dir - The directory containing the event files.
* @returns {Promise<void>} This method does not return a value.
*/
async loadEvents(dir) {
const filePath = join(__dirname, dir);
const files = await fs.readdir(filePath);
for (const file of files) {
const fullPath = join(filePath, file);
const stat = await fs.lstat(fullPath);
if (stat.isDirectory()) {
await this.loadEvents(join(dir, file));
continue;
}
if (file.endsWith(".js")) {
try {
const Event = require(fullPath);
if (!(Event.prototype instanceof BaseEvent)) {
this.logger.error(`"${file}" is not a valid event file.`);
continue;
}
const event = new Event();
if (!event.name || !event.name.length) {
this.logger.error(`Cannot load "${file}" event: Event name is missing!`);
continue;
}
event.once ? this.once(event.name, event.execute.bind(event, this)) : this.on(event.name, event.execute.bind(event, this));
this.logger.log(`Successfully loaded "${file}" event. (Event: ${event.name})`);
} catch (e) {
this.logger.error(`Error loading event "${file}":`, e);
}
}
}
}
/**
* Returns the default language from the list of available languages.
* @returns {Language} The default language.
*/
get defaultLanguage() {
return this.languages.find(language => language.default);
}
/**
* Translates a key using the specified locale, or the default language if no locale is provided.
* @param {string} key The translation key to look up.
* @param {any[]} args Any arguments to pass to the translation function.
* @param {string} [locale=this.defaultLanguage.name] The locale to use for the translation. Defaults to the default language.
* @returns {string} The translated string.
*/
translate(key, args, locale = this.defaultLanguage.name) {
const lang = this.translations.get(locale);
return lang(key, args);
}
/**
* Generates an EmbedBuilder instance with the provided data.
* @param {Object} data - The data to use for the embed.
* @param {string} [data.title] - The title of the embed.
* @param {string} [data.description] - The description of the embed.
* @param {string} [data.thumbnail] - The URL of the thumbnail image for the embed.
* @param {Object[]} [data.fields] - An array of field objects for the embed.
* @param {string} [data.image] - The URL of the image for the embed.
* @param {string} [data.url] - The URL to be used as the embed's hyperlink.
* @param {string} [data.color] - The HEX color of the embed's border.
* @param {string|Object} [data.footer] - The text to be displayed as the embed's footer.
* @param {Date} [data.timestamp] - The timestamp to be displayed in the embed.
* @param {string|Object} [data.author] - The author information for the embed.
* @returns {EmbedBuilder} The generated EmbedBuilder instance.
*/
embed(data) {
const embed = new EmbedBuilder()
.setTitle(data.title ?? null)
.setDescription(data.description ?? null)
.setThumbnail(data.thumbnail ?? null)
.addFields(data.fields ?? [])
.setImage(data.image ?? null)
.setURL(data.url ?? null)
.setColor(data.color ?? this.config.embed.color)
.setFooter(typeof data.footer === "object" ? data.footer : data.footer ? { text: data.footer } : this.config.embed.footer)
.setTimestamp(data.timestamp ?? null)
.setAuthor(typeof data.author === "string" ? { name: data.author, iconURL: this.user.avatarURL() } : (data.author ?? null));
return embed;
}
/**
* Creates an invite for the specified guild.
* @param {string} guildId - The ID of the guild to create the invite for.
* @returns {Promise<string>} The URL of the created invite, or an error message if no suitable channel was found or the bot lacks the necessary permissions.
*/
async createInvite(guildId) {
const guild = this.guilds.cache.get(guildId),
member = guild.members.me,
channel = guild.channels.cache.find(ch => ch.permissionsFor(member.id).has(PermissionsBitField.FLAGS.CREATE_INSTANT_INVITE) && (ch.type === ChannelType.GuildText || ch.type === ChannelType.GuildVoice));
if (channel) return (await channel.createInvite()).url || "No channels found or missing permissions";
}
/**
* Returns a User data from the database.
* @param {string} userID - The ID of the user to find or create.
* @returns {Promise<import("./User.js")>} The user data object, either retrieved from the database or newly created.
*/
async getUserData(userID) {
let userData = await this.usersData.findOne({ id: userID });
if (!userData) {
userData = new this.usersData({ id: userID });
await userData.save();
}
this.databaseCache.users.set(userID, userData);
return userData;
}
/**
* Returns a Member data from the database.
* @param {string} memberId - The ID of the member to find or create.
* @param {string} guildId - The ID of the guild the member belongs to.
* @returns {Promise<import("./Member.js")>} The member data object, either retrieved from the database or newly created.
*/
async getMemberData(memberId, guildId) {
let memberData = await this.membersData.findOne({ guildID: guildId, id: memberId });
if (!memberData) {
memberData = new this.membersData({ id: memberId, guildID: guildId });
await memberData.save();
const guildData = await this.getGuildData(guildId);
if (guildData) {
guildData.members.push(memberData._id);
await guildData.save();
}
}
this.databaseCache.members.set(`${memberId}/${guildId}`, memberData);
return memberData;
}
/**
* Returns a Guild data from the database.
* @param {string} guildId - The ID of the guild to find or create.
* @returns {Promise<import("./Guild.js")>} The guild data object, either retrieved from the database or newly created.
*/
async getGuildData(guildId) {
let guildData = await this.guildsData.findOne({ id: guildId }).populate("members");
if (!guildData) {
guildData = new this.guildsData({ id: guildId });
await guildData.save();
}
this.databaseCache.guilds.set(guildId, guildData);
return guildData;
}
}
export default JaBaClient;

View file

@ -1,74 +0,0 @@
const { SlashCommandBuilder, PermissionsBitField, InteractionContextType, ApplicationIntegrationType } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
class Addemoji extends BaseCommand {
/**
*
* @param {import("../../base/Client")} client
*/
constructor(client) {
super({
command: new SlashCommandBuilder()
.setName("addemoji")
.setDescription(client.translate("administration/addemoji:DESCRIPTION"))
.setDescriptionLocalizations({
uk: client.translate("administration/addemoji:DESCRIPTION", null, "uk-UA"),
ru: client.translate("administration/addemoji:DESCRIPTION", null, "ru-RU"),
})
.setIntegrationTypes([ApplicationIntegrationType.GuildInstall])
.setContexts([InteractionContextType.Guild])
.setDefaultMemberPermissions(PermissionsBitField.Flags.ManageGuild)
.addStringOption(option =>
option
.setName("link")
.setDescription(client.translate("common:LINK"))
.setDescriptionLocalizations({
uk: client.translate("common:LINK", null, "uk-UA"),
ru: client.translate("common:LINK", null, "ru-RU"),
})
.setRequired(true),
)
.addStringOption(option =>
option
.setName("name")
.setDescription(client.translate("common:NAME"))
.setDescriptionLocalizations({
uk: client.translate("common:NAME", null, "uk-UA"),
ru: client.translate("common:NAME", null, "ru-RU"),
})
.setRequired(true),
),
dirname: __dirname,
ownerOnly: false,
});
}
/**
*
* @param {import("../../base/Client")} client
* @param {import("discord.js").ChatInputCommandInteraction} interaction
*/
async execute(client, interaction) {
const link = interaction.options.getString("link"),
name = interaction.options.getString("name");
interaction.guild.emojis
.create({
name: name,
attachment: link,
})
.then(emoji =>
interaction.success("administration/stealemoji:SUCCESS", {
emoji: emoji.toString(),
}, { ephemeral: true }),
)
.catch(e => {
interaction.error("administration/stealemoji:ERROR", {
emoji: name,
e,
}, { ephemeral: true });
});
}
}
module.exports = Addemoji;

View file

@ -1,97 +0,0 @@
const { SlashCommandBuilder, PermissionsBitField, ChannelType, InteractionContextType, ApplicationIntegrationType } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
class Automod extends BaseCommand {
/**
*
* @param {import("../../base/Client")} client
*/
constructor(client) {
super({
command: new SlashCommandBuilder()
.setName("automod")
.setDescription(client.translate("administration/automod:DESCRIPTION"))
.setDescriptionLocalizations({
uk: client.translate("administration/automod:DESCRIPTION", null, "uk-UA"),
ru: client.translate("administration/automod:DESCRIPTION", null, "ru-RU"),
})
.setIntegrationTypes([ApplicationIntegrationType.GuildInstall])
.setContexts([InteractionContextType.Guild])
.setDefaultMemberPermissions(PermissionsBitField.Flags.ManageGuild)
.addSubcommand(subcommand =>
subcommand
.setName("toggle")
.setDescription(client.translate("administration/automod:TOGGLE"))
.setDescriptionLocalizations({
uk: client.translate("administration/automod:TOGGLE", null, "uk-UA"),
ru: client.translate("administration/automod:TOGGLE", null, "ru-RU"),
})
.addBooleanOption(option =>
option
.setName("state")
.setDescription(client.translate("common:STATE"))
.setDescriptionLocalizations({
uk: client.translate("common:STATE", null, "uk-UA"),
ru: client.translate("common:STATE", null, "ru-RU"),
})
.setRequired(true),
),
)
.addSubcommand(subcommand =>
subcommand
.setName("ignore")
.setDescription(client.translate("administration/automod:IGNORE"))
.setDescriptionLocalizations({
uk: client.translate("administration/automod:IGNORE", null, "uk-UA"),
ru: client.translate("administration/automod:IGNORE", null, "ru-RU"),
})
.addChannelOption(option =>
option
.setName("channel")
.setDescription(client.translate("common:CHANNEL"))
.setDescriptionLocalizations({
uk: client.translate("common:CHANNEL", null, "uk-UA"),
ru: client.translate("common:CHANNEL", null, "ru-RU"),
})
.addChannelTypes(ChannelType.GuildText)
.setRequired(true),
),
),
dirname: __dirname,
ownerOnly: false,
});
}
/**
*
* @param {import("../../base/Client")} client
* @param {import("discord.js").ChatInputCommandInteraction} interaction
*/
async execute(client, interaction) {
const { data } = interaction,
state = interaction.options.getBoolean("state"),
channel = interaction.options.getChannel("channel"),
command = interaction.options.getSubcommand();
if (command === "toggle") {
data.guild.plugins.automod = {
enabled: state,
ignored: [],
};
await data.guild.save();
interaction.success(`administration/automod:${state ? "ENABLED" : "DISABLED"}`);
} else if (command === "ignore") {
data.guild.plugins.automod.ignored.push(channel.id);
await data.guild.save();
interaction.success("administration/automod:DISABLED_CHANNEL", {
channel: channel.toString(),
});
}
}
}
module.exports = Automod;

View file

@ -1,76 +0,0 @@
const { SlashCommandBuilder, PermissionsBitField, InteractionContextType, ApplicationIntegrationType } = require("discord.js");
const BaseCommand = require("../../base/BaseCommand");
class Autorole extends BaseCommand {
/**
*
* @param {import("../../base/Client")} client
*/
constructor(client) {
super({
command: new SlashCommandBuilder()
.setName("autorole")
.setDescription(client.translate("administration/autorole:DESCRIPTION"))
.setDescriptionLocalizations({
uk: client.translate("administration/autorole:DESCRIPTION", null, "uk-UA"),
ru: client.translate("administration/autorole:DESCRIPTION", null, "ru-RU"),
})
.setIntegrationTypes([ApplicationIntegrationType.GuildInstall])
.setContexts([InteractionContextType.Guild])
.setDefaultMemberPermissions(PermissionsBitField.Flags.ManageGuild)
.addBooleanOption(option =>
option
.setName("state")
.setDescription(client.translate("common:STATE"))
.setDescriptionLocalizations({
uk: client.translate("common:STATE", null, "uk-UA"),
ru: client.translate("common:STATE", null, "ru-RU"),
})
.setRequired(true),
)
.addRoleOption(option =>
option
.setName("role")
.setDescription(client.translate("common:ROLE"))
.setDescriptionLocalizations({
uk: client.translate("common:ROLE", null, "uk-UA"),
ru: client.translate("common:ROLE", null, "ru-RU"),
}),
),
dirname: __dirname,
ownerOnly: false,
});
}
/**
*
* @param {import("../../base/Client")} client
* @param {import("discord.js").ChatInputCommandInteraction} interaction
*/
async execute(client, interaction) {
const guildData = interaction.data.guild,
state = interaction.options.getBoolean("state"),
role = interaction.options.getRole("role");
guildData.plugins.autorole = {
enabled: state,
role,
};
if (state && role) {
await guildData.save();
interaction.success("administration/autorole:ENABLED", {
role: role.toString(),
});
} else {
guildData.plugins.autorole.enabled = false;
await guildData.save();
interaction.success("administration/autorole:DISABLED");
}
}
}
module.exports = Autorole;

View file

@ -17,6 +17,7 @@
"owner": {
"id": "123456789098765432"
},
"_comment_paths": "Change './src' to './dist' for prod",
"paths": {
"commands": "./src/commands",
"events": "./src/events",

View file

@ -1,7 +1,7 @@
import globals from "globals";
import pluginJs from "@eslint/js";
import tsParser from "@typescript-eslint/parser";
import stylisticJs from "@stylistic/eslint-plugin-js";
import stylistic from "@stylistic/eslint-plugin";
import tsPlugin from "@typescript-eslint/eslint-plugin";
/** @type {import("eslint").Linter.Config[]} */
@ -18,7 +18,7 @@ export default [
},
plugins: {
"@typescript-eslint": tsPlugin,
"@stylistic/js": stylisticJs,
"@stylistic": stylistic,
},
rules: {
"no-unused-vars": "off",
@ -31,14 +31,14 @@ export default [
"no-var": "error",
"prefer-const": "error",
yoda: "error",
"@stylistic/js/arrow-spacing": ["error", { before: true, after: true }],
"@stylistic/js/comma-dangle": ["error", "always-multiline"],
"@stylistic/js/comma-spacing": ["error", { before: false, after: true }],
"@stylistic/js/comma-style": ["error", "last"],
"@stylistic/js/dot-location": ["error", "property"],
"@stylistic/js/keyword-spacing": ["error", { before: true, after: true }],
"@stylistic/js/no-multi-spaces": "error",
"@stylistic/js/no-multiple-empty-lines": [
"@stylistic/arrow-spacing": ["error", { before: true, after: true }],
"@stylistic/comma-dangle": ["error", "always-multiline"],
"@stylistic/comma-spacing": ["error", { before: false, after: true }],
"@stylistic/comma-style": ["error", "last"],
"@stylistic/dot-location": ["error", "property"],
"@stylistic/keyword-spacing": ["error", { before: true, after: true }],
"@stylistic/no-multi-spaces": "error",
"@stylistic/no-multiple-empty-lines": [
"error",
{
max: 2,
@ -46,12 +46,12 @@ export default [
maxBOF: 0,
},
],
"@stylistic/js/no-trailing-spaces": ["error"],
"@stylistic/js/object-curly-spacing": ["error", "always"],
"@stylistic/js/quotes": ["error", "double"],
"@stylistic/js/indent": ["error", "tab"],
"@stylistic/js/semi": ["error", "always"],
"@stylistic/js/space-infix-ops": "error",
"@stylistic/no-trailing-spaces": ["error"],
"@stylistic/object-curly-spacing": ["error", "always"],
"@stylistic/quotes": ["error", "double"],
"@stylistic/indent": ["error", "tab"],
"@stylistic/semi": ["error", "always"],
"@stylistic/space-infix-ops": "error",
},
},
];

View file

@ -1,7 +1,6 @@
{
"name": "jaba",
"version": "0.0.0",
"description": "My Discord Bot",
"main": "src/index.js",
"type": "module",
"scripts": {
@ -11,38 +10,35 @@
},
"author": "https://github.com/JonnyBro",
"dependencies": {
"@discord-player/extractor": "^4.5.1",
"@discordjs/opus": "^0.9.0",
"@discordjs/opus": "^0.10.0",
"@discordjs/voice": "^0.18.0",
"@napi-rs/canvas": "^0.1.63",
"chalk": "^4.1.2",
"cron": "^3.2.1",
"@napi-rs/canvas": "^0.1.68",
"chalk": "^5.4.1",
"cron": "^4.1.0",
"discord-giveaways": "^6.0.1",
"discord-player": "^6.7.1",
"discord-player-youtubei": "1.3.5",
"discord.js": "^14.18.0",
"gamedig": "^5.1.4",
"i18next": "^24.0.0",
"gamedig": "^5.2.0",
"i18next": "^24.2.2",
"i18next-fs-backend": "^2.6.0",
"md5": "^2.3.0",
"mongoose": "^8.8.2",
"mongoose": "^8.12.1",
"ms": "^2.1.3",
"node-fetch": "^2.7.0"
"node-fetch": "^3.3.2"
},
"devDependencies": {
"@eslint/js": "^9.16.0",
"@stylistic/eslint-plugin-js": "^2.11.0",
"@eslint/js": "^9.21.0",
"@stylistic/eslint-plugin": "^4.2.0",
"@types/md5": "^2.3.5",
"@types/ms": "^0.7.34",
"@types/node": "^22.10.5",
"@typescript-eslint/eslint-plugin": "^8.19.1",
"@typescript-eslint/parser": "^8.19.1",
"eslint": "^9.16.0",
"globals": "^15.13.0",
"prettier": "^3.4.2",
"@types/ms": "^2.1.0",
"@types/node": "^22.13.9",
"@typescript-eslint/eslint-plugin": "^8.26.0",
"@typescript-eslint/parser": "^8.26.0",
"eslint": "^9.21.0",
"globals": "^16.0.0",
"prettier": "^3.5.3",
"prettier-eslint": "^16.3.0",
"tsc-alias": "^1.8.10",
"tsx": "^4.19.2",
"typescript": "^5.7.3"
"tsc-alias": "^1.8.11",
"tsx": "^4.19.3",
"typescript": "^5.8.2"
}
}

1291
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -1,17 +1,17 @@
import { replyError, replySuccess } from "@/helpers/extenders.js";
import { CommandData, SlashCommandProps } from "@/types.js";
import useClient from "@/utils/use-client.js";
import { ApplicationCommandOptionType, ApplicationIntegrationType, InteractionContextType } from "discord.js";
import { ApplicationCommandOptionType, ApplicationIntegrationType, InteractionContextType, MessageFlags } from "discord.js";
const client = useClient();
export const data: CommandData = {
name: "addemoji",
description: client.translate("administration/addemoji:DESCRIPTION"),
description: client.i18n.translate("administration/addemoji:DESCRIPTION"),
// eslint-disable-next-line camelcase
description_localizations: {
uk: client.translate("administration/addemoji:DESCRIPTION", { lng: "uk-UA" }),
ru: client.translate("administration/addemoji:DESCRIPTION", { lng: "ru-RU" }),
ru: client.i18n.translate("administration/addemoji:DESCRIPTION", { lng: "ru-RU" }),
uk: client.i18n.translate("administration/addemoji:DESCRIPTION", { lng: "uk-UA" }),
},
// eslint-disable-next-line camelcase
integration_types: [ApplicationIntegrationType.GuildInstall],
@ -19,34 +19,34 @@ export const data: CommandData = {
options: [
{
name: "link",
description: client.translate("common:LINK"),
description: client.i18n.translate("common:LINK"),
type: ApplicationCommandOptionType.String,
required: true,
// eslint-disable-next-line camelcase
description_localizations: {
uk: client.translate("common:LINK", { lng: "uk-UA" }),
ru: client.translate("common:LINK", { lng: "ru-RU" }),
ru: client.i18n.translate("common:LINK", { lng: "ru-RU" }),
uk: client.i18n.translate("common:LINK", { lng: "uk-UA" }),
},
},
{
name: "name",
description: client.translate("common:NAME"),
description: client.i18n.translate("common:NAME"),
type: ApplicationCommandOptionType.String,
required: true,
// eslint-disable-next-line camelcase
description_localizations: {
uk: client.translate("common:NAME", { lng: "uk-UA" }),
ru: client.translate("common:NAME", { lng: "ru-RU" }),
ru: client.i18n.translate("common:NAME", { lng: "ru-RU" }),
uk: client.i18n.translate("common:NAME", { lng: "uk-UA" }),
},
},
],
};
export const run = async ({ interaction }: SlashCommandProps) => {
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
const attachment = interaction.options.getString("link") || "",
name = interaction.options.getString("name") || "";
const attachment = interaction.options.getString("link") || "";
const name = interaction.options.getString("name") || "";
try {
const emoji = await interaction.guild?.emojis.create({ name, attachment });

View file

@ -0,0 +1,229 @@
import { replyError, translateContext } from "@/helpers/extenders.js";
import { CommandData, SlashCommandProps } from "@/types.js";
import { createEmbed } from "@/utils/create-embed.js";
import useClient from "@/utils/use-client.js";
import { ApplicationCommandOptionType, ApplicationIntegrationType, ChannelType, ChatInputCommandInteraction, InteractionContextType, MessageFlags, PermissionsBitField } from "discord.js";
const client = useClient();
export const data: CommandData = {
name: "config",
description: client.i18n.translate("administration/config:DESCRIPTION"),
// eslint-disable-next-line camelcase
description_localizations: {
ru: client.i18n.translate("administration/config:DESCRIPTION", { lng: "ru-RU" }),
uk: client.i18n.translate("administration/config:DESCRIPTION", { lng: "uk-UA" }),
},
// eslint-disable-next-line camelcase
integration_types: [ApplicationIntegrationType.GuildInstall],
contexts: [InteractionContextType.Guild],
// eslint-disable-next-line camelcase
default_member_permissions: String(PermissionsBitField.Flags.ManageGuild),
options: [
{
name: "list",
description: client.i18n.translate("administration/config:LIST"),
// eslint-disable-next-line camelcase
description_localizations: {
ru: client.i18n.translate("administration/config:LIST", { lng: "ru-RU" }),
uk: client.i18n.translate("administration/config:LIST", { lng: "uk-UA" }),
},
type: ApplicationCommandOptionType.Subcommand,
},
{
name: "set",
description: client.i18n.translate("administration/config:SET"),
// eslint-disable-next-line camelcase
description_localizations: {
ru: client.i18n.translate("administration/config:SET", { lng: "ru-RU" }),
uk: client.i18n.translate("administration/config:SET", { lng: "uk-UA" }),
},
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "parameter",
description: client.i18n.translate("administration/config:PARAMETER"),
// eslint-disable-next-line camelcase
description_localizations: {
ru: client.i18n.translate("administration/config:PARAMETER", { lng: "ru-RU" }),
uk: client.i18n.translate("administration/config:PARAMETER", { lng: "uk-UA" }),
},
type: ApplicationCommandOptionType.String,
required: true,
choices: [
{ name: client.i18n.translate("administration/config:BIRTHDAYS"), value: "birthdays" },
{ name: client.i18n.translate("administration/config:MODLOGS"), value: "modlogs" },
{ name: client.i18n.translate("administration/config:REPORTS"), value: "reports" },
{ name: client.i18n.translate("administration/config:SUGGESTIONS"), value: "suggestions" },
{ name: client.i18n.translate("administration/config:TICKETSCATEGORY"), value: "tickets.ticketsCategory" },
{ name: client.i18n.translate("administration/config:TICKETLOGS"), value: "tickets.ticketLogs" },
{ name: client.i18n.translate("administration/config:TRANSCRIPTIONLOGS"), value: "tickets.transcriptionLogs" },
{ name: client.i18n.translate("administration/config:MESSAGEUPDATE"), value: "monitoring.messageUpdate" },
{ name: client.i18n.translate("administration/config:MESSAGEDELETE"), value: "monitoring.messageDelete" },
],
},
{
name: "boolean",
description: client.i18n.translate("common:STATE"),
// eslint-disable-next-line camelcase
description_localizations: {
ru: client.i18n.translate("common:STATE", { lng: "ru-RU" }),
uk: client.i18n.translate("common:STATE", { lng: "uk-UA" }),
},
type: ApplicationCommandOptionType.Boolean,
required: true,
},
{
name: "channel",
description: client.i18n.translate("common:CHANNEL"),
// eslint-disable-next-line camelcase
description_localizations: {
ru: client.i18n.translate("common:CHANNEL", { lng: "ru-RU" }),
uk: client.i18n.translate("common:CHANNEL", { lng: "uk-UA" }),
},
type: ApplicationCommandOptionType.Channel,
required: false,
},
],
},
],
};
export const run = async ({ interaction }: SlashCommandProps) => {
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
if (!interaction.guild) return replyError(interaction, "misc:GUILD_ONLY", null, { edit: true });
const guildData = await client.getGuildData(interaction.guild.id);
const command = interaction.options.getSubcommand();
if (command === "list") {
const embed = createEmbed({
author: {
name: interaction.guild.name,
iconURL: interaction.guild.iconURL() || "",
},
fields: [
{
name: await translateContext(interaction, "administration/config:WELCOME_TITLE"),
value: guildData.plugins.welcome.enabled
? await translateContext(interaction, "administration/config:WELCOME_CONTENT", {
channel: `<#${guildData.plugins.welcome.channel}>`,
withImage: guildData.plugins.welcome.withImage ? await translateContext(interaction, "common:YES") : await translateContext(interaction, "common:NO"),
})
: await translateContext(interaction, "common:DISABLED"),
inline: true,
},
{
name: await translateContext(interaction, "administration/config:GOODBYE_TITLE"),
value: guildData.plugins.goodbye.enabled
? await translateContext(interaction, "administration/config:GOODBYE_CONTENT", {
channel: `<#${guildData.plugins.goodbye.channel}>`,
withImage: guildData.plugins.goodbye.withImage ? await translateContext(interaction, "common:YES") : await translateContext(interaction, "common:NO"),
})
: await translateContext(interaction, "common:DISABLED"),
inline: true,
},
{
name: await translateContext(interaction, "administration/config:MONITORING_CHANNELS"),
value:
`${await translateContext(interaction, "administration/config:MESSAGEUPDATE")}: ${guildData.plugins?.monitoring?.messageUpdate ? `<#${guildData.plugins?.monitoring?.messageUpdate}>` : `*${await translateContext(interaction, "common:NOT_DEFINED")}*`}\n` +
`${await translateContext(interaction, "administration/config:MESSAGEDELETE")}: ${guildData.plugins?.monitoring?.messageDelete ? `<#${guildData.plugins?.monitoring?.messageDelete}>` : `*${await translateContext(interaction, "common:NOT_DEFINED")}*`}\n`,
},
{
name: await translateContext(interaction, "administration/config:SPECIAL_CHANNELS"),
value:
`${await translateContext(interaction, "administration/config:BIRTHDAYS")}: ${guildData.plugins?.birthdays ? `<#${guildData.plugins.birthdays}>` : `*${await translateContext(interaction, "common:NOT_DEFINED")}*`}\n` +
`${await translateContext(interaction, "administration/config:MODLOGS")}: ${guildData.plugins?.modlogs ? `<#${guildData.plugins.modlogs}>` : `*${await translateContext(interaction, "common:NOT_DEFINED")}*`}\n` +
`${await translateContext(interaction, "administration/config:REPORTS")}: ${guildData.plugins?.reports ? `<#${guildData.plugins.reports}>` : `*${await translateContext(interaction, "common:NOT_DEFINED")}*`}\n` +
`${await translateContext(interaction, "administration/config:SUGGESTIONS")}: ${guildData.plugins?.suggestions ? `<#${guildData.plugins.suggestions}>` : `*${await translateContext(interaction, "common:NOT_DEFINED")}*`}\n` +
`${await translateContext(interaction, "administration/config:TICKETSCATEGORY")}: ${guildData.plugins?.tickets?.ticketsCategory ? `<#${guildData.plugins?.tickets?.ticketsCategory}>` : `*${await translateContext(interaction, "common:NOT_DEFINED")}*`}\n` +
`${await translateContext(interaction, "administration/config:TICKETLOGS")}: ${guildData.plugins?.tickets?.ticketLogs ? `<#${guildData.plugins?.tickets?.ticketLogs}>` : `*${await translateContext(interaction, "common:NOT_DEFINED")}*`}\n` +
`${await translateContext(interaction, "administration/config:TRANSCRIPTIONLOGS")}: ${guildData.plugins?.tickets?.transcriptionLogs ? `<#${guildData.plugins?.tickets?.transcriptionLogs}>` : `*${await translateContext(interaction, "common:NOT_DEFINED")}*`}\n`,
},
],
});
return interaction.editReply({
embeds: [embed],
});
} else {
const parameter = interaction.options.getString("parameter", true),
state = interaction.options.getBoolean("state", true),
channel = interaction.options.getChannel("channel");
await changeSetting(interaction, guildData, parameter, state, channel);
}
};
async function changeSetting(interaction: ChatInputCommandInteraction, data: any, parameter: string, state: boolean, channel: any) { // TODO: Proper type for channel
const parameterSplitted = parameter.split(".");
if (parameterSplitted.length === 2) {
if (data.plugins[parameterSplitted[0]] === undefined) data.plugins[parameterSplitted[0]] = {};
if (!state) {
data.plugins[parameterSplitted[0]][parameterSplitted[1]] = null;
data.markModified(`plugins.${parameter}`);
await data.save();
return interaction.reply({
content: `${await translateContext(interaction, `administration/config:${parameterSplitted.length === 2 ? parameterSplitted[1].toUpperCase() : parameter.toUpperCase()}`)}: **${await translateContext(interaction, "common:DISABLED")}**`,
ephemeral: true,
});
} else {
if (parameterSplitted[1] === "ticketsCategory" && channel?.type !== ChannelType.GuildCategory) return interaction.reply({ content: await translateContext(interaction, "administration/config:TICKETS_NOT_CATEGORY"), ephemeral: true });
if (channel) {
data.plugins[parameterSplitted[0]][parameterSplitted[1]] = channel.id;
data.markModified(`plugins.${parameter}`);
await data.save();
return interaction.reply({
content: `${await translateContext(interaction, `administration/config:${parameterSplitted.length === 2 ? parameterSplitted[1].toUpperCase() : parameter.toUpperCase()}`)}: **${await translateContext(interaction, "common:ENABLED")}** (${channel.toString()})`,
ephemeral: true,
});
} else {
return interaction.reply({
content: `${await translateContext(interaction, `administration/config:${parameterSplitted.length === 2 ? parameterSplitted[1].toUpperCase() : parameter.toUpperCase()}`)}: ${
data.plugins[parameter] ? `**${await translateContext(interaction, "common:ENABLED")}** (<#${data.plugins[parameter]}>)` : `**${await translateContext(interaction, "common:DISABLED")}**`
}`,
ephemeral: true,
});
}
};
} else {
if (!state) {
data.plugins[parameter] = null;
data.markModified(`plugins.${parameter}`);
await data.save();
return interaction.reply({
content: `${client.i18n.translate(`administration/config:${parameter.toUpperCase()}`)}: **${client.i18n.translate("common:DISABLED")}**`,
ephemeral: true,
});
} else {
if (channel) {
data.plugins[parameter] = channel.id;
data.markModified(`plugins.${parameter}`);
await data.save();
return interaction.reply({
content: `${client.i18n.translate(`administration/config:${parameter.toUpperCase()}`)}: **${client.i18n.translate("common:ENABLED")}** (${channel.toString()})`,
ephemeral: true,
});
} else {
return interaction.reply({
content: `${client.i18n.translate(`administration/config:${parameter.toUpperCase()}`)}: ${
data.plugins[parameter] ? `**${client.i18n.translate("common:ENABLED")}** (<#${data.plugins[parameter]}>)` : `**${client.i18n.translate("common:DISABLED")}**`
}`,
ephemeral: true,
});
}
}
}
}

View file

@ -1,17 +1,17 @@
import { replyError, replySuccess } from "@/helpers/extenders.js";
import { CommandData, SlashCommandProps } from "@/types.js";
import useClient from "@/utils/use-client.js";
import { ApplicationCommandOptionType, ApplicationIntegrationType, InteractionContextType } from "discord.js";
import { ApplicationCommandOptionType, ApplicationIntegrationType, InteractionContextType, MessageFlags } from "discord.js";
const client = useClient();
export const data: CommandData = {
name: "birthdate",
description: client.translate("economy/birthdate:DESCRIPTION"),
description: client.i18n.translate("economy/birthdate:DESCRIPTION"),
// eslint-disable-next-line camelcase
description_localizations: {
uk: client.translate("economy/birthdate:DESCRIPTION", { lng: "uk-UA" }),
ru: client.translate("economy/birthdate:DESCRIPTION", { lng: "ru-RU" }),
uk: client.i18n.translate("economy/birthdate:DESCRIPTION", { lng: "uk-UA" }),
ru: client.i18n.translate("economy/birthdate:DESCRIPTION", { lng: "ru-RU" }),
},
// eslint-disable-next-line camelcase
integration_types: [ApplicationIntegrationType.GuildInstall, ApplicationIntegrationType.UserInstall],
@ -19,75 +19,73 @@ export const data: CommandData = {
options: [
{
name: "day",
description: client.translate("economy/birthdate:DAY"),
description: client.i18n.translate("economy/birthdate:DAY"),
type: ApplicationCommandOptionType.Integer,
// eslint-disable-next-line camelcase
description_localizations: {
uk: client.translate("economy/birthdate:DAY", { lng: "uk-UA" }),
ru: client.translate("economy/birthdate:DAY", { lng: "ru-RU" }),
uk: client.i18n.translate("economy/birthdate:DAY", { lng: "uk-UA" }),
ru: client.i18n.translate("economy/birthdate:DAY", { lng: "ru-RU" }),
},
},
{
name: "month",
description: client.translate("economy/birthdate:MONTH"),
description: client.i18n.translate("economy/birthdate:MONTH"),
type: ApplicationCommandOptionType.Integer,
// eslint-disable-next-line camelcase
description_localizations: {
uk: client.translate("economy/birthdate:MONTH", { lng: "uk-UA" }),
ru: client.translate("economy/birthdate:MONTH", { lng: "ru-RU" }),
uk: client.i18n.translate("economy/birthdate:MONTH", { lng: "uk-UA" }),
ru: client.i18n.translate("economy/birthdate:MONTH", { lng: "ru-RU" }),
},
choices: [
{ name: client.translate("misc:MONTHS:JANUARY"), value: 1 },
{ name: client.translate("misc:MONTHS:FEBRUARY"), value: 2 },
{ name: client.translate("misc:MONTHS:MARCH"), value: 3 },
{ name: client.translate("misc:MONTHS:APRIL"), value: 4 },
{ name: client.translate("misc:MONTHS:MAY"), value: 5 },
{ name: client.translate("misc:MONTHS:JUNE"), value: 6 },
{ name: client.translate("misc:MONTHS:JULY"), value: 7 },
{ name: client.translate("misc:MONTHS:AUGUST"), value: 8 },
{ name: client.translate("misc:MONTHS:SEPTEMBER"), value: 9 },
{ name: client.translate("misc:MONTHS:OCTOBER"), value: 10 },
{ name: client.translate("misc:MONTHS:NOVEMBER"), value: 11 },
{ name: client.translate("misc:MONTHS:DECEMBER"), value: 12 },
{ name: client.i18n.translate("misc:MONTHS:JANUARY"), value: 1 },
{ name: client.i18n.translate("misc:MONTHS:FEBRUARY"), value: 2 },
{ name: client.i18n.translate("misc:MONTHS:MARCH"), value: 3 },
{ name: client.i18n.translate("misc:MONTHS:APRIL"), value: 4 },
{ name: client.i18n.translate("misc:MONTHS:MAY"), value: 5 },
{ name: client.i18n.translate("misc:MONTHS:JUNE"), value: 6 },
{ name: client.i18n.translate("misc:MONTHS:JULY"), value: 7 },
{ name: client.i18n.translate("misc:MONTHS:AUGUST"), value: 8 },
{ name: client.i18n.translate("misc:MONTHS:SEPTEMBER"), value: 9 },
{ name: client.i18n.translate("misc:MONTHS:OCTOBER"), value: 10 },
{ name: client.i18n.translate("misc:MONTHS:NOVEMBER"), value: 11 },
{ name: client.i18n.translate("misc:MONTHS:DECEMBER"), value: 12 },
],
},
{
name: "year",
description: client.translate("economy/birthdate:YEAR"),
description: client.i18n.translate("economy/birthdate:YEAR"),
type: ApplicationCommandOptionType.Integer,
// eslint-disable-next-line camelcase
description_localizations: {
uk: client.translate("economy/birthdate:YEAR", { lng: "uk-UA" }),
ru: client.translate("economy/birthdate:YEAR", { lng: "ru-RU" }),
uk: client.i18n.translate("economy/birthdate:YEAR", { lng: "uk-UA" }),
ru: client.i18n.translate("economy/birthdate:YEAR", { lng: "ru-RU" }),
},
},
{
name: "clear",
type: ApplicationCommandOptionType.Boolean,
description: client.translate("economy/birthdate:CLEAR"),
description: client.i18n.translate("economy/birthdate:CLEAR"),
// eslint-disable-next-line camelcase
description_localizations: {
uk: client.translate("economy/birthdate:CLEAR", { lng: "uk-UA" }),
ru: client.translate("economy/birthdate:CLEAR", { lng: "ru-RU" }),
uk: client.i18n.translate("economy/birthdate:CLEAR", { lng: "uk-UA" }),
ru: client.i18n.translate("economy/birthdate:CLEAR", { lng: "ru-RU" }),
},
},
{
name: "ephemeral",
type: ApplicationCommandOptionType.Boolean,
description: client.translate("misc:EPHEMERAL_RESPONSE"),
description: client.i18n.translate("misc:EPHEMERAL_RESPONSE"),
// eslint-disable-next-line camelcase
description_localizations: {
uk: client.translate("misc:EPHEMERAL_RESPONSE", { lng: "uk-UA" }),
ru: client.translate("misc:EPHEMERAL_RESPONSE", { lng: "ru-RU" }),
uk: client.i18n.translate("misc:EPHEMERAL_RESPONSE", { lng: "uk-UA" }),
ru: client.i18n.translate("misc:EPHEMERAL_RESPONSE", { lng: "ru-RU" }),
},
},
],
};
export const run = async ({ interaction, client }: SlashCommandProps) => {
await interaction.deferReply({
ephemeral: interaction.options.getBoolean("ephemeral") || false,
});
export const run = async ({ interaction }: SlashCommandProps) => {
await interaction.deferReply({ flags: interaction.options.getBoolean("ephemeral") ? MessageFlags.Ephemeral : undefined });
const userData = await client.getUserData(interaction.user.id);
@ -95,24 +93,18 @@ export const run = async ({ interaction, client }: SlashCommandProps) => {
userData.birthdate = null;
await userData.save();
return replySuccess(
interaction,
"economy/birthdate:SUCCESS",
{ date: "none" },
{
edit: true,
},
);
return replySuccess(interaction, "economy/birthdate:SUCCESS", { date: "none" }, { edit: true });
}
const day = interaction.options.getInteger("day")!,
month = interaction.options.getInteger("month")!,
year = interaction.options.getInteger("year")!,
const day = interaction.options.getInteger("day", true),
month = interaction.options.getInteger("month", true),
year = interaction.options.getInteger("year", true),
date = new Date(year, month - 1, day);
date.setHours(12);
// This should be the same day in all timezones
date.setHours(13);
const d = Math.floor(date.getTime() / 1000);
const d = date.getTime();
if (!(day === date.getDate() && month - 1 === date.getMonth() && year === date.getFullYear())) return replyError(interaction, "economy/birthdate:INVALID_DATE", null, { edit: true });
if (date.getTime() > Date.now()) return replyError(interaction, "economy/birthdate:DATE_TOO_HIGH", null, { edit: true });
@ -126,7 +118,7 @@ export const run = async ({ interaction, client }: SlashCommandProps) => {
interaction,
"economy/birthdate:SUCCESS",
{
date: `<t:${d}:D>`,
date: `<t:${Math.floor(d / 1000)}:D>`,
},
{
edit: true,

View file

@ -9,7 +9,7 @@ export const CLIENT_INTENTS = [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildModeration,
GatewayIntentBits.GuildEmojisAndStickers,
GatewayIntentBits.GuildExpressions,
GatewayIntentBits.GuildIntegrations,
GatewayIntentBits.GuildInvites,
GatewayIntentBits.GuildVoiceStates,

View file

@ -15,7 +15,7 @@ export const data = {
export async function run(client: ExtendedClient) {
let guildsCount = client.guilds.cache.size;
const status = ["Use /help to see all the commands!", `I'm in ${guildsCount} ${getNoun(guildsCount, [client.translate("misc:NOUNS:SERVER:1"), client.translate("misc:NOUNS:SERVER:2"), client.translate("misc:NOUNS:SERVER:5")])}!`];
const status = ["Use /help to see all the commands!", `I'm in ${guildsCount} ${getNoun(guildsCount, [client.i18n.translate("misc:NOUNS:SERVER:1"), client.i18n.translate("misc:NOUNS:SERVER:2"), client.i18n.translate("misc:NOUNS:SERVER:5")])}!`];
logger.ready(`${getUsername(client.user)} is online! Serving ${guildsCount}`);

View file

@ -2,7 +2,7 @@ import { resolve } from "node:path";
import logger from "@/helpers/logger.js";
import { getFilePaths } from "@/utils/get-path.js";
import { toFileURL } from "@/utils/resolve-file.js";
import { GuildQueueEvents, useMainPlayer } from "discord-player";
// import { GuildQueueEvents, useMainPlayer } from "discord-player";
import { ExtendedClient } from "@/structures/client.js";
import { ClientEvents } from "discord.js";
@ -63,15 +63,14 @@ export class EventHandler {
}
$registerEvents() {
const player = useMainPlayer();
// const player = useMainPlayer();
this.events.forEach(event => {
if (event.data.player) {
player.events.on(event.data.name as keyof GuildQueueEvents, event.run);
} else if (event.data.once) {
this.client.once(event.data.name, event.run);
} else {
this.client.on(event.data.name, event.run);
}
/* if (event.data.player) player.events.on(event.data.name as keyof GuildQueueEvents, event.run);
else */
if (event.data.once) this.client.once(event.data.name, event.run);
else this.client.on(event.data.name, event.run);
});
}
}

View file

@ -1,10 +1,11 @@
import { BaseInteraction, CacheType, Interaction, InteractionReplyOptions, Message, User } from "discord.js";
import { BaseInteraction, CacheType, Interaction, InteractionReplyOptions, Message, MessageFlags, User } from "discord.js";
import useClient from "@/utils/use-client.js";
interface Options extends InteractionReplyOptions {
prefixEmoji?: string;
locale?: string;
edit?: boolean;
ephemeral?: boolean;
mention?: boolean;
}
@ -22,47 +23,54 @@ const getAppEmojis = () => {
const formatReply = (message: string, prefixEmoji?: string) => {
const emojis = getAppEmojis();
const emoji = emojis.find(emoji => emoji.name === prefixEmoji);
return prefixEmoji ? `${emoji?.toString()} ${message}` : `${message}`;
const emoji = emojis.find(emoji => emoji.name === prefixEmoji) || ":white_small_square:";
return prefixEmoji ? `${emoji.toString()} ${message}` : `${message}`;
};
export const getUsername = (user: User) => (user.discriminator === "0" ? user.username : user.tag);
export const replyTranslated = async <T extends CacheType = CacheType>(context: Interaction<T> | Message, key: string, args: Record<string, unknown> | null, options: Options) => {
export const translateContext = async <T extends CacheType = CacheType>(context: Interaction<T> | Message, key: string, args?: Record<string, unknown> | null, options?: Options) => {
const client = useClient();
const locale = options.locale || client.configService.get("defaultLang");
const translated = client.translate(key, {
const inGuild = context.guild ? await getLocale(context.guild.id) : "";
const locale = options?.locale || inGuild || client.configService.get("defaultLang");
const translated = client.i18n.translate(key, {
lng: locale,
...args,
});
const content = formatReply(translated, options.prefixEmoji);
return translated;
};
export const replyTranslated = async <T extends CacheType = CacheType>(context: Interaction<T> | Message, key: string, args?: Record<string, unknown> | null, options?: Options) => {
const translated = await translateContext(context, key, args, options);
const content = formatReply(translated, options?.prefixEmoji);
if (context instanceof BaseInteraction) {
if (!context.isRepliable()) return;
if (options.edit) return await context.editReply({ content });
if (options?.edit) return await context.editReply({ content });
await context.reply({
content,
ephemeral: options.ephemeral || false,
flags: options?.ephemeral ? MessageFlags.Ephemeral : undefined,
});
return;
}
if (options.edit) return await context.edit({ content, allowedMentions: { repliedUser: options.mention || false } });
if (options?.edit) return await context.edit({ content, allowedMentions: { repliedUser: options.mention || false } });
await context.reply({
content,
allowedMentions: { repliedUser: options.mention || false },
allowedMentions: { repliedUser: options?.mention || false },
});
};
export const replySuccess = async <T extends CacheType = CacheType>
(context: Interaction<T> | Message, key: string, args: Record<string, unknown> | null,
options: Options = { prefixEmoji: "success" }) => await replyTranslated(context, key, args, options);
options: Options = { prefixEmoji: "success" }) => await replyTranslated(context, key, args, { prefixEmoji: "success", ...options });
export const replyError = async <T extends CacheType = CacheType>
(context: Interaction<T> | Message, key: string, args: Record<string, unknown> | null,
options: Options = { prefixEmoji: "error" }) => await replyTranslated(context, key, args, options);
options: Options = { prefixEmoji: "error" }) => await replyTranslated(context, key, args, { prefixEmoji: "error", ...options });

View file

@ -49,13 +49,13 @@ export const data = {
},
fields: [
{
name: client.translate("economy/birthdate:HAPPY_BIRTHDAY", {
name: client.i18n.translate("economy/birthdate:HAPPY_BIRTHDAY", {
lng: data.language,
}),
value: client.translate("economy/birthdate:HAPPY_BIRTHDAY_MESSAGE", {
value: client.i18n.translate("economy/birthdate:HAPPY_BIRTHDAY_MESSAGE", {
lng: data.language,
user: user.id,
age: `**${age}** ${getNoun(age, [client.translate("misc:NOUNS:AGE:1", data.language), client.translate("misc:NOUNS:AGE:2", data.language), client.translate("misc:NOUNS:AGE:5", data.language)])}`,
age: `**${age}** ${getNoun(age, [client.i18n.translate("misc:NOUNS:AGE:1", data.language), client.i18n.translate("misc:NOUNS:AGE:2", data.language), client.i18n.translate("misc:NOUNS:AGE:5", data.language)])}`,
}),
},
],

View file

@ -33,21 +33,21 @@ export const data: CronTaskData = {
mustSent.forEach(async r => {
const embed = createEmbed({
author: {
name: client.translate("general/remindme:EMBED_TITLE"),
name: client.i18n.translate("general/remindme:EMBED_TITLE"),
},
fields: [
{
name: client.translate("general/remindme:EMBED_CREATED"),
name: client.i18n.translate("general/remindme:EMBED_CREATED"),
value: `<t:${r.createdAt}:f>`,
inline: true,
},
{
name: client.translate("general/remindme:EMBED_TIME"),
name: client.i18n.translate("general/remindme:EMBED_TIME"),
value: `<t:${r.sendAt}:f>`,
inline: true,
},
{
name: client.translate("common:MESSAGE"),
name: client.i18n.translate("common:MESSAGE"),
value: r.message,
},
],

View file

@ -59,8 +59,8 @@ export default class InternationalizationService {
options?:
| TOptionsBase
| {
[key: string]: string;
},
[key: string]: string;
},
) {
const lng = options?.lng || this.options.defaultLanguage;
return i18next.t(key, { lng, ...options });

View file

@ -29,7 +29,7 @@
"KICK_NOT_DEFINED": "Kick: Not set",
"LIST": "Display server settings",
"SET": "Modify server settings",
"SETTING": "Settings",
"PARAMETER": "Parameter",
"WELCOME_TITLE": "Welcome",
"WELCOME_CONTENT": "Channel: {{channel}}\nCard: {{withImage}}"
}

View file

@ -29,7 +29,7 @@
"KICK_NOT_DEFINED": "Кик: Не назначено",
"LIST": "Показать настройки сервера",
"SET": "Изменить настройки сервера",
"SETTING": "Параметр",
"PARAMETER": "Параметр",
"WELCOME_TITLE": "Приветствие",
"WELCOME_CONTENT": "Канал: {{channel}}\nКарточка: {{withImage}}"
}

View file

@ -29,7 +29,7 @@
"KICK_NOT_DEFINED": "Кік: Не призначено",
"LIST": "Показати налаштування серверу",
"SET": "Змінити налаштування серверу ",
"SETTING": "Параметр",
"PARAMETER": "Параметр",
"WELCOME_TITLE": "Привітання",
"WELCOME_CONTENT": "Канал: {{channel}}\nКартка: {{withImage}}"
}

View file

@ -7,7 +7,7 @@ import ConfigService from "@/services/config/index.js";
import InternationalizationService from "@/services/languages/index.js";
import { SUPER_CONTEXT } from "@/constants/index.js";
import { cacheRemindsData } from "@/types.js";
import { Player } from "discord-player";
// import { Player } from "discord-player";
export class ExtendedClient extends Client<true> {
configService = new ConfigService();
@ -27,15 +27,15 @@ export class ExtendedClient extends Client<true> {
});
constructor(options: ClientOptions) {
if (SUPER_CONTEXT.getStore()) {
return SUPER_CONTEXT.getStore() as ExtendedClient;
}
if (SUPER_CONTEXT.getStore()) return SUPER_CONTEXT.getStore() as ExtendedClient;
super(options);
new Handlers(this);
// @ts-ignore - because ExtendedClient != Client<boolean> from discord.js
new Player(this);
// new Player(this);
SUPER_CONTEXT.enterWith(this);
}