1+ process . env . FONTCONFIG_PATH = "/dev/null" ;
12import { createCanvas , Image , loadImage , registerFont } from "canvas" ;
23import path from "path" ;
34import { fetchGuildConfig } from "../models/guildSchema" ;
@@ -41,30 +42,28 @@ export const generateFrame = async (
4142 ctx . fillStyle = hexColor ;
4243 ctx . fillRect ( 0 , 0 , width , height ) ;
4344
44- const fileBuffer = fs . readFileSync ( framePath ) ;
45- if ( ! fileBuffer ) return null ;
46-
47- //Loads frame
48- await loadImage ( fileBuffer ) . then ( ( img : Image ) =>
49- ctx . drawImage ( img , 0 , 0 , width , height ) ,
50- ) ;
45+ const bgImg = await safeLoadImage ( framePath ) ;
46+ if ( bgImg ) ctx . drawImage ( bgImg , 0 , 0 , width , height ) ;
5147
5248 //loads avatar
5349 if ( memberAvatar ) {
5450 const pngAvatar = memberAvatar . replace ( / \. w e b p ( \? .* ) ? $ / , ".png$1" ) ;
55- await loadImage ( pngAvatar ) . then ( ( img : Image ) =>
56- ctx . drawImage ( img , width / 2 - 125 , 80 , 250 , 250 ) ,
57- ) ;
51+ const avatarImg = await safeLoadImage ( pngAvatar ) ;
52+ if ( avatarImg ) {
53+ ctx . drawImage ( avatarImg , width / 2 - 125 , 80 , 250 , 250 ) ;
54+ }
5855 }
5956
57+ const fallbackFont = '"Sansumu", "Arial"' ;
58+
6059 //writes name
61- ctx . font = " 50pt Sansumu" ;
60+ ctx . font = ` 50pt ${ fallbackFont } ` ;
6261 ctx . textAlign = "center" ;
6362 ctx . fillStyle = "#FFFFFF" ;
6463 ctx . fillText ( name , width / 2 , 400 ) ;
6564
6665 //writes level
67- ctx . font = " 40pt Sansumu" ;
66+ ctx . font = ` 40pt ${ fallbackFont } ` ;
6867 ctx . fillText ( `Level: ${ level } ` , width / 2 , 470 ) ;
6968
7069 //renders xp bar
@@ -77,14 +76,13 @@ export const generateFrame = async (
7776 roundRect ( ctx , 65 , 500 , bar , 40 , 20 , true , false ) ;
7877
7978 //writes xp amount
80- ctx . font = " 40pt Sansumu" ;
79+ ctx . font = ` 40pt ${ fallbackFont } ` ;
8180 ctx . fillText ( `${ xpPercentage } %` , width / 2 , 600 ) ;
8281
8382 //loads foreground frame if there is one
8483 if ( foregroundFramePath != null ) {
85- await loadImage ( foregroundFramePath ) . then ( ( img : Image ) =>
86- ctx . drawImage ( img , 0 , 0 , width , height ) ,
87- ) ;
84+ const fgImg = await safeLoadImage ( foregroundFramePath ) ;
85+ if ( fgImg ) ctx . drawImage ( fgImg , 0 , 0 , width , height ) ;
8886 }
8987
9088 //returns the image
@@ -143,3 +141,26 @@ export const roundRect = (
143141 ctx . stroke ( ) ;
144142 }
145143} ;
144+
145+ const safeLoadImage = async ( source : string ) : Promise < Image | null > => {
146+ try {
147+ // If it's a URL (Avatar or hosted frame)
148+ if ( source . startsWith ( "http://" ) || source . startsWith ( "https://" ) ) {
149+ const response = await fetch ( source ) ;
150+ if ( ! response . ok ) return null ;
151+ const arrayBuffer = await response . arrayBuffer ( ) ;
152+ return await loadImage ( Buffer . from ( arrayBuffer ) ) ;
153+ }
154+
155+ // If it's a local file path
156+ if ( fs . existsSync ( source ) ) {
157+ const fileBuffer = fs . readFileSync ( source ) ;
158+ return await loadImage ( fileBuffer ) ;
159+ }
160+
161+ return null ;
162+ } catch ( error ) {
163+ console . error ( `Failed to load image from ${ source } :` , error ) ;
164+ return null ;
165+ }
166+ } ;
0 commit comments