import Canvas, { GlobalFonts } from '@napi-rs/canvas'; import { AttachmentBuilder } from 'discord.js'; import path from 'path'; import * as schema from '@/db/schema.js'; import { drawMultilineText, roundRect } from './helpers.js'; const __dirname = path.resolve(); /** * Generates an achievement card for a user * TODO: Make this look better * @param achievement - The achievement to generate a card for * @returns - The generated card as an AttachmentBuilder */ export async function generateAchievementCard( achievement: schema.achievementDefinitionsTableTypes, ): Promise { GlobalFonts.registerFromPath( path.join(__dirname, 'assets', 'fonts', 'Manrope-Bold.ttf'), 'Manrope Bold', ); GlobalFonts.registerFromPath( path.join(__dirname, 'assets', 'fonts', 'Manrope-Regular.ttf'), 'Manrope', ); const width = 600; const height = 180; const canvas = Canvas.createCanvas(width, height); const ctx = canvas.getContext('2d'); const gradient = ctx.createLinearGradient(0, 0, width, 0); gradient.addColorStop(0, '#5865F2'); gradient.addColorStop(1, '#EB459E'); ctx.fillStyle = gradient; roundRect({ ctx, x: 0, y: 0, width, height, radius: 16, fill: true }); ctx.lineWidth = 4; ctx.strokeStyle = '#FFFFFF'; roundRect({ ctx, x: 2, y: 2, width: width - 4, height: height - 4, radius: 16, fill: false, }); const padding = 40; const iconSize = 72; const iconX = padding; const iconY = height / 2 - iconSize / 2; try { const iconImage = await Canvas.loadImage( achievement.imageUrl || path.join(__dirname, 'assets', 'images', 'trophy.png'), ); ctx.save(); ctx.beginPath(); ctx.arc( iconX + iconSize / 2, iconY + iconSize / 2, iconSize / 2, 0, Math.PI * 2, ); ctx.clip(); ctx.drawImage(iconImage, iconX, iconY, iconSize, iconSize); ctx.restore(); ctx.beginPath(); ctx.arc( iconX + iconSize / 2, iconY + iconSize / 2, iconSize / 2 + 4, 0, Math.PI * 2, ); ctx.lineWidth = 3; ctx.strokeStyle = '#FFFFFF'; ctx.stroke(); } catch (e) { console.error('Error loading icon:', e); } const textX = iconX + iconSize + 24; const titleY = 60; const nameY = titleY + 35; const descY = nameY + 34; ctx.fillStyle = '#FFFFFF'; ctx.font = '22px "Manrope Bold"'; ctx.fillText('Achievement Unlocked!', textX, titleY); ctx.font = '32px "Manrope Bold"'; ctx.fillText(achievement.name, textX, nameY); ctx.font = '20px "Manrope"'; drawMultilineText( ctx, achievement.description, textX, descY, width - textX - 32, 24, ); const buffer = canvas.toBuffer('image/png'); return new AttachmentBuilder(buffer, { name: 'achievement.png' }); }