discord-html-transcripts-fix 1.1.0 โ 1.2.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 +86 -78
- package/dist/downloader/images.js +60 -85
- package/dist/generator/index.js +194 -43
- package/dist/generator/renderers/attachment.js +27 -36
- package/dist/generator/renderers/components/Container.js +36 -14
- package/dist/generator/renderers/components/Media Gallery.js +24 -18
- package/dist/generator/renderers/components.js +7 -2
- package/dist/generator/renderers/content.js +48 -48
- package/dist/generator/renderers/embed.js +55 -34
- package/dist/generator/renderers/message.js +159 -46
- package/dist/generator/renderers/reply.js +47 -39
- package/dist/generator/renderers/systemMessage.js +131 -70
- package/dist/generator/transcript.js +42 -56
- package/dist/index.js +59 -84
- package/dist/static/client.js +216 -208
- package/dist/types.js +1 -0
- package/dist/utils/buildProfiles.js +51 -59
- package/dist/utils/utils.js +73 -26
- package/package.json +1 -1
|
@@ -19,56 +19,52 @@ var RenderType;
|
|
|
19
19
|
RenderType[RenderType["NORMAL"] = 2] = "NORMAL";
|
|
20
20
|
RenderType[RenderType["WEBHOOK"] = 3] = "WEBHOOK";
|
|
21
21
|
})(RenderType || (exports.RenderType = RenderType = {}));
|
|
22
|
-
|
|
23
|
-
* Renders discord markdown content
|
|
24
|
-
* @param content - The content to render
|
|
25
|
-
* @param context - The context to render the content in
|
|
26
|
-
* @returns
|
|
27
|
-
*/
|
|
22
|
+
|
|
28
23
|
async function MessageContent({ content, context }) {
|
|
29
24
|
if (context.type === RenderType.REPLY && content.length > 180)
|
|
30
25
|
content = content.slice(0, 180) + '...';
|
|
31
|
-
// parse the markdown
|
|
32
26
|
const parsed = (0, discord_markdown_parser_1.default)(content, context.type === RenderType.EMBED || context.type === RenderType.WEBHOOK ? 'extended' : 'normal');
|
|
33
|
-
// check if the parsed content is only emojis
|
|
34
27
|
const isOnlyEmojis = parsed.every((node) => ['emoji', 'twemoji'].includes(node.type) || (node.type === 'text' && node.content.trim().length === 0));
|
|
35
28
|
if (isOnlyEmojis) {
|
|
36
|
-
// now check if there are less than or equal to 25 emojis
|
|
37
29
|
const emojis = parsed.filter((node) => ['emoji', 'twemoji'].includes(node.type));
|
|
38
30
|
if (emojis.length <= 25) {
|
|
39
|
-
context._internal = {
|
|
40
|
-
largeEmojis: true,
|
|
41
|
-
};
|
|
31
|
+
context._internal = { largeEmojis: true };
|
|
42
32
|
}
|
|
43
33
|
}
|
|
44
34
|
return (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: parsed, context: context });
|
|
45
35
|
}
|
|
46
|
-
|
|
47
|
-
async function MessageASTNodes({ nodes, context
|
|
36
|
+
|
|
37
|
+
async function MessageASTNodes({ nodes, context }) {
|
|
48
38
|
if (Array.isArray(nodes)) {
|
|
49
39
|
return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: nodes.map((node, i) => ((0, jsx_runtime_1.jsx)(MessageSingleASTNode, { node: node, context: context }, i))) }));
|
|
50
40
|
}
|
|
51
|
-
|
|
52
|
-
return (0, jsx_runtime_1.jsx)(MessageSingleASTNode, { node: nodes, context: context });
|
|
53
|
-
}
|
|
41
|
+
return (0, jsx_runtime_1.jsx)(MessageSingleASTNode, { node: nodes, context: context });
|
|
54
42
|
}
|
|
43
|
+
|
|
55
44
|
async function MessageSingleASTNode({ node, context }) {
|
|
56
|
-
|
|
57
|
-
if (!node)
|
|
58
|
-
return null;
|
|
45
|
+
if (!node) return null;
|
|
59
46
|
try {
|
|
60
47
|
return await renderASTNode(node, context);
|
|
61
|
-
}
|
|
62
|
-
|
|
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
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.warn('[discord-html-transcripts-fix] AST node render failed:', err && err.message ? err.message : err);
|
|
65
50
|
try {
|
|
66
51
|
if (typeof node.content === 'string') return node.content;
|
|
67
52
|
if (Array.isArray(node.content)) return (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context });
|
|
68
|
-
} catch (_e) {
|
|
53
|
+
} catch (_e) {}
|
|
69
54
|
return null;
|
|
70
55
|
}
|
|
71
56
|
}
|
|
57
|
+
|
|
58
|
+
// Detect Discord "guide" pseudo-channels: <id:guide>, <id:browse>, <id:customize>, <id:welcome>, <id:linked-roles>
|
|
59
|
+
const GUIDE_LABELS = {
|
|
60
|
+
'guide': 'Server Guide',
|
|
61
|
+
'browse': 'Channels & Roles',
|
|
62
|
+
'customize': 'Customize',
|
|
63
|
+
'welcome': 'Welcome',
|
|
64
|
+
'linked-roles': 'Linked Roles',
|
|
65
|
+
'home': 'Home',
|
|
66
|
+
};
|
|
67
|
+
|
|
72
68
|
async function renderASTNode(node, context) {
|
|
73
69
|
var _a, _b;
|
|
74
70
|
const type = node.type;
|
|
@@ -76,10 +72,10 @@ async function renderASTNode(node, context) {
|
|
|
76
72
|
case 'text':
|
|
77
73
|
return node.content;
|
|
78
74
|
case 'link':
|
|
79
|
-
return ((0, jsx_runtime_1.jsx)("discord-link", { href: node.target, children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
|
|
75
|
+
return ((0, jsx_runtime_1.jsx)("discord-link", { href: (0, utils_1.safeHref)(node.target), children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
|
|
80
76
|
case 'url':
|
|
81
77
|
case 'autolink':
|
|
82
|
-
return ((0, jsx_runtime_1.jsx)("discord-link", { href: node.target, target: "_blank", rel: "noreferrer", children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
|
|
78
|
+
return ((0, jsx_runtime_1.jsx)("discord-link", { href: (0, utils_1.safeHref)(node.target), target: "_blank", rel: "noreferrer", children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
|
|
83
79
|
case 'blockQuote':
|
|
84
80
|
if (context.type === RenderType.REPLY) {
|
|
85
81
|
return (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context });
|
|
@@ -87,24 +83,35 @@ async function renderASTNode(node, context) {
|
|
|
87
83
|
return ((0, jsx_runtime_1.jsx)("discord-quote", { children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
|
|
88
84
|
case 'br':
|
|
89
85
|
case 'newline':
|
|
90
|
-
if (context.type === RenderType.REPLY)
|
|
91
|
-
return ' ';
|
|
86
|
+
if (context.type === RenderType.REPLY) return ' ';
|
|
92
87
|
return (0, jsx_runtime_1.jsx)("br", {});
|
|
93
88
|
case 'channel': {
|
|
94
89
|
const id = node.id;
|
|
90
|
+
// Discord pseudo channels (<id:guide>, <id:browse>, โฆ) come through with letters as id
|
|
91
|
+
if (typeof id === 'string' && /^[a-z][a-z-]*$/.test(id)) {
|
|
92
|
+
const label = GUIDE_LABELS[id] || id;
|
|
93
|
+
return ((0, jsx_runtime_1.jsx)("discord-mention", { type: "channel", className: "dht-mention", children: label }));
|
|
94
|
+
}
|
|
95
95
|
const channel = await context.callbacks.resolveChannel(id);
|
|
96
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}>` }));
|
|
97
97
|
}
|
|
98
98
|
case 'role': {
|
|
99
99
|
const id = node.id;
|
|
100
100
|
const role = await context.callbacks.resolveRole(id);
|
|
101
|
-
|
|
101
|
+
const color = role?.hexColor ? (0, utils_1.safeColor)(role.hexColor, undefined) : undefined;
|
|
102
|
+
return ((0, jsx_runtime_1.jsx)("discord-mention", { type: "role", color: context.type === RenderType.REPLY ? undefined : color, "data-mention-type": "role", "data-mention-id": id, className: "dht-mention", children: role ? role.name : `<@&${id}>` }));
|
|
102
103
|
}
|
|
103
104
|
case 'user': {
|
|
104
105
|
const id = node.id;
|
|
105
106
|
const user = await context.callbacks.resolveUser(id);
|
|
106
107
|
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}>` });
|
|
107
108
|
}
|
|
109
|
+
case 'slashCommand':
|
|
110
|
+
case 'command': {
|
|
111
|
+
// </name:id> renders as blue pill like Discord
|
|
112
|
+
const name = node.name || node.content || 'command';
|
|
113
|
+
return ((0, jsx_runtime_1.jsx)("discord-mention", { type: "command", className: "dht-mention dht-mention--cmd", children: '/' + String(name) }));
|
|
114
|
+
}
|
|
108
115
|
case 'here':
|
|
109
116
|
case 'everyone':
|
|
110
117
|
return ((0, jsx_runtime_1.jsx)("discord-mention", { type: 'role', highlight: true, children: `@${type}` }));
|
|
@@ -124,53 +131,46 @@ async function renderASTNode(node, context) {
|
|
|
124
131
|
case 'strikethrough':
|
|
125
132
|
return ((0, jsx_runtime_1.jsx)("s", { children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
|
|
126
133
|
case 'emoticon':
|
|
127
|
-
return typeof node.content === 'string' ?
|
|
134
|
+
return typeof node.content === 'string' ? node.content : ((0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }));
|
|
128
135
|
case 'spoiler':
|
|
129
136
|
return ((0, jsx_runtime_1.jsx)("discord-spoiler", { children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
|
|
130
137
|
case 'emoji':
|
|
131
138
|
case 'twemoji':
|
|
132
139
|
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 }));
|
|
133
140
|
case 'timestamp': {
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
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;
|
|
141
|
+
// Defensive: invalid <t:...> markers must not throw
|
|
142
|
+
const raw = node.timestamp;
|
|
143
|
+
let dateMs = NaN;
|
|
140
144
|
if (typeof raw === 'number') {
|
|
141
145
|
dateMs = raw < 1e12 ? raw * 1000 : raw;
|
|
142
146
|
} else if (typeof raw === 'string') {
|
|
143
|
-
|
|
147
|
+
const n = parseInt(raw, 10);
|
|
144
148
|
if (Number.isFinite(n)) dateMs = n < 1e12 ? n * 1000 : n;
|
|
145
149
|
}
|
|
146
|
-
|
|
147
|
-
|
|
150
|
+
const dt = new Date(dateMs);
|
|
151
|
+
let iso = '';
|
|
148
152
|
try { iso = Number.isFinite(dt.getTime()) ? dt.toISOString() : ''; } catch (_e) { iso = ''; }
|
|
149
153
|
return (0, jsx_runtime_1.jsx)("discord-time", { children: iso });
|
|
150
154
|
}
|
|
151
|
-
// return <DiscordTime timestamp={parseInt(node.timestamp) * 1000} format={node.format} />;
|
|
152
155
|
default: {
|
|
153
|
-
|
|
154
|
-
return typeof node.content === 'string' ? (node.content) : ((0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }));
|
|
156
|
+
return typeof node.content === 'string' ? node.content : ((0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }));
|
|
155
157
|
}
|
|
156
158
|
}
|
|
157
159
|
}
|
|
160
|
+
|
|
158
161
|
function getChannelType(channelType) {
|
|
159
162
|
switch (channelType) {
|
|
160
163
|
case discord_js_1.ChannelType.GuildCategory:
|
|
161
164
|
case discord_js_1.ChannelType.GuildAnnouncement:
|
|
162
165
|
case discord_js_1.ChannelType.GuildText:
|
|
163
166
|
case discord_js_1.ChannelType.DM:
|
|
164
|
-
case discord_js_1.ChannelType.GroupDM:
|
|
165
|
-
case discord_js_1.ChannelType.GuildDirectory:
|
|
166
|
-
case discord_js_1.ChannelType.GuildMedia:
|
|
167
167
|
return 'channel';
|
|
168
168
|
case discord_js_1.ChannelType.GuildVoice:
|
|
169
169
|
case discord_js_1.ChannelType.GuildStageVoice:
|
|
170
170
|
return 'voice';
|
|
171
|
+
case discord_js_1.ChannelType.AnnouncementThread:
|
|
171
172
|
case discord_js_1.ChannelType.PublicThread:
|
|
172
173
|
case discord_js_1.ChannelType.PrivateThread:
|
|
173
|
-
case discord_js_1.ChannelType.AnnouncementThread:
|
|
174
174
|
return 'thread';
|
|
175
175
|
case discord_js_1.ChannelType.GuildForum:
|
|
176
176
|
return 'forum';
|
|
@@ -178,4 +178,4 @@ function getChannelType(channelType) {
|
|
|
178
178
|
return 'channel';
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
-
//# sourceMappingURL=content.js.map
|
|
181
|
+
//# sourceMappingURL=content.js.map
|
|
@@ -1,44 +1,65 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
2
|
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
3
|
+
var ownKeys = function(o){ownKeys = Object.getOwnPropertyNames || function (o){var ar=[];for (var k in o) if (Object.prototype.hasOwnProperty.call(o,k)) ar[ar.length]=k;return ar;};return ownKeys(o);};
|
|
4
|
+
return function (mod){if (mod && mod.__esModule) return mod;var result={};if (mod != null) for (var k=ownKeys(mod), i=0; i<k.length; i++) if (k[i] !== "default") Object.defineProperty(result,k[i],{get:function(){return mod[k[i]];}});Object.defineProperty(result,"default",{value:mod,enumerable:true});return result;};
|
|
34
5
|
})();
|
|
35
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
7
|
exports.DiscordEmbed = DiscordEmbed;
|
|
37
8
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
9
|
const embeds_1 = require("../../utils/embeds");
|
|
39
10
|
const content_1 = __importStar(require("./content"));
|
|
11
|
+
const utils_1 = require("../../utils/utils");
|
|
12
|
+
|
|
40
13
|
async function DiscordEmbed({ embed, context }) {
|
|
41
|
-
|
|
42
|
-
|
|
14
|
+
const title = embed.title ?? undefined;
|
|
15
|
+
const authorImage = embed.author?.proxyIconURL ?? embed.author?.iconURL;
|
|
16
|
+
const authorName = embed.author?.name;
|
|
17
|
+
const authorUrl = embed.author?.url ? (0, utils_1.safeHref)(embed.author.url) : undefined;
|
|
18
|
+
const color = embed.hexColor ? (0, utils_1.safeColor)(embed.hexColor, undefined) : undefined;
|
|
19
|
+
const image = embed.image?.proxyURL ?? embed.image?.url;
|
|
20
|
+
const thumb = embed.thumbnail?.proxyURL ?? embed.thumbnail?.url;
|
|
21
|
+
const url = embed.url ? (0, utils_1.safeHref)(embed.url) : undefined;
|
|
22
|
+
const provider = embed.provider?.name;
|
|
23
|
+
const video = embed.video?.url ? (0, utils_1.safeHref)(embed.video.url) : null;
|
|
24
|
+
|
|
25
|
+
return ((0, jsx_runtime_1.jsxs)("discord-embed", {
|
|
26
|
+
"embed-title": title,
|
|
27
|
+
slot: "embeds",
|
|
28
|
+
"author-image": authorImage,
|
|
29
|
+
"author-name": authorName,
|
|
30
|
+
"author-url": authorUrl,
|
|
31
|
+
color: color,
|
|
32
|
+
image: image,
|
|
33
|
+
thumbnail: thumb,
|
|
34
|
+
url: url,
|
|
35
|
+
children: [
|
|
36
|
+
provider && ((0, jsx_runtime_1.jsx)("div", { className: "dht-embed-provider", slot: "author", children: provider })),
|
|
37
|
+
embed.description && ((0, jsx_runtime_1.jsx)("discord-embed-description", {
|
|
38
|
+
slot: "description",
|
|
39
|
+
children: (0, jsx_runtime_1.jsx)(content_1.default, { content: embed.description, context: Object.assign({}, context, { type: content_1.RenderType.EMBED }) })
|
|
40
|
+
})),
|
|
41
|
+
embed.fields.length > 0 && ((0, jsx_runtime_1.jsx)("discord-embed-fields", {
|
|
42
|
+
slot: "fields",
|
|
43
|
+
children: embed.fields.map((field, id) => ((0, jsx_runtime_1.jsx)(EmbedField, { embed, field, id, context }, `${context.message.id}-e-${context.index}-f-${id}`)))
|
|
44
|
+
})),
|
|
45
|
+
video && ((0, jsx_runtime_1.jsx)("div", { className: "dht-embed-video", children: (0, jsx_runtime_1.jsx)("a", { href: video, target: "_blank", rel: "noreferrer", children: 'โถ Open video' }) })),
|
|
46
|
+
embed.footer && ((0, jsx_runtime_1.jsx)("discord-embed-footer", {
|
|
47
|
+
slot: "footer",
|
|
48
|
+
footerImage: embed.footer.proxyIconURL ?? embed.footer.iconURL,
|
|
49
|
+
timestamp: embed.timestamp ?? undefined,
|
|
50
|
+
children: embed.footer.text
|
|
51
|
+
}))
|
|
52
|
+
]
|
|
53
|
+
}, `${context.message.id}-e-${context.index}`));
|
|
43
54
|
}
|
|
44
|
-
|
|
55
|
+
|
|
56
|
+
// Extracted as proper async component (was inline async arrow inside .map() โ undefined React behavior)
|
|
57
|
+
async function EmbedField({ embed, field, id, context }) {
|
|
58
|
+
return (0, jsx_runtime_1.jsx)("discord-embed-field", {
|
|
59
|
+
"field-title": field.name,
|
|
60
|
+
inline: !!field.inline,
|
|
61
|
+
"inline-index": field.inline ? (0, embeds_1.calculateInlineIndex)(embed.fields, id) : undefined,
|
|
62
|
+
children: (0, jsx_runtime_1.jsx)(content_1.default, { content: field.value, context: Object.assign({}, context, { type: content_1.RenderType.EMBED }) })
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=embed.js.map
|
|
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.default = DiscordMessage;
|
|
40
40
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
41
|
+
const discord_js_1 = require("discord.js");
|
|
41
42
|
const utils_1 = require("../../utils/utils");
|
|
42
43
|
const attachment_1 = require("./attachment");
|
|
43
44
|
const components_1 = __importDefault(require("./components"));
|
|
@@ -45,12 +46,21 @@ const content_1 = __importStar(require("./content"));
|
|
|
45
46
|
const embed_1 = require("./embed");
|
|
46
47
|
const reply_1 = __importDefault(require("./reply"));
|
|
47
48
|
const systemMessage_1 = __importDefault(require("./systemMessage"));
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
|
|
50
|
+
const FLAG_SUPPRESS_EMBEDS = 1 << 2;
|
|
51
|
+
const FLAG_IS_VOICE_MESSAGE = 1 << 13;
|
|
52
|
+
|
|
53
|
+
function t(context, key, fallback) {
|
|
54
|
+
const dict = context?.i18n?.[context?.lang] || context?.i18n?.en || {};
|
|
55
|
+
return dict[key] || fallback || key;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function DiscordMessage({ message, context }) {
|
|
50
59
|
try {
|
|
51
60
|
if (message.system)
|
|
52
|
-
return (0, jsx_runtime_1.jsx)(systemMessage_1.default, { message: message });
|
|
53
|
-
|
|
61
|
+
return (0, jsx_runtime_1.jsx)(systemMessage_1.default, { message: message, context: context });
|
|
62
|
+
|
|
63
|
+
const isCrosspost = message.reference && message.reference.guildId !== message.guild?.id;
|
|
54
64
|
const stickers = message.stickers && message.stickers.size > 0
|
|
55
65
|
? Array.from(message.stickers.values()).map((s) => ({
|
|
56
66
|
id: s.id,
|
|
@@ -60,36 +70,93 @@ async function DiscordMessage({ message, context, }) {
|
|
|
60
70
|
}))
|
|
61
71
|
: [];
|
|
62
72
|
const poll = (message.poll && (message.poll.question || (message.poll.answers && message.poll.answers.size))) ? message.poll : null;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
73
|
+
|
|
74
|
+
const flags = (typeof message.flags?.bitfield === 'number') ? message.flags.bitfield : 0;
|
|
75
|
+
const suppressEmbeds = !!(flags & FLAG_SUPPRESS_EMBEDS);
|
|
76
|
+
const isVoice = !!(flags & FLAG_IS_VOICE_MESSAGE);
|
|
77
|
+
|
|
78
|
+
// Forwarded message snapshots (messageSnapshots) โ render before the empty body
|
|
79
|
+
const snapshots = Array.isArray(message.messageSnapshots) ? message.messageSnapshots : [];
|
|
80
|
+
|
|
81
|
+
const isApp = !!(message.author?.bot && message.applicationId);
|
|
82
|
+
|
|
83
|
+
const editedAtIso = message.editedAt instanceof Date ? message.editedAt.toISOString() : null;
|
|
84
|
+
const pinned = !!message.pinned;
|
|
85
|
+
|
|
86
|
+
return ((0, jsx_runtime_1.jsxs)("discord-message", {
|
|
87
|
+
id: `m-${message.id}`,
|
|
88
|
+
timestamp: message.createdAt,
|
|
89
|
+
edited: message.editedAt !== null,
|
|
90
|
+
server: isCrosspost ?? undefined,
|
|
91
|
+
highlight: message.mentions.everyone || pinned,
|
|
92
|
+
profile: message.author.id,
|
|
93
|
+
children: [
|
|
94
|
+
pinned && (0, jsx_runtime_1.jsx)("div", { className: "dht-pinned-indicator", title: "Pinned", children: "๐" }),
|
|
95
|
+
(0, jsx_runtime_1.jsx)(reply_1.default, { message: message, context: context }),
|
|
96
|
+
message.interaction && ((0, jsx_runtime_1.jsx)("discord-command", { slot: "reply", profile: message.interaction.user.id, command: '/' + message.interaction.commandName })),
|
|
97
|
+
isApp && (0, jsx_runtime_1.jsx)("span", { className: "dht-app-badge", "data-i18n": "appBadge", children: t(context, 'appBadge', 'APP') }),
|
|
98
|
+
editedAtIso && (0, jsx_runtime_1.jsx)("span", { className: "dht-edit-marker", title: editedAtIso, "data-edit-iso": editedAtIso, "data-i18n": "edited", "data-i18n-params": JSON.stringify({ time: editedAtIso }), children: '(' + t(context, 'edited', 'edited') + ')' }),
|
|
99
|
+
Array.isArray(message.editHistory) && message.editHistory.length > 0 && renderEditHistory(message.editHistory, context),
|
|
100
|
+
snapshots.length > 0 && renderSnapshots(snapshots, context),
|
|
101
|
+
message.content && ((0, jsx_runtime_1.jsx)(content_1.default, { content: message.content, context: Object.assign({}, context, { type: message.webhookId ? content_1.RenderType.WEBHOOK : content_1.RenderType.NORMAL }) })),
|
|
102
|
+
(0, jsx_runtime_1.jsx)(attachment_1.Attachments, { message: message, context: context }),
|
|
103
|
+
isVoice && renderVoiceIndicator(message, context),
|
|
104
|
+
stickers.length > 0 && renderStickers(stickers),
|
|
105
|
+
poll && renderPoll(poll, context),
|
|
106
|
+
!suppressEmbeds && message.embeds.map((embed, id) => ((0, jsx_runtime_1.jsx)(embed_1.DiscordEmbed, { embed: embed, context: Object.assign({}, context, { index: id, message }) }, id))),
|
|
107
|
+
suppressEmbeds && message.embeds.length > 0 && (0, jsx_runtime_1.jsx)("span", { className: "dht-suppressed", "data-i18n": "suppressedEmbeds", children: t(context, 'suppressedEmbeds', '(embeds hidden)') }),
|
|
108
|
+
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))) })),
|
|
109
|
+
message.reactions.cache.size > 0 && ((0, jsx_runtime_1.jsx)("discord-reactions", {
|
|
110
|
+
slot: "reactions",
|
|
111
|
+
children: Array.from(message.reactions.cache.values()).map((reaction, id) => {
|
|
112
|
+
const burst = reaction.burst_count > 0 || (reaction.count_details?.burst > 0);
|
|
113
|
+
return ((0, jsx_runtime_1.jsx)("discord-reaction", {
|
|
114
|
+
name: reaction.emoji?.name || ':unknown:',
|
|
115
|
+
emoji: (0, utils_1.parseDiscordEmoji)(reaction.emoji) || undefined,
|
|
116
|
+
count: reaction.count ?? 0,
|
|
117
|
+
"data-burst": burst ? 'true' : undefined,
|
|
118
|
+
className: burst ? 'dht-reaction--burst' : undefined
|
|
119
|
+
}, `${message.id}r${id}`));
|
|
120
|
+
})
|
|
121
|
+
})),
|
|
122
|
+
message.hasThread && message.thread && renderThread(message.thread, context),
|
|
123
|
+
]
|
|
124
|
+
}, message.id));
|
|
125
|
+
} catch (err) {
|
|
126
|
+
console.warn('[discord-html-transcripts-fix] Render failed', message?.id, err?.message || err);
|
|
127
|
+
const safeAuthor = (message?.author?.displayName || message?.author?.username) || 'Unknown';
|
|
128
|
+
return ((0, jsx_runtime_1.jsx)("discord-message", { id: `m-${message?.id}`, children: (0, jsx_runtime_1.jsx)("span", { style: { opacity: 0.6 }, children: `[message from ${safeAuthor} failed to render]` }) }, message?.id));
|
|
76
129
|
}
|
|
77
130
|
}
|
|
78
131
|
|
|
132
|
+
function renderThread(thread, context) {
|
|
133
|
+
const cta = thread.messageCount
|
|
134
|
+
? `${thread.messageCount} Message${thread.messageCount > 1 ? 's' : ''}`
|
|
135
|
+
: 'View Thread';
|
|
136
|
+
const badges = [];
|
|
137
|
+
if (thread.archived) badges.push((0, jsx_runtime_1.jsx)("span", { className: "dht-thread-badge dht-thread-badge--archived", "data-i18n": "threadArchived", children: t(context, 'threadArchived', 'Archived') }, 'a'));
|
|
138
|
+
if (thread.locked) badges.push((0, jsx_runtime_1.jsx)("span", { className: "dht-thread-badge dht-thread-badge--locked", "data-i18n": "threadLocked", children: t(context, 'threadLocked', 'Locked') }, 'l'));
|
|
139
|
+
return (0, jsx_runtime_1.jsxs)("discord-thread", { slot: "thread", name: thread.name, cta, children: [
|
|
140
|
+
badges,
|
|
141
|
+
thread.lastMessage
|
|
142
|
+
? (0, jsx_runtime_1.jsx)("discord-thread-message", { profile: thread.lastMessage.author.id, children: (0, jsx_runtime_1.jsx)(content_1.default, { content: thread.lastMessage.content.length > 128 ? thread.lastMessage.content.substring(0, 125) + '...' : thread.lastMessage.content, context: Object.assign({}, context, { type: content_1.RenderType.REPLY }) }) })
|
|
143
|
+
: 'Thread messages not saved.'
|
|
144
|
+
] });
|
|
145
|
+
}
|
|
146
|
+
|
|
79
147
|
function renderStickers(stickers) {
|
|
80
148
|
return (0, jsx_runtime_1.jsx)("div", {
|
|
81
149
|
className: "dht-stickers",
|
|
82
|
-
style: { display: 'flex', gap: '8px', flexWrap: 'wrap', marginTop: '4px' },
|
|
83
150
|
children: stickers.map((s) => (0, jsx_runtime_1.jsx)("img", {
|
|
84
151
|
src: s.url,
|
|
85
152
|
alt: s.name || 'sticker',
|
|
86
153
|
title: s.name || '',
|
|
87
|
-
|
|
154
|
+
className: "dht-sticker"
|
|
88
155
|
}, s.id))
|
|
89
156
|
});
|
|
90
157
|
}
|
|
91
158
|
|
|
92
|
-
function renderPoll(poll) {
|
|
159
|
+
function renderPoll(poll, context) {
|
|
93
160
|
let totalVotes = 0;
|
|
94
161
|
const answers = poll.answers ? Array.from(poll.answers.values()) : [];
|
|
95
162
|
for (const a of answers) totalVotes += a.voteCount || 0;
|
|
@@ -97,39 +164,85 @@ function renderPoll(poll) {
|
|
|
97
164
|
|
|
98
165
|
return (0, jsx_runtime_1.jsxs)("div", {
|
|
99
166
|
className: "dht-poll",
|
|
100
|
-
style: {
|
|
101
|
-
border: '1px solid #2b2d31', background: '#1e1f22', borderRadius: '8px',
|
|
102
|
-
padding: '12px 14px', marginTop: '6px', maxWidth: '480px'
|
|
103
|
-
},
|
|
104
167
|
children: [
|
|
105
|
-
(0, jsx_runtime_1.jsxs)("div", {
|
|
168
|
+
(0, jsx_runtime_1.jsxs)("div", { className: "dht-poll-q", children: ['๐ ', poll.question?.text || 'Poll'] }),
|
|
106
169
|
(0, jsx_runtime_1.jsx)("div", {
|
|
107
|
-
|
|
170
|
+
className: "dht-poll-answers",
|
|
108
171
|
children: answers.map((a, i) => {
|
|
109
|
-
const text =
|
|
172
|
+
const text = a.text ?? a.media?.text ?? `Option ${i + 1}`;
|
|
110
173
|
const votes = a.voteCount || 0;
|
|
111
174
|
const pct = totalVotes > 0 ? Math.round((votes / totalVotes) * 100) : 0;
|
|
112
|
-
return (0, jsx_runtime_1.jsxs)("div", {
|
|
113
|
-
|
|
114
|
-
children: [
|
|
115
|
-
(0, jsx_runtime_1.jsx)("
|
|
116
|
-
|
|
117
|
-
|
|
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);
|
|
175
|
+
return (0, jsx_runtime_1.jsxs)("div", { className: "dht-poll-a", children: [
|
|
176
|
+
(0, jsx_runtime_1.jsx)("div", { className: "dht-poll-bar", style: { width: pct + '%' } }),
|
|
177
|
+
(0, jsx_runtime_1.jsxs)("div", { className: "dht-poll-line", children: [
|
|
178
|
+
(0, jsx_runtime_1.jsx)("span", { children: String(text) }),
|
|
179
|
+
(0, jsx_runtime_1.jsxs)("span", { className: "dht-poll-pct", children: [votes, ' (', pct, '%)'] })
|
|
180
|
+
] })
|
|
181
|
+
] }, i);
|
|
127
182
|
})
|
|
128
183
|
}),
|
|
129
|
-
(0, jsx_runtime_1.jsxs)("div", {
|
|
130
|
-
children: [totalVotes, ' Stimme', totalVotes !== 1 ? 'n' : '', expires ? ' ยท endet ' + expires : '']
|
|
131
|
-
})
|
|
184
|
+
(0, jsx_runtime_1.jsxs)("div", { className: "dht-poll-foot", children: [totalVotes, ' votes', expires ? ' ยท ends ' + expires : ''] })
|
|
132
185
|
]
|
|
133
186
|
});
|
|
134
187
|
}
|
|
135
|
-
|
|
188
|
+
|
|
189
|
+
function renderVoiceIndicator(message, context) {
|
|
190
|
+
const audio = message.attachments?.find?.((a) => typeof a.contentType === 'string' && a.contentType.startsWith('audio/'));
|
|
191
|
+
const duration = audio?.duration_secs || audio?.durationSecs;
|
|
192
|
+
const waveformB64 = audio?.waveform;
|
|
193
|
+
let waveformSvg = null;
|
|
194
|
+
if (typeof waveformB64 === 'string' && waveformB64.length > 0) {
|
|
195
|
+
try {
|
|
196
|
+
const buf = Buffer.from(waveformB64, 'base64');
|
|
197
|
+
const bars = Array.from(buf).slice(0, 100);
|
|
198
|
+
const max = bars.length ? Math.max(1, ...bars) : 1;
|
|
199
|
+
const width = bars.length * 3;
|
|
200
|
+
const rects = bars.map((b, i) => {
|
|
201
|
+
const h = Math.max(2, (b / max) * 20);
|
|
202
|
+
return `<rect x="${i * 3}" y="${(22 - h) / 2}" width="2" height="${h}" rx="1" fill="#5865F2"/>`;
|
|
203
|
+
}).join('');
|
|
204
|
+
waveformSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} 22" width="${width}" height="22">${rects}</svg>`;
|
|
205
|
+
} catch (_e) {}
|
|
206
|
+
}
|
|
207
|
+
return (0, jsx_runtime_1.jsxs)("div", { className: "dht-voice", children: [
|
|
208
|
+
(0, jsx_runtime_1.jsx)("span", { className: "dht-voice-icon", children: '๐ค' }),
|
|
209
|
+
(0, jsx_runtime_1.jsx)("span", { className: "dht-voice-label", "data-i18n": "voiceMessage", children: t(context, 'voiceMessage', 'Voice message') }),
|
|
210
|
+
waveformSvg && (0, jsx_runtime_1.jsx)("span", { className: "dht-voice-wave", dangerouslySetInnerHTML: { __html: waveformSvg } }),
|
|
211
|
+
duration ? (0, jsx_runtime_1.jsxs)("span", { className: "dht-voice-dur", children: [Math.round(duration), 's'] }) : null,
|
|
212
|
+
] });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function renderSnapshots(snapshots, context) {
|
|
216
|
+
return (0, jsx_runtime_1.jsx)("div", {
|
|
217
|
+
className: "dht-forwarded-wrap",
|
|
218
|
+
children: snapshots.map((snap, i) => {
|
|
219
|
+
const m = snap.message || snap;
|
|
220
|
+
const author = m?.author?.displayName || m?.author?.username || 'Unknown';
|
|
221
|
+
const text = m?.content || '';
|
|
222
|
+
return (0, jsx_runtime_1.jsxs)("div", { className: "dht-forwarded", children: [
|
|
223
|
+
(0, jsx_runtime_1.jsxs)("div", { className: "dht-forwarded-head", children: [
|
|
224
|
+
(0, jsx_runtime_1.jsx)("span", { className: "dht-forwarded-icon", children: 'โช๏ธ' }),
|
|
225
|
+
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "forwardedFrom", children: t(context, 'forwardedFrom', 'Forwarded from') }),
|
|
226
|
+
(0, jsx_runtime_1.jsx)("strong", { children: ' ' + author }),
|
|
227
|
+
] }),
|
|
228
|
+
text && (0, jsx_runtime_1.jsx)("div", { className: "dht-forwarded-body", children: (0, jsx_runtime_1.jsx)(content_1.default, { content: text, context: Object.assign({}, context, { type: content_1.RenderType.REPLY }) }) }),
|
|
229
|
+
Array.isArray(m?.attachments) && m.attachments.length > 0 && (0, jsx_runtime_1.jsxs)("div", { className: "dht-forwarded-attachments", children: [m.attachments.length, ' attachment(s)'] }),
|
|
230
|
+
] }, i);
|
|
231
|
+
})
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function renderEditHistory(history, context) {
|
|
236
|
+
// Optional feature โ host bot may attach `message.editHistory: [{content, editedAt}, โฆ]`
|
|
237
|
+
return (0, jsx_runtime_1.jsxs)("details", { className: "dht-edit-history", children: [
|
|
238
|
+
(0, jsx_runtime_1.jsx)("summary", { "data-i18n": "editHistoryTitle", children: t(context, 'editHistoryTitle', 'Edit history') }),
|
|
239
|
+
(0, jsx_runtime_1.jsx)("ol", { children: history.map((entry, i) => {
|
|
240
|
+
const when = entry.editedAt instanceof Date ? entry.editedAt.toISOString() : (typeof entry.editedAt === 'string' ? entry.editedAt : '');
|
|
241
|
+
return (0, jsx_runtime_1.jsxs)("li", { children: [
|
|
242
|
+
when && (0, jsx_runtime_1.jsxs)("time", { dateTime: when, children: [t(context, 'editHistoryAt', 'at'), ' ', when] }),
|
|
243
|
+
(0, jsx_runtime_1.jsx)("div", { children: String(entry.content || '') })
|
|
244
|
+
] }, i);
|
|
245
|
+
}) })
|
|
246
|
+
] });
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=message.js.map
|