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.
Files changed (2) hide show
  1. package/README.md +128 -81
  2. 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** (search, lightbox, mention popups, i18n) and **hardened security**.
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
- ## Changes compared to the original
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
- ## Usage
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
- ### Hooking edit history (optional)
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
- If your bot tracks message edits, attach the history to the message before passing it to the renderer:
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', editedAt: new Date('2025-05-13T11:02Z') },
106
- { content: 'corrected version', editedAt: new Date('2025-05-13T11:05Z') },
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 then render a collapsible `<details>` block next to the `(edited)` marker.
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.6.0",
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",