Markoudstaal / node-red-contrib-discord-advanced

Recieve, send, edit and delete Discord messages in node-red.
MIT License
47 stars 16 forks source link

How to attach image from buffer into an embed? #57

Closed Slyke closed 1 year ago

Slyke commented 2 years ago

I'm trying the following code:

const nodeFetch = context.global.get('nodeFetch');
const url = 'http://some/url/with/a.jpg';

const imageRequest = await nodeFetch(url, { encoding: null });
const imageBuffer = await Buffer.from(await imageRequest.arrayBuffer());

msg.payload = 'the message';
msg.embeds = {
    "description": 'description',
    "content": 'content',
    "title": 'title',
    files:[{
        "file": imageBuffer,
        "filename": "test.jpg"
    }]
};

return msg;

But I only get this: image

I can see that the buffer is being filled correctly: image

I have also manually encoded an image and dumped the base64 encoded stream like such:

  "file": Buffer.from('LONG BASE 64 TEXT HERE', 'base64'),

I read through this documentation: https://v13.discordjs.guide/popular-topics/embeds.html#using-an-embed-object https://github.com/Markoudstaal/node-red-contrib-discord-advanced/wiki/discordMessageManager

But not sure why it's not working.

javis86 commented 2 years ago

msg.attachments String A location to an attachment. Can be online by URL or in the local filesystem. https://github.com/Markoudstaal/node-red-contrib-discord-advanced/wiki/discordMessageManager

First, you need to save your buffer in a file, and then put the fullpath in the attachments array

Slyke commented 2 years ago

Ohhh, it doesn't allow for buffers like in DiscordJS? Can I just dump to /tmp, attach to message, and then delete the original file in the temp directory?

javis86 commented 2 years ago

Ohh, I only follow the node documentation, but I never touch this part of the code. Following the discordjs docs, you can pass the buffer but i don't know why it's not working image

javis86 commented 2 years ago

Ohhh, maybe you need put the Buffer in the "msg.attachments", not inside embeds object. And then, reference it on the image field in the embed

image

Slyke commented 2 years ago

Hmmm, how do I attach it to msg.attachments and then refer to it on msg.embeds since it's a buffer? Attachments only takes a string or an array of strings.

Slyke commented 2 years ago

If putting the buffer directly on msg.attachments it errors saying that msg.attachments must be a string. If I toString() the buffer I get the following error: image

Slyke commented 2 years ago

I couldn't figure it out with a buffer. Not sure it's doable. I got it working using a temporary file:

const nodeFetch = context.global.get('nodeFetch');
const fs = context.global.get('fs');
const url = 'http://some/url/with/a.jpg';

// Random number to help avoid filename collisions (even if they are unlikely)
const getRandomIntInclusive = (min, max) => {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1) + min);
};

const pictureFilename = `${new Date().getTime()}_${getRandomIntInclusive(0, 9999).toString().padStart(4, 0)}.jpg`;
const savePath = "/tmp/"; // I run NodeRed inside Kubernetes and the tmp directory is cleared on each reboot.
const fullPath = `${savePath}${pictureFilename}`;

const res = await nodeFetch(url);
const fileStream = fs.createWriteStream(`${savePath}${pictureFilename}`);

await new Promise((resolve, reject) => { // From buffer to file
  res.body.pipe(fileStream);
  res.body.on("error", reject); // No error handling in this example
  fileStream.on("finish", resolve);
});

msg.payload = `Temporary File: '${savePath}${pictureFilename}'`;

msg.attachments = fullPath;

setTimeout(() => { // This can also be achieved without a timeout if executed after Discord has sent the message.
    fs.unlink(fullPath, () => {}); // Delete the file after 3 seconds
}, 3000);

return msg;

Feel free to close this issue, or can continue to try to get it working with streams if you want, lol.

not7cd commented 1 year ago

Here https://github.com/Markoudstaal/node-red-contrib-discord-advanced/blob/d32e89727a44206437b6d85404811354a5b329fa/discord/discordMessageManager.js#L198-L203

You have the possibility to put attachments twice through new MessageAttachment() which can have more arguments. The resulting object looks like this:

MessageAttachment {
  attachment: MessageAttachment {
    attachment: 'https://example.com/original_name.png',
    name: 'other_name.png'
  },
  name: null
}
Slyke commented 1 year ago

But does that work with file buffers?

not7cd commented 1 year ago

Yes, I was just able to get it working with buffs. You need to edit this file: .node-red/node_modules/node-red-contrib-discord-advanced/discordMessageManager.js line 202

         if (typeof inputAttachments === 'string') {
              attachments.push(new MessageAttachment(inputAttachments));
            } else if (Array.isArray(inputAttachments)) {
              inputAttachments.forEach(attachment => {
               // just remove object creation and pass through objects
                attachments.push(attachment);
              });
            } 

I'm getting image buffer from an other block. I'm utterly confused how AttachmentBuilder and MessageAttachment differ.

console.log(Buffer.isBuffer(msg.payload.image));
let file = new discordjs.AttachmentBuilder(msg.payload.image);
file.name = "cam-1.jpg";

let newMessage = {}; 
newMessage.channel = msg.payload.channelId;
newMessage.payload = {
    "content": "",
    "tts": false,
    "embeds": [
        {
            "type": "rich",
            "title": `Vortex of Doom`,
            "description": `Tutaj będą zdjęcia`,
            "image": {
                "url": 'attachment://cam-1.jpg',
            },
        }
    ],
    "attachments": [file]
}
return newMessage;
Slyke commented 1 year ago

Awesome! Will test it out once it's merged.

javis86 commented 1 year ago

This approach is using a function node with a reference to the latest version of discord.js, right? Could you post a json flow of this case?

not7cd commented 1 year ago

@javis86, this awaits merge of my PR request. You are free to patch it yourself

javis86 commented 1 year ago

I tested on a node-red instance and It failed. I have already installed discord.js 13.8 for functions nodes, and this version does not have Attachment builder. i going to test the actual functionality, for backward compatibility and then i will merge it We need to migrate to discord.js version 14 for full support of these feature

javis86 commented 1 year ago

I'm moving discord.js version to 14.7 on #64, I added the possibility of work with buffers, like this:

msg.embed = {
    "title": `Toyota Etios`,
      "description": `Compact Car`,
      "color": 0x00FFFF,
      "fields": [
        {
          "name": `Type`,
          "value": `Hatch`
        }
      ],
      "image": {
        "url": "attachment://etios2.jpg"
      }
};
msg.attachments = { buffer: msg.payload, name: "etios2.jpg"};

return msg;