Spent too much time trying to fix main thread offload crash

This commit is contained in:
2025-11-11 17:51:49 +01:00
parent db5ec89e78
commit abb42a49ec
7 changed files with 86 additions and 63 deletions

2
app.js
View File

@@ -3,7 +3,7 @@ import express from "express";
import {Logger} from "./wwwroot/core/logging/logger.js"; import {Logger} from "./wwwroot/core/logging/logger.js";
import {launch} from "./bot.js"; import {launch} from "./bot.js";
//await launch(); await launch();
const app = express(); const app = express();
app.use(express.json()); app.use(express.json());

36
bot.js
View File

@@ -2,6 +2,7 @@ import {data} from "./wwwroot/core/appData.js";
import JsonManager from "./wwwroot/core/utils/jsonManager.js"; import JsonManager from "./wwwroot/core/utils/jsonManager.js";
import {Logger} from "./wwwroot/core/logging/logger.js"; import {Logger} from "./wwwroot/core/logging/logger.js";
import {launchWorker} from "./wwwroot/core/namecards/workerLauncher.js"; import {launchWorker} from "./wwwroot/core/namecards/workerLauncher.js";
import {AttachmentBuilder} from "discord.js";
const launch = async () => { const launch = async () => {
try{ try{
@@ -18,16 +19,37 @@ const launch = async () => {
await data.sender.send(data.socialChannelID, message) await data.sender.send(data.socialChannelID, message)
}); });
data.client.once('clientReady', async () => { /*data.client.once('clientReady', async () => {
await data.sender.send(data.updateChannelID, "I'm now online ! ✅") await data.sender.send(data.updateChannelID, "I'm now online ! ✅")
console.log(`✅ Logged in as ${data.client.user.tag}`); console.log(`✅ Logged in as ${data.client.user.tag}`);
}); });*/
data.client.on('messageCreate', (message) => { data.client.on('messageCreate', async (message) => {
const isGuildOwner = message.guild && message.author.id === message.guild.ownerId; const isGuildOwner = message.guild && message.author.id === message.guild.ownerId;
if (message.content === '/login' && isGuildOwner) { if (message.content === '/login' && isGuildOwner) {
message.reply(data.instagramTokenManager.getOauthUrl()); message.reply(data.instagramTokenManager.getOauthUrl());
} }
if (message.content === '/name-card-test') {
try {
const member = message.member;
const avatarUrl = message.author.displayAvatarURL({ extension: 'png', size: 512 });
const username = member.displayName;
const result = await launchWorker({
templatePath: data.nameCardTemplate,
avatarURL: avatarUrl,
username: username,
});
const attachment = new AttachmentBuilder(Buffer.from(result.result), { name: 'namecard.png' });
const channel = await message.guild.channels.fetch(data.welcomeChannelID);
await channel.send({ content: `Welcome ${username}!`, files: [attachment] });
console.log('Name card sent!');
} catch (err) {
console.error('Failed to generate/send name card:', err);
}
}
}); });
data.client.on('guildMemberAdd', async member => { data.client.on('guildMemberAdd', async member => {
@@ -37,7 +59,9 @@ const launch = async () => {
avatarURL: member.user.avatarURL, avatarURL: member.user.avatarURL,
displayName: member.user.displayName, displayName: member.user.displayName,
}).then(buffer => { }).then(buffer => {
//Send name card here... const attachment = new AttachmentBuilder(buffer, { name: 'namecard.png' });
const channel = member.guild.channels.cache.get(data.welcomeChannelID);
channel.send({ files: [attachment] });
console.log('Name card sent!'); console.log('Name card sent!');
}).catch(console.error); }).catch(console.error);
} catch (err) { } catch (err) {
@@ -45,7 +69,7 @@ const launch = async () => {
} }
}); });
process.on('SIGINT', async () => { /*process.on('SIGINT', async () => {
try { try {
await data.sender.send(data.updateChannelID, "I'm shutting down, now. Bye! 👋") await data.sender.send(data.updateChannelID, "I'm shutting down, now. Bye! 👋")
} catch (error) { } catch (error) {
@@ -54,7 +78,7 @@ const launch = async () => {
await data.client.destroy(); await data.client.destroy();
process.exit(0); process.exit(0);
} }
}); });*/
await data.client.login(process.env.DISCORD_TOKEN); await data.client.login(process.env.DISCORD_TOKEN);
data.instagramPoller.start(600000); data.instagramPoller.start(600000);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 KiB

After

Width:  |  Height:  |  Size: 556 KiB

View File

@@ -22,6 +22,7 @@ const instagramSecret = process.env.INSTAGRAM_CLIENT_SECRET;
const updateChannelID = process.env.BOT_UPDATE_CHANNEL_ID; const updateChannelID = process.env.BOT_UPDATE_CHANNEL_ID;
const socialChannelID = process.env.SOCIAL_CHANNEL_ID const socialChannelID = process.env.SOCIAL_CHANNEL_ID
const welcomeChannelID = process.env.WELCOME_CHANNEL_ID
const filePath = ".instagram_client_tokens.json"; const filePath = ".instagram_client_tokens.json";
@@ -39,12 +40,12 @@ export const data = {
instagramPoller, instagramPoller,
updateChannelID, updateChannelID,
socialChannelID, socialChannelID,
welcomeChannelID,
instagramTokenManager, instagramTokenManager,
nameCardTemplate nameCardTemplate
//tiktokTokenManager //tiktokTokenManager
}; };
/* /*
const tiktokTokenManager = new TikTokTokenManager( const tiktokTokenManager = new TikTokTokenManager(
{filepath : ".tiktok_client_tokens.json", clientKey : "", clientSecret : ""} {filepath : ".tiktok_client_tokens.json", clientKey : "", clientSecret : ""}

View File

@@ -5,7 +5,7 @@ import { createCanvas, loadImage, registerFont } from "canvas";
export class NameCardCreator { export class NameCardCreator {
constructor(templatePath) { constructor(templatePath) {
this.templatePath = templatePath; this.templatePath = templatePath;
this.loadFont("./wwwroot/assets/fonts/Fredoka/static/Fredoka-Bold.ttf") this.loadFont("./wwwroot/assets/fonts/Fredoka/static/Fredoka-Bold.ttf");
} }
loadFont(fontPath) { loadFont(fontPath) {
@@ -13,27 +13,31 @@ export class NameCardCreator {
if (!fs.existsSync(fullPath)) { if (!fs.existsSync(fullPath)) {
throw new Error(`Font file not found at ${fullPath}`); throw new Error(`Font file not found at ${fullPath}`);
} }
registerFont(fullPath, {family: "Fredoka Bold"}); registerFont(fullPath, { family: "Fredoka Bold" });
} }
/** /**
* Crée la carte de bienvenue entièrement via Canvas *
* @param avatarPath
* @param name
* @returns {Promise<Buffer>}
*/ */
async getWelcomeCard(avatarPath, name) { async getWelcomeCard(avatarPath, name) {
let canvas, ctx, smallCanvas, smallCtx;
try { try {
const canvasWidth = 3000; const canvasWidth = 3000;
const canvasHeight = 1000; const canvasHeight = 1000;
const canvas = createCanvas(canvasWidth, canvasHeight); canvas = createCanvas(canvasWidth, canvasHeight);
const ctx = canvas.getContext("2d"); ctx = canvas.getContext("2d");
const template = await loadImage(this.templatePath); const template = await loadImage(this.templatePath);
ctx.drawImage(template, 0, 0, canvasWidth, canvasHeight); ctx.drawImage(template, 0, 0, canvasWidth, canvasHeight);
const avatarSize = 675; const avatarSize = 675;
const avatar = await loadImage(avatarPath); const avatar = await loadImage(avatarPath);
const avatarX = 225; const avatarX = 225;
const avatarY = (canvasHeight - avatarSize) / 2; const avatarY = (canvasHeight - avatarSize) / 2;
ctx.save(); ctx.save();
ctx.beginPath(); ctx.beginPath();
ctx.arc( ctx.arc(
@@ -63,11 +67,10 @@ export class NameCardCreator {
ctx.stroke(); ctx.stroke();
const messageX = avatarX + 1525; const messageX = avatarX + 1525;
const messageY = (canvasHeight / 2)+75; const messageY = (canvasHeight / 2) + 75;
ctx.fillStyle = "#ede6e6"; ctx.fillStyle = "#ede6e6";
ctx.textAlign = "center"; ctx.textAlign = "center";
ctx.textBaseline = "middle"; ctx.textBaseline = "middle";
ctx.font = '115px "Fredoka Bold"'; ctx.font = '115px "Fredoka Bold"';
const lines = [`Welcome ${name}, to the`, "Spicy Jail ~"]; const lines = [`Welcome ${name}, to the`, "Spicy Jail ~"];
@@ -78,31 +81,24 @@ export class NameCardCreator {
ctx.fillText(line, messageX, startY + i * lineHeight); ctx.fillText(line, messageX, startY + i * lineHeight);
}); });
const result = this.scallDown(canvas, 1500, 500) smallCanvas = createCanvas(1500, 500);
smallCtx = smallCanvas.getContext("2d");
smallCtx.drawImage(canvas, 0, 0, 1500, 500);
fs.writeFileSync("./tests/result/namecard.png", result); return smallCanvas.toBuffer();
console.log("✅ Name card created: namecard.png");
return result;
} catch (err) { } catch (err) {
console.error("Error creating name card:", err); console.error("Error creating name card:", err);
throw err; throw err;
} finally {
canvas.width = 0;
canvas.height = 0;
smallCanvas.width = 0;
smallCanvas.height = 0;
canvas = null;
smallCanvas = null;
ctx = null;
smallCtx = null;
} }
} }
/**
*
* @param target
* @param width
* @param height
* @returns {Buffer}
*/
scallDown(target, width, height) {
const smallCanvas = createCanvas(width, height);
const smallCtx = smallCanvas.getContext('2d');
smallCtx.drawImage(target, 0, 0, width, height);
return smallCanvas.toBuffer();
}
} }

View File

@@ -8,20 +8,9 @@ parentPort.on("message", async (data) => {
const creator = new NameCardCreator(templatePath); const creator = new NameCardCreator(templatePath);
const buffer = await creator.getWelcomeCard(avatarURL, username); const buffer = await creator.getWelcomeCard(avatarURL, username);
parentPort.postMessage({ result: buffer }); parentPort.postMessage({ result: buffer }, [buffer.buffer]);
} catch (err) { } catch (err) {
parentPort.postMessage({ error: err.message }); parentPort.postMessage({ error: err.message });
} }
}); });
/*(async () => {
try {
const { creator, avatarURL, userName } = workerData;
const buffer = await creator.getWelcomeCard(avatarURL, userName);
parentPort.postMessage(buffer); // return the buffer to main thread
} catch (err) {
parentPort.postMessage({ error: err.message });
}
})();*/

View File

@@ -1,31 +1,44 @@
import path from "path"; import path from "path";
import { Worker } from 'worker_threads'; import { Worker } from 'worker_threads';
import {Logger} from "../logging/logger.js"; import { fileURLToPath } from 'url';
function launchWorker({templatePath, avatarURL, username}) { /**
*
* @param templatePath
* @param avatarURL
* @param username
* @returns {Promise<unknown>}
*/
function launchWorker({ templatePath, avatarURL, username }) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const workerFile = path.resolve('./wwwroot/core/namecards/nameCardWorker.js');
const filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);
const workerFile = path.join(dirname, 'nameCardWorker.js');
const worker = new Worker(workerFile); const worker = new Worker(workerFile);
worker.postMessage({templatePath, avatarURL, username}); worker.postMessage({ templatePath, avatarURL, username });
worker.on('message', (result) => { worker.once('message', async (result) => {
console.log("worker terminated");
resolve(result); resolve(result);
worker.terminate()
.catch(err => Logger.error("Unable to terminate worker", err.message));
}); });
worker.on('error', (err) => { worker.once('error', async (err) => {
reject(err); reject(err);
worker.terminate()
.catch(err => Logger.error("Unable to terminate worker", err.message));
}); });
worker.on('exit', (code) => { worker.once('exit', async (code) => {
if (code !== 0) reject(new Error(`Worker stopped with code ${code}`)); if (code === 0) {
console.log('Worker exited cleanly.');
} else {
console.warn('Worker exited with error code:', code);
}
}); });
}); });
} }
export {launchWorker} export {launchWorker}