Skip to main content

DisMessage

Pixel-perfect 1:1 Discord message & friend request renderer. Uses Discord's actual HTML+CSS (ripped from a live page export) so the output is indistinguishable from a real screenshot. Only the avatar, username, text, and badges are swapped in at render time.

It's literally Discord's UI code with different data plugged in. 🐱

Install

pip install dismessage[all]
python -m playwright install chromium

That's it. Now you can render fake Discord messages that look so real your friends will question reality.

Quick start

Fake a conversation

from datetime import datetime, timezone
from dismessage import Author, Message, render_png

foo = Author(
    id="123",
    name="foo",
    display_name="Foo",
    avatar_url="https://cdn.discordapp.com/avatars/123/abc.webp?size=80",
    avatar_decoration_url="https://cdn.discordapp.com/avatar-decoration-presets/DEF.png?size=80&pas=true",
    clan_tag="myguild",
    clan_badge_url="https://cdn.discordapp.com/clan-badges/456/badge.png?size=16",
)
bar = Author(
    id="456",
    name="bar",
    display_name="BarBot",
    avatar_url="https://cdn.discordapp.com/avatars/456/xyz.webp?size=80",
    bot=True, verified_app=True,
)

now = datetime.now(timezone.utc)
messages = [
    Message(author=foo, content="hello **world** -# subtext", timestamp=now),
    Message(author=bar,   content="hi `code` and ||spoiler||", timestamp=now),
]

render_png(messages, "out.png")

Fake a friend request

from dismessage import Author, FriendRequest, render_friend_request_png

req = FriendRequest(
    author=Author(
        id="123",
        name="foo",           # shows as grey subtext
        display_name="Foo",   # shows as the main name
        avatar_url="https://cdn.discordapp.com/avatars/123/abc.webp?size=32",
    ),
    count=1,
)

render_friend_request_png(req, "request.png")

Fetch and render a REAL Discord message

from dismessage import fetch_messages, render_png

messages = fetch_messages(
    token="YOUR_BOT_TOKEN",
    channel_id=1532447895683596358,
    message_id=1532447918504804482,
    context_before=2,
    context_after=2,
)
render_png(messages, "real.png")

The fetcher auto-resolves the author's avatar, profile effect (avatar decoration), guild tag, and verified-app badge from GET /users/{id}.

What it supports

Messages

  • 1:1 visual fidelity — uses Discord's real DOM + CSS, not a reimplementation
  • Avatar decorations (profile effects) — the overlay PNGs (cat ears, etc.)
  • Guild tags (clan tags) — the chip next to the username with badge image + tag text
  • Bot badges — "✓ APP" for verified bots, "APP" for unverified (no more "BOT" — Discord changed it)
  • Role colors — username color from the highest role
  • Twemoji support — all unicode emoji rendered as high-res SVG images from the jdecked/twemoji CDN (the exact fork Discord uses)
  • Full Discord markdown:
    • **bold**, *italic*, __underline__, ~~strikethrough~~
    • `inline code` and ```code blocks```
    • ||spoilers|| (hover-to-reveal)
    • # H1, ## H2, ### H3 headers
    • -# subtext (Discord's small grey text)
    • > blockquote
    • @everyone, @here, <@user>, <#channel>, <@&role> mentions (all rendered as blue)
    • <:name:id> custom emoji (animated + static)
    • Bare URLs auto-linked
  • Message grouping — consecutive messages from the same author within 5 min are grouped
  • Quality controlquality=3.0 for 3x resolution, quality=1.0 for low-RAM servers
  • Custom crop areacrop_x, crop_y, crop_width, crop_height params (supports string aliases like "box_x - 16")
  • Persistent Chromium — browser launches once and stays open, so renders are ~0.4s instead of ~3s

Friend requests

  • The "Received — 1" header + card with avatar, display name, username subtext, Accept/Ignore buttons
  • No guild tag, no profile effect — matches real Discord's friend request card

API reference

Author

Field Type Description
id str User ID (used for default avatar fallback).
name str Raw username (e.g. "cy.xn").
display_name Optional[str] Display name (e.g. "F² Cyanic"). Overrides name for display.
color Optional[str] CSS color for the username (role color).
avatar_url Optional[str] Avatar image URL.
avatar_decoration_url Optional[str] Avatar decoration ("profile effect") PNG URL.
clan_tag Optional[str] Guild tag text (e.g. "meow").
clan_badge_url Optional[str] Clan badge image URL.
bot bool Show the "APP" badge.
verified_app bool Add the checkmark to the APP badge.

Message

Field Type Description
author Author The message author.
content str Discord markdown content.
timestamp Optional[datetime] Message timestamp (shown as "7:00 PM").
grouped_with_previous Optional[bool] Force grouping on/off. None = auto-detect.
reply_to Optional[Message] Reference message (renders the slim reply header).
accessories_html str Extra HTML to inject (embeds, attachments).

FriendRequest

Field Type Description
author Author The user who sent the request.
count int Number in the "Received — N" header. Default: 1.
subtitle str Custom subtitle. Defaults to the author's username.

Functions

Function Description
render_html(messages, output_path=None) Render messages to standalone HTML.
render_png(messages, output_path, *, quality=2.0, lite=False, persistent=True, crop_x=None, crop_y=None, crop_width=None, crop_height=None) Render messages to PNG via Chromium.
render_friend_request_html(request, output_path=None) Render friend request card to HTML.
render_friend_request_png(request, output_path, *, quality=2.0, ...) Render friend request card to PNG.
fetch_messages(token, channel_id, message_id, ...) Fetch a real Discord message + context.
render_markdown(text) Convert Discord markdown to HTML.
shutdown_browser() Close the persistent Chromium instance.

Crop params

Crop params accept numbers (int/float) or string aliases:

render_png(messages, "out.png",
    crop_x="box_x - 16",         # auto-detect element's X, minus 16px
    crop_y="box_y",               # auto-detect Y, no offset
    crop_width="box_width + 32",  # auto-detect width, plus 32px
    crop_height=200,              # literal number
)

If all crop params are None (default), auto-detects the message area.

Optional dependencies

Install command What you get
pip install dismessage HTML output only (zero deps)
pip install dismessage[render] + PNG output (Playwright)
pip install dismessage[fetch] + fetch_messages() (httpx)
pip install dismessage[lite] + Pillow lite renderer
pip install dismessage[all] Everything

FAQ

Q: Why is the payload so big? A: It contains Discord's actual CSS (~4 MB uncompressed, ~800 KB gzip-compressed). This is the price of pixel-perfect rendering — we use Discord's real stylesheets instead of trying to reimplement them. The payload is loaded once at import time and cached in memory.

Q: Does this violate Discord's ToS? A: Probably not — it's a rendering library, not a self-bot. But don't use it to deceive people or impersonate others. Be normal.

Q: Can I use this without Chromium? A: For HTML output, yes (zero dependencies). For PNG output, you need a browser engine — there's no way around it for Discord's complex CSS. The persistent browser makes it fast (~0.4s per render after warmup).

Q: Will Discord break this? A: If Discord changes their CSS class names (which they do occasionally), the templates will need updating. Just re-export the page and rebuild the payload.

License

MIT. Go wild.


built with blood, sweat, and a lot of print() debugging by F² Cyanic
powered by Discord's actual CSS (we stole nothing, we just copied what your browser already downloaded)
⚠️ WARNING: may cause confusion, arguments, and people asking "wait is that real?"
🐱 meow

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dismessage-0.5.0.tar.gz (1.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dismessage-0.5.0-py3-none-any.whl (1.2 MB view details)

Uploaded Python 3

File details

Details for the file dismessage-0.5.0.tar.gz.

File metadata

  • Download URL: dismessage-0.5.0.tar.gz
  • Upload date:
  • Size: 1.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.2

File hashes

Hashes for dismessage-0.5.0.tar.gz
Algorithm Hash digest
SHA256 8d2cd91ff16ff03c976fbae8b7f6ba6d8312243f8d6181abc5bc78a3c0676beb
MD5 0604e117fdc5f1bc2c363a1bae156d7b
BLAKE2b-256 663de7c28507461970095d5c70a7ee549b0b1a9102484cdd4037f6c497faef00

See more details on using hashes here.

File details

Details for the file dismessage-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: dismessage-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 1.2 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.2

File hashes

Hashes for dismessage-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2318baa0a269487c3c8338f2a632bf58ef71e653453b0b07c8925937d87481b5
MD5 9efbea8af9fa986776b319fde3b04d41
BLAKE2b-256 595547ae02b397f5e057f7a6247df9941751b726b27f3f4c0d18203106007c24

See more details on using hashes here.

Release history Release notifications | RSS feed

0.6.1

2 files

0.6.0

2 files

0.5.2

2 files

This release

0.5.0 This release

2 files

0.4.8

2 files

0.4.7

2 files

0.4.6

2 files

0.4.2

2 files

0.4.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page