discord-html-transcripts-fix 1.6.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -81
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,113 +1,160 @@
|
|
|
1
1
|
# `discord-html-transcripts-fix`
|
|
2
2
|
|
|
3
|
-
A nicely formatted HTML transcript generator for discord.js with full **Components V2** support, an **interactive viewer
|
|
3
|
+
A nicely formatted HTML transcript generator for [discord.js](https://discord.js.org/) with full **Components V2** support, an **interactive viewer**, and **hardened security**.
|
|
4
4
|
|
|
5
5
|
Forked from [discord-html-transcripts](https://github.com/ItzDerock/discord-html-transcripts).
|
|
6
6
|
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
### Security
|
|
10
|
-
- **Critical fix**: `</script>` breakout via inlined JSON is prevented (`<`, `>`, `&`, U+2028/2029 are escaped)
|
|
11
|
-
- **Critical fix**: markdown links with `javascript:`, `data:`, `vbscript:` and similar dangerous URI schemes are rewritten to `#`
|
|
12
|
-
- **Hardened**: inline `style="color:…"` sinks in the mention popup are hex-validated to block CSS injection
|
|
13
|
-
- **Hardened**: `data:` URI MIME types from the image downloader are restricted to image types only (no `text/html` smuggling)
|
|
14
|
-
- **Fixed**: `process.exit(1)` on discord.js version mismatch removed — library no longer kills the host bot
|
|
15
|
-
|
|
16
|
-
### Robustness
|
|
17
|
-
- **Fix**: invalid Discord timestamp markers (`<t:abc:F>`, oversized values) no longer abort the transcript with `RangeError`
|
|
18
|
-
- **Fix**: per-AST-node error boundary in `MessageSingleASTNode` and per-message error boundary in `DiscordMessage` — one broken message can never kill the whole render any more
|
|
19
|
-
- **Fix**: `parseDiscordEmoji` no longer throws on deleted reactions with `emoji.name === null`
|
|
20
|
-
- **Fix**: `formatBytes(null/undefined/NaN)` no longer returns `NaN undefined`
|
|
21
|
-
- **Fix**: `createTranscript` slice uses the resolved limit instead of the raw `limit`
|
|
22
|
-
- **Fix**: embed fields are rendered through a proper async component (was an inline `async` arrow inside `.map()` — undefined React behaviour)
|
|
23
|
-
- **Fix**: `JoinMessage` text is now deterministic per message ID — re-rendering the same channel always yields the same join line
|
|
24
|
-
- **Fix**: random `console.log` calls in production paths replaced by the `debug` namespace
|
|
25
|
-
|
|
26
|
-
### New: interactive viewer
|
|
27
|
-
- **Clickable mentions** — clicking a user / role / channel mention opens a Discord-style popup with avatar, display name, username, all roles (colored pills), server-since, account-since, color, member count, channel topic, etc.
|
|
28
|
-
- **Search bar** — Ctrl+F is intercepted, live highlighting with "x of n matches", prev / next, Enter / Shift+Enter to navigate, Escape to close
|
|
29
|
-
- **Image lightbox** — click any image to open fullscreen, arrow keys to nav, Escape to close
|
|
30
|
-
- **Participant sidebar (TOC)** — toggle button (top-left), sorted list of every participant, click to jump to their first message
|
|
31
|
-
- **Back-to-top** floating button fades in after scrolling
|
|
32
|
-
- **Date separators** between messages — `— Tuesday, May 13 2026 —` style, automatically inserted on day boundaries
|
|
33
|
-
- **Stats footer** — `324 messages · 12 participants · 5 images` (auto-computed)
|
|
34
|
-
- **Language switcher** (i18n) — built-in English and German dictionaries, switch live in the viewer. Pass your own via `options.i18n: { de: { … } }` to extend
|
|
35
|
-
|
|
36
|
-
### New: more Discord content rendered
|
|
37
|
-
- **Stickers** — image, name, alt
|
|
38
|
-
- **Polls** — question, answer bars with vote counts and percentages, expiry
|
|
39
|
-
- **Forwarded messages (`messageSnapshots`)** — quoted-block style with original author
|
|
40
|
-
- **Voice messages** — `🎤` indicator, inline SVG waveform from `attachment.waveform`, duration
|
|
41
|
-
- **Edit timestamp tooltip** on `(edited)` marker
|
|
42
|
-
- **Optional edit history** — pass `message.editHistory = [{content, editedAt}, …]` and the viewer shows a collapsible `<details>` block
|
|
43
|
-
- **Pinned indicator** (`📌` badge on pinned messages)
|
|
44
|
-
- **App badge** for application/bot messages with `applicationId`
|
|
45
|
-
- **Suppressed embeds flag** is honored — when set, embeds aren't rendered (and a small `(embeds hidden)` note is shown instead)
|
|
46
|
-
- **Cross-guild replies** are no longer silently dropped — they show a "Message from another server" pill
|
|
47
|
-
- **Burst / super-reactions** are flagged
|
|
48
|
-
- **Thread state badges** — `Archived`, `Locked`
|
|
49
|
-
- **GIFV / animated GIFs** are rendered with `<video autoplay loop muted>` like in Discord
|
|
50
|
-
- **Attachment description (alt text)** is used as `alt`/`title`
|
|
51
|
-
- **`<id:guide>`, `<id:browse>`, `<id:customize>`** etc. pseudo-channels show as styled pills with proper labels
|
|
52
|
-
- **`</cmd:id>` slash command mentions** render as blue pills
|
|
53
|
-
- **Embed video** link and **embed provider** ("YouTube" etc.) are shown
|
|
54
|
-
- **`RoleSubscriptionPurchase`** system message renders properly
|
|
55
|
-
- **`AutoModerationAction`** shows the matched rule name and blocked content excerpt
|
|
56
|
-
- **Container component** with `accent_color` (left border) and `spoiler` support
|
|
57
|
-
- **Media gallery** per-item spoiler support
|
|
58
|
-
- Many additional system message types (`ChannelNameChange`, `ChannelIconChange`, `ThreadCreated`, `ChatInputCommand`, `ContextMenuCommand`, `Call`, `ChannelFollowAdd`, `RecipientRemove`, guild incident reports, poll result)
|
|
59
|
-
|
|
60
|
-
### Performance
|
|
61
|
-
- **Single-pass profile collector** — `buildAllContext` builds profiles + extended dicts in one walk over the message list
|
|
62
|
-
- **Image downloader is concurrent** — bounded pool (default 6, configurable via `withConcurrency`) instead of sequential
|
|
63
|
-
- **`@skyra/discord-components-core` version is pinned** to an exact resolved version → CDN cacheable
|
|
64
|
-
- **Inline JSON** is shipped via `<script type="application/json">` so it doesn't block HTML parse
|
|
65
|
-
- **Emoji URL resolution** is memoized
|
|
66
|
-
- **`returnType: 'stream'`** option streams the rendered HTML out instead of buffering — usable for 5,000+ message tickets
|
|
67
|
-
|
|
68
|
-
### DX
|
|
69
|
-
- **`react`, `react-dom`, `debug` moved into regular dependencies** so users don't install them manually (`debug` was actually a missing runtime dep in the original — `images.js` requires it)
|
|
70
|
-
- TypeScript build target unchanged; only `discord.js` remains a peer dependency
|
|
71
|
-
|
|
72
|
-
## Requirements
|
|
73
|
-
|
|
74
|
-
Only `discord.js` as a peer dependency. Everything else is auto-installed:
|
|
7
|
+
## Install
|
|
75
8
|
|
|
76
9
|
```bash
|
|
77
10
|
npm install discord-html-transcripts-fix
|
|
78
11
|
```
|
|
79
12
|
|
|
80
|
-
|
|
13
|
+
Only `discord.js` is a peer dependency; everything else (React, Lit SSR, markdown parser, etc.) is auto-installed.
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
81
16
|
|
|
82
17
|
```js
|
|
83
18
|
const { createTranscript } = require('discord-html-transcripts-fix');
|
|
84
19
|
|
|
85
20
|
const attachment = await createTranscript(channel, {
|
|
86
|
-
// existing options
|
|
87
21
|
limit: -1,
|
|
88
22
|
saveImages: false,
|
|
89
|
-
poweredBy: true,
|
|
90
|
-
// new options
|
|
91
|
-
language: 'de', // 'en' (default) or 'de' — also exposed live in the viewer
|
|
92
|
-
i18n: { de: { joined: 'kam an' } }, // optional overrides per language
|
|
93
|
-
returnType: 'stream', // 'buffer' | 'string' | 'attachment' (default) | 'stream'
|
|
94
23
|
});
|
|
95
24
|
|
|
96
25
|
channel.send({ files: [attachment] });
|
|
97
26
|
```
|
|
98
27
|
|
|
99
|
-
|
|
28
|
+
## Options
|
|
29
|
+
|
|
30
|
+
| Option | Type | Default | Description |
|
|
31
|
+
|---|---|---|---|
|
|
32
|
+
| `limit` | `number` | `-1` | Max messages to fetch. `-1` = recursive (all). |
|
|
33
|
+
| `filter` | `(m) => boolean` | `() => true` | Predicate to filter messages. |
|
|
34
|
+
| `returnType` | `'attachment'` \| `'buffer'` \| `'string'` \| `'stream'` | `'attachment'` | Return value shape. `'stream'` is best for 5k+ message exports. |
|
|
35
|
+
| `filename` | `string` | `transcript-{channel-id}.html` | Output filename when returning as attachment. |
|
|
36
|
+
| `saveImages` | `boolean` | `false` | Download images and inline them as base64 data URLs. |
|
|
37
|
+
| `favicon` | `'guild'` \| `string` | `'guild'` | Page favicon — `'guild'` uses the server icon, or pass a URL. |
|
|
38
|
+
| `hydrate` | `boolean` | `false` | Server-side hydrate via `@lit-labs/ssr` (slower; usually leave off). |
|
|
39
|
+
| `language` | `'en'` \| `'de'` | `'en'` | UI language for participant labels, filter strings, etc. |
|
|
40
|
+
| `i18n` | `Partial<Record<lang, Record<key,string>>>` | — | Override individual strings per language. |
|
|
41
|
+
| `statsFooter` | `false` \| `{ enabled?, template? }` | `{ enabled: true }` | Bottom stats line. See below. |
|
|
42
|
+
| `footerText` | `string` | — | Legacy "Exported X messages" line. Only renders when `statsFooter` is disabled. |
|
|
43
|
+
| `poweredBy` | `boolean` | `false` | Show the original "Powered by discord-html-transcripts" credit link. |
|
|
44
|
+
| `callbacks` | `{ resolveUser, resolveRole, resolveChannel }` | — | Custom resolvers for mentions. |
|
|
45
|
+
|
|
46
|
+
### Configurable stats footer
|
|
100
47
|
|
|
101
|
-
|
|
48
|
+
```js
|
|
49
|
+
await createTranscript(channel, {
|
|
50
|
+
statsFooter: {
|
|
51
|
+
template: '{messages} Nachrichten · {participants} Teilnehmer · {images} Bilder · {from} → {to} · {span}',
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Or disable entirely:
|
|
56
|
+
await createTranscript(channel, { statsFooter: false });
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Placeholders: `{messages}`, `{participants}`, `{images}`, `{from}`, `{to}`, `{span}`.
|
|
60
|
+
|
|
61
|
+
### Optional edit history
|
|
62
|
+
|
|
63
|
+
If your bot tracks edits, attach them to the message *before* rendering:
|
|
102
64
|
|
|
103
65
|
```js
|
|
104
66
|
message.editHistory = [
|
|
105
|
-
{ content: 'first version',
|
|
106
|
-
{ content: 'corrected version', editedAt: new Date('
|
|
67
|
+
{ content: 'first version', editedAt: new Date('2026-05-13T11:02Z') },
|
|
68
|
+
{ content: 'corrected version', editedAt: new Date('2026-05-13T11:05Z') },
|
|
107
69
|
];
|
|
108
70
|
```
|
|
109
71
|
|
|
110
|
-
The viewer will
|
|
72
|
+
The viewer will render a collapsible `<details>` block next to the `(edited)` marker.
|
|
73
|
+
|
|
74
|
+
## Interactive viewer
|
|
75
|
+
|
|
76
|
+
One floating button sits **top-right** — the hamburger menu. It opens a sidebar containing:
|
|
77
|
+
|
|
78
|
+
- **Live search** at the top — keyword highlighting (`n of N` matches with prev/next), Ctrl/Cmd-F focuses this field
|
|
79
|
+
- **Participants** (collapsible, open by default) — sorted list of authors; click to jump to their first message
|
|
80
|
+
- **Filter** (collapsible, open by default) — author, role, date range, pinned-only, has-image, has-embed, has-attachment, has-container/V2
|
|
81
|
+
|
|
82
|
+
Inline behaviour:
|
|
83
|
+
|
|
84
|
+
- **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.
|
|
85
|
+
- **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.
|
|
86
|
+
- **Clickable slash commands** — `<discord-command>` pills open a popup listing the resolved command and every parameter (`name: value`).
|
|
87
|
+
- **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.
|
|
88
|
+
- **Image lightbox** — click any image to open fullscreen, arrow keys to navigate, Escape to close.
|
|
89
|
+
- **Date separators** between messages — `Tuesday, May 13 2026` style, automatically inserted on day boundaries.
|
|
90
|
+
- **Author name color** uses the **highest listed role's color**, matching the Discord client.
|
|
91
|
+
|
|
92
|
+
## Content rendered
|
|
93
|
+
|
|
94
|
+
In addition to plain text, replies, embeds, and attachments, the viewer supports:
|
|
95
|
+
|
|
96
|
+
- **Components V2** — Containers, Sections, Text Display, Media Gallery, Thumbnail, File, Separator, with accent colors and spoiler support
|
|
97
|
+
- **Action rows** — buttons with proper spacing and Discord-style colors (`primary`, `secondary`, `success`, `destructive`)
|
|
98
|
+
- **Stickers** — PNG, APNG, GIF, Lottie placeholder
|
|
99
|
+
- **Polls** — question, answer bars with vote counts and percentages, expiry
|
|
100
|
+
- **Forwarded messages** (`messageSnapshots`) — quoted-block style with original author, recursive nesting
|
|
101
|
+
- **Voice messages** — `🎤` indicator, inline SVG waveform from `attachment.waveform`, duration
|
|
102
|
+
- **Pinned messages** — Discord-style amber left rail (no extra icon clutter)
|
|
103
|
+
- **Slash command interactions** — `{user} used /cmd` header + clickable pill that reveals parameters
|
|
104
|
+
- **System messages** — `ChannelPinnedMessage`, `ChannelNameChange`, `ChannelIconChange`, `ThreadCreated`, `ChatInputCommand`, `ContextMenuCommand`, `Call`, `ChannelFollowAdd`, `RecipientRemove`, `RoleSubscriptionPurchase`, guild incident reports, poll result, AutoMod actions
|
|
105
|
+
- **Cross-guild replies** — show a "Message from another server" pill
|
|
106
|
+
- **Burst / super-reactions** flagged
|
|
107
|
+
- **Thread state badges** — `Archived`, `Locked`
|
|
108
|
+
- **GIFV / animated GIFs** — `<video autoplay loop muted>` like Discord
|
|
109
|
+
- **Attachment description (alt text)** used as `alt`/`title`
|
|
110
|
+
- **`<id:guide>`, `<id:browse>`, `<id:customize>`** pseudo-channels → styled pills with proper labels
|
|
111
|
+
- **`</cmd:id>` slash command mentions** → blue monospace pills
|
|
112
|
+
- **Embed video** link and **embed provider** ("YouTube" etc.) shown
|
|
113
|
+
- **Edit history** with collapsible `<details>` (opt-in via `message.editHistory`)
|
|
114
|
+
- **Suppressed embeds flag** is honored — when set, embeds aren't rendered (a small `(embeds hidden)` note is shown)
|
|
115
|
+
|
|
116
|
+
## Changes vs. the original
|
|
117
|
+
|
|
118
|
+
### Security
|
|
119
|
+
|
|
120
|
+
- **Critical fix** `</script>` breakout via inlined JSON is prevented (`<`, `>`, `&`, U+2028/2029 escaped)
|
|
121
|
+
- **Critical fix** markdown links with `javascript:`, `data:`, `vbscript:` and other dangerous URI schemes are rewritten to `#`
|
|
122
|
+
- **Hardened** inline `style="color:…"` sinks in the mention popup are hex-validated to block CSS injection
|
|
123
|
+
- **Hardened** `data:` URI MIME types from the image downloader are restricted to image types only (no `text/html` smuggling)
|
|
124
|
+
- **Fixed** `process.exit(1)` on discord.js version mismatch removed — library no longer kills the host bot
|
|
125
|
+
|
|
126
|
+
### Robustness
|
|
127
|
+
|
|
128
|
+
- **Fix** invalid Discord timestamp markers (`<t:abc:F>`, oversized values) no longer abort the transcript with `RangeError`
|
|
129
|
+
- **Fix** per-AST-node error boundary in `MessageSingleASTNode` and per-message error boundary in `DiscordMessage` — one broken message can never kill the whole render
|
|
130
|
+
- **Fix** `parseDiscordEmoji` no longer throws on deleted reactions with `emoji.name === null`
|
|
131
|
+
- **Fix** `formatBytes(null/undefined/NaN)` no longer returns `NaN undefined`
|
|
132
|
+
- **Fix** `createTranscript` slice uses the resolved limit instead of the raw `limit`
|
|
133
|
+
- **Fix** embed fields render through a proper async component (was an inline `async` arrow inside `.map()`)
|
|
134
|
+
- **Fix** `JoinMessage` text is deterministic per message id — re-rendering the same channel always yields the same join line
|
|
135
|
+
- **Fix** random `console.log` calls in production paths replaced by the `debug` namespace
|
|
136
|
+
|
|
137
|
+
### Layout
|
|
138
|
+
|
|
139
|
+
- **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.
|
|
140
|
+
- **Fix** `<discord-system-message>` is no longer forced to `display:block`; the pin needle on "X pinned a message…" now sits at the left where Discord renders it.
|
|
141
|
+
- **Fix** action-row buttons no longer touch — `discord-action-row` ships an 8 px gap rule.
|
|
142
|
+
- **Fix** duplicate APP badge on bot/application messages removed — the web component already renders one.
|
|
143
|
+
|
|
144
|
+
### Performance
|
|
145
|
+
|
|
146
|
+
- **Single-pass profile collector** — `buildAllContext` builds profiles + extended dicts in one walk over the message list
|
|
147
|
+
- **Image downloader is concurrent** — bounded pool (default 6, configurable via `withConcurrency`) instead of sequential
|
|
148
|
+
- **`@skyra/discord-components-core` version is pinned** to an exact resolved version → CDN cacheable
|
|
149
|
+
- **Inline JSON** is shipped via `<script type="application/json">` so it doesn't block HTML parse
|
|
150
|
+
- **Emoji URL resolution** is memoized
|
|
151
|
+
- **`returnType: 'stream'`** option streams the rendered HTML out instead of buffering — usable for 5,000+ message tickets
|
|
152
|
+
|
|
153
|
+
### DX
|
|
154
|
+
|
|
155
|
+
- **`react`, `react-dom`, `debug` moved into regular dependencies** so users don't install them manually (`debug` was actually a missing runtime dep in the original — `images.js` requires it)
|
|
156
|
+
- TypeScript declarations cover all new options
|
|
157
|
+
- `discord.js` remains the only peer dependency
|
|
111
158
|
|
|
112
159
|
## Credits
|
|
113
160
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discord-html-transcripts-fix",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "A nicely formatted html transcript generator for discord.js. Bugfix fork with support for the latest discord.js and Components v2.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|