discord-html-transcripts-fix 1.0.0 → 1.1.0
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 +15 -2
- package/dist/generator/index.js +14 -4
- package/dist/generator/renderers/content.js +34 -5
- package/dist/generator/renderers/message.js +84 -8
- package/dist/generator/renderers/systemMessage.js +50 -16
- package/dist/index.js +2 -4
- package/dist/static/client.js +204 -11
- package/dist/utils/buildProfiles.js +130 -10
- package/dist/utils/utils.js +12 -4
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -6,13 +6,26 @@ This is a fork of [discord-html-transcripts](https://github.com/ItzDerock/discor
|
|
|
6
6
|
|
|
7
7
|
- Fixed compatibility with the latest discord.js version
|
|
8
8
|
- Added support for Discord Components v2
|
|
9
|
+
- **Bug fix**: defensive timestamp parsing — invalid `<t:...>` markers in messages no longer throw `RangeError: Invalid time value` and abort the whole transcript
|
|
10
|
+
- **Bug fix**: removed `process.exit(1)` on discord.js version mismatch — the library now only warns, so the host bot keeps running
|
|
11
|
+
- **Bug fix**: render error boundaries at AST-node and message level — a single broken message can no longer kill the entire transcript; it falls back to a "[message failed to render]" placeholder
|
|
12
|
+
- **Bug fix**: `parseDiscordEmoji` no longer crashes on deleted / unknown reactions where `emoji.name` is `null`
|
|
13
|
+
- **New**: clickable user / role / channel mentions — clicking opens a Discord-style popup with avatar, display name, username, roles (as colored pills), join date, account creation date, role color/position/member count, channel topic, IDs, etc.
|
|
14
|
+
- **New**: `buildExtendedContext` collects full user / role / channel details and ships them in `window.$discordMessage` for the popups (also includes mentioned-but-silent users)
|
|
15
|
+
- **New**: stickers in messages are rendered (image, name)
|
|
16
|
+
- **New**: polls are rendered with answer bars, vote counts, percentages, expiry time
|
|
17
|
+
- **New**: more reliable reaction rendering — handles missing emoji name / unknown custom emojis gracefully
|
|
18
|
+
- **New**: many system message types that were previously stubbed (`undefined`) are now rendered — channel name change, channel icon change, thread created, slash-command used, context-menu command, call started, channel-follow added, recipient left, AutoMod block, guild incident reports, poll result
|
|
19
|
+
- **New**: floating "back-to-top" button that fades in after scrolling
|
|
20
|
+
- **New**: `dht-mention` hover effect on all clickable mentions
|
|
21
|
+
- **DX**: `react`, `react-dom` and `debug` moved into regular `dependencies`, so users no longer have to install them manually (`debug` was actually a missing runtime dependency in the original — `images.js` requires it)
|
|
9
22
|
|
|
10
23
|
## Requirements
|
|
11
24
|
|
|
12
|
-
|
|
25
|
+
Only [`discord.js`](https://discord.js.org/) is required as a peer dependency (you already have it in your bot). `react`, `react-dom`, and `debug` are installed automatically. Just run:
|
|
13
26
|
|
|
14
27
|
```bash
|
|
15
|
-
npm install
|
|
28
|
+
npm install discord-html-transcripts-fix
|
|
16
29
|
```
|
|
17
30
|
|
|
18
31
|
## Usage
|
package/dist/generator/index.js
CHANGED
|
@@ -41,19 +41,29 @@ catch (_b) { } // ignore errors
|
|
|
41
41
|
async function render(_a) {
|
|
42
42
|
var _b;
|
|
43
43
|
var { messages, channel, callbacks } = _a, options = __rest(_a, ["messages", "channel", "callbacks"]);
|
|
44
|
-
const profiles = (0, buildProfiles_1.buildProfiles)(messages);
|
|
45
|
-
const
|
|
44
|
+
const profiles = await (0, buildProfiles_1.buildProfiles)(messages);
|
|
45
|
+
const extended = await (0, buildProfiles_1.buildExtendedContext)(messages, channel).catch((e) => {
|
|
46
|
+
console.warn('[discord-html-transcripts-fix] buildExtendedContext failed:', e && e.message ? e.message : e);
|
|
47
|
+
return { users: {}, roles: {}, channels: {} };
|
|
48
|
+
});
|
|
49
|
+
const globalData = {
|
|
50
|
+
profiles,
|
|
51
|
+
users: extended.users,
|
|
52
|
+
roles: extended.roles,
|
|
53
|
+
channels: extended.channels,
|
|
54
|
+
};
|
|
55
|
+
const { prelude } = await (0, static_1.prerenderToNodeStream)((0, jsx_runtime_1.jsxs)("html", { children: [(0, jsx_runtime_1.jsxs)("head", { children: [(0, jsx_runtime_1.jsx)("meta", { charSet: "utf-8" }), (0, jsx_runtime_1.jsx)("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), (0, jsx_runtime_1.jsx)("link", { rel: "preconnect", href: "https://cdn.jsdelivr.net/" }), (0, jsx_runtime_1.jsx)("link", { rel: "preconnect", href: "https://cdn.discordapp.com" }), (0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: client_1.ggSansFont } }), (0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: styles_1.globalStyles } }), (0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: client_1.mentionPopupStyles } }), (0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: client_1.transcriptUtils } }), (0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: DiscordImage_1.DiscordAttachmentStyles } }), (0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: DiscordHighlightedCode_1.DiscordHighlightStyles } }), (0, jsx_runtime_1.jsx)("link", { rel: "icon", type: "image/png", href: options.favicon === 'guild'
|
|
46
56
|
? channel.isDMBased()
|
|
47
57
|
? undefined
|
|
48
58
|
: ((_b = channel.guild.iconURL({ size: 16, extension: 'png' })) !== null && _b !== void 0 ? _b : undefined)
|
|
49
59
|
: options.favicon }), (0, jsx_runtime_1.jsx)("title", { children: channel.isDMBased() ? 'Direct Messages' : channel.name }), (0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: {
|
|
50
60
|
__html: client_1.scrollToMessage,
|
|
51
61
|
} }), !options.hydrate && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: {
|
|
52
|
-
__html: `globalThis.$discordMessage
|
|
62
|
+
__html: `globalThis.$discordMessage=${JSON.stringify(globalData)}`,
|
|
53
63
|
} }), (0, jsx_runtime_1.jsx)("script", { type: "module", src: `https://cdn.jsdelivr.net/npm/@skyra/discord-components-core@${discordComponentsVersion}/+esm` })] }))] }), (0, jsx_runtime_1.jsx)("body", { style: {
|
|
54
64
|
margin: 0,
|
|
55
65
|
minHeight: '100vh',
|
|
56
|
-
}, children: (0, jsx_runtime_1.jsx)(transcript_1.default, Object.assign({ messages: messages, channel: channel, callbacks: callbacks }, options)) }), options.hydrate && (0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.revealSpoiler } })] }));
|
|
66
|
+
}, children: (0, jsx_runtime_1.jsx)(transcript_1.default, Object.assign({ messages: messages, channel: channel, callbacks: callbacks }, options)) }), (0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.mentionPopup } }), (0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.backToTop } }), options.hydrate && (0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.revealSpoiler } })] }));
|
|
57
67
|
const markup = await (0, utils_1.streamToString)(prelude);
|
|
58
68
|
if (options.hydrate) {
|
|
59
69
|
const { render: renderLit } = await import('@lit-labs/ssr');
|
|
@@ -56,6 +56,21 @@ async function MessageSingleASTNode({ node, context }) {
|
|
|
56
56
|
var _a, _b;
|
|
57
57
|
if (!node)
|
|
58
58
|
return null;
|
|
59
|
+
try {
|
|
60
|
+
return await renderASTNode(node, context);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
console.warn('[discord-html-transcripts-fix] Failed to render AST node, falling back to raw:', err && err.message ? err.message : err);
|
|
64
|
+
// Fallback: render as plain text from node.content if possible
|
|
65
|
+
try {
|
|
66
|
+
if (typeof node.content === 'string') return node.content;
|
|
67
|
+
if (Array.isArray(node.content)) return (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context });
|
|
68
|
+
} catch (_e) { /* ignore */ }
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function renderASTNode(node, context) {
|
|
73
|
+
var _a, _b;
|
|
59
74
|
const type = node.type;
|
|
60
75
|
switch (type) {
|
|
61
76
|
case 'text':
|
|
@@ -78,17 +93,17 @@ async function MessageSingleASTNode({ node, context }) {
|
|
|
78
93
|
case 'channel': {
|
|
79
94
|
const id = node.id;
|
|
80
95
|
const channel = await context.callbacks.resolveChannel(id);
|
|
81
|
-
return ((0, jsx_runtime_1.jsx)("discord-mention", { type: channel ? (channel.isDMBased() ? 'channel' : getChannelType(channel.type)) : 'channel', children: channel ? (channel.isDMBased() ? 'DM Channel' : channel.name) : `<#${id}>` }));
|
|
96
|
+
return ((0, jsx_runtime_1.jsx)("discord-mention", { type: channel ? (channel.isDMBased() ? 'channel' : getChannelType(channel.type)) : 'channel', "data-mention-type": "channel", "data-mention-id": id, className: "dht-mention", children: channel ? (channel.isDMBased() ? 'DM Channel' : channel.name) : `<#${id}>` }));
|
|
82
97
|
}
|
|
83
98
|
case 'role': {
|
|
84
99
|
const id = node.id;
|
|
85
100
|
const role = await context.callbacks.resolveRole(id);
|
|
86
|
-
return ((0, jsx_runtime_1.jsx)("discord-mention", { type: "role", color: context.type === RenderType.REPLY ? undefined : role === null || role === void 0 ? void 0 : role.hexColor, children: role ? role.name : `<@&${id}>` }));
|
|
101
|
+
return ((0, jsx_runtime_1.jsx)("discord-mention", { type: "role", color: context.type === RenderType.REPLY ? undefined : role === null || role === void 0 ? void 0 : role.hexColor, "data-mention-type": "role", "data-mention-id": id, className: "dht-mention", children: role ? role.name : `<@&${id}>` }));
|
|
87
102
|
}
|
|
88
103
|
case 'user': {
|
|
89
104
|
const id = node.id;
|
|
90
105
|
const user = await context.callbacks.resolveUser(id);
|
|
91
|
-
return (0, jsx_runtime_1.jsx)("discord-mention", { type: "user", children: user ? ((_a = user.displayName) !== null && _a !== void 0 ? _a : user.username) : `<@${id}>` });
|
|
106
|
+
return (0, jsx_runtime_1.jsx)("discord-mention", { type: "user", "data-mention-type": "user", "data-mention-id": id, className: "dht-mention", children: user ? ((_a = user.displayName) !== null && _a !== void 0 ? _a : user.username) : `<@${id}>` });
|
|
92
107
|
}
|
|
93
108
|
case 'here':
|
|
94
109
|
case 'everyone':
|
|
@@ -115,10 +130,24 @@ async function MessageSingleASTNode({ node, context }) {
|
|
|
115
130
|
case 'emoji':
|
|
116
131
|
case 'twemoji':
|
|
117
132
|
return ((0, jsx_runtime_1.jsx)("discord-custom-emoji", { name: node.name, url: (0, utils_1.parseDiscordEmoji)(node), embedEmoji: context.type === RenderType.EMBED, jumbo: (_b = context._internal) === null || _b === void 0 ? void 0 : _b.largeEmojis }));
|
|
118
|
-
case 'timestamp':
|
|
133
|
+
case 'timestamp': {
|
|
119
134
|
// TODO: Make this reactive
|
|
120
135
|
// https://github.com/ItzDerock/discord-components/blob/main/packages/core/src/components/discord-time/discord-time.tsx
|
|
121
|
-
|
|
136
|
+
// Defensive parsing: Discord timestamps are Unix seconds (string oder number),
|
|
137
|
+
// ungültige Werte (z.B. <t:abc:F> oder absurd große Zahlen) sollen kein RangeError werfen.
|
|
138
|
+
var raw = node.timestamp;
|
|
139
|
+
var dateMs = NaN;
|
|
140
|
+
if (typeof raw === 'number') {
|
|
141
|
+
dateMs = raw < 1e12 ? raw * 1000 : raw;
|
|
142
|
+
} else if (typeof raw === 'string') {
|
|
143
|
+
var n = parseInt(raw, 10);
|
|
144
|
+
if (Number.isFinite(n)) dateMs = n < 1e12 ? n * 1000 : n;
|
|
145
|
+
}
|
|
146
|
+
var dt = new Date(dateMs);
|
|
147
|
+
var iso = '';
|
|
148
|
+
try { iso = Number.isFinite(dt.getTime()) ? dt.toISOString() : ''; } catch (_e) { iso = ''; }
|
|
149
|
+
return (0, jsx_runtime_1.jsx)("discord-time", { children: iso });
|
|
150
|
+
}
|
|
122
151
|
// return <DiscordTime timestamp={parseInt(node.timestamp) * 1000} format={node.format} />;
|
|
123
152
|
default: {
|
|
124
153
|
// console.log(`[discord-html-transcripts] Unknown node type: ${type}`, node);
|
|
@@ -47,13 +47,89 @@ const reply_1 = __importDefault(require("./reply"));
|
|
|
47
47
|
const systemMessage_1 = __importDefault(require("./systemMessage"));
|
|
48
48
|
async function DiscordMessage({ message, context, }) {
|
|
49
49
|
var _a;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
try {
|
|
51
|
+
if (message.system)
|
|
52
|
+
return (0, jsx_runtime_1.jsx)(systemMessage_1.default, { message: message });
|
|
53
|
+
const isCrosspost = message.reference && message.reference.guildId !== ((_a = message.guild) === null || _a === void 0 ? void 0 : _a.id);
|
|
54
|
+
const stickers = message.stickers && message.stickers.size > 0
|
|
55
|
+
? Array.from(message.stickers.values()).map((s) => ({
|
|
56
|
+
id: s.id,
|
|
57
|
+
name: s.name,
|
|
58
|
+
url: s.url || `https://media.discordapp.net/stickers/${s.id}.png`,
|
|
59
|
+
format: s.format,
|
|
60
|
+
}))
|
|
61
|
+
: [];
|
|
62
|
+
const poll = (message.poll && (message.poll.question || (message.poll.answers && message.poll.answers.size))) ? message.poll : null;
|
|
63
|
+
return ((0, jsx_runtime_1.jsxs)("discord-message", { id: `m-${message.id}`, timestamp: message.createdAt, edited: message.editedAt !== null, server: isCrosspost !== null && isCrosspost !== void 0 ? isCrosspost : undefined, highlight: message.mentions.everyone, profile: message.author.id, children: [(0, jsx_runtime_1.jsx)(reply_1.default, { message: message, context: context }), message.interaction && ((0, jsx_runtime_1.jsx)("discord-command", { slot: "reply", profile: message.interaction.user.id, command: '/' + message.interaction.commandName })), message.content && ((0, jsx_runtime_1.jsx)(content_1.default, { content: message.content, context: Object.assign(Object.assign({}, context), { type: message.webhookId ? content_1.RenderType.WEBHOOK : content_1.RenderType.NORMAL }) })), (0, jsx_runtime_1.jsx)(attachment_1.Attachments, { message: message, context: context }), stickers.length > 0 && renderStickers(stickers), poll && renderPoll(poll), message.embeds.map((embed, id) => ((0, jsx_runtime_1.jsx)(embed_1.DiscordEmbed, { embed: embed, context: Object.assign(Object.assign({}, context), { index: id, message }) }, id))), message.components.length > 0 && ((0, jsx_runtime_1.jsx)("discord-attachments", { slot: "components", children: message.components.map((component, id) => ((0, jsx_runtime_1.jsx)(components_1.default, { id: id, component: component, context: context }, id))) })), message.reactions.cache.size > 0 && ((0, jsx_runtime_1.jsx)("discord-reactions", { slot: "reactions", children: Array.from(message.reactions.cache.values()).map((reaction, id) => {
|
|
64
|
+
var _a, _b;
|
|
65
|
+
return ((0, jsx_runtime_1.jsx)("discord-reaction", { name: ((_a = reaction.emoji) === null || _a === void 0 ? void 0 : _a.name) || ':unknown:', emoji: (0, utils_1.parseDiscordEmoji)(reaction.emoji) || undefined, count: (_b = reaction.count) !== null && _b !== void 0 ? _b : 0 }, `${message.id}r${id}`));
|
|
66
|
+
}) })), message.hasThread && message.thread && ((0, jsx_runtime_1.jsx)("discord-thread", { slot: "thread", name: message.thread.name, cta: message.thread.messageCount
|
|
67
|
+
? `${message.thread.messageCount} Message${message.thread.messageCount > 1 ? 's' : ''}`
|
|
68
|
+
: 'View Thread', children: message.thread.lastMessage ? ((0, jsx_runtime_1.jsx)("discord-thread-message", { profile: message.thread.lastMessage.author.id, children: (0, jsx_runtime_1.jsx)(content_1.default, { content: message.thread.lastMessage.content.length > 128
|
|
69
|
+
? message.thread.lastMessage.content.substring(0, 125) + '...'
|
|
70
|
+
: message.thread.lastMessage.content, context: Object.assign(Object.assign({}, context), { type: content_1.RenderType.REPLY }) }) })) : (`Thread messages not saved.`) }))] }, message.id));
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
console.warn('[discord-html-transcripts-fix] Failed to render message', message && message.id, err && err.message ? err.message : err);
|
|
74
|
+
const safeAuthor = (message && message.author && (message.author.displayName || message.author.username)) || 'Unknown';
|
|
75
|
+
return ((0, jsx_runtime_1.jsxs)("discord-message", { id: `m-${message && message.id}`, children: [(0, jsx_runtime_1.jsx)("span", { style: { opacity: 0.6 }, children: `[Nachricht von ${safeAuthor} konnte nicht gerendert werden]` })] }, message && message.id));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function renderStickers(stickers) {
|
|
80
|
+
return (0, jsx_runtime_1.jsx)("div", {
|
|
81
|
+
className: "dht-stickers",
|
|
82
|
+
style: { display: 'flex', gap: '8px', flexWrap: 'wrap', marginTop: '4px' },
|
|
83
|
+
children: stickers.map((s) => (0, jsx_runtime_1.jsx)("img", {
|
|
84
|
+
src: s.url,
|
|
85
|
+
alt: s.name || 'sticker',
|
|
86
|
+
title: s.name || '',
|
|
87
|
+
style: { width: '160px', height: '160px', borderRadius: '8px', objectFit: 'contain', background: 'rgba(255,255,255,0.02)' }
|
|
88
|
+
}, s.id))
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function renderPoll(poll) {
|
|
93
|
+
let totalVotes = 0;
|
|
94
|
+
const answers = poll.answers ? Array.from(poll.answers.values()) : [];
|
|
95
|
+
for (const a of answers) totalVotes += a.voteCount || 0;
|
|
96
|
+
const expires = poll.expiresAt instanceof Date ? poll.expiresAt.toLocaleString('de-DE') : '';
|
|
97
|
+
|
|
98
|
+
return (0, jsx_runtime_1.jsxs)("div", {
|
|
99
|
+
className: "dht-poll",
|
|
100
|
+
style: {
|
|
101
|
+
border: '1px solid #2b2d31', background: '#1e1f22', borderRadius: '8px',
|
|
102
|
+
padding: '12px 14px', marginTop: '6px', maxWidth: '480px'
|
|
103
|
+
},
|
|
104
|
+
children: [
|
|
105
|
+
(0, jsx_runtime_1.jsxs)("div", { style: { fontWeight: 700, marginBottom: '8px', fontSize: '15px' }, children: ['📊 ', poll.question && poll.question.text ? poll.question.text : 'Poll'] }),
|
|
106
|
+
(0, jsx_runtime_1.jsx)("div", {
|
|
107
|
+
style: { display: 'flex', flexDirection: 'column', gap: '6px' },
|
|
108
|
+
children: answers.map((a, i) => {
|
|
109
|
+
const text = (a.text || a.poll_media?.text || a.media?.text || a.answer_id || `Option ${i + 1}`);
|
|
110
|
+
const votes = a.voteCount || 0;
|
|
111
|
+
const pct = totalVotes > 0 ? Math.round((votes / totalVotes) * 100) : 0;
|
|
112
|
+
return (0, jsx_runtime_1.jsxs)("div", {
|
|
113
|
+
style: { background: '#2b2d31', borderRadius: '6px', padding: '6px 10px', position: 'relative', overflow: 'hidden' },
|
|
114
|
+
children: [
|
|
115
|
+
(0, jsx_runtime_1.jsx)("div", { style: {
|
|
116
|
+
position: 'absolute', top: 0, left: 0, height: '100%',
|
|
117
|
+
width: pct + '%', background: 'rgba(88,101,242,0.25)', zIndex: 0
|
|
118
|
+
}}),
|
|
119
|
+
(0, jsx_runtime_1.jsxs)("div", { style: { position: 'relative', zIndex: 1, display: 'flex', justifyContent: 'space-between', gap: '8px' },
|
|
120
|
+
children: [
|
|
121
|
+
(0, jsx_runtime_1.jsx)("span", { children: String(text) }),
|
|
122
|
+
(0, jsx_runtime_1.jsxs)("span", { style: { color: '#b5bac1', fontSize: '12px', whiteSpace: 'nowrap' }, children: [votes, ' (', pct, '%)'] })
|
|
123
|
+
]
|
|
124
|
+
})
|
|
125
|
+
]
|
|
126
|
+
}, i);
|
|
127
|
+
})
|
|
128
|
+
}),
|
|
129
|
+
(0, jsx_runtime_1.jsxs)("div", { style: { marginTop: '8px', fontSize: '11px', color: '#949ba4' },
|
|
130
|
+
children: [totalVotes, ' Stimme', totalVotes !== 1 ? 'n' : '', expires ? ' · endet ' + expires : '']
|
|
131
|
+
})
|
|
132
|
+
]
|
|
133
|
+
});
|
|
58
134
|
}
|
|
59
135
|
//# sourceMappingURL=message.js.map
|
|
@@ -21,23 +21,62 @@ async function SystemMessage({ message }) {
|
|
|
21
21
|
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, type: "boost", children: [(0, jsx_runtime_1.jsx)(Highlight, { color: (_f = (_e = message.member) === null || _e === void 0 ? void 0 : _e.roles.color) === null || _f === void 0 ? void 0 : _f.hexColor, children: (_g = message.author.displayName) !== null && _g !== void 0 ? _g : message.author.username }), ' ', "boosted the server!"] }, message.id));
|
|
22
22
|
case discord_js_1.MessageType.ThreadStarterMessage:
|
|
23
23
|
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `ms-${message.id}`, type: "thread", children: [(0, jsx_runtime_1.jsx)(Highlight, { color: (_j = (_h = message.member) === null || _h === void 0 ? void 0 : _h.roles.color) === null || _j === void 0 ? void 0 : _j.hexColor, children: (_k = message.author.displayName) !== null && _k !== void 0 ? _k : message.author.username }), ' ', "started a thread: ", (0, jsx_runtime_1.jsx)("i", { "data-goto": (_l = message.reference) === null || _l === void 0 ? void 0 : _l.messageId, children: message.content })] }, message.id));
|
|
24
|
-
|
|
24
|
+
case discord_js_1.MessageType.ChannelNameChange: {
|
|
25
|
+
const authorName = message.author.displayName || message.author.username;
|
|
26
|
+
const color = message.member?.roles?.color?.hexColor;
|
|
27
|
+
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, type: "edit", children: [(0, jsx_runtime_1.jsx)(Highlight, { color, children: authorName }), ' ', 'changed the channel name: ', (0, jsx_runtime_1.jsx)("strong", { children: message.content })] }, message.id));
|
|
28
|
+
}
|
|
29
|
+
case discord_js_1.MessageType.ChannelIconChange: {
|
|
30
|
+
const authorName = message.author.displayName || message.author.username;
|
|
31
|
+
const color = message.member?.roles?.color?.hexColor;
|
|
32
|
+
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, type: "edit", children: [(0, jsx_runtime_1.jsx)(Highlight, { color, children: authorName }), ' ', 'changed the channel icon.'] }, message.id));
|
|
33
|
+
}
|
|
34
|
+
case discord_js_1.MessageType.ThreadCreated: {
|
|
35
|
+
const authorName = message.author.displayName || message.author.username;
|
|
36
|
+
const color = message.member?.roles?.color?.hexColor;
|
|
37
|
+
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, type: "thread", children: [(0, jsx_runtime_1.jsx)(Highlight, { color, children: authorName }), ' ', 'started a thread: ', (0, jsx_runtime_1.jsx)("i", { "data-goto": message.reference?.messageId, children: message.content || 'New thread' })] }, message.id));
|
|
38
|
+
}
|
|
39
|
+
case discord_js_1.MessageType.ChatInputCommand:
|
|
40
|
+
case discord_js_1.MessageType.ContextMenuCommand: {
|
|
41
|
+
const authorName = message.author.displayName || message.author.username;
|
|
42
|
+
const color = message.member?.roles?.color?.hexColor;
|
|
43
|
+
const cmd = message.interaction?.commandName ? '/' + message.interaction.commandName : 'a command';
|
|
44
|
+
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, type: "call", children: [(0, jsx_runtime_1.jsx)(Highlight, { color, children: authorName }), ' ', 'used ', (0, jsx_runtime_1.jsx)("strong", { children: cmd })] }, message.id));
|
|
45
|
+
}
|
|
46
|
+
case discord_js_1.MessageType.Call: {
|
|
47
|
+
const authorName = message.author.displayName || message.author.username;
|
|
48
|
+
const color = message.member?.roles?.color?.hexColor;
|
|
49
|
+
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, type: "call", children: [(0, jsx_runtime_1.jsx)(Highlight, { color, children: authorName }), ' ', 'started a call.'] }, message.id));
|
|
50
|
+
}
|
|
51
|
+
case discord_js_1.MessageType.ChannelFollowAdd: {
|
|
52
|
+
const authorName = message.author.displayName || message.author.username;
|
|
53
|
+
const color = message.member?.roles?.color?.hexColor;
|
|
54
|
+
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, type: "edit", children: [(0, jsx_runtime_1.jsx)(Highlight, { color, children: authorName }), ' ', 'added ', (0, jsx_runtime_1.jsx)("strong", { children: message.content }), ' to follow this channel.'] }, message.id));
|
|
55
|
+
}
|
|
56
|
+
case discord_js_1.MessageType.RecipientRemove: {
|
|
57
|
+
const authorName = message.author.displayName || message.author.username;
|
|
58
|
+
const color = message.member?.roles?.color?.hexColor;
|
|
59
|
+
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, type: "leave", children: [(0, jsx_runtime_1.jsx)(Highlight, { color, children: authorName }), ' ', 'left.'] }, message.id));
|
|
60
|
+
}
|
|
61
|
+
case discord_js_1.MessageType.AutoModerationAction:
|
|
62
|
+
return ((0, jsx_runtime_1.jsx)("discord-system-message", { id: `m-${message.id}`, type: "boost", children: 'AutoMod blocked a message.' }, message.id));
|
|
63
|
+
case discord_js_1.MessageType.GuildIncidentReportRaid:
|
|
64
|
+
case discord_js_1.MessageType.GuildIncidentReportFalseAlarm:
|
|
65
|
+
case discord_js_1.MessageType.GuildIncidentAlertModeEnabled:
|
|
66
|
+
case discord_js_1.MessageType.GuildIncidentAlertModeDisabled:
|
|
67
|
+
return ((0, jsx_runtime_1.jsx)("discord-system-message", { id: `m-${message.id}`, type: "boost", children: 'Server safety alert.' }, message.id));
|
|
68
|
+
case discord_js_1.MessageType.PollResult:
|
|
69
|
+
return ((0, jsx_runtime_1.jsx)("discord-system-message", { id: `m-${message.id}`, type: "edit", children: 'A poll ended.' }, message.id));
|
|
70
|
+
// These types are rendered through the normal message pipeline (DiscordMessage)
|
|
25
71
|
case discord_js_1.MessageType.Default:
|
|
26
|
-
case discord_js_1.MessageType.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
case discord_js_1.MessageType.ChannelIconChange:
|
|
30
|
-
case discord_js_1.MessageType.ChannelFollowAdd:
|
|
72
|
+
case discord_js_1.MessageType.Reply:
|
|
73
|
+
return undefined;
|
|
74
|
+
// Less common / niche types: rendered nothing (Discord client also de-emphasizes these)
|
|
31
75
|
case discord_js_1.MessageType.GuildDiscoveryDisqualified:
|
|
32
76
|
case discord_js_1.MessageType.GuildDiscoveryRequalified:
|
|
33
77
|
case discord_js_1.MessageType.GuildDiscoveryGracePeriodInitialWarning:
|
|
34
78
|
case discord_js_1.MessageType.GuildDiscoveryGracePeriodFinalWarning:
|
|
35
|
-
case discord_js_1.MessageType.ThreadCreated:
|
|
36
|
-
case discord_js_1.MessageType.Reply:
|
|
37
|
-
case discord_js_1.MessageType.ChatInputCommand:
|
|
38
79
|
case discord_js_1.MessageType.GuildInviteReminder:
|
|
39
|
-
case discord_js_1.MessageType.ContextMenuCommand:
|
|
40
|
-
case discord_js_1.MessageType.AutoModerationAction:
|
|
41
80
|
case discord_js_1.MessageType.RoleSubscriptionPurchase:
|
|
42
81
|
case discord_js_1.MessageType.InteractionPremiumUpsell:
|
|
43
82
|
case discord_js_1.MessageType.StageStart:
|
|
@@ -46,12 +85,7 @@ async function SystemMessage({ message }) {
|
|
|
46
85
|
case discord_js_1.MessageType.StageRaiseHand:
|
|
47
86
|
case discord_js_1.MessageType.StageTopic:
|
|
48
87
|
case discord_js_1.MessageType.GuildApplicationPremiumSubscription:
|
|
49
|
-
case discord_js_1.MessageType.GuildIncidentAlertModeEnabled:
|
|
50
|
-
case discord_js_1.MessageType.GuildIncidentAlertModeDisabled:
|
|
51
|
-
case discord_js_1.MessageType.GuildIncidentReportRaid:
|
|
52
|
-
case discord_js_1.MessageType.GuildIncidentReportFalseAlarm:
|
|
53
88
|
case discord_js_1.MessageType.PurchaseNotification:
|
|
54
|
-
case discord_js_1.MessageType.PollResult:
|
|
55
89
|
return undefined;
|
|
56
90
|
default:
|
|
57
91
|
return undefined;
|
package/dist/index.js
CHANGED
|
@@ -30,12 +30,10 @@ var transcript_1 = require("./generator/transcript");
|
|
|
30
30
|
Object.defineProperty(exports, "DiscordMessages", { enumerable: true, get: function () { return __importDefault(transcript_1).default; } });
|
|
31
31
|
var images_2 = require("./downloader/images");
|
|
32
32
|
Object.defineProperty(exports, "TranscriptImageDownloader", { enumerable: true, get: function () { return images_2.TranscriptImageDownloader; } });
|
|
33
|
-
// version check
|
|
33
|
+
// version check (warn only — never kill the host process from a library)
|
|
34
34
|
const versionPrefix = discord_js_1.version.split('.')[0];
|
|
35
35
|
if (versionPrefix !== '14' && versionPrefix !== '15') {
|
|
36
|
-
console.
|
|
37
|
-
` For v13.x.x support, please install discord-html-transcripts v2.x.x using "npm install discord-html-transcripts@^2".`);
|
|
38
|
-
process.exit(1);
|
|
36
|
+
console.warn(`[discord-html-transcripts-fix] Compatible with discord.js v14.x and v15.x, you are using v${discord_js_1.version}. Continuing — transcripts may render incorrectly.`);
|
|
39
37
|
}
|
|
40
38
|
/**
|
|
41
39
|
*
|
package/dist/static/client.js
CHANGED
|
@@ -1,31 +1,224 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// TODO: create some sort of build system to compile this file
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.ggSansFont = exports.revealSpoiler = exports.scrollToMessage = void 0;
|
|
3
|
+
exports.ggSansFont = exports.revealSpoiler = exports.scrollToMessage = exports.mentionPopup = exports.mentionPopupStyles = exports.backToTop = exports.transcriptUtils = void 0;
|
|
4
|
+
|
|
5
5
|
/*
|
|
6
|
-
//
|
|
6
|
+
// === scrollToMessage source ===
|
|
7
|
+
// Whenever the user clicks on element with data-goto attribute, scroll to that message
|
|
7
8
|
document.addEventListener('click', (e) => {
|
|
8
9
|
const target = e.target;
|
|
9
10
|
if(!target) return;
|
|
10
|
-
|
|
11
11
|
const goto = target?.getAttribute('data-goto');
|
|
12
|
-
|
|
13
12
|
if (goto) {
|
|
14
|
-
const message = document.getElementById(`m
|
|
13
|
+
const message = document.getElementById(`m-${goto}`);
|
|
15
14
|
if (message) {
|
|
16
15
|
message.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
17
16
|
message.style.backgroundColor = 'rgba(148, 156, 247, 0.1)';
|
|
18
17
|
message.style.transition = 'background-color 0.5s ease';
|
|
19
|
-
setTimeout(() => {
|
|
20
|
-
message.style.backgroundColor = 'transparent';
|
|
21
|
-
}, 1000);
|
|
18
|
+
setTimeout(() => { message.style.backgroundColor = 'transparent'; }, 1000);
|
|
22
19
|
} else {
|
|
23
|
-
console.warn(`Message
|
|
20
|
+
console.warn(`Message ${goto} not found.`);
|
|
24
21
|
}
|
|
25
22
|
}
|
|
26
23
|
});
|
|
27
24
|
*/
|
|
28
25
|
exports.scrollToMessage = 'document.addEventListener("click",t=>{let e=t.target;if(!e)return;let o=e?.getAttribute("data-goto");if(o){let r=document.getElementById(`m-${o}`);r?(r.scrollIntoView({behavior:"smooth",block:"center"}),r.style.backgroundColor="rgba(148, 156, 247, 0.1)",r.style.transition="background-color 0.5s ease",setTimeout(()=>{r.style.backgroundColor="transparent"},1e3)):console.warn("Message ${goto} not found.")}});';
|
|
26
|
+
|
|
29
27
|
exports.revealSpoiler = 'const s=document.querySelectorAll(".discord-spoiler");s.forEach(s=>s.addEventListener("click",()=>{if(s.classList.contains("discord-spoiler")){s.classList.remove("discord-spoiler");s.classList.add("discord-spoiler--revealed");}}));';
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Mention popup: click on a user/role/channel mention to see detailed info
|
|
31
|
+
// Reads from globalThis.$discordMessage.{users,roles,channels,profiles}.
|
|
32
|
+
// ============================================================================
|
|
33
|
+
exports.mentionPopup = `(function(){
|
|
34
|
+
var DATA = function(){ return (typeof globalThis !== 'undefined' && globalThis.$discordMessage) || {}; };
|
|
35
|
+
function esc(s){ return String(s==null?'':s).replace(/[&<>"']/g, function(c){ return ({'&':'&','<':'<','>':'>','"':'"',"'":'''})[c]; }); }
|
|
36
|
+
function fmt(iso){ if(!iso) return ''; try { var d = new Date(iso); return isNaN(d.getTime()) ? '' : d.toLocaleString('de-DE',{dateStyle:'medium', timeStyle:'short'}); } catch(_){ return ''; } }
|
|
37
|
+
|
|
38
|
+
var popup = null;
|
|
39
|
+
function ensure(){
|
|
40
|
+
if (popup) return popup;
|
|
41
|
+
popup = document.createElement('div');
|
|
42
|
+
popup.className = 'dht-popup';
|
|
43
|
+
popup.innerHTML = '<div class="dht-popup-body"></div><button type="button" class="dht-popup-close" aria-label="Schliessen">×</button>';
|
|
44
|
+
document.body.appendChild(popup);
|
|
45
|
+
popup.querySelector('.dht-popup-close').addEventListener('click', hide);
|
|
46
|
+
document.addEventListener('click', function(e){
|
|
47
|
+
if (!popup || popup.style.display !== 'block') return;
|
|
48
|
+
if (popup.contains(e.target)) return;
|
|
49
|
+
if (e.target.closest && e.target.closest('[data-mention-id]')) return;
|
|
50
|
+
hide();
|
|
51
|
+
});
|
|
52
|
+
document.addEventListener('keydown', function(e){ if (e.key === 'Escape') hide(); });
|
|
53
|
+
window.addEventListener('resize', hide);
|
|
54
|
+
return popup;
|
|
55
|
+
}
|
|
56
|
+
function hide(){ if (popup) popup.style.display = 'none'; }
|
|
57
|
+
function showAt(el, html){
|
|
58
|
+
var p = ensure();
|
|
59
|
+
p.querySelector('.dht-popup-body').innerHTML = html;
|
|
60
|
+
p.style.display = 'block';
|
|
61
|
+
var r = el.getBoundingClientRect();
|
|
62
|
+
var pr = p.getBoundingClientRect();
|
|
63
|
+
var pad = 8;
|
|
64
|
+
var top = r.bottom + 6;
|
|
65
|
+
var left = r.left;
|
|
66
|
+
if (top + pr.height > window.innerHeight - pad) top = Math.max(pad, r.top - pr.height - 6);
|
|
67
|
+
if (left + pr.width > window.innerWidth - pad) left = Math.max(pad, window.innerWidth - pr.width - pad);
|
|
68
|
+
if (left < pad) left = pad;
|
|
69
|
+
p.style.top = top + 'px';
|
|
70
|
+
p.style.left = left + 'px';
|
|
71
|
+
}
|
|
72
|
+
function rolePill(role){
|
|
73
|
+
var c = role && role.color ? role.color : '#5865F2';
|
|
74
|
+
return '<span class="dht-role-pill" style="--dht-role:' + esc(c) + ';">' + esc(role && role.name ? role.name : '@role') + '</span>';
|
|
75
|
+
}
|
|
76
|
+
function userHtml(id){
|
|
77
|
+
var d = DATA();
|
|
78
|
+
var u = (d.users && d.users[id]) || null;
|
|
79
|
+
var p = (d.profiles && d.profiles[id]) || null;
|
|
80
|
+
if (!u && !p) return '<i>Unbekannter User</i><div class="dht-popup-id">ID: <code>' + esc(id) + '</code></div>';
|
|
81
|
+
var name = (u && (u.displayName || u.username)) || (p && p.author) || 'Unknown';
|
|
82
|
+
var username = u && u.username ? '@' + u.username : '';
|
|
83
|
+
var avatar = (u && u.avatar) || (p && p.avatar) || '';
|
|
84
|
+
var color = (u && u.bannerColor) || (p && p.roleColor) || '#dbdee1';
|
|
85
|
+
var bot = (u && u.bot) || (p && p.bot);
|
|
86
|
+
var roles = u && Array.isArray(u.roles) && u.roles.length ? u.roles.map(rolePill).join('') : '';
|
|
87
|
+
var joinedAt = u && u.joinedAt ? fmt(u.joinedAt) : '';
|
|
88
|
+
var createdAt = u && u.createdAt ? fmt(u.createdAt) : '';
|
|
89
|
+
return ''
|
|
90
|
+
+ '<div class="dht-popup-head">'
|
|
91
|
+
+ (avatar ? '<img class="dht-popup-avatar" src="' + esc(avatar) + '" alt="">' : '<div class="dht-popup-avatar dht-popup-avatar--ph"></div>')
|
|
92
|
+
+ '<div class="dht-popup-headtext">'
|
|
93
|
+
+ '<div class="dht-popup-name" style="color:' + esc(color) + ';">' + esc(name) + (bot ? '<span class="dht-popup-badge">BOT</span>' : '') + '</div>'
|
|
94
|
+
+ (username ? '<div class="dht-popup-sub">' + esc(username) + '</div>' : '')
|
|
95
|
+
+ '</div>'
|
|
96
|
+
+ '</div>'
|
|
97
|
+
+ (roles ? '<div class="dht-popup-section"><div class="dht-popup-label">Rollen</div><div class="dht-popup-roles">' + roles + '</div></div>' : '')
|
|
98
|
+
+ (joinedAt ? '<div class="dht-popup-row"><span class="dht-popup-label">Server seit</span><span>' + esc(joinedAt) + '</span></div>' : '')
|
|
99
|
+
+ (createdAt ? '<div class="dht-popup-row"><span class="dht-popup-label">Account seit</span><span>' + esc(createdAt) + '</span></div>' : '')
|
|
100
|
+
+ '<div class="dht-popup-id">ID: <code>' + esc(id) + '</code></div>';
|
|
101
|
+
}
|
|
102
|
+
function roleHtml(id){
|
|
103
|
+
var r = (DATA().roles || {})[id];
|
|
104
|
+
if (!r) return '<i>Unbekannte Rolle</i><div class="dht-popup-id">ID: <code>' + esc(id) + '</code></div>';
|
|
105
|
+
var color = r.color || '#dbdee1';
|
|
106
|
+
return ''
|
|
107
|
+
+ '<div class="dht-popup-head">'
|
|
108
|
+
+ '<div class="dht-popup-rolemark" style="background:' + esc(color) + ';"></div>'
|
|
109
|
+
+ '<div class="dht-popup-headtext">'
|
|
110
|
+
+ '<div class="dht-popup-name" style="color:' + esc(color) + ';">@' + esc(r.name) + '</div>'
|
|
111
|
+
+ (r.color ? '<div class="dht-popup-sub"><code>' + esc(r.color) + '</code></div>' : '')
|
|
112
|
+
+ '</div>'
|
|
113
|
+
+ '</div>'
|
|
114
|
+
+ (r.memberCount != null ? '<div class="dht-popup-row"><span class="dht-popup-label">Mitglieder</span><span>' + esc(r.memberCount) + '</span></div>' : '')
|
|
115
|
+
+ (r.position != null ? '<div class="dht-popup-row"><span class="dht-popup-label">Position</span><span>' + esc(r.position) + '</span></div>' : '')
|
|
116
|
+
+ (r.hoist ? '<div class="dht-popup-row"><span class="dht-popup-label">Hoisted</span><span>Ja</span></div>' : '')
|
|
117
|
+
+ (r.mentionable ? '<div class="dht-popup-row"><span class="dht-popup-label">Erwähnbar</span><span>Ja</span></div>' : '')
|
|
118
|
+
+ (r.managed ? '<div class="dht-popup-row"><span class="dht-popup-label">Verwaltet</span><span>Bot/Integration</span></div>' : '')
|
|
119
|
+
+ '<div class="dht-popup-id">ID: <code>' + esc(id) + '</code></div>';
|
|
120
|
+
}
|
|
121
|
+
function channelHtml(id){
|
|
122
|
+
var c = (DATA().channels || {})[id];
|
|
123
|
+
if (!c) return '<i>Channel</i><div class="dht-popup-id">ID: <code>' + esc(id) + '</code></div>';
|
|
124
|
+
return ''
|
|
125
|
+
+ '<div class="dht-popup-head">'
|
|
126
|
+
+ '<div class="dht-popup-rolemark" style="background:#5865F2;">#</div>'
|
|
127
|
+
+ '<div class="dht-popup-headtext">'
|
|
128
|
+
+ '<div class="dht-popup-name">#' + esc(c.name) + '</div>'
|
|
129
|
+
+ (c.parent ? '<div class="dht-popup-sub">in ' + esc(c.parent) + '</div>' : '')
|
|
130
|
+
+ '</div>'
|
|
131
|
+
+ '</div>'
|
|
132
|
+
+ (c.topic ? '<div class="dht-popup-section"><div class="dht-popup-label">Topic</div><div>' + esc(c.topic) + '</div></div>' : '')
|
|
133
|
+
+ (c.nsfw ? '<div class="dht-popup-row"><span class="dht-popup-label">NSFW</span><span>Ja</span></div>' : '')
|
|
134
|
+
+ '<div class="dht-popup-id">ID: <code>' + esc(id) + '</code></div>';
|
|
135
|
+
}
|
|
136
|
+
document.addEventListener('click', function(e){
|
|
137
|
+
var el = e.target && e.target.closest ? e.target.closest('[data-mention-id]') : null;
|
|
138
|
+
if (!el) return;
|
|
139
|
+
var t = el.getAttribute('data-mention-type');
|
|
140
|
+
var id = el.getAttribute('data-mention-id');
|
|
141
|
+
if (!t || !id) return;
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
e.stopPropagation();
|
|
144
|
+
var html = '';
|
|
145
|
+
if (t === 'user') html = userHtml(id);
|
|
146
|
+
else if (t === 'role') html = roleHtml(id);
|
|
147
|
+
else if (t === 'channel') html = channelHtml(id);
|
|
148
|
+
else return;
|
|
149
|
+
showAt(el, html);
|
|
150
|
+
});
|
|
151
|
+
})();`;
|
|
152
|
+
|
|
153
|
+
// ============================================================================
|
|
154
|
+
// Mention popup styles
|
|
155
|
+
// ============================================================================
|
|
156
|
+
exports.mentionPopupStyles = `
|
|
157
|
+
.dht-mention { cursor: pointer; }
|
|
158
|
+
.dht-mention:hover { filter: brightness(1.15); }
|
|
159
|
+
.dht-popup {
|
|
160
|
+
position: fixed; display: none; max-width: 340px; min-width: 240px;
|
|
161
|
+
background: #1e1f22; color: #dbdee1; border: 1px solid #2b2d31;
|
|
162
|
+
border-radius: 10px; padding: 14px 16px 12px;
|
|
163
|
+
box-shadow: 0 12px 32px rgba(0,0,0,0.55), 0 2px 6px rgba(0,0,0,0.35);
|
|
164
|
+
font-family: "gg sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
165
|
+
font-size: 14px; line-height: 1.4; z-index: 99999;
|
|
166
|
+
}
|
|
167
|
+
.dht-popup-close {
|
|
168
|
+
position: absolute; top: 6px; right: 8px; background: none; border: none;
|
|
169
|
+
color: #949ba4; cursor: pointer; font-size: 20px; line-height: 1; padding: 4px 8px;
|
|
170
|
+
border-radius: 4px;
|
|
171
|
+
}
|
|
172
|
+
.dht-popup-close:hover { background: rgba(255,255,255,0.06); color: #dbdee1; }
|
|
173
|
+
.dht-popup-head { display: flex; align-items: center; gap: 10px; margin-right: 18px; }
|
|
174
|
+
.dht-popup-avatar { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; flex: none; }
|
|
175
|
+
.dht-popup-avatar--ph { background: #313338; }
|
|
176
|
+
.dht-popup-rolemark { width: 32px; height: 32px; border-radius: 8px; flex: none; display: flex; align-items: center; justify-content: center; font-weight: 700; color: #fff; }
|
|
177
|
+
.dht-popup-headtext { min-width: 0; }
|
|
178
|
+
.dht-popup-name { font-weight: 600; font-size: 16px; word-break: break-word; }
|
|
179
|
+
.dht-popup-sub { font-size: 12px; color: #949ba4; word-break: break-all; }
|
|
180
|
+
.dht-popup-badge { background: #5865F2; color: #fff; font-size: 10px; padding: 1px 6px; border-radius: 4px; margin-left: 6px; vertical-align: middle; }
|
|
181
|
+
.dht-popup-section { margin-top: 10px; }
|
|
182
|
+
.dht-popup-row { display: flex; justify-content: space-between; gap: 12px; margin-top: 6px; font-size: 13px; }
|
|
183
|
+
.dht-popup-label { color: #949ba4; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; }
|
|
184
|
+
.dht-popup-roles { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
|
|
185
|
+
.dht-role-pill {
|
|
186
|
+
display: inline-flex; align-items: center;
|
|
187
|
+
background: color-mix(in srgb, var(--dht-role) 18%, transparent);
|
|
188
|
+
color: var(--dht-role); border: 1px solid color-mix(in srgb, var(--dht-role) 40%, transparent);
|
|
189
|
+
padding: 1px 8px; border-radius: 999px; font-size: 11px; line-height: 1.5;
|
|
190
|
+
}
|
|
191
|
+
.dht-popup-id { margin-top: 10px; padding-top: 8px; border-top: 1px solid #2b2d31; font-size: 11px; color: #6e727a; }
|
|
192
|
+
.dht-popup-id code { background: rgba(255,255,255,0.05); padding: 1px 4px; border-radius: 3px; }
|
|
193
|
+
`;
|
|
194
|
+
|
|
195
|
+
// ============================================================================
|
|
196
|
+
// Back-to-top floating button
|
|
197
|
+
// ============================================================================
|
|
198
|
+
exports.backToTop = `(function(){
|
|
199
|
+
var btn = document.createElement('button');
|
|
200
|
+
btn.className = 'dht-back-top';
|
|
201
|
+
btn.type = 'button';
|
|
202
|
+
btn.title = 'Nach oben scrollen';
|
|
203
|
+
btn.setAttribute('aria-label','Nach oben scrollen');
|
|
204
|
+
btn.innerHTML = '▲';
|
|
205
|
+
document.body.appendChild(btn);
|
|
206
|
+
btn.addEventListener('click', function(){ window.scrollTo({ top: 0, behavior: 'smooth' }); });
|
|
207
|
+
function toggle(){ btn.style.opacity = window.scrollY > 400 ? '1' : '0'; btn.style.pointerEvents = window.scrollY > 400 ? 'auto' : 'none'; }
|
|
208
|
+
window.addEventListener('scroll', toggle, { passive: true });
|
|
209
|
+
toggle();
|
|
210
|
+
})();`;
|
|
211
|
+
|
|
212
|
+
exports.transcriptUtils = `
|
|
213
|
+
.dht-back-top {
|
|
214
|
+
position: fixed; bottom: 24px; right: 24px; width: 44px; height: 44px;
|
|
215
|
+
border-radius: 50%; border: none; background: #5865F2; color: #fff;
|
|
216
|
+
font-size: 16px; cursor: pointer; box-shadow: 0 6px 18px rgba(0,0,0,0.45);
|
|
217
|
+
opacity: 0; pointer-events: none; transition: opacity 0.2s ease, transform 0.2s ease;
|
|
218
|
+
z-index: 9999;
|
|
219
|
+
}
|
|
220
|
+
.dht-back-top:hover { transform: translateY(-2px); }
|
|
221
|
+
`;
|
|
222
|
+
|
|
30
223
|
exports.ggSansFont = '@font-face{src:url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/ggsans-normal-400.woff2);font-family:"gg sans";font-weight:400;font-style:normal}@font-face{src:url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/ggsans-normal-500.woff2);font-family:"gg sans";font-weight:500;font-style:normal}@font-face{src:url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/ggsans-normal-600.woff2);font-family:"gg sans";font-weight:600;font-style:normal}@font-face{src:url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/ggsans-normal-700.woff2);font-family:"gg sans";font-weight:700;font-style:normal}@font-face{src:url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/ggsans-normal-800.woff2);font-family:"gg sans";font-weight:800;font-style:normal}@font-face{src:url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/ggsans-italic-400.woff2);font-family:"gg sans";font-weight:400;font-style:italic}@font-face{src:url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/ggsans-italic-500.woff2);font-family:"gg sans";font-weight:500;font-style:italic}@font-face{src:url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/ggsans-italic-600.woff2);font-family:"gg sans";font-weight:600;font-style:italic}@font-face{src:url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/ggsans-italic-700.woff2);font-family:"gg sans";font-weight:700;font-style:italic}@font-face{src:url(https://cdn.jsdelivr.net/gh/Tyrrrz/DiscordFonts@master/ggsans-italic-800.woff2);font-family:"gg sans";font-weight:800;font-style:italic}';
|
|
31
|
-
//# sourceMappingURL=client.js.map
|
|
224
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -1,32 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildProfiles = buildProfiles;
|
|
4
|
+
exports.buildExtendedContext = buildExtendedContext;
|
|
4
5
|
const discord_js_1 = require("discord.js");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Original profile builder — kept for backwards compatibility with
|
|
9
|
+
* <discord-mention> web component lookups via window.$discordMessage.profiles.
|
|
10
|
+
*/
|
|
5
11
|
async function buildProfiles(messages) {
|
|
6
12
|
const profiles = {};
|
|
7
|
-
// loop through messages
|
|
8
13
|
for (const message of messages) {
|
|
9
|
-
// add all users
|
|
10
14
|
const author = message.author;
|
|
11
|
-
if (!profiles[author.id]) {
|
|
12
|
-
// add profile
|
|
15
|
+
if (author && !profiles[author.id]) {
|
|
13
16
|
profiles[author.id] = buildProfile(message.member, author);
|
|
14
17
|
}
|
|
15
|
-
// add interaction users
|
|
16
18
|
if (message.interaction) {
|
|
17
19
|
const user = message.interaction.user;
|
|
18
|
-
if (!profiles[user.id]) {
|
|
20
|
+
if (user && !profiles[user.id]) {
|
|
19
21
|
profiles[user.id] = buildProfile(null, user);
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
|
-
|
|
23
|
-
if (message.thread && message.thread.lastMessage) {
|
|
24
|
+
if (message.thread && message.thread.lastMessage && message.thread.lastMessage.author) {
|
|
24
25
|
profiles[message.thread.lastMessage.author.id] = buildProfile(message.thread.lastMessage.member, message.thread.lastMessage.author);
|
|
25
26
|
}
|
|
27
|
+
// Also include users mentioned in messages — so popups work even if they never spoke
|
|
28
|
+
if (message.mentions && message.mentions.users) {
|
|
29
|
+
for (const [uid, u] of message.mentions.users) {
|
|
30
|
+
if (!profiles[uid]) {
|
|
31
|
+
const member = message.mentions.members && message.mentions.members.get
|
|
32
|
+
? message.mentions.members.get(uid)
|
|
33
|
+
: null;
|
|
34
|
+
profiles[uid] = buildProfile(member, u);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
26
38
|
}
|
|
27
|
-
// return as a JSON
|
|
28
39
|
return profiles;
|
|
29
40
|
}
|
|
41
|
+
|
|
30
42
|
function buildProfile(member, author) {
|
|
31
43
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
32
44
|
return {
|
|
@@ -39,4 +51,112 @@ function buildProfile(member, author) {
|
|
|
39
51
|
verified: (_h = author.flags) === null || _h === void 0 ? void 0 : _h.has(discord_js_1.UserFlags.VerifiedBot),
|
|
40
52
|
};
|
|
41
53
|
}
|
|
42
|
-
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Extended dictionaries used by the mention-popup client script.
|
|
57
|
+
* Contains:
|
|
58
|
+
* users: full user/member details (avatar, banner color, all roles, joinedAt, createdAt, flags)
|
|
59
|
+
* roles: all roles referenced by any rendered user OR mentioned in messages
|
|
60
|
+
* channels: channel info (name, parent category, topic, type)
|
|
61
|
+
*/
|
|
62
|
+
async function buildExtendedContext(messages, channel) {
|
|
63
|
+
const users = {};
|
|
64
|
+
const roles = {};
|
|
65
|
+
const channels = {};
|
|
66
|
+
const guild = channel && !channel.isDMBased && !channel.isDMBased() ? channel.guild : null;
|
|
67
|
+
|
|
68
|
+
const seenUserIds = new Set();
|
|
69
|
+
const seenRoleIds = new Set();
|
|
70
|
+
const seenChannelIds = new Set();
|
|
71
|
+
|
|
72
|
+
const includeRoleFromGuild = (roleId) => {
|
|
73
|
+
if (!guild || seenRoleIds.has(roleId)) return;
|
|
74
|
+
const role = guild.roles.cache.get(roleId);
|
|
75
|
+
if (!role) return;
|
|
76
|
+
seenRoleIds.add(roleId);
|
|
77
|
+
roles[roleId] = {
|
|
78
|
+
id: role.id,
|
|
79
|
+
name: role.name,
|
|
80
|
+
color: role.hexColor && role.hexColor !== '#000000' ? role.hexColor : null,
|
|
81
|
+
position: role.position,
|
|
82
|
+
hoist: role.hoist,
|
|
83
|
+
mentionable: role.mentionable,
|
|
84
|
+
memberCount: typeof role.members?.size === 'number' ? role.members.size : null,
|
|
85
|
+
managed: role.managed,
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const includeChannelFromGuild = (channelId) => {
|
|
90
|
+
if (!guild || seenChannelIds.has(channelId)) return;
|
|
91
|
+
const ch = guild.channels.cache.get(channelId);
|
|
92
|
+
if (!ch) return;
|
|
93
|
+
seenChannelIds.add(channelId);
|
|
94
|
+
channels[channelId] = {
|
|
95
|
+
id: ch.id,
|
|
96
|
+
name: ch.name,
|
|
97
|
+
type: ch.type,
|
|
98
|
+
parent: ch.parent ? ch.parent.name : null,
|
|
99
|
+
topic: 'topic' in ch && typeof ch.topic === 'string' ? ch.topic : null,
|
|
100
|
+
nsfw: 'nsfw' in ch ? !!ch.nsfw : false,
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const includeUser = (member, author) => {
|
|
105
|
+
if (!author || seenUserIds.has(author.id)) return;
|
|
106
|
+
seenUserIds.add(author.id);
|
|
107
|
+
const memberRoles = member && member.roles && member.roles.cache
|
|
108
|
+
? Array.from(member.roles.cache.values()).filter((r) => r.id !== guild?.id)
|
|
109
|
+
.sort((a, b) => b.position - a.position)
|
|
110
|
+
: [];
|
|
111
|
+
// include role records as well
|
|
112
|
+
for (const r of memberRoles) includeRoleFromGuild(r.id);
|
|
113
|
+
users[author.id] = {
|
|
114
|
+
id: author.id,
|
|
115
|
+
username: author.username,
|
|
116
|
+
displayName: member?.nickname || author.displayName || author.username,
|
|
117
|
+
globalName: author.globalName || null,
|
|
118
|
+
avatar: member?.displayAvatarURL?.({ size: 128 }) || author.displayAvatarURL?.({ size: 128 }) || null,
|
|
119
|
+
bannerColor: member?.displayHexColor && member.displayHexColor !== '#000000' ? member.displayHexColor : null,
|
|
120
|
+
bot: !!author.bot,
|
|
121
|
+
system: !!author.system,
|
|
122
|
+
joinedAt: member?.joinedAt ? member.joinedAt.toISOString() : null,
|
|
123
|
+
createdAt: author.createdAt ? author.createdAt.toISOString() : null,
|
|
124
|
+
roles: memberRoles.map((r) => ({
|
|
125
|
+
id: r.id,
|
|
126
|
+
name: r.name,
|
|
127
|
+
color: r.hexColor && r.hexColor !== '#000000' ? r.hexColor : null,
|
|
128
|
+
position: r.position,
|
|
129
|
+
})),
|
|
130
|
+
highestRole: memberRoles[0]
|
|
131
|
+
? { name: memberRoles[0].name, color: memberRoles[0].hexColor }
|
|
132
|
+
: null,
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
for (const message of messages) {
|
|
137
|
+
if (message.author) includeUser(message.member, message.author);
|
|
138
|
+
if (message.interaction?.user) includeUser(null, message.interaction.user);
|
|
139
|
+
if (message.thread?.lastMessage?.author) {
|
|
140
|
+
includeUser(message.thread.lastMessage.member, message.thread.lastMessage.author);
|
|
141
|
+
}
|
|
142
|
+
if (message.mentions) {
|
|
143
|
+
if (message.mentions.users) {
|
|
144
|
+
for (const [uid, u] of message.mentions.users) {
|
|
145
|
+
const member = message.mentions.members && message.mentions.members.get
|
|
146
|
+
? message.mentions.members.get(uid)
|
|
147
|
+
: null;
|
|
148
|
+
includeUser(member, u);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (message.mentions.roles) {
|
|
152
|
+
for (const [rid] of message.mentions.roles) includeRoleFromGuild(rid);
|
|
153
|
+
}
|
|
154
|
+
if (message.mentions.channels) {
|
|
155
|
+
for (const [cid] of message.mentions.channels) includeChannelFromGuild(cid);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { users, roles, channels };
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=buildProfiles.js.map
|
package/dist/utils/utils.js
CHANGED
|
@@ -27,13 +27,21 @@ function formatBytes(bytes, decimals = 2) {
|
|
|
27
27
|
return [parseFloat((bytes / Math.pow(k, i)).toFixed(dm)), sizes[i]];
|
|
28
28
|
}
|
|
29
29
|
function parseDiscordEmoji(emoji) {
|
|
30
|
+
if (!emoji) return '';
|
|
30
31
|
if (emoji.id) {
|
|
31
32
|
return `https://cdn.discordapp.com/emojis/${emoji.id}.${emoji.animated ? 'gif' : 'png'}`;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// Defensive: emoji.name may be null for deleted/unknown reactions
|
|
35
|
+
if (typeof emoji.name !== 'string' || !emoji.name) return '';
|
|
36
|
+
try {
|
|
37
|
+
const codepoints = twemoji_1.default.convert
|
|
38
|
+
.toCodePoint(emoji.name.indexOf(String.fromCharCode(0x200d)) < 0 ? emoji.name.replace(/\uFE0F/g, '') : emoji.name)
|
|
39
|
+
.toLowerCase();
|
|
40
|
+
return `https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/svg/${codepoints}.svg`;
|
|
41
|
+
}
|
|
42
|
+
catch (_e) {
|
|
43
|
+
return '';
|
|
44
|
+
}
|
|
37
45
|
}
|
|
38
46
|
/**
|
|
39
47
|
* Converts a stream to a string
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discord-html-transcripts-fix",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
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",
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
"@types/react-dom": "^19.2.3",
|
|
20
20
|
"@typescript-eslint/eslint-plugin": "^8.50.0",
|
|
21
21
|
"@typescript-eslint/parser": "^8.50.0",
|
|
22
|
-
"debug": "^4.4.3",
|
|
23
22
|
"discord.js": "^14.25.1",
|
|
24
23
|
"dotenv": "^17.2.3",
|
|
25
24
|
"eslint": "^9.39.2",
|
|
@@ -34,16 +33,17 @@
|
|
|
34
33
|
"dependencies": {
|
|
35
34
|
"@lit-labs/ssr": "^3.3.1",
|
|
36
35
|
"@skyra/discord-components-core": "^4.0.2",
|
|
36
|
+
"debug": "^4.4.3",
|
|
37
37
|
"discord-markdown-parser": "~1.3.0",
|
|
38
38
|
"highlight.js": "^11.11.1",
|
|
39
|
+
"react": "^19.2.3",
|
|
40
|
+
"react-dom": "^19.2.3",
|
|
39
41
|
"simple-markdown": "^0.7.3",
|
|
40
42
|
"twemoji": "^14.0.2",
|
|
41
43
|
"undici": "^7.16.0"
|
|
42
44
|
},
|
|
43
45
|
"peerDependencies": {
|
|
44
|
-
"discord.js": "^14.0.0 || ^15.0.0"
|
|
45
|
-
"react": "^19.2.3",
|
|
46
|
-
"react-dom": "^19.2.3"
|
|
46
|
+
"discord.js": "^14.0.0 || ^15.0.0"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"test": "echo \"Error: no test specified\" && exit 1",
|