mirror of
https://github.com/JonnyBro/JaBa.git
synced 2024-11-23 21:54:58 +05:00
update to latest jaba version
This commit is contained in:
parent
11bb890a99
commit
3cf0b475af
42 changed files with 6178 additions and 5022 deletions
10
README.md
10
README.md
|
@ -1,12 +1,12 @@
|
||||||
<img width="150" height="150" align="left" style="float: left; margin: 0 10px 0 0;" src="https://images-ext-1.discordapp.net/external/khumNb7SPgoX2KAmnB5-37LF8Hsg_gb9BithY5gaO_w/%3Fsize%3D2048/https/cdn.discordapp.com/avatars/708637495054565426/e1e9a50ec08988d1b25c13f8bd4801bd.webp">
|
|
||||||
|
|
||||||
# JaBa Bot
|
# JaBa Bot
|
||||||
|
|
||||||
|
![JaBa avatar](https://cdn.discordapp.com/avatars/708637495054565426/e1e9a50ec08988d1b25c13f8bd4801bd.webp?size=128)
|
||||||
|
JaBa is an open source Discord Bot written by [Jonny_Bro](https://github.com/JonnyBro) using [discord.js](https://github.com/discordjs/discord.js) and [Mongoose](https://mongoosejs.com).
|
||||||
|
|
||||||
[![image](https://img.shields.io/discord/892727526911258654?logo=discord&&colorB=00BFFF&label=Discord&style=flat-square)](https://discord.gg/Ptkj2n9nzZ)
|
[![image](https://img.shields.io/discord/892727526911258654?logo=discord&&colorB=00BFFF&label=Discord&style=flat-square)](https://discord.gg/Ptkj2n9nzZ)
|
||||||
[![image](https://img.shields.io/badge/discord.js-v14.14.1-blue.svg?logo=npm)](https://github.com/discordjs/discord.js)
|
[![image](https://img.shields.io/badge/discord.js-v14.14.1-blue.svg?logo=npm)](https://github.com/discordjs/discord.js)
|
||||||
[![image](https://www.codefactor.io/repository/github/JonnyBro/JaBa/badge)](https://www.codefactor.io/repository/github/JonnyBro/JaBa)
|
[![image](https://www.codefactor.io/repository/github/JonnyBro/JaBa/badge)](https://www.codefactor.io/repository/github/JonnyBro/JaBa)
|
||||||
[![image](https://img.shields.io/github/license/JonnyBro/JaBa?label=License&style=flat-square)](https://github.com/JonnyBro/JaBa/blob/main/LICENSE)\
|
[![image](https://img.shields.io/github/license/JonnyBro/JaBa?label=License&style=flat-square)](https://github.com/JonnyBro/JaBa/blob/main/LICENSE)
|
||||||
JaBa is an open source Discord Bot written by [Jonny_Bro](https://github.com/JonnyBro) using [discord.js](https://github.com/discordjs/discord.js) and [Mongoose](https://mongoosejs.com).
|
|
||||||
|
|
||||||
## Functionality
|
## Functionality
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ Use [this instruction](https://github.com/JonnyBro/JaBa/wiki/Self-Hosting) to le
|
||||||
If you have any questions you can ask them on my [Discord Server](https://discord.gg/NPkySYKMkN).\
|
If you have any questions you can ask them on my [Discord Server](https://discord.gg/NPkySYKMkN).\
|
||||||
If you want to contribute, feel free to fork this repo and making a pull request!
|
If you want to contribute, feel free to fork this repo and making a pull request!
|
||||||
|
|
||||||
# TODO
|
## TODO
|
||||||
|
|
||||||
* [ ] Finish and release *dashboard-core* submodule.
|
* [ ] Finish and release *dashboard-core* submodule.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const mongoose = require("mongoose"),
|
const mongoose = require("mongoose"),
|
||||||
Canvas = require("canvas");
|
Canvas = require("@napi-rs/canvas");
|
||||||
|
|
||||||
const genToken = () => {
|
const genToken = () => {
|
||||||
let token = "";
|
let token = "";
|
||||||
|
@ -107,7 +107,7 @@ userSchema.method("getAchievements", async function () {
|
||||||
dim += 200;
|
dim += 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
return canvas.toBuffer();
|
return (await canvas.encode("png"));
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = mongoose.model("User", userSchema);
|
module.exports = mongoose.model("User", userSchema);
|
||||||
|
|
|
@ -194,6 +194,7 @@ async function changeSetting(interaction, setting, state, channel) {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
data.plugins[settingSplitted[0]][settingSplitted[1]] = null;
|
data.plugins[settingSplitted[0]][settingSplitted[1]] = null;
|
||||||
|
|
||||||
|
data.markModified(`plugins.${setting}`);
|
||||||
await data.save();
|
await data.save();
|
||||||
|
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
|
@ -206,6 +207,7 @@ async function changeSetting(interaction, setting, state, channel) {
|
||||||
if (channel) {
|
if (channel) {
|
||||||
data.plugins[settingSplitted[0]][settingSplitted[1]] = channel.id;
|
data.plugins[settingSplitted[0]][settingSplitted[1]] = channel.id;
|
||||||
|
|
||||||
|
data.markModified(`plugins.${setting}`);
|
||||||
await data.save();
|
await data.save();
|
||||||
|
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
|
@ -224,6 +226,7 @@ async function changeSetting(interaction, setting, state, channel) {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
data.plugins[setting] = null;
|
data.plugins[setting] = null;
|
||||||
|
|
||||||
|
data.markModified(`plugins.${setting}`);
|
||||||
await data.save();
|
await data.save();
|
||||||
|
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
|
@ -234,6 +237,7 @@ async function changeSetting(interaction, setting, state, channel) {
|
||||||
if (channel) {
|
if (channel) {
|
||||||
data.plugins[setting] = channel.id;
|
data.plugins[setting] = channel.id;
|
||||||
|
|
||||||
|
data.markModified(`plugins.${setting}`);
|
||||||
await data.save();
|
await data.save();
|
||||||
|
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
|
|
|
@ -61,16 +61,16 @@ class Goodbye extends BaseCommand {
|
||||||
uk: client.translate("administration/goodbye:MESSAGE", null, "uk-UA"),
|
uk: client.translate("administration/goodbye:MESSAGE", null, "uk-UA"),
|
||||||
ru: client.translate("administration/goodbye:MESSAGE", null, "ru-RU"),
|
ru: client.translate("administration/goodbye:MESSAGE", null, "ru-RU"),
|
||||||
}),
|
}),
|
||||||
)
|
|
||||||
.addBooleanOption(option =>
|
|
||||||
option
|
|
||||||
.setName("image")
|
|
||||||
.setDescription(client.translate("administration/goodbye:IMAGE"))
|
|
||||||
.setDescriptionLocalizations({
|
|
||||||
uk: client.translate("administration/goodbye:IMAGE", null, "uk-UA"),
|
|
||||||
ru: client.translate("administration/goodbye:IMAGE", null, "ru-RU"),
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
|
// .addBooleanOption(option =>
|
||||||
|
// option
|
||||||
|
// .setName("image")
|
||||||
|
// .setDescription(client.translate("administration/goodbye:IMAGE"))
|
||||||
|
// .setDescriptionLocalizations({
|
||||||
|
// uk: client.translate("administration/goodbye:IMAGE", null, "uk-UA"),
|
||||||
|
// ru: client.translate("administration/goodbye:IMAGE", null, "ru-RU"),
|
||||||
|
// }),
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
dirname: __dirname,
|
dirname: __dirname,
|
||||||
ownerOnly: false,
|
ownerOnly: false,
|
||||||
|
@ -83,13 +83,15 @@ class Goodbye extends BaseCommand {
|
||||||
* @param {import("discord.js").ChatInputCommandInteraction} interaction
|
* @param {import("discord.js").ChatInputCommandInteraction} interaction
|
||||||
*/
|
*/
|
||||||
async execute(client, interaction) {
|
async execute(client, interaction) {
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
|
||||||
const guildData = interaction.data.guild,
|
const guildData = interaction.data.guild,
|
||||||
command = interaction.options.getSubcommand();
|
command = interaction.options.getSubcommand();
|
||||||
|
|
||||||
if (command === "test") {
|
if (command === "test") {
|
||||||
client.emit("guildMemberRemove", interaction.member);
|
client.emit("guildMemberRemove", client, interaction.member);
|
||||||
|
|
||||||
interaction.success("administration/goodbye:TEST_SUCCESS", null, { ephemeral: true });
|
interaction.success("administration/goodbye:TEST_SUCCESS", null, { edit: true });
|
||||||
} else {
|
} else {
|
||||||
const state = interaction.options.getBoolean("state");
|
const state = interaction.options.getBoolean("state");
|
||||||
|
|
||||||
|
@ -101,13 +103,14 @@ class Goodbye extends BaseCommand {
|
||||||
withImage: null,
|
withImage: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
guildData.markModified("plugins.goodbye");
|
||||||
await guildData.save();
|
await guildData.save();
|
||||||
|
|
||||||
interaction.success("administration/goodbye:DISABLED", null, { ephemeral: true });
|
interaction.success("administration/goodbye:DISABLED", null, { edit: true });
|
||||||
} else {
|
} else {
|
||||||
const channel = interaction.options.getChannel("channel") || interaction.channel;
|
const channel = interaction.options.getChannel("channel") || interaction.channel;
|
||||||
const message = interaction.options.getString("message") || interaction.translate("administration/goodbye:DEFAULT_MESSAGE");
|
const message = interaction.options.getString("message") || interaction.translate("administration/goodbye:DEFAULT_MESSAGE");
|
||||||
const image = interaction.options.getBoolean("image") === true ? true : false;
|
const image = false; // interaction.options.getBoolean("image") || false;
|
||||||
|
|
||||||
guildData.plugins.goodbye = {
|
guildData.plugins.goodbye = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
@ -116,11 +119,12 @@ class Goodbye extends BaseCommand {
|
||||||
withImage: image,
|
withImage: image,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
guildData.markModified("plugins.goodbye");
|
||||||
await guildData.save();
|
await guildData.save();
|
||||||
|
|
||||||
interaction.success("administration/goodbye:ENABLED", {
|
interaction.success("administration/goodbye:ENABLED", {
|
||||||
channel: `${channel.toString()}`,
|
channel: `${channel.toString()}`,
|
||||||
}, { ephemeral: true });
|
}, { edit: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,16 +61,16 @@ class Welcome extends BaseCommand {
|
||||||
uk: client.translate("administration/goodbye:MESSAGE", null, "uk-UA"),
|
uk: client.translate("administration/goodbye:MESSAGE", null, "uk-UA"),
|
||||||
ru: client.translate("administration/goodbye:MESSAGE", null, "ru-RU"),
|
ru: client.translate("administration/goodbye:MESSAGE", null, "ru-RU"),
|
||||||
}),
|
}),
|
||||||
)
|
|
||||||
.addBooleanOption(option =>
|
|
||||||
option
|
|
||||||
.setName("image")
|
|
||||||
.setDescription(client.translate("administration/goodbye:IMAGE"))
|
|
||||||
.setDescriptionLocalizations({
|
|
||||||
uk: client.translate("administration/goodbye:IMAGE", null, "uk-UA"),
|
|
||||||
ru: client.translate("administration/goodbye:IMAGE", null, "ru-RU"),
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
|
// .addBooleanOption(option =>
|
||||||
|
// option
|
||||||
|
// .setName("image")
|
||||||
|
// .setDescription(client.translate("administration/goodbye:IMAGE"))
|
||||||
|
// .setDescriptionLocalizations({
|
||||||
|
// uk: client.translate("administration/goodbye:IMAGE", null, "uk-UA"),
|
||||||
|
// ru: client.translate("administration/goodbye:IMAGE", null, "ru-RU"),
|
||||||
|
// }),
|
||||||
|
// ),
|
||||||
),
|
),
|
||||||
dirname: __dirname,
|
dirname: __dirname,
|
||||||
ownerOnly: false,
|
ownerOnly: false,
|
||||||
|
@ -83,13 +83,15 @@ class Welcome extends BaseCommand {
|
||||||
* @param {import("discord.js").ChatInputCommandInteraction} interaction
|
* @param {import("discord.js").ChatInputCommandInteraction} interaction
|
||||||
*/
|
*/
|
||||||
async execute(client, interaction) {
|
async execute(client, interaction) {
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
|
||||||
const guildData = interaction.data.guild,
|
const guildData = interaction.data.guild,
|
||||||
command = interaction.options.getSubcommand();
|
command = interaction.options.getSubcommand();
|
||||||
|
|
||||||
if (command === "test") {
|
if (command === "test") {
|
||||||
client.emit("guildMemberAdd", interaction.member);
|
client.emit("guildMemberAdd", client, interaction.member);
|
||||||
|
|
||||||
interaction.success("administration/goodbye:TEST_SUCCESS", null, { ephemeral: true });
|
interaction.success("administration/goodbye:TEST_SUCCESS", null, { edit: true });
|
||||||
} else {
|
} else {
|
||||||
const state = interaction.options.getBoolean("state");
|
const state = interaction.options.getBoolean("state");
|
||||||
|
|
||||||
|
@ -101,13 +103,14 @@ class Welcome extends BaseCommand {
|
||||||
withImage: null,
|
withImage: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
guildData.markModified("plugins.welcome");
|
||||||
await guildData.save();
|
await guildData.save();
|
||||||
|
|
||||||
interaction.success("administration/welcome:DISABLED", null, { ephemeral: true });
|
interaction.success("administration/welcome:DISABLED", null);
|
||||||
} else {
|
} else {
|
||||||
const channel = interaction.options.getChannel("channel") || interaction.channel;
|
const channel = interaction.options.getChannel("channel") || interaction.channel;
|
||||||
const message = interaction.options.getString("message") || interaction.translate("administration/welcome:DEFAULT_MESSAGE");
|
const message = interaction.options.getString("message") || interaction.translate("administration/welcome:DEFAULT_MESSAGE");
|
||||||
const image = interaction.options.getBoolean("image") === true ? true : false;
|
const image = false; // interaction.options.getBoolean("image") || false;
|
||||||
|
|
||||||
guildData.plugins.welcome = {
|
guildData.plugins.welcome = {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
@ -116,11 +119,12 @@ class Welcome extends BaseCommand {
|
||||||
withImage: image,
|
withImage: image,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
guildData.markModified("plugins.welcome");
|
||||||
await guildData.save();
|
await guildData.save();
|
||||||
|
|
||||||
interaction.success("administration/welcome:ENABLED", {
|
interaction.success("administration/welcome:ENABLED", {
|
||||||
channel: `${channel.toString()}`,
|
channel: `${channel.toString()}`,
|
||||||
}, { ephemeral: true });
|
}, { edit: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,16 +28,13 @@ class Work extends BaseCommand {
|
||||||
*/
|
*/
|
||||||
async execute(client, interaction) {
|
async execute(client, interaction) {
|
||||||
const { member: memberData, user: userData } = interaction.data,
|
const { member: memberData, user: userData } = interaction.data,
|
||||||
isInCooldown = memberData.cooldowns?.work;
|
cooldown = memberData.cooldowns?.work,
|
||||||
|
now = Date.now();
|
||||||
|
|
||||||
if (isInCooldown && isInCooldown > Date.now())
|
if (now < cooldown) return interaction.error("economy/work:COOLDOWN", { time: `<t:${Math.floor(cooldown / 1000)}:R>` });
|
||||||
return interaction.error("economy/work:COOLDOWN", {
|
if (Math.abs(cooldown - now) > 30 * 60 * 60 * 1000) memberData.workStreak = 0;
|
||||||
time: `<t:${Math.floor(isInCooldown / 1000)}:R>`,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Date.now() > memberData.cooldowns.work + 30 * 60 * 60 * 1000) memberData.workStreak = 0;
|
memberData.cooldowns.work = now + 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
memberData.cooldowns.work = Date.now() + 24 * 60 * 60 * 1000;
|
|
||||||
memberData.workStreak = (memberData.workStreak || 0) + 1;
|
memberData.workStreak = (memberData.workStreak || 0) + 1;
|
||||||
|
|
||||||
const embed = client.embed({
|
const embed = client.embed({
|
||||||
|
|
|
@ -124,7 +124,7 @@ class Boosters extends BaseCommand {
|
||||||
const boosters = (await interaction.guild.members.fetch()).filter(m => m.premiumSince);
|
const boosters = (await interaction.guild.members.fetch()).filter(m => m.premiumSince);
|
||||||
if (boosters.size === 0) return interaction.error("general/boosters:NO_BOOSTERS", null, { edit: true });
|
if (boosters.size === 0) return interaction.error("general/boosters:NO_BOOSTERS", null, { edit: true });
|
||||||
|
|
||||||
const embeds = generateBoostersEmbeds(client, interaction, boosters);
|
const embeds = generateBoostersEmbeds(interaction, boosters);
|
||||||
|
|
||||||
const row = new ActionRowBuilder().addComponents(
|
const row = new ActionRowBuilder().addComponents(
|
||||||
new ButtonBuilder().setCustomId("boosters_prev_page").setStyle(ButtonStyle.Primary).setEmoji("⬅️"),
|
new ButtonBuilder().setCustomId("boosters_prev_page").setStyle(ButtonStyle.Primary).setEmoji("⬅️"),
|
||||||
|
@ -159,7 +159,7 @@ function generateBoostersEmbeds(interaction, boosters) {
|
||||||
let j = i;
|
let j = i;
|
||||||
k += 10;
|
k += 10;
|
||||||
|
|
||||||
const info = current.map(member => `${++j}. ${member.toString()} | ${interaction.translate("general/boosters:BOOSTER_SINCE")}: **${Math.floor(new Date(member.premiumSince).getTime() / 1000)}**`).join("\n");
|
const info = current.map(member => `${++j}. ${member.toString()} | ${interaction.translate("general/boosters:BOOSTER_SINCE")}: <t:${Math.floor(member.premiumSinceTimestamp / 1000)}:f>`).join("\n");
|
||||||
|
|
||||||
const embed = interaction.client.embed({
|
const embed = interaction.client.embed({
|
||||||
title: interaction.translate("general/boosters:BOOSTERS_LIST"),
|
title: interaction.translate("general/boosters:BOOSTERS_LIST"),
|
||||||
|
|
|
@ -33,14 +33,13 @@ class CreateTicketEmbed extends BaseCommand {
|
||||||
interaction.data = [];
|
interaction.data = [];
|
||||||
interaction.data.guild = await client.getGuildData(interaction.guildId);
|
interaction.data.guild = await client.getGuildData(interaction.guildId);
|
||||||
|
|
||||||
const guildData = interaction.data.guild,
|
|
||||||
ticketsCategory = guildData.plugins?.tickets?.ticketsCategory,
|
|
||||||
ticketLogs = guildData.plugins?.tickets?.ticketLogs,
|
|
||||||
transcriptionLogs = guildData.plugins?.tickets?.transcriptionLogs;
|
|
||||||
|
|
||||||
const button = interaction.component;
|
const button = interaction.component;
|
||||||
|
|
||||||
if (button.customId === "support_ticket") {
|
if (button.customId === "support_ticket") {
|
||||||
|
const guildData = interaction.data.guild,
|
||||||
|
ticketsCategory = guildData.plugins.tickets.ticketsCategory,
|
||||||
|
ticketLogs = guildData.plugins.tickets.ticketLogs;
|
||||||
|
|
||||||
if (interaction.guild.channels.cache.get(ticketsCategory).children.cache.size >= 50) {
|
if (interaction.guild.channels.cache.get(ticketsCategory).children.cache.size >= 50) {
|
||||||
const sorted = interaction.guild.channels.cache.get(ticketsCategory).children.cache.sort((ch1, ch2) => ch1.createdTimestamp - ch2.createdTimestamp);
|
const sorted = interaction.guild.channels.cache.get(ticketsCategory).children.cache.sort((ch1, ch2) => ch1.createdTimestamp - ch2.createdTimestamp);
|
||||||
|
|
||||||
|
@ -137,8 +136,10 @@ class CreateTicketEmbed extends BaseCommand {
|
||||||
|
|
||||||
collector.on("end", async (_, reason) => {
|
collector.on("end", async (_, reason) => {
|
||||||
if (reason !== "canceled") {
|
if (reason !== "canceled") {
|
||||||
const reversedMessages = (await interaction.channel.messages.fetch()).filter(m => !m.author.bot);
|
const reversedMessages = (await interaction.channel.messages.fetch()).filter(m => !m.author.bot),
|
||||||
const messages = Array.from(reversedMessages.values()).reverse();
|
messages = Array.from(reversedMessages.values()).reverse(),
|
||||||
|
transcriptionLogs = interaction.data.guild.plugins.tickets.transcriptionLogs,
|
||||||
|
ticketLogs = interaction.data.guild.plugins.tickets.ticketLogs;
|
||||||
|
|
||||||
if (messages.length > 1) {
|
if (messages.length > 1) {
|
||||||
let transcript = "---- TICKET CREATED ----\n";
|
let transcript = "---- TICKET CREATED ----\n";
|
||||||
|
|
1
dashboard/.gitignore
vendored
1
dashboard/.gitignore
vendored
|
@ -1,6 +1,7 @@
|
||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
|
/node_modules
|
||||||
/.pnp
|
/.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
# Discord Bot Dashboard Template
|
# Discord Bot Dashboard Template
|
||||||
|
|
||||||
> Made By <https://github.com/fuma-nama>
|
> Forked from [here](https://github.com/FileEditor97/discord-bot-dashboard)
|
||||||
|
|
||||||
![banner](./document/preview-new.png)
|
|
||||||
|
|
||||||
Using Typescript, Next.js 13, React 18 and Chakra ui 2.0
|
Using Typescript, Next.js 13, React 18 and Chakra ui 2.0
|
||||||
|
|
||||||
|
@ -168,9 +166,7 @@ Moreover, you can use redis instead of connecting to the bot server directly
|
||||||
|
|
||||||
The client will pass their access token via the `Authorization` header
|
The client will pass their access token via the `Authorization` header
|
||||||
|
|
||||||
```js
|
`Bearer MY_TOKEN_1212112`
|
||||||
Bearer MY_TOKEN_1212112
|
|
||||||
```
|
|
||||||
|
|
||||||
### Required Routes
|
### Required Routes
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ const nextConfig = {
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
i18n: {
|
i18n: {
|
||||||
locales: ['en', 'cn'],
|
locales: ['en'],
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "dashboard",
|
"name": "nohi-dashboard",
|
||||||
"version": "1.2.0",
|
"version": "0.1.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Fuma Nama (https://github.com/fuma-nama)",
|
||||||
|
"maintainers": [
|
||||||
|
"FileEditor97 (https://github.com/fileeditor97)"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
|
@ -20,35 +25,34 @@
|
||||||
"@chakra-ui/theme-tools": "^2.0.0",
|
"@chakra-ui/theme-tools": "^2.0.0",
|
||||||
"@emotion/react": "^11.8.1",
|
"@emotion/react": "^11.8.1",
|
||||||
"@emotion/styled": "^11.3.0",
|
"@emotion/styled": "^11.3.0",
|
||||||
"@hookform/resolvers": "^2.9.11",
|
"@hookform/resolvers": "^3.9.0",
|
||||||
"@tanstack/react-query": "^4.2.3",
|
"@tanstack/react-query": "^5.50.1",
|
||||||
"apexcharts": "^3.36.3",
|
"apexcharts": "^3.36.3",
|
||||||
"chakra-react-select": "^4.4.2",
|
"chakra-react-select": "^4.4.2",
|
||||||
"cookies-next": "^2.1.1",
|
"cookies-next": "^2.1.1",
|
||||||
"deepmerge-ts": "^4.2.2",
|
"deepmerge-ts": "^7.0.3",
|
||||||
"framer-motion": "^6.0.0",
|
"framer-motion": "^11.0.0",
|
||||||
"next": "^13.2.1",
|
"next": "^14.2.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-apexcharts": "^1.4.0",
|
"react-apexcharts": "^1.4.0",
|
||||||
"react-calendar": "^3.7.0",
|
"react-calendar": "^5.0.0",
|
||||||
"react-colorful": "^5.6.1",
|
"react-colorful": "^5.6.1",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^18.0.0",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-hook-form": "^7.43.2",
|
"react-hook-form": "^7.43.2",
|
||||||
"react-icons": "^4.3.1",
|
"react-icons": "^5.2.1",
|
||||||
"zod": "^3.20.6",
|
"zod": "^3.20.6",
|
||||||
"zustand": "^4.4.6"
|
"zustand": "^4.4.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.21.0",
|
"@babel/core": "^7.21.0",
|
||||||
"@types/node": "16.11.7",
|
"@types/node": "20.14.10",
|
||||||
"@types/react": "^18.0.0",
|
"@types/react": "^18.0.0",
|
||||||
"@types/react-calendar": "^3.5.2",
|
|
||||||
"@types/react-dom": "^18.0.0",
|
"@types/react-dom": "^18.0.0",
|
||||||
"eslint": "^8.0.0",
|
"eslint": "^8.0.0",
|
||||||
"eslint-config-next": "^13.2.1",
|
"eslint-config-next": "^14.2.4",
|
||||||
"eslint-config-prettier": "8.1.0",
|
"eslint-config-prettier": "9.1.0",
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^3.3.2",
|
||||||
"typescript": "^4.8.2"
|
"typescript": "^5.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
||||||
import { CustomFeatures, CustomGuildInfo } from '../config/types';
|
import { CustomFeatures, CustomGuildInfo } from '../config/types';
|
||||||
import { QueryClient, useMutation, useQuery } from '@tanstack/react-query';
|
import { QueryClient, useMutation, useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
||||||
import { UserInfo, getGuild, getGuilds, fetchUserInfo } from '@/api/discord';
|
import { UserInfo, getGuild, getGuilds, fetchUserInfo } from '@/api/discord';
|
||||||
import {
|
import {
|
||||||
disableFeature,
|
disableFeature,
|
||||||
|
@ -41,48 +41,54 @@ export const Mutations = {
|
||||||
export function useGuild(id: string) {
|
export function useGuild(id: string) {
|
||||||
const accessToken = useAccessToken();
|
const accessToken = useAccessToken();
|
||||||
|
|
||||||
return useQuery(['guild', id], () => getGuild(accessToken as string, id), {
|
return useQuery({
|
||||||
enabled: accessToken != null,
|
queryKey: ['guild', id],
|
||||||
|
queryFn: () => getGuild(accessToken as string, id),
|
||||||
|
enabled: accessToken != null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useGuilds() {
|
export function useGuilds() {
|
||||||
const accessToken = useAccessToken();
|
const accessToken = useAccessToken();
|
||||||
|
|
||||||
return useQuery(['user_guilds'], () => getGuilds(accessToken as string), {
|
return useQuery({
|
||||||
enabled: accessToken != null,
|
queryKey: ['user_guilds'],
|
||||||
|
queryFn: () => getGuilds(accessToken as string),
|
||||||
|
enabled: accessToken != null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSelfUserQuery() {
|
export function useSelfUserQuery() {
|
||||||
const accessToken = useAccessToken();
|
const accessToken = useAccessToken();
|
||||||
|
|
||||||
return useQuery<UserInfo>(['users', 'me'], () => fetchUserInfo(accessToken!!), {
|
return useQuery<UserInfo>({
|
||||||
|
queryKey: ['users', 'me'],
|
||||||
|
queryFn: () => fetchUserInfo(accessToken!!),
|
||||||
enabled: accessToken != null,
|
enabled: accessToken != null,
|
||||||
staleTime: Infinity,
|
staleTime: Infinity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useGuildInfoQuery(guild: string) {
|
export function useGuildInfoQuery(guild: string) {
|
||||||
const { status, session } = useSession();
|
const { status, session } = useSession();
|
||||||
|
|
||||||
return useQuery<CustomGuildInfo | null>(
|
return useQuery<CustomGuildInfo | null>({
|
||||||
Keys.guild_info(guild),
|
queryKey: Keys.guild_info(guild),
|
||||||
() => fetchGuildInfo(session!!, guild),
|
queryFn: () => fetchGuildInfo(session!!, guild),
|
||||||
{
|
enabled: status === 'authenticated',
|
||||||
enabled: status === 'authenticated',
|
refetchOnWindowFocus: true,
|
||||||
refetchOnWindowFocus: true,
|
retry: false,
|
||||||
retry: false,
|
staleTime: 0
|
||||||
staleTime: 0,
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFeatureQuery<K extends keyof CustomFeatures>(guild: string, feature: K) {
|
export function useFeatureQuery<K extends keyof CustomFeatures>(guild: string, feature: K) {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
const { status, session } = useSession();
|
const { status, session } = useSession();
|
||||||
|
|
||||||
return useQuery(Keys.features(guild, feature), () => getFeature(session!!, guild, feature), {
|
return useSuspenseQuery({
|
||||||
enabled: status === 'authenticated',
|
queryKey: Keys.features(guild, feature),
|
||||||
|
queryFn: () => getFeature(session!!, guild, feature)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,34 +96,32 @@ export type EnableFeatureOptions = { guild: string; feature: string; enabled: bo
|
||||||
export function useEnableFeatureMutation() {
|
export function useEnableFeatureMutation() {
|
||||||
const { session } = useSession();
|
const { session } = useSession();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation({
|
||||||
async ({ enabled, guild, feature }: EnableFeatureOptions) => {
|
mutationFn: async ({ enabled, guild, feature }: EnableFeatureOptions) => {
|
||||||
if (enabled) return enableFeature(session!!, guild, feature);
|
if (enabled) return enableFeature(session!!, guild, feature);
|
||||||
return disableFeature(session!!, guild, feature);
|
return disableFeature(session!!, guild, feature);
|
||||||
},
|
},
|
||||||
{
|
async onSuccess(_, { guild, feature, enabled }) {
|
||||||
async onSuccess(_, { guild, feature, enabled }) {
|
await client.invalidateQueries({queryKey: Keys.features(guild, feature)});
|
||||||
await client.invalidateQueries(Keys.features(guild, feature));
|
client.setQueryData<GuildInfo | null>(Keys.guild_info(guild), (prev) => {
|
||||||
client.setQueryData<GuildInfo | null>(Keys.guild_info(guild), (prev) => {
|
if (prev == null) return null;
|
||||||
if (prev == null) return null;
|
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
enabledFeatures: prev.enabledFeatures.includes(feature)
|
enabledFeatures: prev.enabledFeatures.includes(feature)
|
||||||
? prev.enabledFeatures
|
? prev.enabledFeatures
|
||||||
: [...prev.enabledFeatures, feature],
|
: [...prev.enabledFeatures, feature],
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
enabledFeatures: prev.enabledFeatures.filter((f) => f !== feature),
|
enabledFeatures: prev.enabledFeatures.filter((f) => f !== feature),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UpdateFeatureOptions = {
|
export type UpdateFeatureOptions = {
|
||||||
|
@ -128,29 +132,33 @@ export type UpdateFeatureOptions = {
|
||||||
export function useUpdateFeatureMutation() {
|
export function useUpdateFeatureMutation() {
|
||||||
const { session } = useSession();
|
const { session } = useSession();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation({
|
||||||
(options: UpdateFeatureOptions) =>
|
mutationFn: (options: UpdateFeatureOptions) =>
|
||||||
updateFeature(session!!, options.guild, options.feature, options.options),
|
updateFeature(session!!, options.guild, options.feature, options.options),
|
||||||
{
|
onSuccess(updated, options) {
|
||||||
onSuccess(updated, options) {
|
const key = Keys.features(options.guild, options.feature);
|
||||||
const key = Keys.features(options.guild, options.feature);
|
|
||||||
|
|
||||||
return client.setQueryData(key, updated);
|
return client.setQueryData(key, updated);
|
||||||
},
|
},
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useGuildRolesQuery(guild: string) {
|
export function useGuildRolesQuery(guild: string) {
|
||||||
const { session } = useSession();
|
const { session } = useSession();
|
||||||
|
|
||||||
return useQuery(Keys.guildRoles(guild), () => fetchGuildRoles(session!!, guild));
|
return useQuery({
|
||||||
|
queryKey: Keys.guildRoles(guild),
|
||||||
|
queryFn: () => fetchGuildRoles(session!!, guild)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useGuildChannelsQuery(guild: string) {
|
export function useGuildChannelsQuery(guild: string) {
|
||||||
const { session } = useSession();
|
const { session } = useSession();
|
||||||
|
|
||||||
return useQuery(Keys.guildChannels(guild), () => fetchGuildChannels(session!!, guild));
|
return useQuery({
|
||||||
|
queryKey: Keys.guildChannels(guild),
|
||||||
|
queryFn: () => fetchGuildChannels(session!!, guild)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSelfUser(): UserInfo {
|
export function useSelfUser(): UserInfo {
|
||||||
|
|
|
@ -46,7 +46,7 @@ export function FeatureItem({
|
||||||
<CardFooter as={ButtonGroup} mt={3}>
|
<CardFooter as={ButtonGroup} mt={3}>
|
||||||
<Button
|
<Button
|
||||||
size={{ base: 'sm', md: 'md' }}
|
size={{ base: 'sm', md: 'md' }}
|
||||||
disabled={mutation.isLoading}
|
disabled={mutation.isPending}
|
||||||
{...(enabled
|
{...(enabled
|
||||||
? {
|
? {
|
||||||
variant: 'action',
|
variant: 'action',
|
||||||
|
|
|
@ -41,14 +41,14 @@ export function UpdateFeaturePanel({
|
||||||
<Text color="TextSecondary">{config.description}</Text>
|
<Text color="TextSecondary">{config.description}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<ButtonGroup mt={3}>
|
<ButtonGroup mt={3}>
|
||||||
<Button variant="danger" isLoading={enableMutation.isLoading} onClick={onDisable}>
|
<Button variant="danger" isLoading={enableMutation.isPending} onClick={onDisable}>
|
||||||
<view.T text={(e) => e.bn.disable} />
|
<view.T text={(e) => e.bn.disable} />
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{result.component}
|
{result.component}
|
||||||
<Savebar isLoading={mutation.isLoading} result={result} />
|
<Savebar isLoading={mutation.isPending} result={result} />
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Chakra imports
|
// Chakra imports
|
||||||
import { Box, HStack, Icon, Spacer, Text } from '@chakra-ui/react';
|
import { Box, HStack, Image, Spacer, Text } from '@chakra-ui/react';
|
||||||
import { config } from '@/config/common';
|
import { config } from '@/config/common';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { SelectField } from '../forms/SelectField';
|
import { SelectField } from '../forms/SelectField';
|
||||||
|
@ -19,7 +19,7 @@ export default function AuthLayout({ children }: { children: ReactNode }) {
|
||||||
px={{ base: 5, lg: 10 }}
|
px={{ base: 5, lg: 10 }}
|
||||||
py={2}
|
py={2}
|
||||||
>
|
>
|
||||||
{config.icon != null && <Icon color="TextPrimary" as={config.icon} w={10} h={10} />}
|
{config.icon != null && <Image src={config.icon} boxSize={10} alt='Logo'/>}
|
||||||
<Text fontWeight="600" fontSize="lg">
|
<Text fontWeight="600" fontSize="lg">
|
||||||
{config.name}
|
{config.name}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Box, Center } from '@chakra-ui/layout';
|
|
||||||
import AppLayout from '../app';
|
import AppLayout from '../app';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import GuildNavbar from './guild-navbar';
|
import GuildNavbar from './guild-navbar';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { FaChevronLeft as ChevronLeftIcon } from 'react-icons/fa';
|
import { FaChevronLeft as ChevronLeftIcon } from 'react-icons/fa';
|
||||||
import { Box, Flex, Text } from '@chakra-ui/layout';
|
import { Box, Flex, Text } from '@chakra-ui/layout';
|
||||||
import { Avatar, Icon, IconButton, SkeletonCircle } from '@chakra-ui/react';
|
import { Avatar, Icon, SkeletonCircle } from '@chakra-ui/react';
|
||||||
import { iconUrl } from '@/api/discord';
|
import { iconUrl } from '@/api/discord';
|
||||||
import { useGuildPreview } from '@/api/hooks';
|
import { useGuildPreview } from '@/api/hooks';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
|
|
@ -1,29 +1,12 @@
|
||||||
import { createIcon } from '@chakra-ui/react';
|
|
||||||
import { PermissionFlags } from '@/api/discord';
|
import { PermissionFlags } from '@/api/discord';
|
||||||
import { AppConfig } from './types';
|
import { AppConfig } from './types';
|
||||||
|
|
||||||
const BotIcon = createIcon({
|
|
||||||
displayName: 'OmagizeLogo',
|
|
||||||
viewBox: '0 0 512 512',
|
|
||||||
path: (
|
|
||||||
<g>
|
|
||||||
<path
|
|
||||||
d="m494.6,241.1l-50.1-47c-50.5-47.3-117.4-73.3-188.5-73.3-71.1,0-138,26-188.4,73.3l-50.1,47c-12.1,12.9-4.3,26.5 0,29.8l50.1,47c50.4,47.3 117.3,73.3 188.4,73.3 71.1,0 138-26 188.4-73.3l50.1-47c4.7-3.9 12.2-17.6 0.1-29.8zm-238.6,74.9c-33.1,0-60-26.9-60-60 0-33.1 26.9-60 60-60 33.1,0 60,26.9 60,60 0,33.1-26.9,60-60,60zm-194.7-60l34.3-32.1c32-30 72-49.9 115.5-58.1-33.1,16.6-55.8,50.8-55.8,90.2 0,39.4 22.8,73.7 55.8,90.2-43.5-8.1-83.5-28.1-115.5-58.1l-34.3-32.1zm355.2,32.1c-32,30-72,50-115.5,58.1 33.1-16.6 55.8-50.8 55.8-90.2 0-39.4-22.8-73.6-55.8-90.2 43.5,8.1 83.5,28.1 115.5,58.1l34.3,32.1-34.3,32.1z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="m256,235.2c-11.3,0-20.8,9.5-20.8,20.8 0,11.3 9.5,20.8 20.8,20.8 11.3,0 20.8-9.5 20.8-20.8 0-11.3-9.5-20.8-20.8-20.8z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const config: AppConfig = {
|
export const config: AppConfig = {
|
||||||
name: 'Demo Bot',
|
name: 'VOTL Bot',
|
||||||
icon: BotIcon,
|
icon:
|
||||||
|
'https://cdn.fileeditor.dev/media/votl/logo.png',
|
||||||
inviteUrl:
|
inviteUrl:
|
||||||
'https://discord.com/api/oauth2/authorize?client_id=1070011901385375845&permissions=8&scope=bot',
|
'https://discord.com/oauth2/authorize?client_id=397461072342417408&permissions=8&integration_type=0&scope=bot+applications.commands',
|
||||||
guild: {
|
guild: {
|
||||||
//filter guilds that user has no permissions to manage it
|
//filter guilds that user has no permissions to manage it
|
||||||
filter: (guild) => (Number(guild.permissions) & PermissionFlags.ADMINISTRATOR) !== 0,
|
filter: (guild) => (Number(guild.permissions) & PermissionFlags.ADMINISTRATOR) !== 0,
|
||||||
|
|
|
@ -23,16 +23,6 @@ const { T } = createI18n(provider, {
|
||||||
memes: 'Memes Time',
|
memes: 'Memes Time',
|
||||||
'memes description': 'Send memes everyday',
|
'memes description': 'Send memes everyday',
|
||||||
},
|
},
|
||||||
cn: {
|
|
||||||
music: '音樂播放器',
|
|
||||||
'music description': '在您的 Discord 服務器中播放音樂',
|
|
||||||
gaming: '遊戲',
|
|
||||||
'gaming description': 'Enjoy playing games with your friends',
|
|
||||||
'reaction role': '反應角色',
|
|
||||||
'reaction role description': '單擊按鈕時為用戶賦予角色',
|
|
||||||
memes: '模因時間',
|
|
||||||
'memes description': '每天發送模因',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,9 +7,4 @@ export const auth = createI18n(provider, {
|
||||||
'login description': 'Login and start using our bot today',
|
'login description': 'Login and start using our bot today',
|
||||||
login_bn: 'Login with Discord',
|
login_bn: 'Login with Discord',
|
||||||
},
|
},
|
||||||
cn: {
|
|
||||||
login: '登入控制面板',
|
|
||||||
'login description': '登錄並開始使用我們的機器人',
|
|
||||||
login_bn: '使用 Discord 登錄',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,15 +13,4 @@ export const common = createI18n(provider, {
|
||||||
pages: 'Pages',
|
pages: 'Pages',
|
||||||
logout: 'Logout',
|
logout: 'Logout',
|
||||||
},
|
},
|
||||||
cn: {
|
|
||||||
loading: '加載中',
|
|
||||||
search: '搜索',
|
|
||||||
'select lang': '選擇你的語言',
|
|
||||||
'select role': '選擇身份組',
|
|
||||||
'select channel': '選擇頻道',
|
|
||||||
dashboard: '儀表板',
|
|
||||||
profile: '用戶資料',
|
|
||||||
pages: '所有頁面',
|
|
||||||
logout: '登出',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,25 +23,4 @@ export const dashboard = createI18n(provider, {
|
||||||
description: 'Use of commands of your server',
|
description: 'Use of commands of your server',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cn: {
|
|
||||||
pricing: '價錢',
|
|
||||||
learn_more: '了解更多',
|
|
||||||
invite: {
|
|
||||||
title: '邀請我們的機器人',
|
|
||||||
description: '一鍵試用我們的 Discord 機器人',
|
|
||||||
bn: '現在邀請',
|
|
||||||
},
|
|
||||||
servers: {
|
|
||||||
title: '選擇服務器',
|
|
||||||
description: '自定義您的服務器',
|
|
||||||
},
|
|
||||||
vc: {
|
|
||||||
create: '創建語音通道',
|
|
||||||
'created channels': '已創建語音頻道',
|
|
||||||
},
|
|
||||||
command: {
|
|
||||||
title: '命令使用量',
|
|
||||||
description: '使用你的服務器命令使用量',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,19 +17,4 @@ export const feature = createI18n(provider, {
|
||||||
discard: 'Discard',
|
discard: 'Discard',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cn: {
|
|
||||||
unsaved: '您有未保存的更改',
|
|
||||||
error: {
|
|
||||||
'not enabled': '未啟用',
|
|
||||||
'not enabled description': '嘗試啟用此功能?',
|
|
||||||
'not found': '未找到功能',
|
|
||||||
'not found description': '奇怪...我們找不到它',
|
|
||||||
},
|
|
||||||
bn: {
|
|
||||||
enable: '啟用功能',
|
|
||||||
disable: '關閉功能',
|
|
||||||
save: '保存更改',
|
|
||||||
discard: '放棄',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,22 +20,4 @@ export const guild = createI18n(provider, {
|
||||||
settings: 'Settings',
|
settings: 'Settings',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cn: {
|
|
||||||
features: '管理機器人功能',
|
|
||||||
banner: {
|
|
||||||
title: '立即免費試用',
|
|
||||||
description: '為您的服務器定制機器人',
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
'not found': '它在哪裡?',
|
|
||||||
'not found description': '機器人無法訪問服務器,我們邀請他吧!',
|
|
||||||
load: '無法加載服務器',
|
|
||||||
},
|
|
||||||
bn: {
|
|
||||||
'enable feature': '啟用功能',
|
|
||||||
'config feature': '配置',
|
|
||||||
invite: '邀請機器人',
|
|
||||||
settings: '設置',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,14 +13,4 @@ export const profile = createI18n(provider, {
|
||||||
'dev mode': 'Developer Mode',
|
'dev mode': 'Developer Mode',
|
||||||
'dev mode description': 'Used for debugging and testing',
|
'dev mode description': 'Used for debugging and testing',
|
||||||
},
|
},
|
||||||
cn: {
|
|
||||||
logout: common.translations.cn.logout,
|
|
||||||
language: '你的語言',
|
|
||||||
'language description': '選擇你的語言',
|
|
||||||
settings: '設置',
|
|
||||||
'dark mode': '黑暗模式',
|
|
||||||
'dark mode description': '啟用深色主題可以保護您的眼睛',
|
|
||||||
'dev mode': '開發者模式',
|
|
||||||
'dev mode description': '用於調試和測試',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,10 +4,9 @@ import Router, { useRouter } from 'next/router';
|
||||||
/**
|
/**
|
||||||
* Supported languages
|
* Supported languages
|
||||||
*/
|
*/
|
||||||
export type Languages = 'en' | 'cn';
|
export type Languages = 'en';
|
||||||
export const { languages, names } = initLanguages<Languages>({
|
export const { languages, names } = initLanguages<Languages>({
|
||||||
en: 'English',
|
en: 'English',
|
||||||
cn: '中文',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const provider = initI18n<Languages>({
|
export const provider = initI18n<Languages>({
|
||||||
|
|
|
@ -8,9 +8,9 @@ export type AppConfig = {
|
||||||
*/
|
*/
|
||||||
name: string;
|
name: string;
|
||||||
/**
|
/**
|
||||||
* icon (react component)
|
* Image Url
|
||||||
*/
|
*/
|
||||||
icon?: (props: any) => ReactElement;
|
icon: string;
|
||||||
/**
|
/**
|
||||||
* Guild settings
|
* Guild settings
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -45,7 +45,7 @@ function NotEnabled() {
|
||||||
<Text color="TextSecondary">{t.error['not enabled description']}</Text>
|
<Text color="TextSecondary">{t.error['not enabled description']}</Text>
|
||||||
<Button
|
<Button
|
||||||
mt={3}
|
mt={3}
|
||||||
isLoading={enable.isLoading}
|
isLoading={enable.isPending}
|
||||||
onClick={() => enable.mutate({ enabled: true, guild, feature })}
|
onClick={() => enable.mutate({ enabled: true, guild, feature })}
|
||||||
variant="action"
|
variant="action"
|
||||||
px={6}
|
px={6}
|
||||||
|
|
|
@ -50,7 +50,7 @@ export function GuildSelect() {
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (guilds.status === 'loading')
|
if (guilds.status === 'pending')
|
||||||
return (
|
return (
|
||||||
<SimpleGrid columns={{ base: 1, md: 2, xl: 3 }} gap={3}>
|
<SimpleGrid columns={{ base: 1, md: 2, xl: 3 }} gap={3}>
|
||||||
<Skeleton minH="88px" rounded="2xl" />
|
<Skeleton minH="88px" rounded="2xl" />
|
||||||
|
|
|
@ -105,7 +105,7 @@ const ProfilePage: NextPageWithLayout = () => {
|
||||||
<Button
|
<Button
|
||||||
leftIcon={<IoLogOut />}
|
leftIcon={<IoLogOut />}
|
||||||
variant="danger"
|
variant="danger"
|
||||||
isLoading={logout.isLoading}
|
isLoading={logout.isPending}
|
||||||
onClick={() => logout.mutate()}
|
onClick={() => logout.mutate()}
|
||||||
>
|
>
|
||||||
{t.logout}
|
{t.logout}
|
||||||
|
|
|
@ -22,7 +22,7 @@ export async function logout() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await client.invalidateQueries(Keys.login);
|
await client.invalidateQueries({queryKey: Keys.login});
|
||||||
await Router.push('/auth/signin');
|
await Router.push('/auth/signin');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,10 @@ type SessionResult =
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useSession(): SessionResult {
|
export function useSession(): SessionResult {
|
||||||
const { isError, isLoading, data } = useQuery(Keys.login, () => auth());
|
const { isError, isLoading, data } = useQuery({
|
||||||
|
queryKey: Keys.login,
|
||||||
|
queryFn: () => auth()
|
||||||
|
});
|
||||||
|
|
||||||
if (isError)
|
if (isError)
|
||||||
return {
|
return {
|
||||||
|
@ -53,7 +56,7 @@ export function useSession(): SessionResult {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: 'authenticated',
|
status: 'authenticated',
|
||||||
session: data,
|
session: data!,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,5 +67,8 @@ export function useAccessToken() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useLogoutMutation() {
|
export function useLogoutMutation() {
|
||||||
return useMutation(['logout'], () => logout());
|
return useMutation({
|
||||||
|
mutationKey: ['logout'],
|
||||||
|
mutationFn: () => logout()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ export function getServerSession(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setServerSession(req: NextApiRequest, res: NextApiResponse, data: AccessToken) {
|
export function setServerSession(req: NextApiRequest, res: NextApiResponse, data: AccessToken) {
|
||||||
|
// @ts-ignore
|
||||||
setCookie(TokenCookie, data, { req, res, ...options });
|
setCookie(TokenCookie, data, { req, res, ...options });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +52,7 @@ export async function removeSession(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const session = getServerSession(req);
|
const session = getServerSession(req);
|
||||||
|
|
||||||
if (session.success) {
|
if (session.success) {
|
||||||
|
// @ts-ignore
|
||||||
deleteCookie(TokenCookie, { req, res, ...options });
|
deleteCookie(TokenCookie, { req, res, ...options });
|
||||||
await revokeToken(session.data.access_token);
|
await revokeToken(session.data.access_token);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { AccessToken } from '@/utils/auth/server';
|
||||||
import { Options } from './core';
|
import { Options } from './core';
|
||||||
|
|
||||||
const bot_api_endpoint = process.env.NEXT_PUBLIC_API_ENDPOINT ?? 'http://localhost:8080';
|
const bot_api_endpoint = process.env.NEXT_PUBLIC_API_ENDPOINT ?? 'http://localhost:8080';
|
||||||
const discord_api_endpoint = 'https://discord.com/api/v9';
|
const discord_api_endpoint = 'https://discord.com/api/v10';
|
||||||
|
|
||||||
export function botRequest<T extends Options>(session: AccessToken, options: T): T {
|
export function botRequest<T extends Options>(session: AccessToken, options: T): T {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -58,13 +58,14 @@ class CommandHandler extends BaseEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
client.logger.cmd(
|
client.logger.cmd(
|
||||||
`User ${interaction.user.getUsername()} used ${command.command.name} in ${interaction.guild ? interaction.guild.name : "DM"} with arguments: ${
|
`[${interaction.guild ? interaction.guild.name : "DM"}]: [${interaction.user.getUsername()}] => /${command.command.name}${
|
||||||
interaction.options.data.length > 0
|
interaction.options.data.length > 0
|
||||||
? interaction.options.data
|
? `, args: [${interaction.options.data
|
||||||
.map(arg => {
|
.map(arg => {
|
||||||
return `${arg.name}: ${arg.value}`;
|
return `${arg.name}: ${arg.value}`;
|
||||||
}).join(", ")
|
})
|
||||||
: "no args"
|
.join(", ")}]`
|
||||||
|
: ""
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,9 @@
|
||||||
const Canvas = require("canvas"),
|
// const Canvas = require("@napi-rs/canvas"),
|
||||||
BaseEvent = require("../../base/BaseEvent"),
|
// BaseEvent = require("../../base/BaseEvent"),
|
||||||
{ AttachmentBuilder } = require("discord.js"),
|
// { AttachmentBuilder } = require("discord.js"),
|
||||||
{ resolve } = require("path");
|
// { applyText } = require("../../helpers/functions");
|
||||||
|
|
||||||
Canvas.registerFont(resolve("./assets/fonts/RubikMonoOne-Regular.ttf"), { family: "RubikMonoOne" });
|
const BaseEvent = require("../../base/BaseEvent");
|
||||||
Canvas.registerFont(resolve("./assets/fonts/KeepCalm-Medium.ttf"), { family: "KeepCalm" });
|
|
||||||
|
|
||||||
const 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
class GuildMemberAdd extends BaseEvent {
|
class GuildMemberAdd extends BaseEvent {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -45,6 +36,7 @@ class GuildMemberAdd extends BaseEvent {
|
||||||
.replace(/{server}/g, member.guild.name)
|
.replace(/{server}/g, member.guild.name)
|
||||||
.replace(/{membercount}/g, member.guild.memberCount);
|
.replace(/{membercount}/g, member.guild.memberCount);
|
||||||
|
|
||||||
|
/*
|
||||||
if (guildData.plugins.welcome.withImage) {
|
if (guildData.plugins.welcome.withImage) {
|
||||||
const canvas = Canvas.createCanvas(1024, 450),
|
const canvas = Canvas.createCanvas(1024, 450),
|
||||||
ctx = canvas.getContext("2d");
|
ctx = canvas.getContext("2d");
|
||||||
|
@ -132,14 +124,15 @@ class GuildMemberAdd extends BaseEvent {
|
||||||
);
|
);
|
||||||
ctx.drawImage(avatar, 45, 90, 270, 270);
|
ctx.drawImage(avatar, 45, 90, 270, 270);
|
||||||
|
|
||||||
const attachment = new AttachmentBuilder(canvas.toBuffer(), { name: "welcome-image.png" });
|
const attachment = new AttachmentBuilder((await canvas.encode("png")), { name: "welcome.png" });
|
||||||
|
|
||||||
channel.send({
|
channel.send({
|
||||||
content: message,
|
content: message,
|
||||||
files: [attachment],
|
files: [attachment],
|
||||||
});
|
});
|
||||||
} else
|
} else */
|
||||||
channel.send({ content: message });
|
|
||||||
|
channel.send({ content: message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,9 @@
|
||||||
const Canvas = require("canvas"),
|
// const Canvas = require("@napi-rs/canvas"),
|
||||||
BaseEvent = require("../../base/BaseEvent"),
|
// BaseEvent = require("../../base/BaseEvent"),
|
||||||
{ AttachmentBuilder } = require("discord.js"),
|
// { AttachmentBuilder } = require("discord.js"),
|
||||||
{ resolve } = require("path");
|
// { applyText } = require("../../helpers/functions");
|
||||||
|
|
||||||
Canvas.registerFont(resolve("./assets/fonts/RubikMonoOne-Regular.ttf"), { family: "RubikMonoOne" });
|
const BaseEvent = require("../../base/BaseEvent");
|
||||||
Canvas.registerFont(resolve("./assets/fonts/KeepCalm-Medium.ttf"), { family: "KeepCalm" });
|
|
||||||
|
|
||||||
const 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
class GuildMemberRemove extends BaseEvent {
|
class GuildMemberRemove extends BaseEvent {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -43,6 +34,7 @@ class GuildMemberRemove extends BaseEvent {
|
||||||
.replace(/{server}/g, member.guild.name)
|
.replace(/{server}/g, member.guild.name)
|
||||||
.replace(/{membercount}/g, member.guild.memberCount);
|
.replace(/{membercount}/g, member.guild.memberCount);
|
||||||
|
|
||||||
|
/*
|
||||||
if (guildData.plugins.goodbye.withImage) {
|
if (guildData.plugins.goodbye.withImage) {
|
||||||
const canvas = Canvas.createCanvas(1024, 450),
|
const canvas = Canvas.createCanvas(1024, 450),
|
||||||
ctx = canvas.getContext("2d");
|
ctx = canvas.getContext("2d");
|
||||||
|
@ -135,14 +127,15 @@ class GuildMemberRemove extends BaseEvent {
|
||||||
);
|
);
|
||||||
ctx.drawImage(avatar, 45, 90, 270, 270);
|
ctx.drawImage(avatar, 45, 90, 270, 270);
|
||||||
|
|
||||||
const attachment = new AttachmentBuilder(canvas.toBuffer(), { name: "goodbye-image.png" });
|
const attachment = new AttachmentBuilder((await canvas.encode("png")), { name: "goodbye-image.png" });
|
||||||
|
|
||||||
channel.send({
|
channel.send({
|
||||||
content: message,
|
content: message,
|
||||||
files: [attachment],
|
files: [attachment],
|
||||||
});
|
});
|
||||||
} else
|
} else */
|
||||||
channel.send({ content: message });
|
|
||||||
|
channel.send({ content: message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,18 @@ const { CronJob } = require("cron");
|
||||||
module.exports.init = async client => {
|
module.exports.init = async client => {
|
||||||
const cronjob = new CronJob("0 5 * * *", async function () {
|
const cronjob = new CronJob("0 5 * * *", async function () {
|
||||||
client.guilds.cache.forEach(async guild => {
|
client.guilds.cache.forEach(async guild => {
|
||||||
const guildData = await client.getGuildData(guild.id);
|
try {
|
||||||
const channel = guildData.plugins.birthdays ? client.channels.cache.get(guildData.plugins.birthdays) || (await client.channels.fetch(guildData.plugins.birthdays)) : null;
|
console.log(`Checking birthdays for "${guild.name}"`);
|
||||||
|
|
||||||
if (guildData.plugins.birthdays && client.channels.cache.get(guildData.plugins.birthdays)) {
|
const guildData = await client.getGuildData(guild.id);
|
||||||
const date = new Date(),
|
const channel = guildData.plugins.birthdays ? await client.channels.fetch(guildData.plugins.birthdays) : null;
|
||||||
currentDay = date.getDate(),
|
|
||||||
currentMonth = date.getMonth() + 1,
|
|
||||||
currentYear = date.getFullYear();
|
|
||||||
|
|
||||||
if (channel) {
|
if (channel) {
|
||||||
|
const date = new Date(),
|
||||||
|
currentDay = date.getDate(),
|
||||||
|
currentMonth = date.getMonth() + 1,
|
||||||
|
currentYear = date.getFullYear();
|
||||||
|
|
||||||
client.usersData.find({ birthdate: { $gt: 1 } }).then(async users => {
|
client.usersData.find({ birthdate: { $gt: 1 } }).then(async users => {
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
if (!guild.members.cache.find(m => m.id === user.id)) return;
|
if (!guild.members.cache.find(m => m.id === user.id)) return;
|
||||||
|
@ -53,6 +55,9 @@ module.exports.init = async client => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code === 10003) console.log(`No channel found for ${guild.name}`);
|
||||||
|
else throw err;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -70,51 +75,49 @@ module.exports.init = async client => {
|
||||||
module.exports.run = async client => {
|
module.exports.run = async client => {
|
||||||
client.guilds.cache.forEach(async guild => {
|
client.guilds.cache.forEach(async guild => {
|
||||||
const guildData = await client.getGuildData(guild.id);
|
const guildData = await client.getGuildData(guild.id);
|
||||||
const channel = guildData.plugins.birthdays ? client.channels.cache.get(guildData.plugins.birthdays) || (await client.channels.fetch(guildData.plugins.birthdays)) : null;
|
const channel = guildData.plugins.birthdays ? await client.channels.fetch(guildData.plugins.birthdays) : null;
|
||||||
|
|
||||||
if (guildData.plugins.birthdays) {
|
if (channel) {
|
||||||
const date = new Date(),
|
const date = new Date(),
|
||||||
currentDay = date.getDate(),
|
currentDay = date.getDate(),
|
||||||
currentMonth = date.getMonth() + 1,
|
currentMonth = date.getMonth() + 1,
|
||||||
currentYear = date.getFullYear();
|
currentYear = date.getFullYear();
|
||||||
|
|
||||||
if (channel) {
|
client.usersData.find({ birthdate: { $gt: 1 } }).then(async users => {
|
||||||
client.usersData.find({ birthdate: { $gt: 1 } }).then(async users => {
|
for (const user of users) {
|
||||||
for (const user of users) {
|
if (!guild.members.cache.find(m => m.id === user.id)) return;
|
||||||
if (!guild.members.cache.find(m => m.id === user.id)) return;
|
|
||||||
|
|
||||||
const userDate = new Date(user.birthdate * 1000),
|
const userDate = new Date(user.birthdate * 1000),
|
||||||
day = userDate.getDate(),
|
day = userDate.getDate(),
|
||||||
month = userDate.getMonth() + 1,
|
month = userDate.getMonth() + 1,
|
||||||
year = userDate.getFullYear(),
|
year = userDate.getFullYear(),
|
||||||
age = currentYear - year;
|
age = currentYear - year;
|
||||||
|
|
||||||
if (currentMonth === month && currentDay === day) {
|
if (currentMonth === month && currentDay === day) {
|
||||||
const embed = client.embed({
|
const embed = client.embed({
|
||||||
author: client.user.getUsername(),
|
author: client.user.getUsername(),
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: client.translate("economy/birthdate:HAPPY_BIRTHDAY", null, guildData.language),
|
name: client.translate("economy/birthdate:HAPPY_BIRTHDAY", null, guildData.language),
|
||||||
value: client.translate("economy/birthdate:HAPPY_BIRTHDAY_MESSAGE", {
|
value: client.translate("economy/birthdate:HAPPY_BIRTHDAY_MESSAGE", {
|
||||||
user: user.id,
|
user: user.id,
|
||||||
age: `**${age}** ${client.functions.getNoun(
|
age: `**${age}** ${client.functions.getNoun(
|
||||||
age,
|
age,
|
||||||
client.translate("misc:NOUNS:AGE:1", null, guildData.language),
|
client.translate("misc:NOUNS:AGE:1", null, guildData.language),
|
||||||
client.translate("misc:NOUNS:AGE:2", null, guildData.language),
|
client.translate("misc:NOUNS:AGE:2", null, guildData.language),
|
||||||
client.translate("misc:NOUNS:AGE:5", null, guildData.language),
|
client.translate("misc:NOUNS:AGE:5", null, guildData.language),
|
||||||
)}`,
|
)}`,
|
||||||
}, guildData.language),
|
}, guildData.language),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
channel.send({
|
channel.send({
|
||||||
embeds: [embed],
|
embeds: [embed],
|
||||||
}).then(m => m.react("🎉"));
|
}).then(m => m.react("🎉"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -1,4 +1,9 @@
|
||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
|
// const { GlobalFonts } = require("@napi-rs/canvas"),
|
||||||
|
// const { resolve } = require("path");
|
||||||
|
|
||||||
|
// GlobalFonts.registerFromPath(resolve("./assets/fonts/RubikMonoOne-Regular.ttf"), "RubikMonoOne");
|
||||||
|
// GlobalFonts.registerFromPath(resolve("./assets/fonts/KeepCalm-Medium.ttf"), "KeepCalm");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
/**
|
/**
|
||||||
|
@ -125,4 +130,23 @@ module.exports = {
|
||||||
|
|
||||||
return five;
|
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;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
14
package.json
14
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "jaba",
|
"name": "jaba",
|
||||||
"version": "4.6.0",
|
"version": "4.6.1",
|
||||||
"description": "My Discord Bot",
|
"description": "My Discord Bot",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -11,15 +11,15 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discord-player/extractor": "^4.4.7",
|
"@discord-player/extractor": "^4.4.7",
|
||||||
"@discordjs/opus": "^0.9.0",
|
"@discordjs/opus": "^0.9.0",
|
||||||
"@discordjs/rest": "^2.2.0",
|
"@discordjs/rest": "^2.3.0",
|
||||||
"@discordjs/voice": "^0.16.1",
|
"@discordjs/voice": "^0.17.0",
|
||||||
"canvas": "^2.11.2",
|
"@napi-rs/canvas": "^0.1.53",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"cron": "^2.4.4",
|
"cron": "^2.4.4",
|
||||||
"discord-api-types": "^0.37.71",
|
"discord-api-types": "^0.37.90",
|
||||||
"discord-giveaways": "^6.0.1",
|
"discord-giveaways": "^6.0.1",
|
||||||
"discord-player": "^6.6.8",
|
"discord-player": "^6.6.10",
|
||||||
"discord.js": "^14.14.1",
|
"discord.js": "^14.15.3",
|
||||||
"gamedig": "^4.1.0",
|
"gamedig": "^4.1.0",
|
||||||
"i18next": "^21.10.0",
|
"i18next": "^21.10.0",
|
||||||
"i18next-fs-backend": "^1.2.0",
|
"i18next-fs-backend": "^1.2.0",
|
||||||
|
|
2945
pnpm-lock.yaml
2945
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue