Tyrrrz / DiscordChatExporter

Exports Discord chat logs to a file
MIT License
7.73k stars 704 forks source link

Support for stickers #638

Closed CanePlayz closed 2 years ago

CanePlayz commented 3 years ago

Flavor

No response

Export format

HTML

Details

Title says it all, support for the new custom and Discord-made stickers (as of now, messages with stickers are just displayed as blank messages in the HTML file).

look-at-the-sky commented 2 years ago

+1 Because this is extremely important to me, I need to save 2 servers and not having stickers really breaks the flow of the chats. I'm also willing to pay anybody who can implement this feature asap.

Tyrrrz commented 2 years ago

Getting stickers is not as straightforward. Discord does not provide asset URLs/SVG data through the API, only sticker metadata. I guess they really don't want people to be able to download them since they cost money.

See: https://discord.com/developers/docs/resources/sticker#sticker-object

96-LB commented 2 years ago

If I remember correctly, official stickers are drawn on an HTML canvas instead of simply being an image, so they'd be particularly difficult to replicate.

Tyrrrz commented 2 years ago

If I remember correctly, official stickers are drawn on an HTML canvas instead of simply being an image, so they'd be particularly difficult to replicate.

Some of them are evidently stored as APNG too. But regardless, none of the image data is publicly available.

Tyrrrz commented 2 years ago

I did some more research and it looks like majority of standard stickers (all of them?) are served in Lottie format. Discord requests the animation manifest in JSON format via a URL like https://discord.com/stickers/749054660769218631.json (where the file name is the ID of the sticker).

Lottie's JSON format schema appears to be described here: https://github.com/airbnb/lottie-web/blob/master/docs/json/animation.json

Tyrrrz commented 2 years ago

So the following approach should work:

  1. Read the sticker_items array inside a message object (reference)
  2. Extract the id
  3. Extract the format_type (can be either 1, 2, or 3, for PNG, APNG, and LOTTIE respectively)
  4. Render the sticker based on the format
    • If PNG or APNG -> render as <img src="https://discord.com/stickers/{ID}.png"> (Note: haven't checked if URL is right; haven't seen a PNG sticker yet)
    • If LOTTIE -> fetch the manifest from https://discord.com/stickers/{ID}.json and use lottie-web JS library to render the image onto a canvas

This is the code Discord uses to initialize a lottie animation on a container:

image

I was able to render a Discord sticker using the following code:

import lottieWeb from "https://cdn.skypack.dev/lottie-web@5.8.1";

/* Shapes */
fetch('https://discord.com/stickers/749054660769218631.json')
  .then(r => r.json())
  .then(d => {
    console.log(d);

    const svgContainer = document.getElementById('svgContainer');
    const animItem = lottieWeb.loadAnimation({
      container: svgContainer,
      renderer: 'svg',
      loop: true,
      autoplay: true,
      animationData: d
    });
    animItem.play();
  }
);

Where svgContainer is just a <div> element.

look-at-the-sky commented 2 years ago

Awesome, you're doing God's work here. I'll make sure to donate to the project!

Tyrrrz commented 2 years ago

Added core support, but stickers currently cannot be rendered in HTML exports due to CORS. If you don't need it in HTML, you can go ahead and use the latest CI build. Otherwise, we'd need to find a way to hack it.

Closing this one in favor of a more specific issue: #803