Quoted is a Matrix bot that turns messages into beautiful stickers — with sticker / custom-emoji rendering, file-attachment cards, full Unicode fonts, and animated-sticker output.
Originally by rustyraven, enhanced by Vadim-Khristenko.
- Quote one or many Matrix messages into a single sticker image
- Configurable command prefix –
commandPrefix: ".."inconfig.yml(commands become<prefix>q/<prefix>help) - Stickers & images – static WebP/PNG/GIF and animated output (animated WebP via ffmpeg)
- Loads the original media (not server thumbnails) so transparency is preserved — no black backgrounds
- Frame extraction uses sharp for GIF/WebP (keeps alpha), ffmpeg for video
- Custom emoji – inline
<img data-mx-emoticon>rendered atrender.emojiSize- If the emoji media is gone (server retention), falls back to the Unicode emoji from
altvia Noto Color Emoji
- If the emoji media is gone (server retention), falls back to the Unicode emoji from
- GIFs & video – Matrix sends GIFs as
m.video; they're rendered animated, with a poster frame extracted via ffmpeg- When media can't be fetched or generated, a clean GIF / VIDEO / STICKER placeholder card is substituted instead of a blank space
- Selectable animated output –
render.animatedFormat: webp | gif | mp4— pick what your bridge/clients support:webp→ native animatedm.sticker(best quality, keeps alpha; some bridges can't show it)gif→ animatedm.image(wide support, keeps transparency)mp4→m.video(universal, but flattens transparency)
- File / document attachments – clean cards with Font Awesome 6 vector icons, name, size, mime
- Transparent background –
render.transparentBackground: truefor Telegram-style stickers with alpha - Full Unicode coverage – Noto Sans / Math / Symbols2 / Color Emoji / CJK + Font Awesome 6
- Renders 𝐕𝐀𝐈, 𝕋𝔼𝕊𝕋, 😀🎉, 你好, etc. correctly
- Authenticated media – uses the modern
/_matrix/client/v1/media/*endpoints withAuthorization: Bearer(legacy/media/v3/is rejected by current servers) - Profile caching – avatars and display names are cached per render batch (one fetch per user)
- Designed logger – aligned, colorized, scoped, with white-on-colour level badges and
(STATUS CODE: …)tags;logLevel: debug|info|warn|error - Runs on Bun
All rendered from live Matrix messages (real users, avatars, and stickers):
| Text quote | Sticker with reply | Conversation | Missing media |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Animated quote output – a Matrix m.video re-encoded into the configured format. GIF keeps transparency and animates inline here:
Same quote in the other output formats: animated WebP (native sticker, smaller) · MP4 (universal, no transparency).
Regenerate all of these against your own homeserver with bun run test/examples.ts.
The bot first replies with a "⏳ Generating quote…" message, then:
- Static quote → that message is removed and a native
m.stickeris sent (and added to the room'sim.poniespack). - Animated quote → the message is edited in place into the result:
mp4→m.video,gif/webp→m.image(both animate in clients).
If a quoted attachment can't be fetched (e.g. server retention deleted it), the quote still renders with a clear placeholder card instead of failing.
git clone https://github.com/Vadim-Khristenko/quoted-matrix.git
cd quoted-matrix
bun install
cp config.example.yml config.yml # edit homeserverUrl, userId, accessToken
bun run src/index.ts # or: bun run --hot src/index.tsdocker build -t quoted .
docker run -d --name quoted -v ./config.yml:/app/config.yml quotedImage includes: Bun, Noto fonts (Sans / Math / Symbols2 / Mono / CJK / Color Emoji), Font Awesome 6, ffmpeg, sharp, ca-certificates.
Reply to any message with ..q — the bot sends a sticker and adds it to the room's "quoted" emote pack.
- ..q 1 – quote that message plus 1 following message (max 10)
- ..q -c – don't render the replied-to message inside the sticker
Prefix and command names are configurable:
commandPrefix: ".."
commands:
quote: "q"
help: "help"Supported message types: m.text (rich HTML + emoji), m.image / m.sticker (static & animated), m.file / m.audio / m.video (attachment card), and custom emoji via <img data-mx-emoticon>.
render:
animatedStickers: true # render animated quotes (needs ffmpeg)
animatedFormat: webp # webp | gif | mp4 – animated output format
transparentBackground: true # alpha background
transparentBubbles: false
stickerMaxSize: 512
emojiSize: 22
maxFrames: 60
fps: 20
ffmpegPath: "ffmpeg" # or full path, use forward slashes on Windows:
# "D:/ffmpeg/bin/ffmpeg.exe"On Windows, write
ffmpegPathwith forward slashes or single quotes — double-quoted YAML interprets\b,\f,\tas escape codes and corrupts the path.
Every line follows a single format:
{TIME} |{module}> [{LEVEL}] (STATUS CODE: {code}) | {message} <|
- Levels:
DEBUG,INFO,SUCCESS,WARN,ERR,FAIL— each a white badge on a coloured background. FAILmarks genuine data failures (media couldn't be fetched, generation fell back, a quote couldn't be built).- The
(STATUS CODE: …)tag appears only when a log carries an HTTP status. matrix-js-sdk's own output (HTTP, sync, push rules) is routed through the same format under thesdkmodule; its verbose stream shows only atlogLevel: debug.
11:08:12.970 |matrix > [SUCCESS] | client ready for @bot @ https://extera.xyz <|
11:08:12.984 |render > [ WARN ] (STATUS CODE: 404) | media emoji extera.xyz/abc <|
11:08:12.984 |render > [ FAIL ] | gif media unavailable, substituting placeholder <|
Set verbosity with logLevel: debug|info|warn|error. Colour is disabled automatically when NO_COLOR is set.
bun run test # renders sample images into tmp/
bun run test/examples.ts # regenerate README examples from your live homeserver
bun run test/diag.ts # probe your homeserver's media endpoints (useful for debugging)If you see tofu □ instead of glyphs/emoji, ensure fonts/ contains the Noto fonts and fa-solid-900.ttf. In Docker these are installed automatically (fonts-noto-core, fonts-noto-color-emoji, fonts-noto-cjk).
Fallback chain: Noto Sans → Noto Sans Math → Noto Sans Symbols2 → Noto Color Emoji → Noto Sans CJK → sans-serif.
AGPL-3.0-only




