使用新生成的图像自动更新/编辑嵌入消息 - Discord.js

Posted

技术标签:

【中文标题】使用新生成的图像自动更新/编辑嵌入消息 - Discord.js【英文标题】:Auto-update/ edit an embedded message with a newly generated image - Discord.js 【发布时间】:2021-05-22 20:25:49 【问题描述】:

我有一个响应!stock 命令的机器人。

当用户键入!stock 时,用户消息将被删除,并以图片作为附件将嵌入内容发送到频道。

此图像是通过 Puppeteer 唯一生成的(使用我根据从数据库中检索到的一些 Mongo 数据构建的 html 字符串)。

这是我的代码:

const config = require('../config');
const 
    doesGivenUserHaveGivenRole,
    generateStockImage
 = require('../utils');

module.exports = 
    name: 'stock',
    description: 'Use to post the latest stock levels',
    guildOnly: true,
    async execute(message, args) 
        // don't allow arguments
        if (args && args.length > 0) return;

        // ignore if we're not an admin
        if (!doesGivenUserHaveGivenRole(message, message.author.id, config.ADMIN_ROLE_ID)) return;

        // delete the author's message
        message.delete();

        // generate a new HTML string & return an embed to send to the channel
        const generatedStockImage = await generateStockImage();

        // send that message
        message.channel.send(generatedStockImage).then(message => 
        // then every minute
            setInterval(async () => 
        // get the image again
                const generatedEdit = await generateStockImage();
        // edit the original message with the new image (simple, right?)
                message.edit(generatedEdit);
            , 60000);
        );
    
;

generateStockImage 函数如下所示:

const generateStockImage = async () => 

    // check if the image already exists
    if (fs.existsSync('./image.png')) 
        // if it does
        fs.unlink('./image.png', (err) => 
            if (err) 
                console.error(err)
                return;
            

            // remove it, as we want to generate a new one for sure
            console.log('removed');
        );
    

    // get my data from MongoDB
    const stock = await getStock();
    console.log(stock[0].stock);

    let html = `<!DOCTYPE html><html><head><style>bodybackground-color: black; color: white;width: 870px;height: 225px;tablefont-family: arial, sans-serif; border-collapse: collapse;td, thborder: 1px solid #dddddd; padding: 4px; text-align: center;.denominationtext-align: centre; font-size: 14px;margin: 0px 0px 10px 0px;.amountfont-size: 35px;.containerdisplay: flex;.tablemargin: 0px 5px 0px 0px; width: 100%;.greencolor: #2ecc71;.yellowcolor: #f1c40f;.redcolor: #c45563;.iconwidth:20px;height:20px;padding-right:3px</style></head><body><div class="container"> `;

    stock.forEach(code => 
        let colour;
        if (code.stock >= 100) 
            colour = 'green';
        
        if (code.stock <= 50) 
            colour = 'yellow';
        
        if (code.stock === 0) 
            colour = 'red';
        

        html += `<!-- some html code I generate dynamically based on stock (above) $code.something -->`;
    );

    // close my html 
    html += `</div></body></html>`;

    // create a new puppeteer browser
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.setViewport(
        width: 880,
        height: 235,
        deviceScaleFactor: 1,
    );
    await page.setContent(html);
    await page.screenshot( path: './image.png' ); // take a screenshot of the page we generated
    await browser.close();
    
    // wait for 8 seconds (to make sure the image is written to file first)
    await sleep(8000);

    const buffer = fs.readFileSync('./image.png');
    const attachment = new Discord.MessageAttachment(buffer, 'image.png');

    const embedToReturn = 
        embed: 
            color: '#4287f5',
            title: 'Current Stock',
            files: [attachment],
            image: 
                url: 'attachment://image.png'
            ,
            timestamp: new Date(),
            footer: 
                text: 'My Bot Name',
                icon_url: 'https://i.imgur.com/myBotLogo.png',
            
        
    

    console.log(embedToReturn);
    return embedToReturn; // return the new embed value

我的代码根据数据库数据成功生成了一张新图片,并保存到项目根目录。

根据我从 MongoDB 收到的数据,我可以打开文件并查看它实际上是一个新生成的图像。

此图像在第一次运行命令时成功发布,但不会在后续“编辑”时更新。

我面临的问题是生成的图像没有在我的嵌入中更新。

message.edit 事件被触发时,它似乎没有使用generatedEdit。 图像保持完全相同,即使嵌入有新的时间戳和消息本身的 (edited) 文本。

它每分钟都会成功地“编辑”消息,但只是没有显示最新生成的图像(即使该图像位于我的项目根目录中,并且我可以看到它已经更新)。

我感觉是以下行导致了问题:const attachment = new Discord.MessageAttachment(buffer, 'image.png');

这与不和谐缓存有关吗?我做错了什么?

【问题讨论】:

您在编辑邮件之前是否尝试过console.loggeneratedEdit,以确保您要发送的图片是正确的? @Levi_OP 是的,还检查了缓冲区以确保图像不同,还检查了输出文件 我认为它必须与缓存有关。您可以尝试在每次附加图像时重命名图像,以免不和谐可能不依赖缓存。 你的意思是你试图用不同的图片编辑邮件吗? @Bqre 是的。这是正确的。 60 秒后,创建一个新图像,将其放入现有的嵌入中 【参考方案1】:

仅通过查看您的代码,我找不到任何逻辑错误,它应该按预期工作。

正如您所提到的,有一种可能性是,Discord 正在缓存图像数据,基于提供的相同文件名。

一些建议:

尝试为您发送到 Discord 服务器的每个图像文件指定一个唯一的名称。

const generateStockImage = async () => 
    const imageName = `image-$Date.now().png`
   
    // note: old cleanup part removed since now each image name is unique.
    // you could for example remove all images except the current "imageName" one 

    // get my data from MongoDB
    const stock = await getStock();
    console.log(stock[0].stock);

    let html = `<!DOCTYPE html><html><head><style>bodybackground-color: black; color: white;width: 870px;height: 225px;tablefont-family: arial, sans-serif; border-collapse: collapse;td, thborder: 1px solid #dddddd; padding: 4px; text-align: center;.denominationtext-align: centre; font-size: 14px;margin: 0px 0px 10px 0px;.amountfont-size: 35px;.containerdisplay: flex;.tablemargin: 0px 5px 0px 0px; width: 100%;.greencolor: #2ecc71;.yellowcolor: #f1c40f;.redcolor: #c45563;.iconwidth:20px;height:20px;padding-right:3px</style></head><body><div class="container"> `;

    stock.forEach(code => 
        let colour;
        if (code.stock >= 100) 
            colour = 'green';
        
        if (code.stock <= 50) 
            colour = 'yellow';
        
        if (code.stock === 0) 
            colour = 'red';
        

        html += `<!-- some html code I generate dynamically based on stock (above) $code.something -->`;
    );

    // close my html 
    html += `</div></body></html>`;

    // create a new puppeteer browser
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.setViewport(
        width: 880,
        height: 235,
        deviceScaleFactor: 1,
    );
    await page.setContent(html);
    await page.screenshot( path: `./$imageName` ); // take a screenshot of the page we generated
    await browser.close();
    
    // wait for 8 seconds (to make sure the image is written to file first)
    await sleep(8000);

    const buffer = fs.readFileSync(`./$imageName`);
    const attachment = new Discord.MessageAttachment(buffer, imageName);

    const embedToReturn = 
        embed: 
            color: '#4287f5',
            title: 'Current Stock',
            files: [attachment],
            image: 
                url: `attachment://$imageName`
            ,
            timestamp: new Date(),
            footer: 
                text: 'My Bot Name',
                icon_url: 'https://i.imgur.com/myBotLogo.png',
            
        
    

    console.log(embedToReturn);
    return embedToReturn; // return the new embed value


这部分也需要考虑:

        // send that message
        message.channel.send(generatedStockImage).then(message => 
        // then every minute
            setInterval(async () => 
        // get the image again
                const generatedEdit = await generateStockImage();
        // edit the original message with the new image (simple, right?)
                message.edit(generatedEdit);
            , 60000);
        );

请注意,在 setInterval() 中,您引用了 message 变量,但如果您的意图是在回调中引用外部上下文 message 或内部上下文 message 变量,则会产生歧义。

为了使您的意图明确并防止由于意外行为而可能出现的错误,我建议不要隐藏回调中的变量。 提示: 甚至还有一个 ESlint 规则,值得考虑:no-shadow

        // send that message
        message.channel.send(generatedStockImage).then(sentMessage => 
        // then every minute
            setInterval(async () => 
        // get the image again
                const generatedEdit = await generateStockImage();
        // edit the original message with the new image (simple, right?)
                sentMessage.edit(generatedEdit);
            , 60000);
        );

如果引用我刚刚重命名的sentVariable对你不起作用,那么你可以直接省略它,只保留原来的引用:

        // send that message
        message.channel.send(generatedStockImage).then(() =>  // or, if want to still see it around, append with _: (_message) =>
        // then every minute
            setInterval(async () => 
        // get the image again
                const generatedEdit = await generateStockImage();
        // edit the original message with the new image (simple, right?)
                message.edit(generatedEdit);
            , 60000);
        );

【讨论】:

以上是关于使用新生成的图像自动更新/编辑嵌入消息 - Discord.js的主要内容,如果未能解决你的问题,请参考以下文章

(discord.py) 有啥方法可以自动保存嵌入图像?

如何使用新生成的 .pot 文件更新现有的 .po 文件?

编辑带有图像附件的嵌入消息在聊天中加倍 - Discord JDA

Discord.js 使用来自其他消息的附件来更新嵌入中的图像

Discord Python Bot - Bot 的消息不会自动嵌入链接/图像/等

oracle 如何删除新生成的序列号