Added code coments, refactored db.ts and redis.ts, and added two new commands

This commit is contained in:
Ahmad 2025-03-16 20:31:43 -04:00
parent b3fbd2358b
commit 890ca26c78
No known key found for this signature in database
GPG key ID: 8FD8A93530D182BF
30 changed files with 1899 additions and 462 deletions

View file

@ -5,11 +5,16 @@ import { AttachmentBuilder, Client, GuildMember, Guild } from 'discord.js';
import { and, eq } from 'drizzle-orm';
import { moderationTable } from '../db/schema.js';
import { db, updateMember } from '../db/db.js';
import { db, handleDbError, updateMember } from '../db/db.js';
import logAction from './logging/logAction.js';
const __dirname = path.resolve();
/**
* Turns a duration string into milliseconds
* @param duration - The duration to parse
* @returns - The parsed duration in milliseconds
*/
export function parseDuration(duration: string): number {
const regex = /^(\d+)(s|m|h|d)$/;
const match = duration.match(regex);
@ -30,17 +35,27 @@ export function parseDuration(duration: string): number {
}
}
/**
* Member banner types
*/
interface generateMemberBannerTypes {
member: GuildMember;
width: number;
height: number;
}
/**
* Generates a welcome banner for a member
* @param member - The member to generate a banner for
* @param width - The width of the banner
* @param height - The height of the banner
* @returns - The generated banner
*/
export async function generateMemberBanner({
member,
width,
height,
}: generateMemberBannerTypes) {
}: generateMemberBannerTypes): Promise<AttachmentBuilder> {
const welcomeBackground = path.join(__dirname, 'assets', 'welcome-bg.png');
const canvas = Canvas.createCanvas(width, height);
const context = canvas.getContext('2d');
@ -92,12 +107,19 @@ export async function generateMemberBanner({
return attachment;
}
/**
* Schedules an unban for a user
* @param client - The client to use
* @param guildId - The guild ID to unban the user from
* @param userId - The user ID to unban
* @param expiresAt - The date to unban the user at
*/
export async function scheduleUnban(
client: Client,
guildId: string,
userId: string,
expiresAt: Date,
) {
): Promise<void> {
const timeUntilUnban = expiresAt.getTime() - Date.now();
if (timeUntilUnban > 0) {
setTimeout(async () => {
@ -106,12 +128,19 @@ export async function scheduleUnban(
}
}
/**
* Executes an unban for a user
* @param client - The client to use
* @param guildId - The guild ID to unban the user from
* @param userId - The user ID to unban
* @param reason - The reason for the unban
*/
export async function executeUnban(
client: Client,
guildId: string,
userId: string,
reason?: string,
) {
): Promise<void> {
try {
const guild = await client.guilds.fetch(guildId);
await guild.members.unban(userId, reason ?? 'Temporary ban expired');
@ -140,26 +169,96 @@ export async function executeUnban(
reason: reason ?? 'Temporary ban expired',
});
} catch (error) {
console.error(`Failed to unban user ${userId}:`, error);
handleDbError(`Failed to unban user ${userId}`, error as Error);
}
}
export async function loadActiveBans(client: Client, guild: Guild) {
const activeBans = await db
.select()
.from(moderationTable)
.where(
and(eq(moderationTable.action, 'ban'), eq(moderationTable.active, true)),
);
/**
* Loads all active bans and schedules unban events
* @param client - The client to use
* @param guild - The guild to load bans for
*/
export async function loadActiveBans(
client: Client,
guild: Guild,
): Promise<void> {
try {
const activeBans = await db
.select()
.from(moderationTable)
.where(
and(
eq(moderationTable.action, 'ban'),
eq(moderationTable.active, true),
),
);
for (const ban of activeBans) {
if (!ban.expiresAt) continue;
for (const ban of activeBans) {
if (!ban.expiresAt) continue;
const timeUntilUnban = ban.expiresAt.getTime() - Date.now();
if (timeUntilUnban <= 0) {
await executeUnban(client, guild.id, ban.discordId);
} else {
await scheduleUnban(client, guild.id, ban.discordId, ban.expiresAt);
const timeUntilUnban = ban.expiresAt.getTime() - Date.now();
if (timeUntilUnban <= 0) {
await executeUnban(client, guild.id, ban.discordId);
} else {
await scheduleUnban(client, guild.id, ban.discordId, ban.expiresAt);
}
}
} catch (error) {
handleDbError('Failed to load active bans', error as Error);
}
}
/**
* Types for the roundRect function
*/
interface roundRectTypes {
ctx: Canvas.SKRSContext2D;
x: number;
y: number;
width: number;
height: number;
fill: boolean;
radius?: number;
}
/**
* Creates a rounded rectangle
* @param ctx - The canvas context to use
* @param x - The x position of the rectangle
* @param y - The y position of the rectangle
* @param width - The width of the rectangle
* @param height - The height of the rectangle
* @param radius - The radius of the corners
* @param fill - Whether to fill the rectangle
*/
export function roundRect({
ctx,
x,
y,
width,
height,
radius,
fill,
}: roundRectTypes): void {
if (typeof radius === 'undefined') {
radius = 5;
}
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
if (fill) {
ctx.fill();
} else {
ctx.stroke();
}
}