Merge pull request #660 from ZynerOrg/dev

Improvements and new cooldown feature
This commit is contained in:
Axel Olausson Holtenäs 2023-06-02 12:58:57 +02:00 committed by GitHub
commit ec05cd75ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 72 additions and 63 deletions

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "xyter",
"version": "2.2.1",
"version": "2.3.0-dev.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "xyter",
"version": "2.2.1",
"version": "2.3.0-dev.1",
"license": "GPL-3.0-only",
"dependencies": {
"@prisma/client": "^4.7.1",

View file

@ -1,6 +1,6 @@
{
"name": "xyter",
"version": "2.2.1",
"version": "2.3.0-dev.1",
"private": true,
"description": "A multi purpose Discord bot written in TypeScript with Discord.js",
"main": "dist/index.js",

View file

@ -1,3 +1,4 @@
import { addDays, startOfDay } from "date-fns";
import {
ChatInputCommandInteraction,
EmbedBuilder,
@ -60,12 +61,10 @@ export const execute = async (interaction: ChatInputCommandInteraction) => {
await sendResponse(interaction, { embeds: [embed] });
const cooldownDuration = 24 * 60 * 60; // 24 hours in seconds
const cooldownName = await generateCooldownName(interaction);
await cooldownManager.setCooldown(
cooldownName,
await generateCooldownName(interaction),
guild,
user,
cooldownDuration
startOfDay(addDays(new Date(), 1))
);
};

View file

@ -1,3 +1,4 @@
import { addMonths, startOfDay } from "date-fns";
import {
ChatInputCommandInteraction,
EmbedBuilder,
@ -66,12 +67,10 @@ export const execute = async (interaction: ChatInputCommandInteraction) => {
await sendResponse(interaction, { embeds: [embed] });
const cooldownDuration = 4 * 7 * 24 * 60 * 60; // 1 month in seconds
const cooldownName = await generateCooldownName(interaction);
await cooldownManager.setCooldown(
cooldownName,
await generateCooldownName(interaction),
guild,
user,
cooldownDuration
startOfDay(addMonths(new Date(), 1))
);
};

View file

@ -1,3 +1,4 @@
import { addWeeks, startOfDay } from "date-fns";
import {
ChatInputCommandInteraction,
EmbedBuilder,
@ -62,12 +63,10 @@ export const execute = async (interaction: ChatInputCommandInteraction) => {
await sendResponse(interaction, { embeds: [embed] });
const cooldownDuration = 7 * 24 * 60 * 60; // 1 week in seconds
const cooldownName = await generateCooldownName(interaction);
await cooldownManager.setCooldown(
cooldownName,
await generateCooldownName(interaction),
guild,
user,
cooldownDuration
startOfDay(addWeeks(new Date(), 1))
);
};

View file

@ -1,4 +1,5 @@
import Chance from "chance";
import { addHours } from "date-fns";
import {
ChatInputCommandInteraction,
EmbedBuilder,
@ -132,6 +133,6 @@ export const execute = async (interaction: ChatInputCommandInteraction) => {
await generateCooldownName(interaction),
guild,
user,
86400
addHours(new Date(), 1)
);
};

View file

@ -1,4 +1,5 @@
import axios from "axios";
import { addSeconds } from "date-fns";
import {
ChatInputCommandInteraction,
EmbedBuilder,
@ -70,14 +71,11 @@ export const execute = async (
],
});
const cooldownName = await generateCooldownName(interaction);
const cooldownDuration = 5;
await cooldownManager.setCooldown(
cooldownName,
await generateCooldownName(interaction),
guild || null,
user,
cooldownDuration
addSeconds(new Date(), 5)
);
} catch (error: unknown) {
if ((error as NodeJS.ErrnoException).code === "ENOTFOUND") {

View file

@ -1,4 +1,5 @@
import axios from "axios";
import { addSeconds } from "date-fns";
import {
ActionRowBuilder,
ButtonBuilder,
@ -39,8 +40,6 @@ export const execute = async (
await deferReply(interaction, false);
const { channel, guild, user } = interaction;
const cooldownItem = await generateCooldownName(interaction);
const cooldownDuration = 15; // 10 seconds
try {
const content: MemeContent = await fetchRandomMeme();
@ -65,10 +64,10 @@ export const execute = async (
}
await cooldownManager.setCooldown(
cooldownItem,
await generateCooldownName(interaction),
guild || null,
user,
cooldownDuration
addSeconds(new Date(), 5)
);
};

View file

@ -1,3 +1,4 @@
import { addMinutes } from "date-fns";
import {
ChannelType,
ChatInputCommandInteraction,
@ -99,6 +100,6 @@ export const execute = async (
await generateCooldownName(interaction),
guild,
user,
5 * 60
addMinutes(new Date(), 5)
);
};

View file

@ -1,3 +1,4 @@
import { addDays } from "date-fns";
import {
ChatInputCommandInteraction,
EmbedBuilder,
@ -82,6 +83,6 @@ export const execute = async (interaction: ChatInputCommandInteraction) => {
await generateCooldownName(interaction),
guild,
user,
24 * 60 * 60
addDays(new Date(), 1)
);
};

View file

@ -5,6 +5,9 @@ import generateCooldownName from "../../../../helpers/generateCooldownName";
import handleCooldown from "./handlers/handleCooldown";
import handleUnavailableCommand from "./handlers/handleUnavailableCommand";
// Create a map to store locks for each identifier (guild ID + user ID + cooldown item)
const commandLocks = new Map();
const cooldownManager = new CooldownManager();
export default async function handleCommandInteraction(
@ -24,6 +27,14 @@ export default async function handleCommandInteraction(
try {
const cooldownItem = await generateCooldownName(interaction);
// Check if the identifier is already locked
if (commandLocks.has(cooldownItem)) {
throw new Error(
"You are unable to execute the same command simultaneously."
);
}
const { guildCooldown, userCooldown, guildMemberCooldown } =
await cooldownManager.checkCooldowns(cooldownItem, guild, user);
@ -38,10 +49,23 @@ export default async function handleCommandInteraction(
userCooldown,
guildMemberCooldown
);
} else {
await currentCommand.execute(interaction);
return;
}
// Create a promise that represents the current command execution
const commandExecutionPromise = currentCommand.execute(interaction);
// Acquire the lock for the identifier and store the command execution promise
commandLocks.set(cooldownItem, commandExecutionPromise);
// Wait for the current command execution to complete
await commandExecutionPromise;
} catch (error) {
await interactionErrorHandler(interaction, error);
} finally {
const cooldownItem = await generateCooldownName(interaction);
// Release the lock for the identifier
commandLocks.delete(cooldownItem);
}
}

View file

@ -1,3 +1,4 @@
import { addSeconds } from "date-fns";
import { Channel, ChannelType, Guild, Message, User } from "discord.js";
import CooldownManager from "../../../handlers/CooldownManager";
import CreditsManager from "../../../handlers/CreditsManager";
@ -93,5 +94,10 @@ async function isUserOnCooldown(guild: Guild, author: User): Promise<boolean> {
}
async function setCooldown(guild: Guild, user: User) {
await cooldownManager.setCooldown(cooldownName, guild, user, 5);
await cooldownManager.setCooldown(
cooldownName,
guild,
user,
addSeconds(new Date(), 5)
);
}

View file

@ -8,9 +8,8 @@ class CooldownManager {
cooldownItem: string,
guild: Guild | null,
user: User | null,
cooldownSeconds: number
expiresAt: Date
): Promise<void> {
const expiresAt = new Date(Date.now() + cooldownSeconds * 1000);
const data = {
cooldownItem,
expiresAt,

View file

@ -1,3 +1,4 @@
import { UserReputation } from "@prisma/client";
import { User } from "discord.js";
import prisma from "./prisma";
@ -38,42 +39,30 @@ class ReputationManager {
}
async repute(user: User, type: "positive" | "negative") {
const userData = await prisma.user.upsert({
where: { id: user.id },
update: {},
create: {
id: user.id,
userReputation: {
create: {
positive: 0,
negative: 0,
},
},
},
include: {
userReputation: true,
},
});
let userReputation: any = {};
if (!userData.userReputation) return null;
let userReputation: UserReputation | null = null;
if (type === "positive") {
userReputation = await prisma.userReputation.upsert({
where: { id: userData.userReputation.id },
where: { id: user.id },
update: { positive: { increment: 1 } },
create: {
positive: 1,
negative: 0,
user: { connect: { id: user.id } },
user: {
connectOrCreate: {
where: {
id: user.id,
},
create: { id: user.id },
},
},
},
});
}
if (type === "negative") {
userReputation = await prisma.userReputation.upsert({
where: { id: userData.userReputation.id },
where: { id: user.id },
update: { negative: { increment: 1 } },
create: {
positive: 0,

View file

@ -25,18 +25,12 @@ export default async (
const errorEmbed = new EmbedBuilder()
.setAuthor({ name: "⚠️ | Request Failed" })
.setDescription(
"An error occurred while processing your request. Please try again later."
error.message ??
"An error occurred while processing your request. Please try again later."
)
.setColor("#FFCC66")
.setTimestamp();
if (error.message !== undefined) {
errorEmbed.addFields({
name: "Error Message",
value: codeBlock(error.message),
});
}
if (process.env.NODE_ENV === "development" && error.stack !== undefined) {
errorEmbed.addFields({
name: "Error Stack",