Merge cf832a0e4a
into 12054c35c5
2
.gitignore
vendored
|
@ -3,7 +3,7 @@
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
# Bot Configuration
|
# Bot Configuration
|
||||||
/config.js
|
/config.json
|
||||||
|
|
||||||
# DB
|
# DB
|
||||||
/giveaways.json
|
/giveaways.json
|
||||||
|
|
|
@ -8,4 +8,4 @@
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
"useTabs": true,
|
"useTabs": true,
|
||||||
"parser": "babel"
|
"parser": "babel"
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ If you want to contribute, feel free to fork this repo and making a pull request
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
* [ ] Refactor [tictactoe](./helpers/tictactoe.js).
|
* [ ] Refactor [tictactoe](./helpers/tictactoe.js).
|
||||||
|
* [ ] Finish and release *dashboard-core* submodule.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
const mongoose = require("mongoose"),
|
|
||||||
Schema = mongoose.Schema,
|
|
||||||
languages = require("../languages/language-meta.json");
|
|
||||||
|
|
||||||
module.exports = mongoose.model("Guild", new Schema({
|
|
||||||
id: { type: String },
|
|
||||||
|
|
||||||
membersData: { type: Object, default: {} },
|
|
||||||
members: [{ type: Schema.Types.ObjectId, ref: "Member" }],
|
|
||||||
|
|
||||||
language: { type: String, default: languages.find(l => l.default).name },
|
|
||||||
plugins: {
|
|
||||||
type: Object,
|
|
||||||
default: {
|
|
||||||
welcome: {
|
|
||||||
enabled: false,
|
|
||||||
message: null,
|
|
||||||
channel: null,
|
|
||||||
withImage: null,
|
|
||||||
},
|
|
||||||
goodbye: {
|
|
||||||
enabled: false,
|
|
||||||
message: null,
|
|
||||||
channel: null,
|
|
||||||
withImage: null,
|
|
||||||
},
|
|
||||||
autorole: {
|
|
||||||
enabled: false,
|
|
||||||
role: null,
|
|
||||||
},
|
|
||||||
automod: {
|
|
||||||
enabled: false,
|
|
||||||
ignored: [],
|
|
||||||
},
|
|
||||||
warnsSanctions: {
|
|
||||||
kick: null,
|
|
||||||
ban: null,
|
|
||||||
},
|
|
||||||
monitoring: {
|
|
||||||
messageUpdate: null,
|
|
||||||
messageDelete: null,
|
|
||||||
},
|
|
||||||
tickets: {
|
|
||||||
count: 0,
|
|
||||||
ticketLogs: null,
|
|
||||||
transcriptionLogs: null,
|
|
||||||
ticketsCategory: null,
|
|
||||||
},
|
|
||||||
suggestions: null,
|
|
||||||
reports: null,
|
|
||||||
birthdays: null,
|
|
||||||
modlogs: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
|
@ -1,33 +0,0 @@
|
||||||
const mongoose = require("mongoose");
|
|
||||||
|
|
||||||
module.exports = mongoose.model("Member", new mongoose.Schema({
|
|
||||||
id: { type: String },
|
|
||||||
guildID: { type: String },
|
|
||||||
|
|
||||||
money: { type: Number, default: 0 },
|
|
||||||
workStreak: { type: Number, default: 0 },
|
|
||||||
bankSold: { type: Number, default: 0 },
|
|
||||||
exp: { type: Number, default: 0 },
|
|
||||||
level: { type: Number, default: 0 },
|
|
||||||
transactions: { type: Array, default: [] },
|
|
||||||
|
|
||||||
registeredAt: { type: Number, default: Date.now() },
|
|
||||||
|
|
||||||
cooldowns: {
|
|
||||||
type: Object,
|
|
||||||
default: {
|
|
||||||
work: 0,
|
|
||||||
rob: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
sanctions: { type: Array, default: [] },
|
|
||||||
mute: {
|
|
||||||
type: Object,
|
|
||||||
default: {
|
|
||||||
muted: false,
|
|
||||||
case: null,
|
|
||||||
endDate: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
|
@ -1,39 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
/* The token of your Discord Bot */
|
|
||||||
token: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
|
||||||
/* UserID of your Discord Bot */
|
|
||||||
userId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
|
||||||
/* The URL of the MongoDB database */
|
|
||||||
mongoDB: "mongodb://127.0.0.1:27017/discordbot",
|
|
||||||
/* Set to true for production */
|
|
||||||
/* If set to false, commands only will be registered on the support.id server */
|
|
||||||
production: true,
|
|
||||||
/* Spotify */
|
|
||||||
spotify: {
|
|
||||||
clientId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
|
||||||
clientSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
|
||||||
},
|
|
||||||
/* YouTube Cookie */
|
|
||||||
youtubeCookie: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
|
||||||
/* Support server */
|
|
||||||
support: {
|
|
||||||
id: "123456789098765432", // The ID of the support server
|
|
||||||
logs: "123456789098765432", // The channel's ID for logs on the support server (when bot joins or leaves a guild)
|
|
||||||
invite: "https://discord.gg/discord", // Invite link to the support server
|
|
||||||
},
|
|
||||||
/* Embeds defaults */
|
|
||||||
embed: {
|
|
||||||
color: "#00FF00", // Color
|
|
||||||
footer: {
|
|
||||||
text: "My Discord Bot | v" + require("./package.json").version, // Footer text
|
|
||||||
},
|
|
||||||
},
|
|
||||||
/* Bot's owner informations */
|
|
||||||
owner: {
|
|
||||||
id: "123456789098765432", // The ID of the bot's owner
|
|
||||||
},
|
|
||||||
/* Add your own API keys here */
|
|
||||||
apiKeys: {
|
|
||||||
shlink: "12345678-1234-1234-1234-123456789098" /* Shlink.io REST API key */,
|
|
||||||
},
|
|
||||||
};
|
|
32
config.sample.json
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||||
|
"userId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||||
|
"mongoDB": "mongodb://127.0.0.1:27017/discordbot",
|
||||||
|
"production": true,
|
||||||
|
"spotify": {
|
||||||
|
"clientId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||||
|
"clientSecret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
|
||||||
|
},
|
||||||
|
"youtubeCookie": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||||
|
"support": {
|
||||||
|
"id": "123456789098765432",
|
||||||
|
"logs": "123456789098765432",
|
||||||
|
"invite": "https://discord.gg/discord"
|
||||||
|
},
|
||||||
|
"embed": {
|
||||||
|
"color": "#00FF00",
|
||||||
|
"footer": {
|
||||||
|
"text": "My Discord Bot | v1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"id": "123456789098765432"
|
||||||
|
},
|
||||||
|
"apiKeys": {},
|
||||||
|
"paths": {
|
||||||
|
"commands": "./src/commands",
|
||||||
|
"events": "./src/events",
|
||||||
|
"locales": "./src/services/languages/locales"
|
||||||
|
},
|
||||||
|
"defaultLang": "en-US"
|
||||||
|
}
|
50
eslint.config.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import globals from "globals";
|
||||||
|
import pluginJs from "@eslint/js";
|
||||||
|
import stylisticJs from "@stylistic/eslint-plugin-js";
|
||||||
|
|
||||||
|
/** @type {import("eslint").Linter.Config[]} */
|
||||||
|
export default [
|
||||||
|
pluginJs.configs.recommended,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: globals.node,
|
||||||
|
ecmaVersion: "latest",
|
||||||
|
sourceType: "module",
|
||||||
|
},
|
||||||
|
ignores: ["node_modules", "dashboard"],
|
||||||
|
plugins: {
|
||||||
|
"@stylistic/js": stylisticJs,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"arrow-body-style": ["error", "as-needed"],
|
||||||
|
camelcase: "error",
|
||||||
|
curly: ["error", "multi-line"],
|
||||||
|
eqeqeq: ["error", "always"],
|
||||||
|
"no-console": "off",
|
||||||
|
"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": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
max: 2,
|
||||||
|
maxEOF: 1,
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
|
@ -1,65 +0,0 @@
|
||||||
const { CronJob } = require("cron");
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {import("../base/Client")} client
|
|
||||||
*/
|
|
||||||
async function checkBirthdays(client) {
|
|
||||||
for (const guild of client.guilds.cache.values()) {
|
|
||||||
try {
|
|
||||||
const guildData = await client.getGuildData(guild.id);
|
|
||||||
const channel = guildData.plugins.birthdays ? await client.channels.fetch(guildData.plugins.birthdays) : null;
|
|
||||||
|
|
||||||
if (channel) {
|
|
||||||
const date = new Date();
|
|
||||||
const currentDay = date.getDate();
|
|
||||||
const currentMonth = date.getMonth() + 1;
|
|
||||||
const currentYear = date.getFullYear();
|
|
||||||
|
|
||||||
const users = await client.usersData.find({ birthdate: { $gt: 1 } });
|
|
||||||
|
|
||||||
for (const user of users) {
|
|
||||||
if (!guild.members.cache.has(user.id)) continue;
|
|
||||||
|
|
||||||
const userDate = new Date(user.birthdate).getFullYear() <= 1970 ? new Date(user.birthdate * 1000) : new Date(user.birthdate);
|
|
||||||
const day = userDate.getDate();
|
|
||||||
const month = userDate.getMonth() + 1;
|
|
||||||
const year = userDate.getFullYear();
|
|
||||||
const age = currentYear - year;
|
|
||||||
|
|
||||||
if (currentMonth === month && currentDay === day) {
|
|
||||||
const embed = client.embed({
|
|
||||||
author: client.user.getUsername(),
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: client.translate("economy/birthdate:HAPPY_BIRTHDAY", null, guildData.language),
|
|
||||||
value: client.translate(
|
|
||||||
"economy/birthdate:HAPPY_BIRTHDAY_MESSAGE",
|
|
||||||
{
|
|
||||||
user: user.id,
|
|
||||||
age: `**${age}** ${client.functions.getNoun(
|
|
||||||
age,
|
|
||||||
client.translate("misc:NOUNS:AGE:1", null, guildData.language),
|
|
||||||
client.translate("misc:NOUNS:AGE:2", null, guildData.language),
|
|
||||||
client.translate("misc:NOUNS:AGE:5", null, guildData.language),
|
|
||||||
)}`,
|
|
||||||
},
|
|
||||||
guildData.language,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
await channel.send({ embeds: [embed] }).then(m => m.react("🎉"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (e.code === 10003) console.log(`No channel found for ${guild.name}`);
|
|
||||||
else console.error(`Error processing birthdays for guild "${guild.name}":`, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.init = async client => new CronJob("0 5 * * *", checkBirthdays(client), null, true, "Europe/Moscow");
|
|
||||||
module.exports.run = async client => await checkBirthdays(client);
|
|
|
@ -1,60 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {import("../base/Client")} client
|
|
||||||
*/
|
|
||||||
async function checkReminds(client) {
|
|
||||||
client.usersData.find({ reminds: { $gt: [] } }).then(users => {
|
|
||||||
for (const user of users) {
|
|
||||||
if (!client.users.cache.has(user.id)) client.users.fetch(user.id);
|
|
||||||
|
|
||||||
client.databaseCache.usersReminds.set(user.id, user);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
client.databaseCache.usersReminds.forEach(async user => {
|
|
||||||
const cachedUser = client.users.cache.get(user.id);
|
|
||||||
|
|
||||||
if (cachedUser) {
|
|
||||||
const dateNow = Math.floor(Date.now() / 1000),
|
|
||||||
reminds = user.reminds,
|
|
||||||
mustSent = reminds.filter(r => r.sendAt < dateNow);
|
|
||||||
|
|
||||||
if (mustSent.length > 0) {
|
|
||||||
mustSent.forEach(r => {
|
|
||||||
const embed = client.embed({
|
|
||||||
author: client.translate("general/remindme:EMBED_TITLE"),
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: client.translate("general/remindme:EMBED_CREATED"),
|
|
||||||
value: `<t:${r.createdAt}:f>`,
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: client.translate("general/remindme:EMBED_TIME"),
|
|
||||||
value: `<t:${r.sendAt}:f>`,
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: client.translate("common:MESSAGE"),
|
|
||||||
value: r.message,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
cachedUser.send({
|
|
||||||
embeds: [embed],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
user.reminds = user.reminds.filter(r => r.sendAt >= dateNow);
|
|
||||||
|
|
||||||
await user.save();
|
|
||||||
|
|
||||||
if (user.reminds.length === 0) client.databaseCache.usersReminds.delete(user.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.init = async client => setInterval(async () => await checkReminds(client), 1000);
|
|
||||||
module.exports.run = async client => await checkReminds(client);
|
|
|
@ -1,147 +0,0 @@
|
||||||
const moment = require("moment");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
/**
|
|
||||||
* Asynchronously iterates over a collection and executes a callback function for each item.
|
|
||||||
*
|
|
||||||
* @param {any[]} collection - The collection to iterate over.
|
|
||||||
* @param {(item: any) => Promise<void>} callback - The async callback function to execute for each item in the collection.
|
|
||||||
* @returns {Promise<void>} A promise that resolves when all items in the collection have been processed.
|
|
||||||
*/
|
|
||||||
async asyncForEach(collection, callback) {
|
|
||||||
const allPromises = collection.map(async key => {
|
|
||||||
await callback(key);
|
|
||||||
});
|
|
||||||
|
|
||||||
return await Promise.all(allPromises);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sorts an array by the specified key in ascending order.
|
|
||||||
*
|
|
||||||
* @param {any[]} array - The array to sort.
|
|
||||||
* @param {string} key - The key to sort the array by.
|
|
||||||
* @returns {any[]} The sorted array.
|
|
||||||
*/
|
|
||||||
sortByKey(array, key) {
|
|
||||||
return array.sort(function (a, b) {
|
|
||||||
const x = a[key];
|
|
||||||
const y = b[key];
|
|
||||||
return x < y ? 1 : x > y ? -1 : 0;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shuffles the elements of the provided array in-place.
|
|
||||||
*
|
|
||||||
* @param {any[]} pArray - The array to shuffle.
|
|
||||||
* @returns {any[]} The shuffled array.
|
|
||||||
*/
|
|
||||||
shuffle(pArray) {
|
|
||||||
const array = [];
|
|
||||||
|
|
||||||
pArray.forEach(element => array.push(element));
|
|
||||||
|
|
||||||
let currentIndex = array.length,
|
|
||||||
temporaryValue,
|
|
||||||
randomIndex;
|
|
||||||
|
|
||||||
while (currentIndex !== 0) {
|
|
||||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
|
||||||
currentIndex -= 1;
|
|
||||||
|
|
||||||
temporaryValue = array[currentIndex];
|
|
||||||
array[currentIndex] = array[randomIndex];
|
|
||||||
array[randomIndex] = temporaryValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return array;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a random integer between the specified minimum and maximum values (inclusive).
|
|
||||||
*
|
|
||||||
* @param {number} [min=0] - The minimum value (inclusive).
|
|
||||||
* @param {number} [max=100] - The maximum value (inclusive).
|
|
||||||
* @returns {number} A random integer between min and max.
|
|
||||||
*/
|
|
||||||
randomNum(min = 0, max = 100) {
|
|
||||||
min = Math.floor(min);
|
|
||||||
max = Math.floor(max);
|
|
||||||
|
|
||||||
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats a date for the specified client and locale.
|
|
||||||
*
|
|
||||||
* @param {Object} client - The client object containing language data.
|
|
||||||
* @param {string} date - The date to format.
|
|
||||||
* @param {string} [format=null] - The date format to use. If not provided, the default format for the client's language will be used.
|
|
||||||
* @param {string} [locale=client.defaultLanguage.name] - The locale to use for formatting the date.
|
|
||||||
* @returns {string} The formatted date.
|
|
||||||
*/
|
|
||||||
printDate(client, date, format = null, locale = client.defaultLanguage.name) {
|
|
||||||
const languageData = client.languages.find(language => language.name === locale);
|
|
||||||
if (format === "" || format === null) format = languageData.defaultMomentFormat;
|
|
||||||
|
|
||||||
return moment(new Date(date)).locale(languageData.moment).format(format);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats a time value relative to the current time.
|
|
||||||
*
|
|
||||||
* @param {Object} client - The client object containing language data.
|
|
||||||
* @param {string|number|Date} time - The time value to format.
|
|
||||||
* @param {boolean} [type=false] - If true, formats the time as "X time ago", otherwise formats it as "in X time".
|
|
||||||
* @param {boolean} [prefix=true] - If true, includes a prefix like "in" or "ago" in the formatted time.
|
|
||||||
* @param {string} [locale=client.defaultLanguage.name] - The locale to use for formatting the time.
|
|
||||||
* @returns {string} The formatted time value.
|
|
||||||
*/
|
|
||||||
convertTime(client, time, type = false, prefix = true, locale = client.defaultLanguage.name) {
|
|
||||||
const languageData = client.languages.find(language => language.name === locale);
|
|
||||||
const m = moment(time).locale(languageData.moment);
|
|
||||||
|
|
||||||
return type ? m.toNow(!prefix) : m.fromNow(!prefix);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the appropriate noun form based on the given number and noun forms.
|
|
||||||
*
|
|
||||||
* @param {number} number - The number to use for determining the noun form.
|
|
||||||
* @param {string} one - The noun form for the singular case.
|
|
||||||
* @param {string} two - The noun form for the dual case.
|
|
||||||
* @param {string} five - The noun form for the plural case.
|
|
||||||
* @returns {string} The appropriate noun form based on the given number.
|
|
||||||
*/
|
|
||||||
getNoun(number, one, two, five) {
|
|
||||||
let n = Math.abs(number);
|
|
||||||
n %= 100;
|
|
||||||
if (n >= 5 && n <= 20) return five;
|
|
||||||
n %= 10;
|
|
||||||
|
|
||||||
if (n === 1) return one;
|
|
||||||
if (n >= 2 && n <= 4) return two;
|
|
||||||
|
|
||||||
return five;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to apply text on a canvas with dynamic font size based on the width constraint.
|
|
||||||
*
|
|
||||||
* @param {import("@napi-rs/canvas").Canvas} canvas - The canvas object where the text will be applied.
|
|
||||||
* @param {string} text - The string of text that needs to be applied on the canvas.
|
|
||||||
* @param {number} defaultFontSize - The initial font size for the text. It is expected to decrease with each iteration.
|
|
||||||
* @param {number} width - The maximum width that the text can occupy before it has to shrink down.
|
|
||||||
* @param {string} font - The name of the font used for drawing the text on the canvas.
|
|
||||||
*
|
|
||||||
* @return {string} - The final calculated font size in a format '<size>px <family>'.
|
|
||||||
*/
|
|
||||||
applyText(canvas, text, defaultFontSize, width, font) {
|
|
||||||
const ctx = canvas.getContext("2d");
|
|
||||||
do ctx.font = `${(defaultFontSize -= 1)}px ${font}`;
|
|
||||||
while (ctx.measureText(text).width > width);
|
|
||||||
|
|
||||||
return ctx.font;
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,51 +0,0 @@
|
||||||
const i18next = require("i18next"),
|
|
||||||
Backend = require("i18next-fs-backend"),
|
|
||||||
path = require("path"),
|
|
||||||
fs = require("fs").promises;
|
|
||||||
|
|
||||||
async function walkDirectory(dir, namespaces = [], folderName = "") {
|
|
||||||
const files = await fs.readdir(dir);
|
|
||||||
|
|
||||||
const languages = [];
|
|
||||||
for (const file of files) {
|
|
||||||
const stat = await fs.stat(path.join(dir, file));
|
|
||||||
if (stat.isDirectory()) {
|
|
||||||
const isLanguage = file.includes("-");
|
|
||||||
if (isLanguage) languages.push(file);
|
|
||||||
|
|
||||||
const folder = await walkDirectory(path.join(dir, file), namespaces, isLanguage ? "" : `${file}/`);
|
|
||||||
|
|
||||||
namespaces = folder.namespaces;
|
|
||||||
} else {
|
|
||||||
namespaces.push(`${folderName}${file.substr(0, file.length - 5)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
namespaces: [...new Set(namespaces)],
|
|
||||||
languages,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = async () => {
|
|
||||||
const options = {
|
|
||||||
loadPath: path.resolve(__dirname, "../languages/{{lng}}/{{ns}}.json"),
|
|
||||||
};
|
|
||||||
|
|
||||||
const { namespaces, languages } = await walkDirectory(path.resolve(__dirname, "../languages/"));
|
|
||||||
|
|
||||||
i18next.use(Backend);
|
|
||||||
|
|
||||||
await i18next.init({
|
|
||||||
backend: options,
|
|
||||||
debug: false,
|
|
||||||
fallbackLng: "en-US",
|
|
||||||
initImmediate: false,
|
|
||||||
interpolation: { escapeValue: false },
|
|
||||||
load: "all",
|
|
||||||
ns: namespaces,
|
|
||||||
preload: languages,
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Map(languages.map(item => [item, i18next.getFixedT(item)]));
|
|
||||||
};
|
|
|
@ -1,52 +0,0 @@
|
||||||
const { bgBlue, black, green } = require("chalk");
|
|
||||||
|
|
||||||
function dateTimePad(value, digits) {
|
|
||||||
let number = value;
|
|
||||||
while (number.toString().length < digits) number = "0" + number;
|
|
||||||
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function format(tDate) {
|
|
||||||
return (
|
|
||||||
dateTimePad(tDate.getDate(), 2) +
|
|
||||||
"-" +
|
|
||||||
dateTimePad(tDate.getMonth() + 1, 2) +
|
|
||||||
"-" +
|
|
||||||
dateTimePad(tDate.getFullYear(), 2) +
|
|
||||||
" " +
|
|
||||||
dateTimePad(tDate.getHours(), 2) +
|
|
||||||
":" +
|
|
||||||
dateTimePad(tDate.getMinutes(), 2) +
|
|
||||||
":" +
|
|
||||||
dateTimePad(tDate.getSeconds(), 2) +
|
|
||||||
"." +
|
|
||||||
dateTimePad(tDate.getMilliseconds(), 3)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = class Logger {
|
|
||||||
static log(content) {
|
|
||||||
return console.log(`[${format(new Date(Date.now()))}]: ${bgBlue("LOG")} ${content}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
static warn(content) {
|
|
||||||
return console.log(`[${format(new Date(Date.now()))}]: ${black.bgYellow("WARN")} ${content}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
static error(content) {
|
|
||||||
return console.log(`[${format(new Date(Date.now()))}]: ${black.bgRed("ERROR")} ${content}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
static debug(content) {
|
|
||||||
return console.log(`[${format(new Date(Date.now()))}]: ${green("DEBUG")} ${content}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
static cmd(content) {
|
|
||||||
return console.log(`[${format(new Date(Date.now()))}]: ${black.bgWhite("CMD")} ${content}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ready(content) {
|
|
||||||
return console.log(`[${format(new Date(Date.now()))}]: ${black.bgGreen("READY")} ${content}`);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,599 +0,0 @@
|
||||||
// Thanks to simply-djs for this =)
|
|
||||||
// TODO: Refactor this please...
|
|
||||||
|
|
||||||
const { ButtonBuilder, ActionRowBuilder, ButtonStyle, ComponentType } = require("discord.js");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import("discord.js").ChatInputCommandInteraction} interaction
|
|
||||||
* @param {any[]} options Array with options (everything is optional)
|
|
||||||
* @param {string} options.userSlash Name of the user option in the interaction
|
|
||||||
* @param {string} options.embedFooter Game's embed footer
|
|
||||||
* @param {string} options.embedColor Game's embed color
|
|
||||||
* @param {string} options.timeoutEmbedColor Game's embed timeout color
|
|
||||||
* @param {string} options.xEmoji Emoji for X
|
|
||||||
* @param {string} options.oEmoji Emoji for O
|
|
||||||
* @param {string} options.idleEmoji Emoji for "nothing"
|
|
||||||
* @returns {Promise<import("discord.js").User>}
|
|
||||||
*/
|
|
||||||
async function tictactoe(interaction, options = {}) {
|
|
||||||
// eslint-disable-next-line no-async-promise-executor
|
|
||||||
return new Promise(async resolve => {
|
|
||||||
try {
|
|
||||||
const { client } = interaction;
|
|
||||||
let opponent;
|
|
||||||
|
|
||||||
if (interaction.commandId) {
|
|
||||||
opponent = interaction.options.getUser(options.userSlash || "user");
|
|
||||||
|
|
||||||
if (!opponent)
|
|
||||||
return interaction.reply({
|
|
||||||
content: interaction.translate("fun/tictactoe:NO_USER"),
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (opponent.bot)
|
|
||||||
return interaction.reply({
|
|
||||||
content: interaction.translate("fun/tictactoe:BOT_USER"),
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (opponent.id == (interaction.user ? interaction.user : interaction.author).id)
|
|
||||||
return interaction.reply({
|
|
||||||
content: interaction.translate("misc:CANT_YOURSELF"),
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
} else if (!interaction.commandId) {
|
|
||||||
opponent = interaction.mentions.members.first()?.user;
|
|
||||||
|
|
||||||
if (!opponent)
|
|
||||||
return interaction.reply({
|
|
||||||
content: interaction.translate("fun/tictactoe:NO_USER"),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (opponent.bot)
|
|
||||||
return interaction.reply({
|
|
||||||
content: interaction.translate("fun/tictactoe:BOT_USER"),
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (opponent.id === interaction.member.id)
|
|
||||||
return interaction.reply({
|
|
||||||
content: interaction.translate("misc:CANT_YOURSELF"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const footer = options.embedFooter || client.config.embed.footer,
|
|
||||||
color = options.embedColor || client.config.embed.color,
|
|
||||||
user = interaction.user ? interaction.user : interaction.author;
|
|
||||||
|
|
||||||
const acceptEmbed = client.embed({
|
|
||||||
author: {
|
|
||||||
name: user.getUsername(),
|
|
||||||
iconURL: user.displayAvatarURL(),
|
|
||||||
},
|
|
||||||
title: interaction.translate("fun/tictactoe:REQUEST_WAIT", {
|
|
||||||
user: opponent.getUsername(),
|
|
||||||
}),
|
|
||||||
color,
|
|
||||||
footer,
|
|
||||||
});
|
|
||||||
|
|
||||||
const accept = new ButtonBuilder().setLabel(interaction.translate("common:ACCEPT")).setStyle(ButtonStyle.Success).setCustomId("acceptttt");
|
|
||||||
const decline = new ButtonBuilder().setLabel(interaction.translate("common:DECLINE")).setStyle(ButtonStyle.Danger).setCustomId("declinettt");
|
|
||||||
const accep = new ActionRowBuilder().addComponents([accept, decline]);
|
|
||||||
|
|
||||||
const m = await interaction.reply({
|
|
||||||
content: interaction.translate("fun/tictactoe:INVITE_USER", {
|
|
||||||
opponent: opponent.id,
|
|
||||||
}),
|
|
||||||
embeds: [acceptEmbed],
|
|
||||||
components: [accep],
|
|
||||||
fetchReply: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const collector = m.createMessageComponentCollector({
|
|
||||||
componentType: ComponentType.Button,
|
|
||||||
time: 30 * 1000,
|
|
||||||
});
|
|
||||||
|
|
||||||
collector.on("collect", async button => {
|
|
||||||
if (button.user.id !== opponent.id)
|
|
||||||
return button.reply({
|
|
||||||
content: interaction.translate("fun/tictactoe:REQUEST_SEND", {
|
|
||||||
opponent: opponent.id,
|
|
||||||
}),
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (button.customId == "declinettt") {
|
|
||||||
button.deferUpdate();
|
|
||||||
return collector.stop("decline");
|
|
||||||
} else if (button.customId == "acceptttt") {
|
|
||||||
button.deferUpdate();
|
|
||||||
collector.stop();
|
|
||||||
|
|
||||||
const fighters = [(interaction.user ? interaction.user : interaction.author).id, opponent.id].sort(() => (Math.random() > 0.5 ? 1 : -1));
|
|
||||||
|
|
||||||
const x_emoji = options.xEmoji || "❌";
|
|
||||||
const o_emoji = options.oEmoji || "⭕";
|
|
||||||
|
|
||||||
const dashmoji = options.idleEmoji || "➖";
|
|
||||||
|
|
||||||
const Args = {
|
|
||||||
user: 0,
|
|
||||||
a1: {
|
|
||||||
style: ButtonStyle.Secondary,
|
|
||||||
emoji: dashmoji,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
a2: {
|
|
||||||
style: ButtonStyle.Secondary,
|
|
||||||
emoji: dashmoji,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
a3: {
|
|
||||||
style: ButtonStyle.Secondary,
|
|
||||||
emoji: dashmoji,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
b1: {
|
|
||||||
style: ButtonStyle.Secondary,
|
|
||||||
emoji: dashmoji,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
b2: {
|
|
||||||
style: ButtonStyle.Secondary,
|
|
||||||
emoji: dashmoji,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
b3: {
|
|
||||||
style: ButtonStyle.Secondary,
|
|
||||||
emoji: dashmoji,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
c1: {
|
|
||||||
style: ButtonStyle.Secondary,
|
|
||||||
emoji: dashmoji,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
c2: {
|
|
||||||
style: ButtonStyle.Secondary,
|
|
||||||
emoji: dashmoji,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
c3: {
|
|
||||||
style: ButtonStyle.Secondary,
|
|
||||||
emoji: dashmoji,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const epm = client.embed({
|
|
||||||
title: interaction.translate("fun/tictactoe:DESCRIPTION"),
|
|
||||||
color,
|
|
||||||
footer,
|
|
||||||
});
|
|
||||||
|
|
||||||
let msg;
|
|
||||||
if (interaction.commandId)
|
|
||||||
msg = await interaction.editReply({
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
interaction.translate("fun/tictactoe:WAITING", {
|
|
||||||
user: Args.userid,
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
else if (!interaction.commandId)
|
|
||||||
msg = await button.message.edit({
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
interaction.translate("fun/tictactoe:WAITING", {
|
|
||||||
user: Args.userid,
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
await ttt(msg);
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-inner-declarations
|
|
||||||
async function ttt(m) {
|
|
||||||
Args.userid = fighters[Args.user];
|
|
||||||
const won = {
|
|
||||||
"<:O_:863314110560993340>": false,
|
|
||||||
"<:X_:863314044781723668>": false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const a1 = new ButtonBuilder().setStyle(Args.a1.style).setEmoji(Args.a1.emoji).setCustomId("a1").setDisabled(Args.a1.disabled);
|
|
||||||
const a2 = new ButtonBuilder().setStyle(Args.a2.style).setEmoji(Args.a2.emoji).setCustomId("a2").setDisabled(Args.a2.disabled);
|
|
||||||
const a3 = new ButtonBuilder().setStyle(Args.a3.style).setEmoji(Args.a3.emoji).setCustomId("a3").setDisabled(Args.a3.disabled);
|
|
||||||
const b1 = new ButtonBuilder().setStyle(Args.b1.style).setEmoji(Args.b1.emoji).setCustomId("b1").setDisabled(Args.b1.disabled);
|
|
||||||
const b2 = new ButtonBuilder().setStyle(Args.b2.style).setEmoji(Args.b2.emoji).setCustomId("b2").setDisabled(Args.b2.disabled);
|
|
||||||
const b3 = new ButtonBuilder().setStyle(Args.b3.style).setEmoji(Args.b3.emoji).setCustomId("b3").setDisabled(Args.b3.disabled);
|
|
||||||
const c1 = new ButtonBuilder().setStyle(Args.c1.style).setEmoji(Args.c1.emoji).setCustomId("c1").setDisabled(Args.c1.disabled);
|
|
||||||
const c2 = new ButtonBuilder().setStyle(Args.c2.style).setEmoji(Args.c2.emoji).setCustomId("c2").setDisabled(Args.c2.disabled);
|
|
||||||
const c3 = new ButtonBuilder().setStyle(Args.c3.style).setEmoji(Args.c3.emoji).setCustomId("c3").setDisabled(Args.c3.disabled);
|
|
||||||
const a = new ActionRowBuilder().addComponents([a1, a2, a3]);
|
|
||||||
const b = new ActionRowBuilder().addComponents([b1, b2, b3]);
|
|
||||||
const c = new ActionRowBuilder().addComponents([c1, c2, c3]);
|
|
||||||
const buttons = [a, b, c];
|
|
||||||
|
|
||||||
if (Args.a1.emoji == o_emoji && Args.b1.emoji == o_emoji && Args.c1.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
|
|
||||||
if (Args.a2.emoji == o_emoji && Args.b2.emoji == o_emoji && Args.c2.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
|
|
||||||
if (Args.a3.emoji == o_emoji && Args.b3.emoji == o_emoji && Args.c3.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
|
|
||||||
if (Args.a1.emoji == o_emoji && Args.b2.emoji == o_emoji && Args.c3.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
|
|
||||||
if (Args.a3.emoji == o_emoji && Args.b2.emoji == o_emoji && Args.c1.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
|
|
||||||
if (Args.a1.emoji == o_emoji && Args.a2.emoji == o_emoji && Args.a3.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
|
|
||||||
if (Args.b1.emoji == o_emoji && Args.b2.emoji == o_emoji && Args.b3.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
|
|
||||||
if (Args.c1.emoji == o_emoji && Args.c2.emoji == o_emoji && Args.c3.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
|
|
||||||
if (won["<:O_:863314110560993340>"] != false)
|
|
||||||
if (Args.user == 0) {
|
|
||||||
const won = await client.users.fetch(fighters[1]).catch(console.error);
|
|
||||||
resolve(won);
|
|
||||||
|
|
||||||
if (options.resultBtn === true)
|
|
||||||
return m
|
|
||||||
.edit({
|
|
||||||
content: interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[1],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
components: buttons,
|
|
||||||
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[1],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
else if (!options.resultBtn || options.resultBtn === false)
|
|
||||||
return m
|
|
||||||
.edit({
|
|
||||||
content: interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[1],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
`${interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[1],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
})}\n\`\`\`\n${Args.a1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.a2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.a3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n${Args.b1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.b2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.b3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n${Args.c1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.c2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.c3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n\`\`\``.replaceAll(dashmoji, "➖"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
components: [],
|
|
||||||
});
|
|
||||||
} else if (Args.user == 1) {
|
|
||||||
const won = await client.users.fetch(fighters[0]).catch(console.error);
|
|
||||||
resolve(won);
|
|
||||||
|
|
||||||
if (options.resultBtn === true)
|
|
||||||
return m
|
|
||||||
.edit({
|
|
||||||
content: interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[0],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
components: buttons,
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[0],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
else if (!options.resultBtn || options.resultBtn === false)
|
|
||||||
return m
|
|
||||||
.edit({
|
|
||||||
content: interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[0],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
`${interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[0],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
})}\n\`\`\`\n${Args.a1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.a2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.a3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n${Args.b1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.b2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.b3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n${Args.c1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.c2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.c3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n\`\`\``.replaceAll(dashmoji, "➖"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
components: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Args.a1.emoji == x_emoji && Args.b1.emoji == x_emoji && Args.c1.emoji == x_emoji) won["<:X_:863314044781723668>"] = true;
|
|
||||||
if (Args.a2.emoji == x_emoji && Args.b2.emoji == x_emoji && Args.c2.emoji == x_emoji) won["<:X_:863314044781723668>"] = true;
|
|
||||||
if (Args.a3.emoji == x_emoji && Args.b3.emoji == x_emoji && Args.c3.emoji == x_emoji) won["<:X_:863314044781723668>"] = true;
|
|
||||||
if (Args.a1.emoji == x_emoji && Args.b2.emoji == x_emoji && Args.c3.emoji == x_emoji) won["<:X_:863314044781723668>"] = true;
|
|
||||||
if (Args.a3.emoji == x_emoji && Args.b2.emoji == x_emoji && Args.c1.emoji == x_emoji) won["<:X_:863314044781723668>"] = true;
|
|
||||||
if (Args.a1.emoji == x_emoji && Args.a2.emoji == x_emoji && Args.a3.emoji == x_emoji) won["<:X_:863314044781723668>"] = true;
|
|
||||||
if (Args.b1.emoji == x_emoji && Args.b2.emoji == x_emoji && Args.b3.emoji == x_emoji) won["<:X_:863314044781723668>"] = true;
|
|
||||||
if (Args.c1.emoji == x_emoji && Args.c2.emoji == x_emoji && Args.c3.emoji == x_emoji) won["<:X_:863314044781723668>"] = true;
|
|
||||||
if (won["<:X_:863314044781723668>"] != false)
|
|
||||||
if (Args.user == 0) {
|
|
||||||
const won = await client.users.fetch(fighters[1]).catch(console.error);
|
|
||||||
resolve(won);
|
|
||||||
|
|
||||||
if (options.resultBtn === true)
|
|
||||||
return m
|
|
||||||
.edit({
|
|
||||||
content: interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[1],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
components: buttons,
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[1],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
else if (!options.resultBtn || options.resultBtn === false)
|
|
||||||
return m
|
|
||||||
.edit({
|
|
||||||
content: interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[1],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
`${interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[1],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
})}\n\`\`\`\n${Args.a1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.a2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.a3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n${Args.b1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.b2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.b3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n${Args.c1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.c2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.c3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n\`\`\``.replaceAll(dashmoji, "➖"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
components: [],
|
|
||||||
});
|
|
||||||
} else if (Args.user == 1) {
|
|
||||||
const won = await client.users.fetch(fighters[0]).catch(console.error);
|
|
||||||
resolve(won);
|
|
||||||
|
|
||||||
if (options.resultBtn === true)
|
|
||||||
return m
|
|
||||||
.edit({
|
|
||||||
content: interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[0],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
components: buttons,
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[0],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
else
|
|
||||||
return m
|
|
||||||
.edit({
|
|
||||||
content: interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[0],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
}),
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
`${interaction.translate("fun/tictactoe:WON", {
|
|
||||||
winner: fighters[0],
|
|
||||||
emoji: client.emojis.cache.get(o_emoji) || "⭕",
|
|
||||||
})}\n\`\`\`\n${Args.a1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.a2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.a3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n${Args.b1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.b2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.b3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n${Args.c1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.c2.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.c3.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")}\n\`\`\``.replaceAll(dashmoji, "➖"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
components: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
m.edit({
|
|
||||||
content: `<@${Args.userid}>`,
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
interaction.translate("fun/tictactoe:WAITING", {
|
|
||||||
user: Args.userid,
|
|
||||||
emoji: Args.user == 0 ? `${client.emojis.cache.get(o_emoji) || "⭕"}` : `${client.emojis.cache.get(x_emoji) || "❌"}`,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
components: [a, b, c],
|
|
||||||
});
|
|
||||||
|
|
||||||
const collector = m.createMessageComponentCollector({
|
|
||||||
componentType: ComponentType.Button,
|
|
||||||
max: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
collector.on("collect", b => {
|
|
||||||
if (b.user.id !== Args.userid) {
|
|
||||||
b.reply({
|
|
||||||
content: interaction.translate("fun/tictactoe:CANT_PLAY"),
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
ttt(m);
|
|
||||||
} else {
|
|
||||||
if (Args.user == 0) {
|
|
||||||
Args.user = 1;
|
|
||||||
Args[b.customId] = {
|
|
||||||
style: ButtonStyle.Success,
|
|
||||||
emoji: o_emoji,
|
|
||||||
disabled: true,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
Args.user = 0;
|
|
||||||
Args[b.customId] = {
|
|
||||||
style: ButtonStyle.Danger,
|
|
||||||
emoji: x_emoji,
|
|
||||||
disabled: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
b.deferUpdate();
|
|
||||||
const map = (obj, fun) =>
|
|
||||||
Object.entries(obj).reduce(
|
|
||||||
(prev, [key, value]) => ({
|
|
||||||
...prev,
|
|
||||||
[key]: fun(key, value),
|
|
||||||
}),
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
const objectFilter = (obj, predicate) =>
|
|
||||||
Object.keys(obj)
|
|
||||||
.filter(key => predicate(obj[key]))
|
|
||||||
.reduce((res, key) => ((res[key] = obj[key]), res), {});
|
|
||||||
const Brgs = objectFilter(
|
|
||||||
map(Args, (_, fruit) => fruit.emoji == dashmoji),
|
|
||||||
num => num == true,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Object.keys(Brgs).length == 0) {
|
|
||||||
if (Args.a1.emoji == o_emoji && Args.b1.emoji == o_emoji && Args.c1.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
if (Args.a2.emoji == o_emoji && Args.b2.emoji == o_emoji && Args.c2.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
if (Args.a3.emoji == o_emoji && Args.b3.emoji == o_emoji && Args.c3.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
if (Args.a1.emoji == o_emoji && Args.b2.emoji == o_emoji && Args.c3.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
if (Args.a3.emoji == o_emoji && Args.b2.emoji == o_emoji && Args.c1.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
if (Args.a1.emoji == o_emoji && Args.a2.emoji == o_emoji && Args.a3.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
if (Args.b1.emoji == o_emoji && Args.b2.emoji == o_emoji && Args.b3.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
if (Args.c1.emoji == o_emoji && Args.c2.emoji == o_emoji && Args.c3.emoji == o_emoji) won["<:O_:863314110560993340>"] = true;
|
|
||||||
|
|
||||||
if (won["<:O_:863314110560993340>"] == true) return ttt(m);
|
|
||||||
else if (won["<:X_:863314044781723668>"] == true) return;
|
|
||||||
else {
|
|
||||||
ttt(m);
|
|
||||||
|
|
||||||
if (options.resultBtn === true)
|
|
||||||
return m
|
|
||||||
.edit({
|
|
||||||
content: interaction.translate("fun/tictactoe:TIE"),
|
|
||||||
embeds: [epm.setDescription(interaction.translate("fun/tictactoe:TIE_DESC"))],
|
|
||||||
});
|
|
||||||
else
|
|
||||||
return m
|
|
||||||
.edit({
|
|
||||||
content: interaction.translate("fun/tictactoe:TIE"),
|
|
||||||
embeds: [
|
|
||||||
epm.setDescription(
|
|
||||||
`${interaction.translate("fun/tictactoe:TIE_DESC")}!\n\`\`\`\n${Args.a1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.a2.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")} | ${Args.a3.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")}\n${Args.b1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.b2.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")} | ${Args.b3.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")}\n${Args.c1.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")} | ${Args.c2.emoji
|
|
||||||
.replace(o_emoji, "⭕")
|
|
||||||
.replace(x_emoji, "❌")} | ${Args.c3.emoji.replace(o_emoji, "⭕").replace(x_emoji, "❌")}\n\`\`\``.replaceAll(dashmoji, "➖"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
components: [],
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ttt(m);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
collector.on("end", (collected, reason) => {
|
|
||||||
if (collected.size === 0 && reason == "time")
|
|
||||||
m.edit({
|
|
||||||
content: interaction.translate("fun/tictactoe:NO_ANSWER", {
|
|
||||||
user: Args.userid,
|
|
||||||
}),
|
|
||||||
components: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
collector.on("end", (_, reason) => {
|
|
||||||
if (reason == "time") {
|
|
||||||
const embed = client.embed({
|
|
||||||
author: {
|
|
||||||
name: user.getUsername(),
|
|
||||||
iconURL: user.displayAvatarURL(),
|
|
||||||
},
|
|
||||||
title: interaction.translate("fun/tictactoe:NO_ANSWER_TITLE"),
|
|
||||||
description: interaction.translate("misc:TIMED_OUT"),
|
|
||||||
color: options.timeoutEmbedColor || "#C90000",
|
|
||||||
footer,
|
|
||||||
});
|
|
||||||
|
|
||||||
m.interaction.editReply({
|
|
||||||
content: interaction.translate("fun/tictactoe:NOT_ANSWERED", {
|
|
||||||
user: opponent.id,
|
|
||||||
}),
|
|
||||||
embeds: [embed],
|
|
||||||
components: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (reason == "decline") {
|
|
||||||
const embed = client.embed({
|
|
||||||
author: {
|
|
||||||
name: user.getUsername(),
|
|
||||||
iconURL: user.displayAvatarURL(),
|
|
||||||
},
|
|
||||||
title: interaction.translate("fun/tictactoe:CANCELED"),
|
|
||||||
description: interaction.translate("fun/tictactoe:CANCELED_DESC", {
|
|
||||||
user: opponent.id,
|
|
||||||
}),
|
|
||||||
color: options.timeoutEmbedColor || "#C90000",
|
|
||||||
footer,
|
|
||||||
});
|
|
||||||
|
|
||||||
m.interaction.editReply({
|
|
||||||
embeds: [embed],
|
|
||||||
components: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.log("TicTacToe errored:", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = tictactoe;
|
|
29
index.js
|
@ -1,29 +0,0 @@
|
||||||
require("./helpers/extenders");
|
|
||||||
|
|
||||||
const { GatewayIntentBits } = require("discord.js"),
|
|
||||||
Client = require("./base/Client");
|
|
||||||
|
|
||||||
const client = new Client({
|
|
||||||
intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildModeration, GatewayIntentBits.GuildEmojisAndStickers, GatewayIntentBits.GuildIntegrations, GatewayIntentBits.GuildInvites, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildPresences, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessageReactions, GatewayIntentBits.GuildMessageTyping, GatewayIntentBits.MessageContent, GatewayIntentBits.DirectMessageTyping, GatewayIntentBits.DirectMessages, GatewayIntentBits.DirectMessageReactions ],
|
|
||||||
allowedMentions: { parse: ["everyone", "roles", "users"] },
|
|
||||||
});
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
console.time("botReady");
|
|
||||||
|
|
||||||
client.translations = await require("./helpers/languages")();
|
|
||||||
|
|
||||||
await client.loadEvents("../events");
|
|
||||||
await client.loadCommands("../commands");
|
|
||||||
await client.init();
|
|
||||||
})();
|
|
||||||
|
|
||||||
client
|
|
||||||
.on("disconnect", () => client.logger.warn("Bot disconnected."))
|
|
||||||
.on("reconnecting", () => client.logger.warn("Bot reconnecting..."))
|
|
||||||
.on("warn", console.log)
|
|
||||||
.on("error", console.log);
|
|
||||||
|
|
||||||
process
|
|
||||||
.on("unhandledRejection", e => console.log(e))
|
|
||||||
.on("uncaughtException", e => console.log(e));
|
|
|
@ -1,23 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "en-US",
|
|
||||||
"nativeName": "English",
|
|
||||||
"moment": "en",
|
|
||||||
"defaultMomentFormat": "HH:mm:ss, MMMM Do YYYY",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ru-RU",
|
|
||||||
"nativeName": "Русский",
|
|
||||||
"moment": "ru",
|
|
||||||
"defaultMomentFormat": "HH:mm:ss, Do MMMM YYYY",
|
|
||||||
"default": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "uk-UA",
|
|
||||||
"nativeName": "Українська",
|
|
||||||
"moment": "uk",
|
|
||||||
"defaultMomentFormat": "HH:mm:ss, Do MMMM YYYY",
|
|
||||||
"default": false
|
|
||||||
}
|
|
||||||
]
|
|
101
package.json
|
@ -2,15 +2,15 @@
|
||||||
"name": "jaba",
|
"name": "jaba",
|
||||||
"version": "4.6.7",
|
"version": "4.6.7",
|
||||||
"description": "My Discord Bot",
|
"description": "My Discord Bot",
|
||||||
"main": "index.js",
|
"main": "src/index.js",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ."
|
"start": "node src/index.js"
|
||||||
},
|
},
|
||||||
"author": "https://github.com/JonnyBro",
|
"author": "https://github.com/JonnyBro",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discord-player/extractor": "^4.5.1",
|
"@discord-player/extractor": "^4.5.1",
|
||||||
"@discordjs/opus": "^0.9.0",
|
"@discordjs/opus": "^0.9.0",
|
||||||
"@discordjs/rest": "^2.4.0",
|
|
||||||
"@discordjs/voice": "^0.18.0",
|
"@discordjs/voice": "^0.18.0",
|
||||||
"@napi-rs/canvas": "^0.1.63",
|
"@napi-rs/canvas": "^0.1.63",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
|
@ -24,99 +24,16 @@
|
||||||
"i18next": "^24.0.0",
|
"i18next": "^24.0.0",
|
||||||
"i18next-fs-backend": "^2.6.0",
|
"i18next-fs-backend": "^2.6.0",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"moment": "^2.29.4",
|
|
||||||
"mongoose": "^8.8.2",
|
"mongoose": "^8.8.2",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
"node-fetch": "^2.7.0"
|
"node-fetch": "^2.7.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.57.1"
|
"@eslint/js": "^9.16.0",
|
||||||
},
|
"@stylistic/eslint-plugin-js": "^2.11.0",
|
||||||
"eslintConfig": {
|
"eslint": "^9.16.0",
|
||||||
"extends": "eslint:recommended",
|
"globals": "^15.13.0",
|
||||||
"env": {
|
"prettier": "^3.4.2",
|
||||||
"commonjs": true,
|
"prettier-eslint": "^16.3.0"
|
||||||
"es6": true,
|
|
||||||
"es2020": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
"Atomics": "readonly",
|
|
||||||
"SharedArrayBuffer": "readonly"
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2020
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"arrow-spacing": [
|
|
||||||
"warn",
|
|
||||||
{
|
|
||||||
"before": true,
|
|
||||||
"after": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"comma-dangle": [
|
|
||||||
"error",
|
|
||||||
"always-multiline"
|
|
||||||
],
|
|
||||||
"comma-spacing": "error",
|
|
||||||
"comma-style": "error",
|
|
||||||
"dot-location": [
|
|
||||||
"error",
|
|
||||||
"property"
|
|
||||||
],
|
|
||||||
"handle-callback-err": "off",
|
|
||||||
"indent": [
|
|
||||||
"error",
|
|
||||||
"tab",
|
|
||||||
{
|
|
||||||
"SwitchCase": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"keyword-spacing": "error",
|
|
||||||
"max-nested-callbacks": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"max": 4
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"max-statements-per-line": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"max": 2
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-console": "off",
|
|
||||||
"no-multi-spaces": "error",
|
|
||||||
"no-multiple-empty-lines": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"max": 2,
|
|
||||||
"maxEOF": 1,
|
|
||||||
"maxBOF": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"no-trailing-spaces": [
|
|
||||||
"error"
|
|
||||||
],
|
|
||||||
"no-var": "error",
|
|
||||||
"object-curly-spacing": [
|
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"prefer-const": "error",
|
|
||||||
"quotes": [
|
|
||||||
"error",
|
|
||||||
"double"
|
|
||||||
],
|
|
||||||
"semi": [
|
|
||||||
"error",
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"space-in-parens": "error",
|
|
||||||
"space-infix-ops": "error",
|
|
||||||
"space-unary-ops": "error",
|
|
||||||
"yoda": "error"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
673
pnpm-lock.yaml
9
src/adapters/cache/ICacheAdapter.js
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export default class ICacheAdapter {
|
||||||
|
get() {}
|
||||||
|
|
||||||
|
set() {}
|
||||||
|
|
||||||
|
clear() {}
|
||||||
|
|
||||||
|
delete() {}
|
||||||
|
}
|
24
src/adapters/cache/MapCache.js
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import ICacheAdapter from "./ICacheAdapter.js";
|
||||||
|
|
||||||
|
export default class MapCache extends ICacheAdapter {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.store = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
return this.store.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, value) {
|
||||||
|
this.store.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.store.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(key) {
|
||||||
|
this.store.delete(key);
|
||||||
|
}
|
||||||
|
}
|
25
src/adapters/database/IDatabaseAdapter.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
export default class IDatabaseAdapter {
|
||||||
|
async connect() {
|
||||||
|
throw new Error("Method `connect` not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect() {
|
||||||
|
throw new Error("Method `disconnect` not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
async find() {
|
||||||
|
throw new Error("Method \"find\" must be implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne() {
|
||||||
|
throw new Error("Method \"findOne\" must be implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateOne() {
|
||||||
|
throw new Error("Method \"updateOne\" must be implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteOne() {
|
||||||
|
throw new Error("Method \"deleteOne\" must be implemented");
|
||||||
|
}
|
||||||
|
}
|
72
src/adapters/database/MongooseAdapter.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import mongoose from "mongoose";
|
||||||
|
import IDatabaseAdapter from "./IDatabaseAdapter.js";
|
||||||
|
import logger from "../../helpers/logger.js";
|
||||||
|
import Cache from "../cache/MapCache.js";
|
||||||
|
|
||||||
|
export default class MongooseAdapter extends IDatabaseAdapter {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} uri - database url connect
|
||||||
|
* @param {mongoose.ConnectOptions} options - database connect options
|
||||||
|
*/
|
||||||
|
constructor(uri, options = {}) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
if (!uri) {
|
||||||
|
throw new Error("MongooseAdapter: URI is required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.uri = uri;
|
||||||
|
this.options = options;
|
||||||
|
this.cache = new Cache();
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect() {
|
||||||
|
await mongoose.connect(this.uri, this.options);
|
||||||
|
logger.log("Database connected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect() {
|
||||||
|
await mongoose.disconnect();
|
||||||
|
logger.warn("Database disconnected.");
|
||||||
|
this.cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#generateCacheKey(modelName, query, options) {
|
||||||
|
return `${modelName}:${JSON.stringify(query)}:${JSON.stringify(options)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async find(model, query = {}, options = {}) {
|
||||||
|
const cacheKey = this.#generateCacheKey(model.modelName, query, options);
|
||||||
|
if (this.cache.get(cacheKey)) {
|
||||||
|
return this.cache.get(cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await model.find(query, null, options).exec();
|
||||||
|
this.cache.set(cacheKey, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(model, query = {}, options = {}) {
|
||||||
|
const cacheKey = this.#generateCacheKey(model.modelName, query, options);
|
||||||
|
if (this.cache.get(cacheKey)) {
|
||||||
|
return this.cache.get(cacheKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await model.findOne(query, null, options).exec();
|
||||||
|
this.cache.set(cacheKey, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateOne(model, filter, update, options = {}) {
|
||||||
|
const result = await model.updateOne(filter, update, options).exec();
|
||||||
|
this.cache.clear();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteOne(model, filter) {
|
||||||
|
const result = await model.deleteOne(filter).exec();
|
||||||
|
this.cache.clear();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
@ -1,5 +1,5 @@
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
const path = require("path");
|
import { sep } from "path";
|
||||||
|
|
||||||
class BaseCommand {
|
class BaseCommand {
|
||||||
constructor(options, client) {
|
constructor(options, client) {
|
||||||
|
@ -15,8 +15,8 @@ class BaseCommand {
|
||||||
/**
|
/**
|
||||||
* @type {String}
|
* @type {String}
|
||||||
*/
|
*/
|
||||||
this.category = this.dirname ? this.dirname.split(path.sep)[parseInt(this.dirname.split(path.sep).length - 1, 10)] : "Other";
|
this.category = this.dirname ? this.dirname.split(sep)[parseInt(this.dirname.split(sep).length - 1, 10)] : "Other";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = BaseCommand;
|
export default BaseCommand;
|
|
@ -11,4 +11,4 @@ class BaseEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = BaseEvent;
|
export default BaseEvent;
|
|
@ -1,31 +1,40 @@
|
||||||
const { Client, Collection, SlashCommandBuilder, ContextMenuCommandBuilder, EmbedBuilder, PermissionsBitField, ChannelType } = require("discord.js"),
|
import { Client, Collection, SlashCommandBuilder, ContextMenuCommandBuilder, EmbedBuilder, PermissionsBitField, ChannelType } from "discord.js";
|
||||||
{ GiveawaysManager } = require("discord-giveaways"),
|
import { GiveawaysManager } from "discord-giveaways";
|
||||||
{ REST } = require("@discordjs/rest"),
|
import { REST } from "@discordjs/rest";
|
||||||
{ Player: DiscordPlayer } = require("discord-player"),
|
import { Player } from "discord-player";
|
||||||
{ SpotifyExtractor } = require("@discord-player/extractor"),
|
import { SpotifyExtractor } from "@discord-player/extractor";
|
||||||
{ YoutubeiExtractor } = require("discord-player-youtubei"),
|
import { YoutubeiExtractor } from "discord-player-youtubei";
|
||||||
{ Routes } = require("discord-api-types/v10");
|
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";
|
||||||
|
|
||||||
const BaseEvent = require("./BaseEvent.js"),
|
import * as emojis from "../../emojis.json";
|
||||||
BaseCommand = require("./BaseCommand.js"),
|
import langs from "../languages/language-meta.js";
|
||||||
path = require("path"),
|
import logger from "../helpers/logger.js";
|
||||||
fs = require("fs").promises,
|
import * as funcs from "../helpers/functions.js";
|
||||||
mongoose = require("mongoose");
|
|
||||||
|
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 {
|
class JaBaClient extends Client {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
this.config = require("../config");
|
this.config = config;
|
||||||
this.customEmojis = require("../emojis");
|
this.customEmojis = emojis;
|
||||||
this.languages = require("../languages/language-meta");
|
this.languages = langs;
|
||||||
this.commands = new Collection();
|
this.commands = new Collection();
|
||||||
this.logger = require("../helpers/logger");
|
this.logger = logger;
|
||||||
this.wait = require("node:timers/promises").setTimeout;
|
this.wait = setTimeout;
|
||||||
this.functions = require("../helpers/functions");
|
this.functions = funcs;
|
||||||
this.guildsData = require("../base/Guild");
|
this.guildsData = guild.default;
|
||||||
this.usersData = require("../base/User");
|
this.usersData = user.default;
|
||||||
this.membersData = require("../base/Member");
|
this.membersData = member.default;
|
||||||
|
|
||||||
this.databaseCache = {};
|
this.databaseCache = {};
|
||||||
this.databaseCache.users = new Collection();
|
this.databaseCache.users = new Collection();
|
||||||
|
@ -42,7 +51,7 @@ class JaBaClient extends Client {
|
||||||
* @returns {Promise<void>} A Promise that resolves when the client is fully initialized.
|
* @returns {Promise<void>} A Promise that resolves when the client is fully initialized.
|
||||||
*/
|
*/
|
||||||
async init() {
|
async init() {
|
||||||
this.player = new DiscordPlayer(this);
|
this.player = new Player(this);
|
||||||
|
|
||||||
await this.player.extractors.register(YoutubeiExtractor, {
|
await this.player.extractors.register(YoutubeiExtractor, {
|
||||||
authentication: this.config.youtubeCookie,
|
authentication: this.config.youtubeCookie,
|
||||||
|
@ -62,25 +71,33 @@ class JaBaClient extends Client {
|
||||||
this.player.events.on("playerStart", async (queue, track) => {
|
this.player.events.on("playerStart", async (queue, track) => {
|
||||||
const m = (
|
const m = (
|
||||||
await queue.metadata.channel.send({
|
await queue.metadata.channel.send({
|
||||||
content: this.translate("music/play:NOW_PLAYING", {
|
content: this.translate(
|
||||||
songName: `${track.title} - ${track.author}`,
|
"music/play:NOW_PLAYING",
|
||||||
songURL: track.url,
|
{
|
||||||
}, queue.metadata.data.guild.language),
|
songName: `${track.title} - ${track.author}`,
|
||||||
|
songURL: track.url,
|
||||||
|
},
|
||||||
|
queue.metadata.data.guild.language,
|
||||||
|
),
|
||||||
})
|
})
|
||||||
).id;
|
).id;
|
||||||
|
|
||||||
if (track.durationMS > 1)
|
if (track.durationMS > 1) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const message = queue.metadata.channel.messages.cache.get(m);
|
const message = queue.metadata.channel.messages.cache.get(m);
|
||||||
|
|
||||||
if (message && message.deletable) message.delete();
|
if (message && message.deletable) message.delete();
|
||||||
}, track.durationMS);
|
}, track.durationMS);
|
||||||
else
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(
|
||||||
const message = queue.metadata.channel.messages.cache.get(m);
|
() => {
|
||||||
|
const message = queue.metadata.channel.messages.cache.get(m);
|
||||||
|
|
||||||
if (message && message.deletable) message.delete();
|
if (message && message.deletable) message.delete();
|
||||||
}, 5 * 60 * 1000);
|
},
|
||||||
|
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("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("emptyChannel", queue => queue.metadata.channel.send(this.translate("music/play:STOP_EMPTY", null, queue.metadata.data.guild.language)));
|
||||||
|
@ -105,9 +122,7 @@ class JaBaClient extends Client {
|
||||||
|
|
||||||
mongoose
|
mongoose
|
||||||
.connect(this.config.mongoDB)
|
.connect(this.config.mongoDB)
|
||||||
.then(() => {
|
.then(this.logger.log("Connected to the MongoDB database."))
|
||||||
this.logger.log("Connected to the MongoDB database.");
|
|
||||||
})
|
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
this.logger.error(`Unable to connect to the MongoDB database.\nError: ${e.message}\n${e.stack}`);
|
this.logger.error(`Unable to connect to the MongoDB database.\nError: ${e.message}\n${e.stack}`);
|
||||||
});
|
});
|
||||||
|
@ -126,8 +141,8 @@ class JaBaClient extends Client {
|
||||||
*/
|
*/
|
||||||
async loadCommands(dir) {
|
async loadCommands(dir) {
|
||||||
const rest = new REST().setToken(this.config.token),
|
const rest = new REST().setToken(this.config.token),
|
||||||
filePath = path.join(__dirname, dir),
|
filePath = join(__dirname, dir),
|
||||||
folders = (await fs.readdir(filePath)).map(file => path.join(filePath, file));
|
folders = (await fs.readdir(filePath)).map(file => join(filePath, file));
|
||||||
|
|
||||||
const commands = [];
|
const commands = [];
|
||||||
for (const folder of folders) {
|
for (const folder of folders) {
|
||||||
|
@ -136,7 +151,7 @@ class JaBaClient extends Client {
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (!file.endsWith(".js")) continue;
|
if (!file.endsWith(".js")) continue;
|
||||||
|
|
||||||
const Command = require(path.join(folder, file));
|
const Command = require(join(folder, file));
|
||||||
|
|
||||||
if (!(Command.prototype instanceof BaseCommand)) continue;
|
if (!(Command.prototype instanceof BaseCommand)) continue;
|
||||||
|
|
||||||
|
@ -157,8 +172,8 @@ class JaBaClient extends Client {
|
||||||
await rest.put(route, { body: commands });
|
await rest.put(route, { body: commands });
|
||||||
|
|
||||||
this.logger.log("Successfully registered application commands.");
|
this.logger.log("Successfully registered application commands.");
|
||||||
} catch (err) {
|
} catch (e) {
|
||||||
this.logger.error("Error registering application commands:", err);
|
this.logger.error("Error registering application commands:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +185,7 @@ class JaBaClient extends Client {
|
||||||
*/
|
*/
|
||||||
async loadCommand(dir, file) {
|
async loadCommand(dir, file) {
|
||||||
try {
|
try {
|
||||||
const Command = require(path.join(dir, `${file}.js`));
|
const Command = require(join(dir, `${file}.js`));
|
||||||
|
|
||||||
if (!(Command.prototype instanceof BaseCommand)) {
|
if (!(Command.prototype instanceof BaseCommand)) {
|
||||||
return this.logger.error(`Tried to load a non-command file: "${file}.js"`);
|
return this.logger.error(`Tried to load a non-command file: "${file}.js"`);
|
||||||
|
@ -182,8 +197,8 @@ class JaBaClient extends Client {
|
||||||
if (typeof command.onLoad === "function") await command.onLoad(this);
|
if (typeof command.onLoad === "function") await command.onLoad(this);
|
||||||
|
|
||||||
this.logger.log(`Successfully loaded "${file}" command file. (Command: ${command.command.name})`);
|
this.logger.log(`Successfully loaded "${file}" command file. (Command: ${command.command.name})`);
|
||||||
} catch (error) {
|
} catch (e) {
|
||||||
this.logger.error(`Error loading command "${file}":`, error);
|
this.logger.error(`Error loading command "${file}":`, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +209,7 @@ class JaBaClient extends Client {
|
||||||
* @returns {void} This method does not return a value.
|
* @returns {void} This method does not return a value.
|
||||||
*/
|
*/
|
||||||
unloadCommand(dir, name) {
|
unloadCommand(dir, name) {
|
||||||
delete require.cache[require.resolve(`${dir}${path.sep}${name}.js`)];
|
delete require.cache[require.resolve(`${dir}${sep}${name}.js`)];
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -205,15 +220,15 @@ class JaBaClient extends Client {
|
||||||
* @returns {Promise<void>} This method does not return a value.
|
* @returns {Promise<void>} This method does not return a value.
|
||||||
*/
|
*/
|
||||||
async loadEvents(dir) {
|
async loadEvents(dir) {
|
||||||
const filePath = path.join(__dirname, dir);
|
const filePath = join(__dirname, dir);
|
||||||
const files = await fs.readdir(filePath);
|
const files = await fs.readdir(filePath);
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const fullPath = path.join(filePath, file);
|
const fullPath = join(filePath, file);
|
||||||
const stat = await fs.lstat(fullPath);
|
const stat = await fs.lstat(fullPath);
|
||||||
|
|
||||||
if (stat.isDirectory()) {
|
if (stat.isDirectory()) {
|
||||||
await this.loadEvents(path.join(dir, file));
|
await this.loadEvents(join(dir, file));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,8 +251,8 @@ class JaBaClient extends Client {
|
||||||
event.once ? this.once(event.name, event.execute.bind(event, this)) : this.on(event.name, event.execute.bind(event, this));
|
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})`);
|
this.logger.log(`Successfully loaded "${file}" event. (Event: ${event.name})`);
|
||||||
} catch (error) {
|
} catch (e) {
|
||||||
this.logger.error(`Error loading event "${file}":`, error);
|
this.logger.error(`Error loading event "${file}":`, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,7 +305,7 @@ class JaBaClient extends Client {
|
||||||
.setColor(data.color ?? this.config.embed.color)
|
.setColor(data.color ?? this.config.embed.color)
|
||||||
.setFooter(typeof data.footer === "object" ? data.footer : data.footer ? { text: data.footer } : this.config.embed.footer)
|
.setFooter(typeof data.footer === "object" ? data.footer : data.footer ? { text: data.footer } : this.config.embed.footer)
|
||||||
.setTimestamp(data.timestamp ?? null)
|
.setTimestamp(data.timestamp ?? null)
|
||||||
.setAuthor(typeof data.author === "string" ? { name: data.author, iconURL: this.user.avatarURL() } : data.author ?? null);
|
.setAuthor(typeof data.author === "string" ? { name: data.author, iconURL: this.user.avatarURL() } : (data.author ?? null));
|
||||||
|
|
||||||
return embed;
|
return embed;
|
||||||
}
|
}
|
||||||
|
@ -311,7 +326,7 @@ class JaBaClient extends Client {
|
||||||
/**
|
/**
|
||||||
* Returns a User data from the database.
|
* Returns a User data from the database.
|
||||||
* @param {string} userID - The ID of the user to find or create.
|
* @param {string} userID - The ID of the user to find or create.
|
||||||
* @returns {Promise<import("./User")>} The user data object, either retrieved from the database or newly created.
|
* @returns {Promise<import("./User.js")>} The user data object, either retrieved from the database or newly created.
|
||||||
*/
|
*/
|
||||||
async getUserData(userID) {
|
async getUserData(userID) {
|
||||||
let userData = await this.usersData.findOne({ id: userID });
|
let userData = await this.usersData.findOne({ id: userID });
|
||||||
|
@ -330,7 +345,7 @@ class JaBaClient extends Client {
|
||||||
* Returns a Member data from the database.
|
* Returns a Member data from the database.
|
||||||
* @param {string} memberId - The ID of the member to find or create.
|
* @param {string} memberId - The ID of the member to find or create.
|
||||||
* @param {string} guildId - The ID of the guild the member belongs to.
|
* @param {string} guildId - The ID of the guild the member belongs to.
|
||||||
* @returns {Promise<import("./Member")>} The member data object, either retrieved from the database or newly created.
|
* @returns {Promise<import("./Member.js")>} The member data object, either retrieved from the database or newly created.
|
||||||
*/
|
*/
|
||||||
async getMemberData(memberId, guildId) {
|
async getMemberData(memberId, guildId) {
|
||||||
let memberData = await this.membersData.findOne({ guildID: guildId, id: memberId });
|
let memberData = await this.membersData.findOne({ guildID: guildId, id: memberId });
|
||||||
|
@ -340,6 +355,7 @@ class JaBaClient extends Client {
|
||||||
await memberData.save();
|
await memberData.save();
|
||||||
|
|
||||||
const guildData = await this.getGuildData(guildId);
|
const guildData = await this.getGuildData(guildId);
|
||||||
|
|
||||||
if (guildData) {
|
if (guildData) {
|
||||||
guildData.members.push(memberData._id);
|
guildData.members.push(memberData._id);
|
||||||
await guildData.save();
|
await guildData.save();
|
||||||
|
@ -353,7 +369,7 @@ class JaBaClient extends Client {
|
||||||
/**
|
/**
|
||||||
* Returns a Guild data from the database.
|
* Returns a Guild data from the database.
|
||||||
* @param {string} guildId - The ID of the guild to find or create.
|
* @param {string} guildId - The ID of the guild to find or create.
|
||||||
* @returns {Promise<import("./Guild")>} The guild data object, either retrieved from the database or newly created.
|
* @returns {Promise<import("./Guild.js")>} The guild data object, either retrieved from the database or newly created.
|
||||||
*/
|
*/
|
||||||
async getGuildData(guildId) {
|
async getGuildData(guildId) {
|
||||||
let guildData = await this.guildsData.findOne({ id: guildId }).populate("members");
|
let guildData = await this.guildsData.findOne({ id: guildId }).populate("members");
|
||||||
|
@ -369,4 +385,4 @@ class JaBaClient extends Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = JaBaClient;
|
export default JaBaClient;
|
8
src/commands/Fun/8ball.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export const data = {
|
||||||
|
name: "8ball",
|
||||||
|
description: "8ball",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const run = () => {
|
||||||
|
console.log("8ball");
|
||||||
|
};
|
|
@ -103,7 +103,8 @@ class Config extends BaseCommand {
|
||||||
? interaction.translate("administration/config:WELCOME_CONTENT", {
|
? interaction.translate("administration/config:WELCOME_CONTENT", {
|
||||||
channel: `<#${guildData.plugins.welcome.channel}>`,
|
channel: `<#${guildData.plugins.welcome.channel}>`,
|
||||||
withImage: guildData.plugins.welcome.withImage ? interaction.translate("common:YES") : interaction.translate("common:NO"),
|
withImage: guildData.plugins.welcome.withImage ? interaction.translate("common:YES") : interaction.translate("common:NO"),
|
||||||
}) : interaction.translate("common:DISABLED"),
|
})
|
||||||
|
: interaction.translate("common:DISABLED"),
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -112,7 +113,8 @@ class Config extends BaseCommand {
|
||||||
? interaction.translate("administration/config:GOODBYE_CONTENT", {
|
? interaction.translate("administration/config:GOODBYE_CONTENT", {
|
||||||
channel: `<#${guildData.plugins.goodbye.channel}>`,
|
channel: `<#${guildData.plugins.goodbye.channel}>`,
|
||||||
withImage: guildData.plugins.goodbye.withImage ? interaction.translate("common:YES") : interaction.translate("common:NO"),
|
withImage: guildData.plugins.goodbye.withImage ? interaction.translate("common:YES") : interaction.translate("common:NO"),
|
||||||
}) : interaction.translate("common:DISABLED"),
|
})
|
||||||
|
: interaction.translate("common:DISABLED"),
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -125,19 +127,22 @@ class Config extends BaseCommand {
|
||||||
(guildData.plugins.warnsSanctions.kick
|
(guildData.plugins.warnsSanctions.kick
|
||||||
? interaction.translate("administration/config:KICK_CONTENT", {
|
? interaction.translate("administration/config:KICK_CONTENT", {
|
||||||
count: guildData.plugins.warnsSanctions.kick,
|
count: guildData.plugins.warnsSanctions.kick,
|
||||||
}) : interaction.translate("administration/config:KICK_NOT_DEFINED")) +
|
})
|
||||||
|
: interaction.translate("administration/config:KICK_NOT_DEFINED")) +
|
||||||
"\n" +
|
"\n" +
|
||||||
(guildData.plugins.warnsSanctions.ban
|
(guildData.plugins.warnsSanctions.ban
|
||||||
? interaction.translate("administration/config:BAN_CONTENT", {
|
? interaction.translate("administration/config:BAN_CONTENT", {
|
||||||
count: guildData.plugins.warnsSanctions.ban,
|
count: guildData.plugins.warnsSanctions.ban,
|
||||||
}) : interaction.translate("administration/config:BAN_NOT_DEFINED")),
|
})
|
||||||
|
: interaction.translate("administration/config:BAN_NOT_DEFINED")),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: interaction.translate("administration/config:AUTOMOD_TITLE"),
|
name: interaction.translate("administration/config:AUTOMOD_TITLE"),
|
||||||
value: guildData.plugins.automod.enabled
|
value: guildData.plugins.automod.enabled
|
||||||
? interaction.translate("administration/config:AUTOMOD_CONTENT", {
|
? interaction.translate("administration/config:AUTOMOD_CONTENT", {
|
||||||
channels: guildData.plugins.automod.ignored.map(ch => ` ${ch}`),
|
channels: guildData.plugins.automod.ignored.map(ch => ` ${ch}`),
|
||||||
}) : interaction.translate("common:DISABLED"),
|
})
|
||||||
|
: interaction.translate("common:DISABLED"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: interaction.translate("administration/config:MONITORING_CHANNELS"),
|
name: interaction.translate("administration/config:MONITORING_CHANNELS"),
|
||||||
|
@ -211,13 +216,14 @@ async function changeSetting(interaction, setting, state, channel) {
|
||||||
content: `${interaction.translate(`administration/config:${settingSplitted.length === 2 ? settingSplitted[1].toUpperCase() : setting.toUpperCase()}`)}: **${interaction.translate("common:ENABLED")}** (${channel.toString()})`,
|
content: `${interaction.translate(`administration/config:${settingSplitted.length === 2 ? settingSplitted[1].toUpperCase() : setting.toUpperCase()}`)}: **${interaction.translate("common:ENABLED")}** (${channel.toString()})`,
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
} else
|
} else {
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content: `${interaction.translate(`administration/config:${settingSplitted.length === 2 ? settingSplitted[1].toUpperCase() : setting.toUpperCase()}`)}: ${
|
content: `${interaction.translate(`administration/config:${settingSplitted.length === 2 ? settingSplitted[1].toUpperCase() : setting.toUpperCase()}`)}: ${
|
||||||
data.plugins[setting] ? `**${interaction.translate("common:ENABLED")}** (<#${data.plugins[setting]}>)` : `**${interaction.translate("common:DISABLED")}**`
|
data.plugins[setting] ? `**${interaction.translate("common:ENABLED")}** (<#${data.plugins[setting]}>)` : `**${interaction.translate("common:DISABLED")}**`
|
||||||
}`,
|
}`,
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
|
@ -241,13 +247,14 @@ async function changeSetting(interaction, setting, state, channel) {
|
||||||
content: `${interaction.translate(`administration/config:${setting.toUpperCase()}`)}: **${interaction.translate("common:ENABLED")}** (${channel.toString()})`,
|
content: `${interaction.translate(`administration/config:${setting.toUpperCase()}`)}: **${interaction.translate("common:ENABLED")}** (${channel.toString()})`,
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
} else
|
} else {
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content: `${interaction.translate(`administration/config:${setting.toUpperCase()}`)}: ${
|
content: `${interaction.translate(`administration/config:${setting.toUpperCase()}`)}: ${
|
||||||
data.plugins[setting] ? `**${interaction.translate("common:ENABLED")}** (<#${data.plugins[setting]}>)` : `**${interaction.translate("common:DISABLED")}**`
|
data.plugins[setting] ? `**${interaction.translate("common:ENABLED")}** (<#${data.plugins[setting]}>)` : `**${interaction.translate("common:DISABLED")}**`
|
||||||
}`,
|
}`,
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -63,7 +63,7 @@ class Shorturl extends BaseCommand {
|
||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
|
|
||||||
interaction.editReply({
|
interaction.editReply({
|
||||||
content: `<https://s.jonnybro.ru/${res.link.slug}>`,
|
content: `<${res.shortLink}>`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
const { SlashCommandBuilder, InteractionContextType, ApplicationIntegrationType } = require("discord.js");
|
const { SlashCommandBuilder, InteractionContextType, ApplicationIntegrationType } = require("discord.js");
|
||||||
const BaseCommand = require("../../base/BaseCommand"),
|
const BaseCommand = require("../../base/BaseCommand"),
|
||||||
fetch = require("node-fetch"),
|
fetch = require("node-fetch");
|
||||||
moment = require("moment");
|
|
||||||
|
|
||||||
class Checkjar extends BaseCommand {
|
class Checkjar extends BaseCommand {
|
||||||
/**
|
/**
|
||||||
|
@ -56,7 +55,7 @@ class Checkjar extends BaseCommand {
|
||||||
jarTransactions.length = 10;
|
jarTransactions.length = 10;
|
||||||
|
|
||||||
jarTransactions.forEach(t => {
|
jarTransactions.forEach(t => {
|
||||||
const time = moment.unix(t.time);
|
const time = new Intl.DateTimeFormat("ru-RU").format(t.time);
|
||||||
|
|
||||||
embed.data.fields.push([
|
embed.data.fields.push([
|
||||||
{
|
{
|