discord-html-transcripts-fix
v1.8.2
Published
A nicely formatted html transcript generator for discord.js. Bugfix fork with support for the latest discord.js and Components v2.
Downloads
1,204
Maintainers
Readme
discord-html-transcripts-fix
A nicely formatted HTML transcript generator for discord.js with full Components V2 support, an interactive viewer, and hardened security.
Forked from discord-html-transcripts.
Install
npm install discord-html-transcripts-fixOnly discord.js is a peer dependency; everything else (React, Lit SSR, markdown parser, etc.) is auto-installed.
Quick start
const { createTranscript } = require('discord-html-transcripts-fix');
const attachment = await createTranscript(channel, {
limit: -1,
saveImages: false,
});
channel.send({ files: [attachment] });Options
| Option | Type | Default | Description |
|---|---|---|---|
| limit | number | -1 | Max messages to fetch. -1 = recursive (all). |
| filter | (m) => boolean | () => true | Predicate to filter messages. |
| returnType | 'attachment' | 'buffer' | 'string' | 'stream' | 'attachment' | Return value shape. 'stream' is best for 5k+ message exports. |
| filename | string | transcript-{channel-id}.html | Output filename when returning as attachment. |
| saveImages | boolean | false | Download images and inline them as base64 data URLs. |
| favicon | 'guild' | string | 'guild' | Page favicon — 'guild' uses the server icon, or pass a URL. |
| hydrate | boolean | false | Server-side hydrate via @lit-labs/ssr (slower; usually leave off). |
| language | 'en' | 'de' | 'en' | UI language for participant labels, filter strings, etc. |
| i18n | Partial<Record<lang, Record<key,string>>> | — | Override individual strings per language. |
| statsFooter | false | { enabled?, template? } | { enabled: true } | Bottom stats line. See below. |
| footerText | string | — | Legacy "Exported X messages" line. Only renders when statsFooter is disabled. |
| poweredBy | boolean | false | Show the original "Powered by discord-html-transcripts" credit link. |
| callbacks | { resolveUser, resolveRole, resolveChannel } | — | Custom resolvers for mentions. |
Configurable stats footer
await createTranscript(channel, {
statsFooter: {
template: '{messages} Nachrichten · {participants} Teilnehmer · {images} Bilder · {from} → {to} · {span}',
},
});
// Or disable entirely:
await createTranscript(channel, { statsFooter: false });Placeholders: {messages}, {participants}, {images}, {from}, {to}, {span}.
Optional edit history
If your bot tracks edits, attach them to the message before rendering:
message.editHistory = [
{ content: 'first version', editedAt: new Date('2026-05-13T11:02Z') },
{ content: 'corrected version', editedAt: new Date('2026-05-13T11:05Z') },
];The viewer will render a collapsible <details> block next to the (edited) marker.
Interactive viewer
One floating button sits top-right — the hamburger menu. It opens a sidebar containing:
- Live search at the top — keyword highlighting (
n of Nmatches with prev/next), Ctrl/Cmd-F focuses this field - Participants (collapsible, open by default) — sorted list of authors; click to jump to their first message
- Filter (collapsible, open by default) — author, role, date range, pinned-only, has-image, has-embed, has-attachment, has-container/V2
Inline behaviour:
- Clickable mentions — user/role/channel pills open a Discord-style popup with avatar, display name, username, role pills (colored), server-since, account-since, color, member count, channel topic, etc.
- Clickable message authors — clicking the avatar or username on a regular message, or the author name on a system message ("X pinned a message…"), opens the same user popup.
- Clickable slash commands —
<discord-command>pills open a popup listing the resolved command and every parameter (name: value). - Copy buttons — user ID, role ID, channel ID, username, color hex, command name, and every slash parameter value get a one-click clipboard button in the popup.
- Image lightbox — click any image to open fullscreen, arrow keys to navigate, Escape to close.
- Date separators between messages —
Tuesday, May 13 2026style, automatically inserted on day boundaries. - Author name color uses the highest listed role's color, matching the Discord client.
Content rendered
In addition to plain text, replies, embeds, and attachments, the viewer supports:
- Components V2 — Containers, Sections, Text Display, Media Gallery, Thumbnail, File, Separator, with accent colors and spoiler support
- Action rows — buttons with proper spacing and Discord-style colors (
primary,secondary,success,destructive) - Stickers — PNG, APNG, GIF, Lottie placeholder
- Polls — question, answer bars with vote counts and percentages, expiry
- Forwarded messages (
messageSnapshots) — quoted-block style with original author, recursive nesting - Voice messages —
🎤indicator, inline SVG waveform fromattachment.waveform, duration - Pinned messages — Discord-style amber left rail (no extra icon clutter)
- Slash command interactions —
{user} used /cmdheader + clickable pill that reveals parameters - System messages —
ChannelPinnedMessage,ChannelNameChange,ChannelIconChange,ThreadCreated,ChatInputCommand,ContextMenuCommand,Call,ChannelFollowAdd,RecipientRemove,RoleSubscriptionPurchase, guild incident reports, poll result, AutoMod actions - Cross-guild replies — show a "Message from another server" pill
- Burst / super-reactions flagged
- Thread state badges —
Archived,Locked - GIFV / animated GIFs —
<video autoplay loop muted>like Discord - Attachment description (alt text) used as
alt/title <id:guide>,<id:browse>,<id:customize>pseudo-channels → styled pills with proper labels</cmd:id>slash command mentions → blue monospace pills- Embed video link and embed provider ("YouTube" etc.) shown
- Edit history with collapsible
<details>(opt-in viamessage.editHistory) - Suppressed embeds flag is honored — when set, embeds aren't rendered (a small
(embeds hidden)note is shown)
Changes vs. the original
Security
- Critical fix
</script>breakout via inlined JSON is prevented (<,>,&, U+2028/2029 escaped) - Critical fix markdown links with
javascript:,data:,vbscript:and other dangerous URI schemes are rewritten to# - Hardened inline
style="color:…"sinks in the mention popup are hex-validated to block CSS injection - Hardened
data:URI MIME types from the image downloader are restricted to image types only (notext/htmlsmuggling) - Fixed
process.exit(1)on discord.js version mismatch removed — library no longer kills the host bot
Robustness
- Fix invalid Discord timestamp markers (
<t:abc:F>, oversized values) no longer abort the transcript withRangeError - Fix per-AST-node error boundary in
MessageSingleASTNodeand per-message error boundary inDiscordMessage— one broken message can never kill the whole render - Fix
parseDiscordEmojino longer throws on deleted reactions withemoji.name === null - Fix
formatBytes(null/undefined/NaN)no longer returnsNaN undefined - Fix
createTranscriptslice uses the resolved limit instead of the rawlimit - Fix embed fields render through a proper async component (was an inline
asyncarrow inside.map()) - Fix
JoinMessagetext is deterministic per message id — re-rendering the same channel always yields the same join line - Fix random
console.logcalls in production paths replaced by thedebugnamespace
Layout
- Fix Components V2 Container/Section used
display:flex;flex-direction:column;gap:8px, which forced every inline<strong>/<br>/ mention pill / text node into its own row. Switched to block flow so messages read like Discord again. - Fix
<discord-system-message>is no longer forced todisplay:block; the pin needle on "X pinned a message…" now sits at the left where Discord renders it. - Fix action-row buttons no longer touch —
discord-action-rowships an 8 px gap rule. - Fix duplicate APP badge on bot/application messages removed — the web component already renders one.
Performance
- Single-pass profile collector —
buildAllContextbuilds profiles + extended dicts in one walk over the message list - Image downloader is concurrent — bounded pool (default 6, configurable via
withConcurrency) instead of sequential @skyra/discord-components-coreversion is pinned to an exact resolved version → CDN cacheable- Inline JSON is shipped via
<script type="application/json">so it doesn't block HTML parse - Emoji URL resolution is memoized
returnType: 'stream'option streams the rendered HTML out instead of buffering — usable for 5,000+ message tickets
DX
react,react-dom,debugmoved into regular dependencies so users don't install them manually (debugwas actually a missing runtime dep in the original —images.jsrequires it)- TypeScript declarations cover all new options
discord.jsremains the only peer dependency
Credits
Original package by ItzDerock. Styles from @derockdev/discord-components.
