【问题标题】:Auto-update/ edit an embedded message with a newly generated image - Discord.js使用新生成的图像自动更新/编辑嵌入消息 - 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>body{background-color: black; color: white;width: 870px;height: 225px;}table{font-family: arial, sans-serif; border-collapse: collapse;}td, th{border: 1px solid #dddddd; padding: 4px; text-align: center;}.denomination{text-align: centre; font-size: 14px;margin: 0px 0px 10px 0px;}.amount{font-size: 35px;}.container{display: flex;}.table{margin: 0px 5px 0px 0px; width: 100%;}.green{color: #2ecc71;}.yellow{color: #f1c40f;}.red{color: #c45563;}.icon{width: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 秒后,创建一个新图像,将其放入现有的嵌入中

标签: javascript async-await discord discord.js puppeteer


【解决方案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>body{background-color: black; color: white;width: 870px;height: 225px;}table{font-family: arial, sans-serif; border-collapse: collapse;}td, th{border: 1px solid #dddddd; padding: 4px; text-align: center;}.denomination{text-align: centre; font-size: 14px;margin: 0px 0px 10px 0px;}.amount{font-size: 35px;}.container{display: flex;}.table{margin: 0px 5px 0px 0px; width: 100%;}.green{color: #2ecc71;}.yellow{color: #f1c40f;}.red{color: #c45563;}.icon{width: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);
        });

【讨论】:

    猜你喜欢
    • 2021-09-04
    • 2020-11-02
    • 2021-01-13
    • 2020-09-08
    • 2021-10-30
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 2018-11-13
    相关资源
    最近更新 更多