brainboxdotcc / DPP

C++ Discord API Bot Library - D++ is Lightweight and scalable for small and huge bots!
https://dpp.dev/
Apache License 2.0
1.07k stars 163 forks source link

slash command reply does not render image when attaching a raw image buffer #1281

Closed eaziz4 closed 2 weeks ago

eaziz4 commented 2 weeks ago

Git commit reference v10.0.32

Describe the bug When replying to a slash command, I want to reply with an image that is stored in a buffer in memory. When I do this, the image does not render in discord, but if I click 'open in browser' and view the image that discord received it looks perfectly normal.

To Reproduce Steps to reproduce the behavior: I am using the following method message& add_file(std::string_view filename, std::string_view filecontent, std::string_view filemimetype = "");

filecontent is a valid png buffer, created by skia. my createImage method returns the image buffer(const void *) and image size(size_t). As it takes a string view, I casted it to a string and provided that as the filecontent.

Expected behavior I want the image to render correctly in discord and not display the "sad poop" image.

Screenshots The first image is using the image buffer and the second is using an actual png file

image

This is the discord attachment of the image buffer

image

System Details:

Additional context This is using the image buffer

auto [imageBuffer, imageSize] = CreateImage::createImage(plusEvBets, devigInfo.devigType);
auto message = dpp::message();
std::string fileContent(reinterpret_cast<const char*>(imageBuffer), imageSize);
message.add_file("odds.png", fileContent, std::string("image/png"));
event.reply(message)

When explicitly saving the buffer to a file and attaching the png file to the message it displays fine


auto [imageBuffer, imageSize] = CreateImage::createImage(plusEvBets, devigInfo.devigType);
std::ofstream outFile("ev.png", std::ios::binary);
if (!outFile) {
    printf("Failed to open file for writing.\n");
    return;
    }
outFile.write(reinterpret_cast<const char*>(imageBuffer), imageSize);
outFile.close();
auto message = dpp::message();
message.add_file("odds.png", dpp::utility::read_file("ev.png"));
event.reply(message)```
ruslan-ilesik commented 2 weeks ago

what image lib do u use? for me with opencv everything works fine if i do

std::vector<uchar> buf;
cv::imencode(".jpg", _image, buf);
std::string s(buf.begin(), buf.end());
return {".jpg", s};

to convert image to string

ruslan-ilesik commented 2 weeks ago

can u try something like

auto [imageBuffer, imageSize] = CreateImage::createImage(plusEvBets, devigInfo.devigType);
std::vector<uint8_t> fileContent(imageBuffer, imageBuffer + imageSize);
message.add_file("odds.png", std::string(fileContent.begin(), fileContent.end()), std::string("image/png"));
event.reply(message);
Mishura4 commented 2 weeks ago

Yep try sending the mime type with add_file as shown above

eaziz4 commented 2 weeks ago

I'm using skia to create my images. I'm now noticing that when running this on my my mac everything works fine, but when running it on linux it does not render. I tried the above and its the same thing. my linux machine is an aws ec2 t3-medium instance with the following cpu info


  CPU op-mode(s):         32-bit, 64-bit
  Address sizes:          46 bits physical, 48 bits virtual
  Byte Order:             Little Endian
CPU(s):                   2
  On-line CPU(s) list:    0,1
Vendor ID:                GenuineIntel
  Model name:             Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz
    CPU family:           6
    Model:                85
    Thread(s) per core:   2
    Core(s) per socket:   1
    Socket(s):            1
    Stepping:             7
    BogoMIPS:             4999.99
    Flags:                fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant
                          _tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadlin
                          e_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch pti fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx avx512f a
                          vx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves ida arat pku ospke
Virtualization features:  
  Hypervisor vendor:      KVM
  Virtualization type:    full
Caches (sum of all):      
  L1d:                    32 KiB (1 instance)
  L1i:                    32 KiB (1 instance)
  L2:                     1 MiB (1 instance)
  L3:                     35.8 MiB (1 instance)
ruslan-ilesik commented 2 weeks ago

this is definetly some problem with your image lib...

as i mentioned i use opencv to do same thing and it works perfectly fine

eaziz4 commented 2 weeks ago

let me try rebuilding my image library

eaziz4 commented 2 weeks ago

I'm mainly confused where the issue would be since the actual image is clearly sent to discord, but just doesn't render the preview. Do you have any ideas on what piece I might be missing?

eaziz4 commented 2 weeks ago

This is how I'm encoding my canvas to an image buffer

std::pair<const void *, size_t> CreateImage::surfaceToEncodedImageBuffer(
    const sk_sp<SkSurface> &surface, const int compressionLevel) {
    const sk_sp<SkImage> image = surface->makeImageSnapshot();
    SkPngEncoder::Options options;
    options.fZLibLevel = compressionLevel;
    options.fFilterFlags = SkPngEncoder::FilterFlag::kNone;

    const sk_sp<SkData> encodedImage = Encode(nullptr, image.get(), options);
    if (!encodedImage) {
        throw std::runtime_error("Failed to encode image.");
    }
    return {encodedImage->data(), encodedImage->size()};
}

int CreateImage::calculateCompressionLevel(const int numRows) {
    constexpr int baseCompressionLevel = 9;
    if (numRows > 5) {
        const int reduction = (numRows - 5) / 5;
        return std::max(baseCompressionLevel - reduction, 0);
    }
    return baseCompressionLevel;
}
braindigitalis commented 2 weeks ago

I'm mainly confused where the issue would be since the actual image is clearly sent to discord, but just doesn't render the preview. Do you have any ideas on what piece I might be missing?

are you sending the mime type now in add_file? discord gets the type from this not the file extension to determine if it can thumbnail it

eaziz4 commented 2 weeks ago

yes I've always been sending the mime type as std::string("image/png")

eaziz4 commented 2 weeks ago

it looks like the issue was with doing event.thinking(true) when receiving the command and using event.edit_response(message) to send the image. If I don't set the event to thinking and use event.reply(message), it renders fine. What is the recommended approach to set the bot to an ephemeral thinking state and reply with a non-ephemeral message with an image?

braindigitalis commented 2 weeks ago

this is unfortunately not possible in the discord api. if you begin an interaction ephemeral, you cant change it to non-ephemeral later, and vice versa. It is what you set it to when you initially reply to the interaction. You could reply separately afterwards using message_create, which by its nature can never be ephemeral. Note though that message_create is rate limited where event.reply is not.

eaziz4 commented 2 weeks ago

If I want to set the event to thinking since my slash command takes a decent amount of time, what's the recommended approach? Having the thinking not be ephemeral is fine, I just need to have more than 5 seconds before replying

braindigitalis commented 2 weeks ago

you can send a thinking then edit the response to contain an image, this works fine. you just cant make the thinking ephemeral and the reply not

eaziz4 commented 2 weeks ago

when I use event.edit_response(message), the image does not render. It only renders if I do event.reply(message). If I use reply, I get this error in my logs ERROR: Error: 40060: Interaction has already been acknowledged.

eaziz4 commented 2 weeks ago

Welp. I had an old instance of my script running that was superceding/conflicting with what I was testing. It all works now. Sorry for the confusion and thanks for the help.

braindigitalis commented 2 weeks ago

youre welcome! You should come along to the discord server, you can get much faster answers there! https://discord.gg/dpp