mirror of
https://github.com/JonnyBro/JaBa.git
synced 2024-12-29 23:03:02 +05:00
Compare commits
24 commits
11bb890a99
...
5e800f7447
Author | SHA1 | Date | |
---|---|---|---|
5e800f7447 | |||
3cf0b475af | |||
89a55df456 | |||
|
464631ad01 | ||
488d2d0bfa | |||
9a335aa3e2 | |||
511976a239 | |||
62e8c7a244 | |||
83866db885 | |||
e079b035b9 | |||
e50c36a967 | |||
4728258071 | |||
6ad65e7019 | |||
8b677f9ec4 | |||
0e38d2086c | |||
d1a6ed597c | |||
d57cc7145d | |||
|
74e97e9ccc | ||
fb8b1d8496 | |||
0baaa76be3 | |||
7a4fe1a80f | |||
9197167f41 | |||
7a280b204c | |||
87dd38a4b1 |
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 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/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://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).
|
||||
[![image](https://img.shields.io/github/license/JonnyBro/JaBa?label=License&style=flat-square)](https://github.com/JonnyBro/JaBa/blob/main/LICENSE)
|
||||
|
||||
## 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 want to contribute, feel free to fork this repo and making a pull request!
|
||||
|
||||
# TODO
|
||||
## TODO
|
||||
|
||||
* [ ] Finish and release *dashboard-core* submodule.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const mongoose = require("mongoose"),
|
||||
Canvas = require("canvas");
|
||||
Canvas = require("@napi-rs/canvas");
|
||||
|
||||
const genToken = () => {
|
||||
let token = "";
|
||||
|
@ -107,7 +107,7 @@ userSchema.method("getAchievements", async function () {
|
|||
dim += 200;
|
||||
}
|
||||
|
||||
return canvas.toBuffer();
|
||||
return (await canvas.encode("png"));
|
||||
});
|
||||
|
||||
module.exports = mongoose.model("User", userSchema);
|
||||
|
|
|
@ -194,6 +194,7 @@ async function changeSetting(interaction, setting, state, channel) {
|
|||
if (!state) {
|
||||
data.plugins[settingSplitted[0]][settingSplitted[1]] = null;
|
||||
|
||||
data.markModified(`plugins.${setting}`);
|
||||
await data.save();
|
||||
|
||||
return interaction.reply({
|
||||
|
@ -206,6 +207,7 @@ async function changeSetting(interaction, setting, state, channel) {
|
|||
if (channel) {
|
||||
data.plugins[settingSplitted[0]][settingSplitted[1]] = channel.id;
|
||||
|
||||
data.markModified(`plugins.${setting}`);
|
||||
await data.save();
|
||||
|
||||
return interaction.reply({
|
||||
|
@ -224,6 +226,7 @@ async function changeSetting(interaction, setting, state, channel) {
|
|||
if (!state) {
|
||||
data.plugins[setting] = null;
|
||||
|
||||
data.markModified(`plugins.${setting}`);
|
||||
await data.save();
|
||||
|
||||
return interaction.reply({
|
||||
|
@ -234,6 +237,7 @@ async function changeSetting(interaction, setting, state, channel) {
|
|||
if (channel) {
|
||||
data.plugins[setting] = channel.id;
|
||||
|
||||
data.markModified(`plugins.${setting}`);
|
||||
await data.save();
|
||||
|
||||
return interaction.reply({
|
||||
|
|
|
@ -61,16 +61,16 @@ class Goodbye extends BaseCommand {
|
|||
uk: client.translate("administration/goodbye:MESSAGE", null, "uk-UA"),
|
||||
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,
|
||||
ownerOnly: false,
|
||||
|
@ -83,13 +83,15 @@ class Goodbye extends BaseCommand {
|
|||
* @param {import("discord.js").ChatInputCommandInteraction} interaction
|
||||
*/
|
||||
async execute(client, interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
const guildData = interaction.data.guild,
|
||||
command = interaction.options.getSubcommand();
|
||||
|
||||
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 {
|
||||
const state = interaction.options.getBoolean("state");
|
||||
|
||||
|
@ -101,13 +103,14 @@ class Goodbye extends BaseCommand {
|
|||
withImage: null,
|
||||
};
|
||||
|
||||
guildData.markModified("plugins.goodbye");
|
||||
await guildData.save();
|
||||
|
||||
interaction.success("administration/goodbye:DISABLED", null, { ephemeral: true });
|
||||
interaction.success("administration/goodbye:DISABLED", null, { edit: true });
|
||||
} else {
|
||||
const channel = interaction.options.getChannel("channel") || interaction.channel;
|
||||
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 = {
|
||||
enabled: true,
|
||||
|
@ -116,11 +119,12 @@ class Goodbye extends BaseCommand {
|
|||
withImage: image,
|
||||
};
|
||||
|
||||
guildData.markModified("plugins.goodbye");
|
||||
await guildData.save();
|
||||
|
||||
interaction.success("administration/goodbye:ENABLED", {
|
||||
channel: `${channel.toString()}`,
|
||||
}, { ephemeral: true });
|
||||
}, { edit: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,16 +61,16 @@ class Welcome extends BaseCommand {
|
|||
uk: client.translate("administration/goodbye:MESSAGE", null, "uk-UA"),
|
||||
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,
|
||||
ownerOnly: false,
|
||||
|
@ -83,13 +83,15 @@ class Welcome extends BaseCommand {
|
|||
* @param {import("discord.js").ChatInputCommandInteraction} interaction
|
||||
*/
|
||||
async execute(client, interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
const guildData = interaction.data.guild,
|
||||
command = interaction.options.getSubcommand();
|
||||
|
||||
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 {
|
||||
const state = interaction.options.getBoolean("state");
|
||||
|
||||
|
@ -101,13 +103,14 @@ class Welcome extends BaseCommand {
|
|||
withImage: null,
|
||||
};
|
||||
|
||||
guildData.markModified("plugins.welcome");
|
||||
await guildData.save();
|
||||
|
||||
interaction.success("administration/welcome:DISABLED", null, { ephemeral: true });
|
||||
interaction.success("administration/welcome:DISABLED", null);
|
||||
} else {
|
||||
const channel = interaction.options.getChannel("channel") || interaction.channel;
|
||||
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 = {
|
||||
enabled: true,
|
||||
|
@ -116,11 +119,12 @@ class Welcome extends BaseCommand {
|
|||
withImage: image,
|
||||
};
|
||||
|
||||
guildData.markModified("plugins.welcome");
|
||||
await guildData.save();
|
||||
|
||||
interaction.success("administration/welcome:ENABLED", {
|
||||
channel: `${channel.toString()}`,
|
||||
}, { ephemeral: true });
|
||||
}, { edit: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,16 +28,13 @@ class Work extends BaseCommand {
|
|||
*/
|
||||
async execute(client, interaction) {
|
||||
const { member: memberData, user: userData } = interaction.data,
|
||||
isInCooldown = memberData.cooldowns?.work;
|
||||
cooldown = memberData.cooldowns?.work,
|
||||
now = Date.now();
|
||||
|
||||
if (isInCooldown && isInCooldown > Date.now())
|
||||
return interaction.error("economy/work:COOLDOWN", {
|
||||
time: `<t:${Math.floor(isInCooldown / 1000)}:R>`,
|
||||
});
|
||||
if (now < cooldown) return interaction.error("economy/work:COOLDOWN", { time: `<t:${Math.floor(cooldown / 1000)}:R>` });
|
||||
if (Math.abs(cooldown - now) > 30 * 60 * 60 * 1000) memberData.workStreak = 0;
|
||||
|
||||
if (Date.now() > memberData.cooldowns.work + 30 * 60 * 60 * 1000) memberData.workStreak = 0;
|
||||
|
||||
memberData.cooldowns.work = Date.now() + 24 * 60 * 60 * 1000;
|
||||
memberData.cooldowns.work = now + 24 * 60 * 60 * 1000;
|
||||
memberData.workStreak = (memberData.workStreak || 0) + 1;
|
||||
|
||||
const embed = client.embed({
|
||||
|
|
|
@ -124,7 +124,7 @@ class Boosters extends BaseCommand {
|
|||
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 });
|
||||
|
||||
const embeds = generateBoostersEmbeds(client, interaction, boosters);
|
||||
const embeds = generateBoostersEmbeds(interaction, boosters);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder().setCustomId("boosters_prev_page").setStyle(ButtonStyle.Primary).setEmoji("⬅️"),
|
||||
|
@ -159,7 +159,7 @@ function generateBoostersEmbeds(interaction, boosters) {
|
|||
let j = i;
|
||||
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({
|
||||
title: interaction.translate("general/boosters:BOOSTERS_LIST"),
|
||||
|
|
|
@ -33,14 +33,13 @@ class CreateTicketEmbed extends BaseCommand {
|
|||
interaction.data = [];
|
||||
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;
|
||||
|
||||
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) {
|
||||
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) => {
|
||||
if (reason !== "canceled") {
|
||||
const reversedMessages = (await interaction.channel.messages.fetch()).filter(m => !m.author.bot);
|
||||
const messages = Array.from(reversedMessages.values()).reverse();
|
||||
const reversedMessages = (await interaction.channel.messages.fetch()).filter(m => !m.author.bot),
|
||||
messages = Array.from(reversedMessages.values()).reverse(),
|
||||
transcriptionLogs = interaction.data.guild.plugins.tickets.transcriptionLogs,
|
||||
ticketLogs = interaction.data.guild.plugins.tickets.ticketLogs;
|
||||
|
||||
if (messages.length > 1) {
|
||||
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.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# Discord Bot Dashboard Template
|
||||
|
||||
> Made By <https://github.com/fuma-nama>
|
||||
|
||||
![banner](./document/preview-new.png)
|
||||
> Forked from [here](https://github.com/FileEditor97/discord-bot-dashboard)
|
||||
|
||||
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
|
||||
|
||||
```js
|
||||
Bearer MY_TOKEN_1212112
|
||||
```
|
||||
`Bearer MY_TOKEN_1212112`
|
||||
|
||||
### Required Routes
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ const nextConfig = {
|
|||
];
|
||||
},
|
||||
i18n: {
|
||||
locales: ['en', 'cn'],
|
||||
locales: ['en'],
|
||||
defaultLocale: 'en',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
{
|
||||
"name": "dashboard",
|
||||
"version": "1.2.0",
|
||||
"name": "nohi-dashboard",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"author": "Fuma Nama (https://github.com/fuma-nama)",
|
||||
"maintainers": [
|
||||
"FileEditor97 (https://github.com/fileeditor97)"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
|
@ -20,35 +25,34 @@
|
|||
"@chakra-ui/theme-tools": "^2.0.0",
|
||||
"@emotion/react": "^11.8.1",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@hookform/resolvers": "^2.9.11",
|
||||
"@tanstack/react-query": "^4.2.3",
|
||||
"@hookform/resolvers": "^3.9.0",
|
||||
"@tanstack/react-query": "^5.50.1",
|
||||
"apexcharts": "^3.36.3",
|
||||
"chakra-react-select": "^4.4.2",
|
||||
"cookies-next": "^2.1.1",
|
||||
"deepmerge-ts": "^4.2.2",
|
||||
"framer-motion": "^6.0.0",
|
||||
"next": "^13.2.1",
|
||||
"deepmerge-ts": "^7.0.3",
|
||||
"framer-motion": "^11.0.0",
|
||||
"next": "^14.2.4",
|
||||
"react": "^18.2.0",
|
||||
"react-apexcharts": "^1.4.0",
|
||||
"react-calendar": "^3.7.0",
|
||||
"react-calendar": "^5.0.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-hook-form": "^7.43.2",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-icons": "^5.2.1",
|
||||
"zod": "^3.20.6",
|
||||
"zustand": "^4.4.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.21.0",
|
||||
"@types/node": "16.11.7",
|
||||
"@types/node": "20.14.10",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-calendar": "^3.5.2",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-config-next": "^13.2.1",
|
||||
"eslint-config-prettier": "8.1.0",
|
||||
"prettier": "^2.6.2",
|
||||
"typescript": "^4.8.2"
|
||||
"eslint-config-next": "^14.2.4",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"prettier": "^3.3.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 { 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 {
|
||||
disableFeature,
|
||||
|
@ -41,48 +41,54 @@ export const Mutations = {
|
|||
export function useGuild(id: string) {
|
||||
const accessToken = useAccessToken();
|
||||
|
||||
return useQuery(['guild', id], () => getGuild(accessToken as string, id), {
|
||||
enabled: accessToken != null,
|
||||
return useQuery({
|
||||
queryKey: ['guild', id],
|
||||
queryFn: () => getGuild(accessToken as string, id),
|
||||
enabled: accessToken != null
|
||||
});
|
||||
}
|
||||
|
||||
export function useGuilds() {
|
||||
const accessToken = useAccessToken();
|
||||
|
||||
return useQuery(['user_guilds'], () => getGuilds(accessToken as string), {
|
||||
enabled: accessToken != null,
|
||||
return useQuery({
|
||||
queryKey: ['user_guilds'],
|
||||
queryFn: () => getGuilds(accessToken as string),
|
||||
enabled: accessToken != null
|
||||
});
|
||||
}
|
||||
|
||||
export function useSelfUserQuery() {
|
||||
const accessToken = useAccessToken();
|
||||
|
||||
return useQuery<UserInfo>(['users', 'me'], () => fetchUserInfo(accessToken!!), {
|
||||
return useQuery<UserInfo>({
|
||||
queryKey: ['users', 'me'],
|
||||
queryFn: () => fetchUserInfo(accessToken!!),
|
||||
enabled: accessToken != null,
|
||||
staleTime: Infinity,
|
||||
staleTime: Infinity
|
||||
});
|
||||
}
|
||||
|
||||
export function useGuildInfoQuery(guild: string) {
|
||||
const { status, session } = useSession();
|
||||
|
||||
return useQuery<CustomGuildInfo | null>(
|
||||
Keys.guild_info(guild),
|
||||
() => fetchGuildInfo(session!!, guild),
|
||||
{
|
||||
enabled: status === 'authenticated',
|
||||
refetchOnWindowFocus: true,
|
||||
retry: false,
|
||||
staleTime: 0,
|
||||
}
|
||||
);
|
||||
return useQuery<CustomGuildInfo | null>({
|
||||
queryKey: Keys.guild_info(guild),
|
||||
queryFn: () => fetchGuildInfo(session!!, guild),
|
||||
enabled: status === 'authenticated',
|
||||
refetchOnWindowFocus: true,
|
||||
retry: false,
|
||||
staleTime: 0
|
||||
});
|
||||
}
|
||||
|
||||
export function useFeatureQuery<K extends keyof CustomFeatures>(guild: string, feature: K) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { status, session } = useSession();
|
||||
|
||||
return useQuery(Keys.features(guild, feature), () => getFeature(session!!, guild, feature), {
|
||||
enabled: status === 'authenticated',
|
||||
return useSuspenseQuery({
|
||||
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() {
|
||||
const { session } = useSession();
|
||||
|
||||
return useMutation(
|
||||
async ({ enabled, guild, feature }: EnableFeatureOptions) => {
|
||||
return useMutation({
|
||||
mutationFn: async ({ enabled, guild, feature }: EnableFeatureOptions) => {
|
||||
if (enabled) return enableFeature(session!!, guild, feature);
|
||||
return disableFeature(session!!, guild, feature);
|
||||
},
|
||||
{
|
||||
async onSuccess(_, { guild, feature, enabled }) {
|
||||
await client.invalidateQueries(Keys.features(guild, feature));
|
||||
client.setQueryData<GuildInfo | null>(Keys.guild_info(guild), (prev) => {
|
||||
if (prev == null) return null;
|
||||
async onSuccess(_, { guild, feature, enabled }) {
|
||||
await client.invalidateQueries({queryKey: Keys.features(guild, feature)});
|
||||
client.setQueryData<GuildInfo | null>(Keys.guild_info(guild), (prev) => {
|
||||
if (prev == null) return null;
|
||||
|
||||
if (enabled) {
|
||||
return {
|
||||
...prev,
|
||||
enabledFeatures: prev.enabledFeatures.includes(feature)
|
||||
? prev.enabledFeatures
|
||||
: [...prev.enabledFeatures, feature],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...prev,
|
||||
enabledFeatures: prev.enabledFeatures.filter((f) => f !== feature),
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
if (enabled) {
|
||||
return {
|
||||
...prev,
|
||||
enabledFeatures: prev.enabledFeatures.includes(feature)
|
||||
? prev.enabledFeatures
|
||||
: [...prev.enabledFeatures, feature],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...prev,
|
||||
enabledFeatures: prev.enabledFeatures.filter((f) => f !== feature),
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export type UpdateFeatureOptions = {
|
||||
|
@ -128,29 +132,33 @@ export type UpdateFeatureOptions = {
|
|||
export function useUpdateFeatureMutation() {
|
||||
const { session } = useSession();
|
||||
|
||||
return useMutation(
|
||||
(options: UpdateFeatureOptions) =>
|
||||
return useMutation({
|
||||
mutationFn: (options: UpdateFeatureOptions) =>
|
||||
updateFeature(session!!, options.guild, options.feature, options.options),
|
||||
{
|
||||
onSuccess(updated, options) {
|
||||
const key = Keys.features(options.guild, options.feature);
|
||||
onSuccess(updated, options) {
|
||||
const key = Keys.features(options.guild, options.feature);
|
||||
|
||||
return client.setQueryData(key, updated);
|
||||
},
|
||||
}
|
||||
);
|
||||
return client.setQueryData(key, updated);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useGuildRolesQuery(guild: string) {
|
||||
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) {
|
||||
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 {
|
||||
|
|
|
@ -46,7 +46,7 @@ export function FeatureItem({
|
|||
<CardFooter as={ButtonGroup} mt={3}>
|
||||
<Button
|
||||
size={{ base: 'sm', md: 'md' }}
|
||||
disabled={mutation.isLoading}
|
||||
disabled={mutation.isPending}
|
||||
{...(enabled
|
||||
? {
|
||||
variant: 'action',
|
||||
|
|
|
@ -41,14 +41,14 @@ export function UpdateFeaturePanel({
|
|||
<Text color="TextSecondary">{config.description}</Text>
|
||||
</Box>
|
||||
<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} />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
|
||||
{result.component}
|
||||
<Savebar isLoading={mutation.isLoading} result={result} />
|
||||
<Savebar isLoading={mutation.isPending} result={result} />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// 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 { ReactNode } from 'react';
|
||||
import { SelectField } from '../forms/SelectField';
|
||||
|
@ -19,7 +19,7 @@ export default function AuthLayout({ children }: { children: ReactNode }) {
|
|||
px={{ base: 5, lg: 10 }}
|
||||
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">
|
||||
{config.name}
|
||||
</Text>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Box, Center } from '@chakra-ui/layout';
|
||||
import AppLayout from '../app';
|
||||
import { ReactNode } from 'react';
|
||||
import GuildNavbar from './guild-navbar';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { FaChevronLeft as ChevronLeftIcon } from 'react-icons/fa';
|
||||
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 { useGuildPreview } from '@/api/hooks';
|
||||
import { motion } from 'framer-motion';
|
||||
|
|
|
@ -1,29 +1,12 @@
|
|||
import { createIcon } from '@chakra-ui/react';
|
||||
import { PermissionFlags } from '@/api/discord';
|
||||
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 = {
|
||||
name: 'Demo Bot',
|
||||
icon: BotIcon,
|
||||
name: 'VOTL Bot',
|
||||
icon:
|
||||
'https://cdn.fileeditor.dev/media/votl/logo.png',
|
||||
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: {
|
||||
//filter guilds that user has no permissions to manage it
|
||||
filter: (guild) => (Number(guild.permissions) & PermissionFlags.ADMINISTRATOR) !== 0,
|
||||
|
|
|
@ -23,16 +23,6 @@ const { T } = createI18n(provider, {
|
|||
memes: 'Memes Time',
|
||||
'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_bn: 'Login with Discord',
|
||||
},
|
||||
cn: {
|
||||
login: '登入控制面板',
|
||||
'login description': '登錄並開始使用我們的機器人',
|
||||
login_bn: '使用 Discord 登錄',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -13,15 +13,4 @@ export const common = createI18n(provider, {
|
|||
pages: 'Pages',
|
||||
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',
|
||||
},
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
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',
|
||||
},
|
||||
},
|
||||
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 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
|
||||
*/
|
||||
export type Languages = 'en' | 'cn';
|
||||
export type Languages = 'en';
|
||||
export const { languages, names } = initLanguages<Languages>({
|
||||
en: 'English',
|
||||
cn: '中文',
|
||||
});
|
||||
|
||||
export const provider = initI18n<Languages>({
|
||||
|
|
|
@ -8,9 +8,9 @@ export type AppConfig = {
|
|||
*/
|
||||
name: string;
|
||||
/**
|
||||
* icon (react component)
|
||||
* Image Url
|
||||
*/
|
||||
icon?: (props: any) => ReactElement;
|
||||
icon: string;
|
||||
/**
|
||||
* Guild settings
|
||||
*/
|
||||
|
|
|
@ -45,7 +45,7 @@ function NotEnabled() {
|
|||
<Text color="TextSecondary">{t.error['not enabled description']}</Text>
|
||||
<Button
|
||||
mt={3}
|
||||
isLoading={enable.isLoading}
|
||||
isLoading={enable.isPending}
|
||||
onClick={() => enable.mutate({ enabled: true, guild, feature })}
|
||||
variant="action"
|
||||
px={6}
|
||||
|
|
|
@ -50,7 +50,7 @@ export function GuildSelect() {
|
|||
</Button>
|
||||
);
|
||||
|
||||
if (guilds.status === 'loading')
|
||||
if (guilds.status === 'pending')
|
||||
return (
|
||||
<SimpleGrid columns={{ base: 1, md: 2, xl: 3 }} gap={3}>
|
||||
<Skeleton minH="88px" rounded="2xl" />
|
||||
|
|
|
@ -105,7 +105,7 @@ const ProfilePage: NextPageWithLayout = () => {
|
|||
<Button
|
||||
leftIcon={<IoLogOut />}
|
||||
variant="danger"
|
||||
isLoading={logout.isLoading}
|
||||
isLoading={logout.isPending}
|
||||
onClick={() => logout.mutate()}
|
||||
>
|
||||
{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');
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,10 @@ type 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)
|
||||
return {
|
||||
|
@ -53,7 +56,7 @@ export function useSession(): SessionResult {
|
|||
|
||||
return {
|
||||
status: 'authenticated',
|
||||
session: data,
|
||||
session: data!,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -64,5 +67,8 @@ export function useAccessToken() {
|
|||
}
|
||||
|
||||
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) {
|
||||
// @ts-ignore
|
||||
setCookie(TokenCookie, data, { req, res, ...options });
|
||||
}
|
||||
|
||||
|
@ -51,6 +52,7 @@ export async function removeSession(req: NextApiRequest, res: NextApiResponse) {
|
|||
const session = getServerSession(req);
|
||||
|
||||
if (session.success) {
|
||||
// @ts-ignore
|
||||
deleteCookie(TokenCookie, { req, res, ...options });
|
||||
await revokeToken(session.data.access_token);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { AccessToken } from '@/utils/auth/server';
|
|||
import { Options } from './core';
|
||||
|
||||
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 {
|
||||
return {
|
||||
|
|
|
@ -58,13 +58,14 @@ class CommandHandler extends BaseEvent {
|
|||
}
|
||||
|
||||
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
|
||||
? `, args: [${interaction.options.data
|
||||
.map(arg => {
|
||||
return `${arg.name}: ${arg.value}`;
|
||||
}).join(", ")
|
||||
: "no args"
|
||||
})
|
||||
.join(", ")}]`
|
||||
: ""
|
||||
}`,
|
||||
);
|
||||
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
const Canvas = require("canvas"),
|
||||
BaseEvent = require("../../base/BaseEvent"),
|
||||
{ AttachmentBuilder } = require("discord.js"),
|
||||
{ resolve } = require("path");
|
||||
// const Canvas = require("@napi-rs/canvas"),
|
||||
// BaseEvent = require("../../base/BaseEvent"),
|
||||
// { AttachmentBuilder } = require("discord.js"),
|
||||
// { applyText } = require("../../helpers/functions");
|
||||
|
||||
Canvas.registerFont(resolve("./assets/fonts/RubikMonoOne-Regular.ttf"), { family: "RubikMonoOne" });
|
||||
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;
|
||||
};
|
||||
const BaseEvent = require("../../base/BaseEvent");
|
||||
|
||||
class GuildMemberAdd extends BaseEvent {
|
||||
constructor() {
|
||||
|
@ -45,6 +36,7 @@ class GuildMemberAdd extends BaseEvent {
|
|||
.replace(/{server}/g, member.guild.name)
|
||||
.replace(/{membercount}/g, member.guild.memberCount);
|
||||
|
||||
/*
|
||||
if (guildData.plugins.welcome.withImage) {
|
||||
const canvas = Canvas.createCanvas(1024, 450),
|
||||
ctx = canvas.getContext("2d");
|
||||
|
@ -132,14 +124,15 @@ class GuildMemberAdd extends BaseEvent {
|
|||
);
|
||||
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({
|
||||
content: message,
|
||||
files: [attachment],
|
||||
});
|
||||
} else
|
||||
channel.send({ content: message });
|
||||
} else */
|
||||
|
||||
channel.send({ content: message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
const Canvas = require("canvas"),
|
||||
BaseEvent = require("../../base/BaseEvent"),
|
||||
{ AttachmentBuilder } = require("discord.js"),
|
||||
{ resolve } = require("path");
|
||||
// const Canvas = require("@napi-rs/canvas"),
|
||||
// BaseEvent = require("../../base/BaseEvent"),
|
||||
// { AttachmentBuilder } = require("discord.js"),
|
||||
// { applyText } = require("../../helpers/functions");
|
||||
|
||||
Canvas.registerFont(resolve("./assets/fonts/RubikMonoOne-Regular.ttf"), { family: "RubikMonoOne" });
|
||||
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;
|
||||
};
|
||||
const BaseEvent = require("../../base/BaseEvent");
|
||||
|
||||
class GuildMemberRemove extends BaseEvent {
|
||||
constructor() {
|
||||
|
@ -43,6 +34,7 @@ class GuildMemberRemove extends BaseEvent {
|
|||
.replace(/{server}/g, member.guild.name)
|
||||
.replace(/{membercount}/g, member.guild.memberCount);
|
||||
|
||||
/*
|
||||
if (guildData.plugins.goodbye.withImage) {
|
||||
const canvas = Canvas.createCanvas(1024, 450),
|
||||
ctx = canvas.getContext("2d");
|
||||
|
@ -135,14 +127,15 @@ class GuildMemberRemove extends BaseEvent {
|
|||
);
|
||||
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({
|
||||
content: message,
|
||||
files: [attachment],
|
||||
});
|
||||
} else
|
||||
channel.send({ content: message });
|
||||
} else */
|
||||
|
||||
channel.send({ content: message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,16 +7,18 @@ const { CronJob } = require("cron");
|
|||
module.exports.init = async client => {
|
||||
const cronjob = new CronJob("0 5 * * *", async function () {
|
||||
client.guilds.cache.forEach(async guild => {
|
||||
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;
|
||||
try {
|
||||
console.log(`Checking birthdays for "${guild.name}"`);
|
||||
|
||||
if (guildData.plugins.birthdays && client.channels.cache.get(guildData.plugins.birthdays)) {
|
||||
const date = new Date(),
|
||||
currentDay = date.getDate(),
|
||||
currentMonth = date.getMonth() + 1,
|
||||
currentYear = date.getFullYear();
|
||||
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(),
|
||||
currentDay = date.getDate(),
|
||||
currentMonth = date.getMonth() + 1,
|
||||
currentYear = date.getFullYear();
|
||||
|
||||
client.usersData.find({ birthdate: { $gt: 1 } }).then(async users => {
|
||||
for (const user of users) {
|
||||
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 => {
|
||||
client.guilds.cache.forEach(async guild => {
|
||||
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(),
|
||||
currentDay = date.getDate(),
|
||||
currentMonth = date.getMonth() + 1,
|
||||
currentYear = date.getFullYear();
|
||||
|
||||
if (channel) {
|
||||
client.usersData.find({ birthdate: { $gt: 1 } }).then(async users => {
|
||||
for (const user of users) {
|
||||
if (!guild.members.cache.find(m => m.id === user.id)) return;
|
||||
client.usersData.find({ birthdate: { $gt: 1 } }).then(async users => {
|
||||
for (const user of users) {
|
||||
if (!guild.members.cache.find(m => m.id === user.id)) return;
|
||||
|
||||
const userDate = new Date(user.birthdate * 1000),
|
||||
day = userDate.getDate(),
|
||||
month = userDate.getMonth() + 1,
|
||||
year = userDate.getFullYear(),
|
||||
age = currentYear - year;
|
||||
const userDate = new Date(user.birthdate * 1000),
|
||||
day = userDate.getDate(),
|
||||
month = userDate.getMonth() + 1,
|
||||
year = userDate.getFullYear(),
|
||||
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),
|
||||
},
|
||||
],
|
||||
});
|
||||
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),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
channel.send({
|
||||
embeds: [embed],
|
||||
}).then(m => m.react("🎉"));
|
||||
}
|
||||
channel.send({
|
||||
embeds: [embed],
|
||||
}).then(m => m.react("🎉"));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
|
@ -1,4 +1,9 @@
|
|||
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 = {
|
||||
/**
|
||||
|
@ -125,4 +130,23 @@ module.exports = {
|
|||
|
||||
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",
|
||||
"version": "4.6.0",
|
||||
"version": "4.6.1",
|
||||
"description": "My Discord Bot",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -11,15 +11,15 @@
|
|||
"dependencies": {
|
||||
"@discord-player/extractor": "^4.4.7",
|
||||
"@discordjs/opus": "^0.9.0",
|
||||
"@discordjs/rest": "^2.2.0",
|
||||
"@discordjs/voice": "^0.16.1",
|
||||
"canvas": "^2.11.2",
|
||||
"@discordjs/rest": "^2.3.0",
|
||||
"@discordjs/voice": "^0.17.0",
|
||||
"@napi-rs/canvas": "^0.1.53",
|
||||
"chalk": "^4.1.2",
|
||||
"cron": "^2.4.4",
|
||||
"discord-api-types": "^0.37.71",
|
||||
"discord-api-types": "^0.37.90",
|
||||
"discord-giveaways": "^6.0.1",
|
||||
"discord-player": "^6.6.8",
|
||||
"discord.js": "^14.14.1",
|
||||
"discord-player": "^6.6.10",
|
||||
"discord.js": "^14.15.3",
|
||||
"gamedig": "^4.1.0",
|
||||
"i18next": "^21.10.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