Quota: Refactor how Quotas are being handled

also renamed limits to quota

I believe this new approach would allow me and bot hosters
to add, implement or change the quota behavior more easily.

Reimplemented the currently existing "Message count" limit
to use the new IQuota, refactoring a code *a little*.
This commit is contained in:
Wroclaw 2023-09-21 07:07:43 +02:00
parent 46bb5c867d
commit 339ef06ff9
5 changed files with 214 additions and 107 deletions

View file

@ -8,7 +8,7 @@ import toOpenAIMessages from "./toOpenAIMessages";
import FunctionManager from "./funcitonManager";
type NonNullableInObject<T, V> = { [k in keyof T]: k extends V ? NonNullable<T[k]> : T[k] };
type apiRequest = DiscordApi.Message | DiscordApi.RepliableInteraction;
export type apiRequest = DiscordApi.Message | DiscordApi.RepliableInteraction;
export type RequestMessage = apiRequest & NonNullableInObject<apiRequest, "channel" | "channelId">;
class ChannelsRunningValue extends Array<RequestMessage> {
@ -60,69 +60,6 @@ export function getAuthor(request: apiRequest) {
return request.user;
}
/**
* gets user remaining limit (or lack of it)
* @param user the user to check
* @param requestTimestamp the timestamp of the user request
* @returns object containing the limit and remaining usage or `false` if there is no limit
*/
export async function getUserLimit(user: string | { id: string }, requestTimestamp: Date) {
const userId: string = typeof user === "string" ? user : user.id;
const userLimits = await database.limits.findUnique({
where: { user: BigInt(userId) }
});
if (userLimits?.vip) return false;
const usedLimit = (await database.usage.count({
select: { _all: true },
where: {
user: BigInt(userId),
timestamp: {
gte: new Date(requestTimestamp.getTime() - 1000 * 60 * 60 * 24 /* 24 hours */)
}
},
}))._all;
if (!userLimits || !userLimits.limit) return {limit: config.userLimits.requests, remaining: config.userLimits.requests - usedLimit};
return {limit: userLimits.limit, remaining: userLimits.limit - usedLimit};
}
/**
* gets the timestamp of nth use inside time limit
* @param user the user or id to check
* @param requestTimestamp the timestamp of the request (message/interaction createdAt)
* @param nth which timestamp in time limit to get (orderedd from oldest to newest)
* @returns `false` if user is vip
* @returns `null` if there is no request
* @returns `Date` timestamp of the nth request
*/
export async function getNthUseInLimitTimestamp(user: string | { id: string }, requestTimestamp: Date, nth = 1) {
const userId: string = typeof user === "string" ? user : user.id;
const userLimits = await database.limits.findUnique({
where: { user: BigInt(userId)}
});
if (userLimits?.vip) return false;
const nthUseInLimit = await database.usage.findFirst({
where: {
user: BigInt(userId),
timestamp: {
gte: new Date(requestTimestamp.getTime() - 1000 * 60 * 60 * 24 /* 24 hours */)
}
},
orderBy: { timestamp: "asc" },
skip: nth - 1,
});
if (!nthUseInLimit) return null;
return nthUseInLimit.timestamp;
}
/**
* Replies to a request
* @param request the request to reply to
@ -194,9 +131,9 @@ export async function queueRequest(request: apiRequest) {
return;
}
const userLimit = await getUserLimit(getAuthor(request), request.createdAt);
const userLimit = await config.quota.checkUser(getAuthor(request), request);
if (userLimit !== false && userLimit.remaining <= 0) {
if (userLimit.used >= userLimit.quota) {
if (request instanceof DiscordApi.Message) {
request.react("🛑").catch(() => { /* NOTE: We send an informaton about limit reached in DM */ });
if (!request.author.dmChannel) await request.author.createDM();
@ -204,14 +141,13 @@ export async function queueRequest(request: apiRequest) {
embeds: [{
color: 0xff0000,
description:
"You've used up your message limit for today,\n" +
`${userLimit.limit} requests in last 24 hours`,
"You've used up your quota,\n" + userLimit.toString(),
}]
}).catch(() => {/* FIXME: What should the bot do in this case to inform of limit reached?*/});
}
else if (request.isRepliable()) {
request.reply({
content: `You've used up your message limit for today, ${userLimit.limit} requests in last 24 hours`,
content: "You've used up your quota\n" + userLimit.toString(),
ephemeral: true,
}).catch(() => { /* Impossible to get there unless connection lost*/ });
}