discord-html-transcripts-fix 1.0.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 +87 -66
- package/dist/downloader/images.js +60 -85
- package/dist/generator/index.js +197 -36
- 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 +68 -39
- package/dist/generator/renderers/embed.js +55 -34
- package/dist/generator/renderers/message.js +201 -12
- package/dist/generator/renderers/reply.js +47 -39
- package/dist/generator/renderers/systemMessage.js +138 -43
- package/dist/generator/transcript.js +42 -56
- package/dist/index.js +59 -86
- package/dist/static/client.js +229 -28
- package/dist/types.js +1 -0
- package/dist/utils/buildProfiles.js +141 -29
- package/dist/utils/utils.js +74 -19
- package/package.json +5 -5
|
@@ -6,62 +6,53 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
6
6
|
const types_1 = require("../../types");
|
|
7
7
|
const utils_1 = require("../../utils/utils");
|
|
8
8
|
const DiscordImage_1 = require("./components/DiscordImage");
|
|
9
|
-
|
|
10
|
-
* Renders all attachments for a message
|
|
11
|
-
* @param message
|
|
12
|
-
* @param context
|
|
13
|
-
* @returns
|
|
14
|
-
*/
|
|
9
|
+
|
|
15
10
|
async function Attachments(props) {
|
|
16
|
-
if (props.message.attachments.size === 0)
|
|
17
|
-
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {});
|
|
11
|
+
if (props.message.attachments.size === 0) return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {});
|
|
18
12
|
return props.message.attachments.map((attachment, id) => ((0, jsx_runtime_1.jsx)(Attachment, { attachment: attachment, message: props.message, context: props.context }, id)));
|
|
19
13
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*/
|
|
24
|
-
async function Attachment({ attachment, context, message, }) {
|
|
25
|
-
let url = attachment.url;
|
|
14
|
+
|
|
15
|
+
async function Attachment({ attachment, context, message }) {
|
|
16
|
+
let url = (0, utils_1.safeHref)(attachment.url) === '#' ? '#' : attachment.url;
|
|
26
17
|
const attachmentType = getAttachmentType(attachment);
|
|
27
18
|
const [bytes, bytesUnit] = (0, utils_1.formatBytes)(attachment.size);
|
|
28
|
-
|
|
19
|
+
const altText = attachment.description || attachment.name;
|
|
20
|
+
|
|
21
|
+
// GIFV detection: animated content served as video but spec-typed as GIF
|
|
22
|
+
const isGifv = attachment.contentType === 'video/mp4' && /\.(gifv?|webm)$/i.test(attachment.name || '');
|
|
23
|
+
|
|
29
24
|
switch (attachmentType) {
|
|
30
25
|
case types_1.AttachmentTypes.Image: {
|
|
31
26
|
const downloaded = await context.callbacks.resolveImageSrc(attachment.toJSON(), message.toJSON());
|
|
32
27
|
if (downloaded !== null) {
|
|
33
|
-
url = downloaded
|
|
28
|
+
url = downloaded ?? url;
|
|
34
29
|
}
|
|
35
|
-
|
|
30
|
+
// GIF / animated content — Discord auto-plays. Render as <video> when needed.
|
|
31
|
+
if (attachment.contentType === 'image/gif' || isGifv) {
|
|
32
|
+
return (0, jsx_runtime_1.jsx)("video", { src: url, autoPlay: true, loop: true, muted: true, playsInline: true, className: "dht-gifv", "aria-label": altText }, attachment.id);
|
|
33
|
+
}
|
|
34
|
+
return (0, jsx_runtime_1.jsx)(DiscordImage_1.DiscordImageAttachment, { url: url, alt: altText, title: attachment.description || undefined }, attachment.id);
|
|
36
35
|
}
|
|
37
36
|
case types_1.AttachmentTypes.Video: {
|
|
38
37
|
return (0, jsx_runtime_1.jsx)("discord-video-attachment", { slot: "attachments", href: url }, attachment.id);
|
|
39
38
|
}
|
|
40
39
|
case types_1.AttachmentTypes.Audio: {
|
|
41
|
-
|
|
40
|
+
// Voice messages have waveform + duration_secs — message.js renders the indicator
|
|
41
|
+
return (0, jsx_runtime_1.jsx)("discord-audio-attachment", { slot: "attachments", href: url, bytes: bytes, bytesUnit: bytesUnit, name: attachment.name, title: attachment.description || undefined }, attachment.id);
|
|
42
42
|
}
|
|
43
43
|
case types_1.AttachmentTypes.File: {
|
|
44
|
-
return (
|
|
44
|
+
return (0, jsx_runtime_1.jsx)("discord-file-attachment", { slot: "attachments", href: url, bytes: bytes, "bytes-unit": bytesUnit, name: attachment.name, title: attachment.description || undefined }, attachment.id);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
* Parses the attachment content type.
|
|
50
|
-
* @param attachment Discord.js attachment object
|
|
51
|
-
* @returns
|
|
52
|
-
*/
|
|
48
|
+
|
|
53
49
|
function getAttachmentType(attachment) {
|
|
54
|
-
|
|
55
|
-
switch (
|
|
56
|
-
case 'audio':
|
|
57
|
-
|
|
58
|
-
case '
|
|
59
|
-
|
|
60
|
-
case 'video':
|
|
61
|
-
return types_1.AttachmentTypes.Video;
|
|
62
|
-
case undefined:
|
|
63
|
-
default:
|
|
64
|
-
return types_1.AttachmentTypes.File;
|
|
50
|
+
const top = attachment.contentType?.split('/')?.[0];
|
|
51
|
+
switch (top) {
|
|
52
|
+
case 'audio': return types_1.AttachmentTypes.Audio;
|
|
53
|
+
case 'image': return types_1.AttachmentTypes.Image;
|
|
54
|
+
case 'video': return types_1.AttachmentTypes.Video;
|
|
55
|
+
default: return types_1.AttachmentTypes.File;
|
|
65
56
|
}
|
|
66
57
|
}
|
|
67
|
-
//# sourceMappingURL=attachment.js.map
|
|
58
|
+
//# sourceMappingURL=attachment.js.map
|
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
|
|
5
|
+
function colorToHex(color) {
|
|
6
|
+
if (color == null) return null;
|
|
7
|
+
if (typeof color === 'string') {
|
|
8
|
+
return color.startsWith('#') ? color : '#' + color.replace(/^0x/i, '');
|
|
9
|
+
}
|
|
10
|
+
if (typeof color === 'number') {
|
|
11
|
+
return '#' + color.toString(16).padStart(6, '0');
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function DiscordContainer({ children, accentColor, spoiler }) {
|
|
17
|
+
const hex = colorToHex(accentColor);
|
|
18
|
+
const style = {
|
|
19
|
+
display: 'flex',
|
|
20
|
+
width: '500px',
|
|
21
|
+
flexDirection: 'column',
|
|
22
|
+
backgroundColor: '#3f4248',
|
|
23
|
+
padding: '16px',
|
|
24
|
+
// Use accent color as a left border (Discord's V2 container style)
|
|
25
|
+
borderLeft: hex ? `4px solid ${hex}` : '1px solid #4f5359',
|
|
26
|
+
borderTop: '1px solid #4f5359',
|
|
27
|
+
borderRight: '1px solid #4f5359',
|
|
28
|
+
borderBottom: '1px solid #4f5359',
|
|
29
|
+
marginTop: '2px',
|
|
30
|
+
marginBottom: '2px',
|
|
31
|
+
borderRadius: '10px',
|
|
32
|
+
gap: '8px',
|
|
33
|
+
};
|
|
34
|
+
const node = (0, jsx_runtime_1.jsx)("div", { style: style, children: children });
|
|
35
|
+
if (spoiler) {
|
|
36
|
+
return (0, jsx_runtime_1.jsx)("span", { className: "discord-spoiler", children: node });
|
|
37
|
+
}
|
|
38
|
+
return node;
|
|
17
39
|
}
|
|
18
40
|
exports.default = DiscordContainer;
|
|
19
|
-
//# sourceMappingURL=Container.js.map
|
|
41
|
+
//# sourceMappingURL=Container.js.map
|
|
@@ -9,24 +9,30 @@ function DiscordMediaGallery({ component }) {
|
|
|
9
9
|
const count = component.items.length;
|
|
10
10
|
const imagesToShow = component.items.slice(0, 10);
|
|
11
11
|
const hasMore = component.items.length > 10;
|
|
12
|
-
return ((0, jsx_runtime_1.jsx)("div", { style: (0, utils_1.getGalleryLayout)(count), children: imagesToShow.map((media, idx) =>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
12
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: (0, utils_1.getGalleryLayout)(count), children: imagesToShow.map((media, idx) => {
|
|
13
|
+
const img = (0, jsx_runtime_1.jsx)("img", { src: media.media.url, alt: media.description || 'Media content', style: {
|
|
14
|
+
width: '100%',
|
|
15
|
+
height: '100%',
|
|
16
|
+
objectFit: 'cover',
|
|
17
|
+
} });
|
|
18
|
+
const wrapped = media.spoiler
|
|
19
|
+
? (0, jsx_runtime_1.jsx)("span", { className: "discord-spoiler", style: { display: 'block', width: '100%', height: '100%' }, children: img })
|
|
20
|
+
: img;
|
|
21
|
+
return ((0, jsx_runtime_1.jsxs)("div", { style: (0, utils_1.getImageStyle)(idx, count), children: [wrapped, hasMore && idx === imagesToShow.length - 1 && ((0, jsx_runtime_1.jsxs)("div", { style: {
|
|
22
|
+
position: 'absolute',
|
|
23
|
+
top: 0,
|
|
24
|
+
left: 0,
|
|
25
|
+
width: '100%',
|
|
26
|
+
height: '100%',
|
|
27
|
+
display: 'flex',
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
justifyContent: 'center',
|
|
30
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
31
|
+
color: 'white',
|
|
32
|
+
fontSize: '20px',
|
|
33
|
+
fontWeight: 'bold',
|
|
34
|
+
}, children: ["+", component.items.length - 10] }))] }, idx));
|
|
35
|
+
}) }));
|
|
30
36
|
}
|
|
31
37
|
exports.default = DiscordMediaGallery;
|
|
32
38
|
//# sourceMappingURL=Media%20Gallery.js.map
|
|
@@ -22,8 +22,13 @@ function ComponentRow({ component, id, context, }) {
|
|
|
22
22
|
switch (component.type) {
|
|
23
23
|
case discord_js_1.ComponentType.ActionRow:
|
|
24
24
|
return ((0, jsx_runtime_1.jsx)("discord-action-row", { children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: component.components.map((nestedComponent, id) => ((0, jsx_runtime_1.jsx)(Component, { component: nestedComponent, id: id }, id))) }) }, id));
|
|
25
|
-
case discord_js_1.ComponentType.Container:
|
|
26
|
-
|
|
25
|
+
case discord_js_1.ComponentType.Container: {
|
|
26
|
+
const accentColor = component.accent_color !== undefined
|
|
27
|
+
? component.accent_color
|
|
28
|
+
: (component.accentColor !== undefined ? component.accentColor : null);
|
|
29
|
+
const spoiler = !!component.spoiler;
|
|
30
|
+
return ((0, jsx_runtime_1.jsx)(Container_1.default, { accentColor: accentColor, spoiler: spoiler, children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: component.components.map((nestedComponent, id) => ((0, jsx_runtime_1.jsx)(ComponentRow, { component: nestedComponent, id: id, context: context }, id))) }) }, id));
|
|
31
|
+
}
|
|
27
32
|
case discord_js_1.ComponentType.File: {
|
|
28
33
|
const attachmentComponent = (0, jsx_runtime_1.jsx)("discord-file-attachment", { href: component.file.url });
|
|
29
34
|
if (component.spoiler) {
|
|
@@ -19,52 +19,63 @@ 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
|
-
|
|
45
|
+
if (!node) return null;
|
|
46
|
+
try {
|
|
47
|
+
return await renderASTNode(node, context);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.warn('[discord-html-transcripts-fix] AST node render failed:', err && err.message ? err.message : err);
|
|
50
|
+
try {
|
|
51
|
+
if (typeof node.content === 'string') return node.content;
|
|
52
|
+
if (Array.isArray(node.content)) return (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context });
|
|
53
|
+
} catch (_e) {}
|
|
58
54
|
return null;
|
|
55
|
+
}
|
|
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
|
+
|
|
68
|
+
async function renderASTNode(node, context) {
|
|
69
|
+
var _a, _b;
|
|
59
70
|
const type = node.type;
|
|
60
71
|
switch (type) {
|
|
61
72
|
case 'text':
|
|
62
73
|
return node.content;
|
|
63
74
|
case 'link':
|
|
64
|
-
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 }) }));
|
|
65
76
|
case 'url':
|
|
66
77
|
case 'autolink':
|
|
67
|
-
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 }) }));
|
|
68
79
|
case 'blockQuote':
|
|
69
80
|
if (context.type === RenderType.REPLY) {
|
|
70
81
|
return (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context });
|
|
@@ -72,23 +83,34 @@ async function MessageSingleASTNode({ node, context }) {
|
|
|
72
83
|
return ((0, jsx_runtime_1.jsx)("discord-quote", { children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
|
|
73
84
|
case 'br':
|
|
74
85
|
case 'newline':
|
|
75
|
-
if (context.type === RenderType.REPLY)
|
|
76
|
-
return ' ';
|
|
86
|
+
if (context.type === RenderType.REPLY) return ' ';
|
|
77
87
|
return (0, jsx_runtime_1.jsx)("br", {});
|
|
78
88
|
case 'channel': {
|
|
79
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
|
+
}
|
|
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
|
-
|
|
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}>` }));
|
|
87
103
|
}
|
|
88
104
|
case 'user': {
|
|
89
105
|
const id = node.id;
|
|
90
106
|
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}>` });
|
|
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}>` });
|
|
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) }));
|
|
92
114
|
}
|
|
93
115
|
case 'here':
|
|
94
116
|
case 'everyone':
|
|
@@ -109,39 +131,46 @@ async function MessageSingleASTNode({ node, context }) {
|
|
|
109
131
|
case 'strikethrough':
|
|
110
132
|
return ((0, jsx_runtime_1.jsx)("s", { children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
|
|
111
133
|
case 'emoticon':
|
|
112
|
-
return typeof node.content === 'string' ?
|
|
134
|
+
return typeof node.content === 'string' ? node.content : ((0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }));
|
|
113
135
|
case 'spoiler':
|
|
114
136
|
return ((0, jsx_runtime_1.jsx)("discord-spoiler", { children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
|
|
115
137
|
case 'emoji':
|
|
116
138
|
case 'twemoji':
|
|
117
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 }));
|
|
118
|
-
case 'timestamp':
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
140
|
+
case 'timestamp': {
|
|
141
|
+
// Defensive: invalid <t:...> markers must not throw
|
|
142
|
+
const raw = node.timestamp;
|
|
143
|
+
let dateMs = NaN;
|
|
144
|
+
if (typeof raw === 'number') {
|
|
145
|
+
dateMs = raw < 1e12 ? raw * 1000 : raw;
|
|
146
|
+
} else if (typeof raw === 'string') {
|
|
147
|
+
const n = parseInt(raw, 10);
|
|
148
|
+
if (Number.isFinite(n)) dateMs = n < 1e12 ? n * 1000 : n;
|
|
149
|
+
}
|
|
150
|
+
const dt = new Date(dateMs);
|
|
151
|
+
let iso = '';
|
|
152
|
+
try { iso = Number.isFinite(dt.getTime()) ? dt.toISOString() : ''; } catch (_e) { iso = ''; }
|
|
153
|
+
return (0, jsx_runtime_1.jsx)("discord-time", { children: iso });
|
|
154
|
+
}
|
|
123
155
|
default: {
|
|
124
|
-
|
|
125
|
-
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 }));
|
|
126
157
|
}
|
|
127
158
|
}
|
|
128
159
|
}
|
|
160
|
+
|
|
129
161
|
function getChannelType(channelType) {
|
|
130
162
|
switch (channelType) {
|
|
131
163
|
case discord_js_1.ChannelType.GuildCategory:
|
|
132
164
|
case discord_js_1.ChannelType.GuildAnnouncement:
|
|
133
165
|
case discord_js_1.ChannelType.GuildText:
|
|
134
166
|
case discord_js_1.ChannelType.DM:
|
|
135
|
-
case discord_js_1.ChannelType.GroupDM:
|
|
136
|
-
case discord_js_1.ChannelType.GuildDirectory:
|
|
137
|
-
case discord_js_1.ChannelType.GuildMedia:
|
|
138
167
|
return 'channel';
|
|
139
168
|
case discord_js_1.ChannelType.GuildVoice:
|
|
140
169
|
case discord_js_1.ChannelType.GuildStageVoice:
|
|
141
170
|
return 'voice';
|
|
171
|
+
case discord_js_1.ChannelType.AnnouncementThread:
|
|
142
172
|
case discord_js_1.ChannelType.PublicThread:
|
|
143
173
|
case discord_js_1.ChannelType.PrivateThread:
|
|
144
|
-
case discord_js_1.ChannelType.AnnouncementThread:
|
|
145
174
|
return 'thread';
|
|
146
175
|
case discord_js_1.ChannelType.GuildForum:
|
|
147
176
|
return 'forum';
|
|
@@ -149,4 +178,4 @@ function getChannelType(channelType) {
|
|
|
149
178
|
return 'channel';
|
|
150
179
|
}
|
|
151
180
|
}
|
|
152
|
-
//# 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
|