discord-message-transcript-base 1.2.0 → 1.3.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/dist/assets/script.js +8 -2
- package/dist/core/customMessages.d.ts +1 -1
- package/dist/core/customMessages.js +3 -1
- package/dist/core/sanitizer.d.ts +8 -0
- package/dist/core/sanitizer.js +150 -0
- package/dist/renderers/html/html.js +39 -21
- package/dist/renderers/html/js.js +8 -2
- package/dist/types/types.d.ts +32 -5
- package/package.json +1 -1
package/dist/assets/script.js
CHANGED
|
@@ -97,8 +97,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
97
97
|
const authorName = repliedToAuthor?.member?.displayName ?? repliedToAuthor?.displayName ?? 'Unknown';
|
|
98
98
|
const authorColor = repliedToAuthor?.member?.displayHexColor ?? 'inherit';
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
replyTextDiv.innerHTML = "";
|
|
101
|
+
|
|
102
|
+
const nameSpan = document.createElement("span");
|
|
103
|
+
nameSpan.style.color = authorColor;
|
|
104
|
+
nameSpan.textContent = authorName;
|
|
105
|
+
|
|
106
|
+
replyTextDiv.appendChild(nameSpan);
|
|
107
|
+
replyTextDiv.appendChild(document.createTextNode(" " + content));
|
|
102
108
|
}
|
|
103
109
|
}
|
|
104
110
|
});
|
|
@@ -5,6 +5,8 @@ export class CustomError extends Error {
|
|
|
5
5
|
Object.setPrototypeOf(this, CustomError.prototype);
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
|
-
export function CustomWarn(message) {
|
|
8
|
+
export function CustomWarn(message, disableWarnings) {
|
|
9
|
+
if (disableWarnings)
|
|
10
|
+
return;
|
|
9
11
|
console.warn(`[discord-message-transcript] Warning: ${message}`);
|
|
10
12
|
}
|
package/dist/core/sanitizer.d.ts
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
+
import { hexColor, JsonAttachment, LookupResult, TranscriptOptionsBase } from "../types/types.js";
|
|
2
|
+
export declare const FALLBACK_PIXEL = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
1
3
|
export declare function sanitize(text: string): string;
|
|
4
|
+
export declare function isValidHexColor(colorInput: string, canReturnNull: false): hexColor;
|
|
5
|
+
export declare function isValidHexColor(colorInput: string | null, canReturnNull: true): hexColor | null;
|
|
6
|
+
export declare function resolveImageURL(url: string, options: TranscriptOptionsBase, canReturnNull: false, attachments?: JsonAttachment[]): Promise<string>;
|
|
7
|
+
export declare function resolveImageURL(url: string | null, options: TranscriptOptionsBase, canReturnNull: true, attachments?: JsonAttachment[]): Promise<string | null>;
|
|
8
|
+
export declare function isSafeForHTML(url: string, options: TranscriptOptionsBase): Promise<boolean>;
|
|
9
|
+
export declare function resolveAllIps(host: string): Promise<LookupResult[]>;
|
package/dist/core/sanitizer.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import net from "node:net";
|
|
2
|
+
import { CustomWarn } from "./customMessages.js";
|
|
3
|
+
import { Resolver } from "dns/promises";
|
|
4
|
+
export const FALLBACK_PIXEL = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
5
|
+
const DNS_SERVERS = ["1.1.1.1", "8.8.8.8"];
|
|
6
|
+
const DNS_LOOKUP_TIMEOUT = 5000;
|
|
7
|
+
const TRUSTED_DISCORD_HOSTS = ["discordapp.com", "discordapp.net"];
|
|
1
8
|
export function sanitize(text) {
|
|
2
9
|
return text
|
|
3
10
|
.replace(/&/g, "&")
|
|
@@ -6,3 +13,146 @@ export function sanitize(text) {
|
|
|
6
13
|
.replace(/"/g, """)
|
|
7
14
|
.replace(/'/g, "'");
|
|
8
15
|
}
|
|
16
|
+
export function isValidHexColor(colorInput, canReturnNull) {
|
|
17
|
+
if (!colorInput)
|
|
18
|
+
return null;
|
|
19
|
+
const hexColorRegex = /^#([A-Fa-f0-9]{3,4}|[A-Fa-f0-9]{6}([A-Fa-f0-9]{2})?)$/;
|
|
20
|
+
let color = colorInput.trim();
|
|
21
|
+
if (/^[A-Fa-f0-9]+$/.test(color)) {
|
|
22
|
+
color = "#" + color;
|
|
23
|
+
}
|
|
24
|
+
if (hexColorRegex.test(color)) {
|
|
25
|
+
return color;
|
|
26
|
+
}
|
|
27
|
+
if (canReturnNull) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return "#000000"; // Falback to a default hexColor if can't be null
|
|
31
|
+
}
|
|
32
|
+
export async function resolveImageURL(url, options, canReturnNull, attachments) {
|
|
33
|
+
if (!url)
|
|
34
|
+
return null;
|
|
35
|
+
// Resolve attachment:// references to actual attachment URL
|
|
36
|
+
if (url.startsWith("attachment://")) {
|
|
37
|
+
const name = url.slice("attachment://".length).trim();
|
|
38
|
+
const found = attachments?.find(a => a.name === name);
|
|
39
|
+
if (found && await isSafeForHTML(found.url, options))
|
|
40
|
+
return found.url;
|
|
41
|
+
return FALLBACK_PIXEL;
|
|
42
|
+
}
|
|
43
|
+
if (await isSafeForHTML(url, options))
|
|
44
|
+
return url;
|
|
45
|
+
if (canReturnNull)
|
|
46
|
+
return null;
|
|
47
|
+
return FALLBACK_PIXEL;
|
|
48
|
+
}
|
|
49
|
+
export async function isSafeForHTML(url, options) {
|
|
50
|
+
const { safeMode, disableWarnings } = options;
|
|
51
|
+
if (!safeMode)
|
|
52
|
+
return true;
|
|
53
|
+
let u;
|
|
54
|
+
try {
|
|
55
|
+
u = new URL(url);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
CustomWarn(`Unsafe URL rejected: Invalid URL format\nURL: ${url}`, disableWarnings);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const host = u.hostname.toLowerCase();
|
|
62
|
+
// If is from discord accept
|
|
63
|
+
if (isTrustedDiscordHost(host))
|
|
64
|
+
return true;
|
|
65
|
+
// Don't accept if isn't https or http
|
|
66
|
+
if (!["http:", "https:"].includes(u.protocol)) {
|
|
67
|
+
CustomWarn(`Unsafe URL rejected: Invalid protocol "${u.protocol}"\nURL: ${url}`, disableWarnings);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
if (u.username || u.password) {
|
|
71
|
+
CustomWarn(`Unsafe URL rejected: Contains username or password\nURL: ${url}`, disableWarnings);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
if (u.port && !["80", "443", ""].includes(u.port)) {
|
|
75
|
+
CustomWarn(`Unsafe URL rejected: Invalid port "${u.port}"\nURL: ${url}`, disableWarnings);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
// Block localhost and loopback addresses (SSRF protection)
|
|
79
|
+
if (host === "localhost" ||
|
|
80
|
+
host === "127.0.0.1" ||
|
|
81
|
+
host.startsWith("0.")) {
|
|
82
|
+
CustomWarn(`Unsafe URL rejected: Blacklisted host "${host}"\nURL: ${url}`, disableWarnings);
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
let ips;
|
|
86
|
+
try {
|
|
87
|
+
ips = await resolveAllIps(host);
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
CustomWarn(`Unsafe URL rejected: DNS lookup failed or timed out for host "${host}". Error: ${e.message}\nURL: ${url}`, disableWarnings);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
// Block private/internal network IPs (SSRF protection)
|
|
94
|
+
for (const ip of ips) {
|
|
95
|
+
if (isPrivateIp(ip.address)) {
|
|
96
|
+
CustomWarn(`Unsafe URL rejected: Private IP address "${ip.address}" resolved for host "${host}"\nURL: ${url}`, disableWarnings);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const path = u.pathname.toLowerCase();
|
|
101
|
+
// External SVGs can execute scripts → allow only from Discord CDN
|
|
102
|
+
if (path.endsWith(".svg")) {
|
|
103
|
+
CustomWarn(`Unsafe URL rejected: External SVG not from Discord CDN\nURL: ${url}`, disableWarnings);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
export async function resolveAllIps(host) {
|
|
109
|
+
const resolver = new Resolver();
|
|
110
|
+
resolver.setServers(DNS_SERVERS);
|
|
111
|
+
const lookupPromise = (async () => {
|
|
112
|
+
const results = [];
|
|
113
|
+
const [v4, v6] = await Promise.allSettled([
|
|
114
|
+
resolver.resolve4(host),
|
|
115
|
+
resolver.resolve6(host)
|
|
116
|
+
]);
|
|
117
|
+
if (v4.status === "fulfilled") {
|
|
118
|
+
for (const ip of v4.value) {
|
|
119
|
+
results.push({ address: ip, family: 4 });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (v6.status === "fulfilled") {
|
|
123
|
+
for (const ip of v6.value) {
|
|
124
|
+
results.push({ address: ip, family: 6 });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (results.length === 0) {
|
|
128
|
+
throw new Error(`No DNS records found for ${host}`);
|
|
129
|
+
}
|
|
130
|
+
return results;
|
|
131
|
+
})();
|
|
132
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`DNS timeout for ${host}`)), DNS_LOOKUP_TIMEOUT));
|
|
133
|
+
return Promise.race([lookupPromise, timeoutPromise]);
|
|
134
|
+
}
|
|
135
|
+
function isPrivateIp(ip) {
|
|
136
|
+
if (!net.isIP(ip))
|
|
137
|
+
return true;
|
|
138
|
+
// Detects private IPv6 ranges
|
|
139
|
+
if (ip.includes(":")) {
|
|
140
|
+
return (ip === "::1" ||
|
|
141
|
+
ip.startsWith("fc") ||
|
|
142
|
+
ip.startsWith("fd") ||
|
|
143
|
+
ip.startsWith("fe80"));
|
|
144
|
+
}
|
|
145
|
+
const parts = ip.split(".").map(Number);
|
|
146
|
+
// Detects private IPv4 ranges
|
|
147
|
+
return (parts[0] === 10 ||
|
|
148
|
+
parts[0] === 127 ||
|
|
149
|
+
(parts[0] === 192 && parts[1] === 168) ||
|
|
150
|
+
(parts[0] === 169 && parts[1] === 254) ||
|
|
151
|
+
(parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31));
|
|
152
|
+
}
|
|
153
|
+
function isTrustedDiscordHost(host) {
|
|
154
|
+
host = host.toLowerCase();
|
|
155
|
+
return TRUSTED_DISCORD_HOSTS.some(trusted => {
|
|
156
|
+
return host === trusted || host.endsWith("." + trusted);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
@@ -55,19 +55,19 @@ export class Html {
|
|
|
55
55
|
<div style="display: flex; flex-direction: column; justify-content: center; gap: 1.25rem;">
|
|
56
56
|
${guild ? `<div id="guild" class="line">
|
|
57
57
|
<h4>Guild: </h4>
|
|
58
|
-
<h4 style="font-weight: normal;">${guild.name}</h4>
|
|
58
|
+
<h4 style="font-weight: normal;">${sanitize(guild.name)}</h4>
|
|
59
59
|
</div>` : ""}
|
|
60
60
|
${channel.parent ? `<div id="category" class="line">
|
|
61
61
|
<h4>Category: </h4>
|
|
62
|
-
<h4 style="font-weight: normal;">${channel.parent.name}</h4>
|
|
62
|
+
<h4 style="font-weight: normal;">${sanitize(channel.parent.name)}</h4>
|
|
63
63
|
</div>` : ""}
|
|
64
64
|
<div id="channel" class="line">
|
|
65
65
|
<h4>Channel: </h4>
|
|
66
|
-
<h4 style="font-weight: normal;">${channel.name}</h4>
|
|
66
|
+
<h4 style="font-weight: normal;">${sanitize(channel.name)}</h4>
|
|
67
67
|
</div>
|
|
68
68
|
${channel.topic ? `<div id="topic" class="line">
|
|
69
69
|
<h4>Topic: </h4>
|
|
70
|
-
<h4 style="font-weight: normal;">${channel.topic}</h4>
|
|
70
|
+
<h4 style="font-weight: normal;">${sanitize(channel.topic)}</h4>
|
|
71
71
|
</div>` : ""}
|
|
72
72
|
</div>
|
|
73
73
|
</div>
|
|
@@ -77,9 +77,9 @@ export class Html {
|
|
|
77
77
|
return (await Promise.all(this.data.messages.map(async (message) => {
|
|
78
78
|
const date = new Date(message.createdTimestamp);
|
|
79
79
|
return `
|
|
80
|
-
<div class="messageDiv" id="${message.id}" data-author-id="${message.authorId}">
|
|
80
|
+
<div class="messageDiv" id="${sanitize(message.id)}" data-author-id="${sanitize(message.authorId)}">
|
|
81
81
|
${message.references && message.references.messageId ?
|
|
82
|
-
`<div class="messageReply" data-id="${message.references.messageId}">
|
|
82
|
+
`<div class="messageReply" data-id="${sanitize(message.references.messageId)}">
|
|
83
83
|
<svg class="messageReplySvg"><use href="#reply-icon"></use></svg>
|
|
84
84
|
<img class="messageReplyImg" src="">
|
|
85
85
|
<div class="replyBadges"></div>
|
|
@@ -126,7 +126,7 @@ export class Html {
|
|
|
126
126
|
<head>
|
|
127
127
|
<meta charset="UTF-8" />
|
|
128
128
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
129
|
-
<title>${options.fileName}</title>
|
|
129
|
+
<title>${sanitize(options.fileName)}</title>
|
|
130
130
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/atom-one-dark.min.css">
|
|
131
131
|
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script>
|
|
132
132
|
${options.selfContained ? `<style>${cssContent}</style>` : `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/discord-message-transcript-base@${packageJson.version}/dist/assets/style.css">`}
|
|
@@ -223,13 +223,13 @@ export class Html {
|
|
|
223
223
|
<div class="embedHeaderLeft">
|
|
224
224
|
${embed.author ? `
|
|
225
225
|
<div class="embedHeaderLeftAuthor">
|
|
226
|
-
${embed.author.iconURL ? `<img class="embedHeaderLeftAuthorImg" src="${
|
|
226
|
+
${embed.author.iconURL ? `<img class="embedHeaderLeftAuthorImg" src="${embed.author.iconURL}">` : ""}
|
|
227
227
|
${embedAuthor}
|
|
228
228
|
</div>` : ""}
|
|
229
229
|
${embedTitle}
|
|
230
230
|
${embed.description ? `<div class="embedDescription">${markdownToHTML(embed.description, this.data.mentions, message.mentions, this.dateFormat)}</div>` : ""}
|
|
231
231
|
</div>
|
|
232
|
-
${embed.thumbnail ? `<img class="embedHeaderThumbnail" src="${
|
|
232
|
+
${embed.thumbnail ? `<img class="embedHeaderThumbnail" src="${embed.thumbnail.url}">` : ""}
|
|
233
233
|
</div>` : ""}
|
|
234
234
|
${embed.fields && embed.fields.length > 0 ? `
|
|
235
235
|
<div class="embedFields">
|
|
@@ -241,11 +241,11 @@ export class Html {
|
|
|
241
241
|
</div>` : ""}
|
|
242
242
|
${embed.image ? `
|
|
243
243
|
<div class="embedImage">
|
|
244
|
-
<img src="${
|
|
244
|
+
<img src="${embed.image.url}">
|
|
245
245
|
</div>` : ""}
|
|
246
246
|
${embed.footer || embed.timestamp ? `
|
|
247
247
|
<div class="embedFooter">
|
|
248
|
-
${embed.footer?.iconURL ? `<img class="embedFooterImg" src="${
|
|
248
|
+
${embed.footer?.iconURL ? `<img class="embedFooterImg" src="${embed.footer.iconURL}">` : ""}
|
|
249
249
|
${embed.footer?.text || embed.timestamp ? `<p class="embedFooterText">${embed.footer?.text ? sanitize(embed.footer.text) : ''}${embed.footer?.text && embed.timestamp ? ' | ' : ''}${embed.timestamp ? this.dateFormat.format(new Date(embed.timestamp)) : ''}</p>` : ""}
|
|
250
250
|
</div>` : ""}
|
|
251
251
|
</div>
|
|
@@ -256,13 +256,23 @@ export class Html {
|
|
|
256
256
|
return attachments.map(attachment => {
|
|
257
257
|
let html = "";
|
|
258
258
|
if (attachment.contentType?.startsWith('image/')) {
|
|
259
|
-
html = `<img class="attachmentImage" src="${
|
|
259
|
+
html = `<img class="attachmentImage" src="${attachment.url}">`;
|
|
260
260
|
}
|
|
261
261
|
else if (attachment.contentType?.startsWith('video/')) {
|
|
262
|
-
|
|
262
|
+
if (attachment.url == "") {
|
|
263
|
+
html = `<video class="attachmentVideo" controls src="${attachment.url}"></video>`;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
html = `<div class="attachmentVideoUnavailable">Video unavailable</div>`;
|
|
267
|
+
}
|
|
263
268
|
}
|
|
264
269
|
else if (attachment.contentType?.startsWith('audio/')) {
|
|
265
|
-
|
|
270
|
+
if (attachment.url == "") {
|
|
271
|
+
html = `<audio class="attachmentAudio" controls src="${attachment.url}"></audio>`;
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
html = `<div class="attachmentAudioUnavailable">Audio unavailable</div>`;
|
|
275
|
+
}
|
|
266
276
|
}
|
|
267
277
|
else {
|
|
268
278
|
let fileSize = attachment.size / 1024;
|
|
@@ -277,9 +287,13 @@ export class Html {
|
|
|
277
287
|
<p class="attachmentFileName">${attachment.name ? sanitize(attachment.name) : 'attachment'}</p>
|
|
278
288
|
<div class="attachmentFileSize">${fileSize.toFixed(2)} ${COUNT_UNIT[count]}</div>
|
|
279
289
|
</div>
|
|
280
|
-
|
|
290
|
+
${attachment.url != "" ?
|
|
291
|
+
`<a class="attachmentDownload" href="${attachment.url}" target="_blank">
|
|
292
|
+
<svg class="attachmentDownloadIcon"><use href="#download-icon"></use></svg>
|
|
293
|
+
</a>` :
|
|
294
|
+
`<span class="attachmentDownload">
|
|
281
295
|
<svg class="attachmentDownloadIcon"><use href="#download-icon"></use></svg>
|
|
282
|
-
</
|
|
296
|
+
</span>`}
|
|
283
297
|
</div>
|
|
284
298
|
`;
|
|
285
299
|
}
|
|
@@ -302,7 +316,7 @@ export class Html {
|
|
|
302
316
|
}
|
|
303
317
|
case JsonComponentType.Container: {
|
|
304
318
|
const html = `
|
|
305
|
-
<div class="container" style="
|
|
319
|
+
<div class="container" style="border-left-color: ${component.hexAccentColor}">
|
|
306
320
|
${this.componentBuilder(message, component.components)}
|
|
307
321
|
</div>
|
|
308
322
|
`;
|
|
@@ -321,9 +335,13 @@ export class Html {
|
|
|
321
335
|
<p class="attachmentFileName">${component.fileName ? sanitize(component.fileName) : 'file'}</p>
|
|
322
336
|
<div class="attachmentFileSize">${fileSize.toFixed(2)} ${COUNT_UNIT[count]}</div>
|
|
323
337
|
</div>
|
|
324
|
-
|
|
338
|
+
${component.url != "" ?
|
|
339
|
+
`<a class="attachmentDownload" href="${component.url}" target="_blank">
|
|
340
|
+
<svg class="attachmentDownloadIcon"><use href="#download-icon"></use></svg>
|
|
341
|
+
</a>` :
|
|
342
|
+
`<span class="attachmentDownload">
|
|
325
343
|
<svg class="attachmentDownloadIcon"><use href="#download-icon"></use></svg>
|
|
326
|
-
</
|
|
344
|
+
</span>`}
|
|
327
345
|
</div>
|
|
328
346
|
`;
|
|
329
347
|
return this.spoilerAttachmentBuilder(component.spoiler, html);
|
|
@@ -334,7 +352,7 @@ export class Html {
|
|
|
334
352
|
${component.items.map(image => {
|
|
335
353
|
return `
|
|
336
354
|
<div class="mediaGalleryItem">
|
|
337
|
-
${this.spoilerAttachmentBuilder(image.spoiler, `<img class="mediaGalleryImg" src="${
|
|
355
|
+
${this.spoilerAttachmentBuilder(image.spoiler, `<img class="mediaGalleryImg" src="${image.media.url}">`)}
|
|
338
356
|
</div>
|
|
339
357
|
`;
|
|
340
358
|
}).join("")}
|
|
@@ -350,7 +368,7 @@ export class Html {
|
|
|
350
368
|
<div class="sectionRight">
|
|
351
369
|
${component.accessory.type == JsonComponentType.Button ? this.buttonBuilder(component.accessory)
|
|
352
370
|
: component.accessory.type == JsonComponentType.Thumbnail ? this.spoilerAttachmentBuilder(component.accessory.spoiler, `
|
|
353
|
-
<img class="sectionThumbnail" src="${
|
|
371
|
+
<img class="sectionThumbnail" src="${component.accessory.media.url}">
|
|
354
372
|
`) : ""}
|
|
355
373
|
</div>
|
|
356
374
|
</div>
|
|
@@ -99,8 +99,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
99
99
|
const authorName = repliedToAuthor?.member?.displayName ?? repliedToAuthor?.displayName ?? 'Unknown';
|
|
100
100
|
const authorColor = repliedToAuthor?.member?.displayHexColor ?? 'inherit';
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
replyTextDiv.innerHTML = "";
|
|
103
|
+
|
|
104
|
+
const nameSpan = document.createElement("span");
|
|
105
|
+
nameSpan.style.color = authorColor;
|
|
106
|
+
nameSpan.textContent = authorName;
|
|
107
|
+
|
|
108
|
+
replyTextDiv.appendChild(nameSpan);
|
|
109
|
+
replyTextDiv.appendChild(document.createTextNode(" " + content));
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
});
|
package/dist/types/types.d.ts
CHANGED
|
@@ -15,6 +15,17 @@ export type JsonTopLevelComponent = JsonActionRow | JsonButtonComponent | JsonSe
|
|
|
15
15
|
* A union of all V2 component types.
|
|
16
16
|
*/
|
|
17
17
|
export type JsonV2Component = JsonContainerComponent | JsonFileComponent | JsonMediaGalleryComponent | JsonSectionComponent | JsonSeparatorComponent | JsonTextDisplayComponent | JsonThumbnailComponent;
|
|
18
|
+
/**
|
|
19
|
+
* A hexColor type
|
|
20
|
+
*/
|
|
21
|
+
export type hexColor = `#${string}`;
|
|
22
|
+
/**
|
|
23
|
+
* Result from dns.lookup
|
|
24
|
+
*/
|
|
25
|
+
export type LookupResult = {
|
|
26
|
+
address: string;
|
|
27
|
+
family: 4 | 6;
|
|
28
|
+
};
|
|
18
29
|
/**
|
|
19
30
|
* A union of all possible timestamp styles for formatting dates and times in Discord.
|
|
20
31
|
*/
|
|
@@ -91,6 +102,12 @@ export interface TranscriptOptionsBase {
|
|
|
91
102
|
* The name of the generated file.
|
|
92
103
|
*/
|
|
93
104
|
fileName: string;
|
|
105
|
+
/**
|
|
106
|
+
* Disable all warnings to keep console output clean.
|
|
107
|
+
* ⚠️ Can hide issues like unsafe URLs or fallback usage.
|
|
108
|
+
* @default false
|
|
109
|
+
*/
|
|
110
|
+
disableWarnings: boolean;
|
|
94
111
|
/**
|
|
95
112
|
* Whether to include attachments.
|
|
96
113
|
*/
|
|
@@ -139,6 +156,14 @@ export interface TranscriptOptionsBase {
|
|
|
139
156
|
* The type of the returned value (buffer, string, etc.).
|
|
140
157
|
*/
|
|
141
158
|
returnType: ReturnTypeBase;
|
|
159
|
+
/**
|
|
160
|
+
* Enables safe mode, blocking potentially unsafe URLs and content.
|
|
161
|
+
* Prevents suspicious links, images, or HTML from being included in the final transcript.
|
|
162
|
+
*
|
|
163
|
+
* ⚠️ Disabling may allow unsafe content to appear in the transcript.
|
|
164
|
+
* @default true
|
|
165
|
+
*/
|
|
166
|
+
safeMode: boolean;
|
|
142
167
|
/**
|
|
143
168
|
* Whether to save images as base64.
|
|
144
169
|
*/
|
|
@@ -161,6 +186,7 @@ export interface TranscriptOptionsBase {
|
|
|
161
186
|
*/
|
|
162
187
|
export interface TranscriptOptionsParse {
|
|
163
188
|
fileName: string;
|
|
189
|
+
disableWarnings: boolean;
|
|
164
190
|
includeAttachments: boolean;
|
|
165
191
|
includeButtons: boolean;
|
|
166
192
|
includeComponents: boolean;
|
|
@@ -173,6 +199,7 @@ export interface TranscriptOptionsParse {
|
|
|
173
199
|
quantity: number;
|
|
174
200
|
returnFormat: ReturnFormat;
|
|
175
201
|
returnType: ReturnTypeParse;
|
|
202
|
+
safeMode: boolean;
|
|
176
203
|
saveImages: boolean;
|
|
177
204
|
selfContained: boolean;
|
|
178
205
|
timeZone: TimeZone;
|
|
@@ -288,7 +315,7 @@ export interface JsonAuthor {
|
|
|
288
315
|
/**
|
|
289
316
|
* The member's display color in hex format.
|
|
290
317
|
*/
|
|
291
|
-
displayHexColor:
|
|
318
|
+
displayHexColor: hexColor;
|
|
292
319
|
/**
|
|
293
320
|
* The member's display name in the guild.
|
|
294
321
|
*/
|
|
@@ -339,7 +366,7 @@ export interface JsonContainerComponent {
|
|
|
339
366
|
/**
|
|
340
367
|
* The accent color of the container's border.
|
|
341
368
|
*/
|
|
342
|
-
hexAccentColor:
|
|
369
|
+
hexAccentColor: hexColor;
|
|
343
370
|
/**
|
|
344
371
|
* Whether the container's content is a spoiler.
|
|
345
372
|
*/
|
|
@@ -453,7 +480,7 @@ export interface JsonEmbed {
|
|
|
453
480
|
iconURL: string | null;
|
|
454
481
|
text: string;
|
|
455
482
|
} | null;
|
|
456
|
-
hexColor:
|
|
483
|
+
hexColor: hexColor | null;
|
|
457
484
|
image: {
|
|
458
485
|
url: string;
|
|
459
486
|
} | null;
|
|
@@ -548,7 +575,7 @@ export interface JsonMessageMentionsChannels {
|
|
|
548
575
|
export interface JsonMessageMentionsRoles {
|
|
549
576
|
id: string;
|
|
550
577
|
name: string;
|
|
551
|
-
color:
|
|
578
|
+
color: hexColor;
|
|
552
579
|
}
|
|
553
580
|
/**
|
|
554
581
|
* A JSON-serializable representation of a user mention.
|
|
@@ -556,7 +583,7 @@ export interface JsonMessageMentionsRoles {
|
|
|
556
583
|
export interface JsonMessageMentionsUsers {
|
|
557
584
|
id: string;
|
|
558
585
|
name: string;
|
|
559
|
-
color:
|
|
586
|
+
color: hexColor | null;
|
|
560
587
|
}
|
|
561
588
|
/**
|
|
562
589
|
* A JSON-serializable representation of a poll.
|