discord-html-transcripts-fix 1.1.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/README.md +86 -78
- package/dist/downloader/images.js +71 -85
- package/dist/generator/index.js +231 -43
- package/dist/generator/renderers/attachment.js +27 -36
- package/dist/generator/renderers/components/Container.js +42 -14
- package/dist/generator/renderers/components/Media Gallery.js +24 -18
- package/dist/generator/renderers/components/Spacing.js +18 -6
- package/dist/generator/renderers/components/section/Section.js +13 -3
- package/dist/generator/renderers/components/section/SectionAccessory.js +8 -7
- package/dist/generator/renderers/components/section/SectionContent.js +9 -4
- package/dist/generator/renderers/components.js +7 -2
- package/dist/generator/renderers/content.js +254 -76
- package/dist/generator/renderers/embed.js +79 -34
- package/dist/generator/renderers/message.js +358 -52
- package/dist/generator/renderers/reply.js +59 -40
- package/dist/generator/renderers/systemMessage.js +182 -68
- package/dist/generator/transcript.js +42 -56
- package/dist/index.js +81 -84
- package/dist/static/client.js +675 -202
- package/dist/types.js +1 -0
- package/dist/utils/buildProfiles.js +70 -60
- package/dist/utils/utils.js +73 -26
- package/package.json +1 -1
|
@@ -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,43 @@ 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_CROSSPOSTED = 1 << 0;
|
|
51
|
+
const FLAG_IS_CROSSPOST = 1 << 1;
|
|
52
|
+
const FLAG_SUPPRESS_EMBEDS = 1 << 2;
|
|
53
|
+
const FLAG_SUPPRESS_NOTIFICATIONS = 1 << 12;
|
|
54
|
+
const FLAG_IS_VOICE_MESSAGE = 1 << 13;
|
|
55
|
+
const FLAG_HAS_SNAPSHOT = 1 << 14;
|
|
56
|
+
const ACTIVITY_TYPES = { 1: 'Join', 2: 'Spectate', 3: 'Listen', 5: 'Join Request' };
|
|
57
|
+
|
|
58
|
+
function abbrCount(n) {
|
|
59
|
+
if (n < 1000) return String(n);
|
|
60
|
+
if (n < 1000000) return (n / 1000).toFixed(n < 10000 ? 1 : 0).replace(/\.0$/, '') + 'K';
|
|
61
|
+
return (n / 1000000).toFixed(1).replace(/\.0$/, '') + 'M';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function t(context, key, fallback) {
|
|
65
|
+
const dict = context?.i18n?.[context?.lang] || context?.i18n?.en || {};
|
|
66
|
+
return dict[key] || fallback || key;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function DiscordMessage({ message, context }) {
|
|
50
70
|
try {
|
|
51
71
|
if (message.system)
|
|
52
|
-
return (0, jsx_runtime_1.jsx)(systemMessage_1.default, { message: message });
|
|
53
|
-
|
|
72
|
+
return (0, jsx_runtime_1.jsx)(systemMessage_1.default, { message: message, context: context });
|
|
73
|
+
|
|
74
|
+
const isCrossGuildReply = message.reference && message.reference.guildId !== message.guild?.id;
|
|
75
|
+
|
|
76
|
+
// Data-attrs for client-side filtering (keyword/author/role/date/has-*).
|
|
77
|
+
const memberRoleIds = message.member?.roles?.cache
|
|
78
|
+
? Array.from(message.member.roles.cache.keys()).join(',')
|
|
79
|
+
: '';
|
|
80
|
+
const authorName = (message.member?.nickname || message.author?.displayName || message.author?.username || '').toLowerCase();
|
|
81
|
+
const ts = message.createdAt instanceof Date ? message.createdAt.getTime() : 0;
|
|
82
|
+
const lowerText = (message.content || '').toLowerCase().slice(0, 4096);
|
|
83
|
+
const hasImage = message.attachments && Array.from(message.attachments.values()).some(a => typeof a.contentType === 'string' && a.contentType.startsWith('image/'));
|
|
84
|
+
const hasEmbed = message.embeds && message.embeds.length > 0;
|
|
85
|
+
const hasAttachment = message.attachments && message.attachments.size > 0;
|
|
54
86
|
const stickers = message.stickers && message.stickers.size > 0
|
|
55
87
|
? Array.from(message.stickers.values()).map((s) => ({
|
|
56
88
|
id: s.id,
|
|
@@ -60,76 +92,350 @@ async function DiscordMessage({ message, context, }) {
|
|
|
60
92
|
}))
|
|
61
93
|
: [];
|
|
62
94
|
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
|
-
|
|
75
|
-
|
|
95
|
+
|
|
96
|
+
const flags = (typeof message.flags?.bitfield === 'number') ? message.flags.bitfield : 0;
|
|
97
|
+
const suppressEmbeds = !!(flags & FLAG_SUPPRESS_EMBEDS);
|
|
98
|
+
const isVoice = !!(flags & FLAG_IS_VOICE_MESSAGE);
|
|
99
|
+
const isCrosspost = !!(flags & FLAG_IS_CROSSPOST);
|
|
100
|
+
const isCrossposted = !!(flags & FLAG_CROSSPOSTED);
|
|
101
|
+
const isSilent = !!(flags & FLAG_SUPPRESS_NOTIFICATIONS);
|
|
102
|
+
const activity = message.activity ? {
|
|
103
|
+
type: ACTIVITY_TYPES[message.activity.type] || 'Activity',
|
|
104
|
+
partyId: message.activity.partyId,
|
|
105
|
+
} : null;
|
|
106
|
+
|
|
107
|
+
// Forwarded message snapshots (messageSnapshots) — render before the empty body
|
|
108
|
+
const snapshots = Array.isArray(message.messageSnapshots) ? message.messageSnapshots : [];
|
|
109
|
+
|
|
110
|
+
const isApp = !!(message.author?.bot && message.applicationId);
|
|
111
|
+
|
|
112
|
+
const editedAtIso = message.editedAt instanceof Date ? message.editedAt.toISOString() : null;
|
|
113
|
+
const pinned = !!message.pinned;
|
|
114
|
+
const verifiedBot = !!message.author?.flags?.has?.(discord_js_1.UserFlags.VerifiedBot);
|
|
115
|
+
|
|
116
|
+
// Forum applied tags — only on the first message of a forum thread (the post itself)
|
|
117
|
+
let appliedTagPills = null;
|
|
118
|
+
if (message.channel?.isThread?.() && message.channel.parent?.availableTags && Array.isArray(message.channel.appliedTags) && message.channel.appliedTags.length > 0) {
|
|
119
|
+
const isFirstInThread = message.id === message.channel.id; // Forum post: thread id === starter message id
|
|
120
|
+
if (isFirstInThread) {
|
|
121
|
+
const available = message.channel.parent.availableTags;
|
|
122
|
+
appliedTagPills = (0, jsx_runtime_1.jsx)("div", {
|
|
123
|
+
className: "dht-applied-tags",
|
|
124
|
+
children: message.channel.appliedTags.map((tagId) => {
|
|
125
|
+
const tag = available.find((t) => t.id === tagId);
|
|
126
|
+
if (!tag) return null;
|
|
127
|
+
return (0, jsx_runtime_1.jsxs)("span", { className: "dht-tag-pill", children: [
|
|
128
|
+
tag.emoji?.name && (0, jsx_runtime_1.jsx)("span", { className: "dht-tag-emoji", children: tag.emoji.name }),
|
|
129
|
+
' ',
|
|
130
|
+
tag.name
|
|
131
|
+
] }, tagId);
|
|
132
|
+
})
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Build a comma-separated badge list for additional flag rendering
|
|
138
|
+
const slashCmdLine = renderSlashCommandLine(message, context);
|
|
139
|
+
|
|
140
|
+
// Resolve author profile data for Skyra slot attributes — without these the
|
|
141
|
+
// <discord-message> web component falls back to a generic display
|
|
142
|
+
const authorMember = message.member;
|
|
143
|
+
const authorDisplay = authorMember?.nickname ?? message.author?.displayName ?? message.author?.username;
|
|
144
|
+
const authorAvatar = authorMember?.displayAvatarURL?.({ size: 64 }) ?? message.author?.displayAvatarURL?.({ size: 64 });
|
|
145
|
+
const authorRoleHex = authorMember?.displayHexColor && authorMember.displayHexColor !== '#000000' ? authorMember.displayHexColor : undefined;
|
|
146
|
+
|
|
147
|
+
return ((0, jsx_runtime_1.jsxs)("discord-message", {
|
|
148
|
+
id: `m-${message.id}`,
|
|
149
|
+
timestamp: message.createdAt,
|
|
150
|
+
edited: message.editedAt !== null,
|
|
151
|
+
server: (isCrossGuildReply || isCrosspost || isCrossposted) ? true : undefined,
|
|
152
|
+
highlight: message.mentions.everyone || pinned,
|
|
153
|
+
profile: message.author.id,
|
|
154
|
+
author: authorDisplay,
|
|
155
|
+
avatar: authorAvatar,
|
|
156
|
+
roleColor: authorRoleHex,
|
|
157
|
+
bot: message.author?.bot && !message.webhookId ? true : undefined,
|
|
158
|
+
verified: verifiedBot ? true : undefined,
|
|
159
|
+
className: pinned ? 'dht-msg dht-msg-pinned' : 'dht-msg',
|
|
160
|
+
"data-author-id": message.author.id,
|
|
161
|
+
"data-author-name": authorName,
|
|
162
|
+
"data-roles": memberRoleIds,
|
|
163
|
+
"data-timestamp": ts ? String(ts) : undefined,
|
|
164
|
+
"data-text": lowerText,
|
|
165
|
+
"data-pinned": pinned ? 'true' : undefined,
|
|
166
|
+
"data-has-image": hasImage ? 'true' : undefined,
|
|
167
|
+
"data-has-embed": hasEmbed ? 'true' : undefined,
|
|
168
|
+
"data-has-attachment": hasAttachment ? 'true' : undefined,
|
|
169
|
+
children: [
|
|
170
|
+
pinned && (0, jsx_runtime_1.jsx)("div", { className: "dht-pinned-indicator", title: "Pinned message", children: "📌" }),
|
|
171
|
+
appliedTagPills,
|
|
172
|
+
(0, jsx_runtime_1.jsx)(reply_1.default, { message: message, context: context }),
|
|
173
|
+
message.interaction && ((0, jsx_runtime_1.jsx)("discord-command", { slot: "reply", profile: message.interaction.user.id, command: '/' + message.interaction.commandName })),
|
|
174
|
+
slashCmdLine,
|
|
175
|
+
isApp && (0, jsx_runtime_1.jsxs)("span", { className: "dht-app-badge", "data-i18n": "appBadge", children: [t(context, 'appBadge', 'APP'), verifiedBot && (0, jsx_runtime_1.jsx)("span", { className: "dht-verified-check", title: "Verified Bot", children: '✓' })] }),
|
|
176
|
+
isCrossposted && (0, jsx_runtime_1.jsx)("span", { className: "dht-badge dht-badge-crosspost", title: "Published from announcement channel", children: '📣 Published' }),
|
|
177
|
+
isCrosspost && (0, jsx_runtime_1.jsx)("span", { className: "dht-badge dht-badge-crosspost", title: "Follows another channel", children: '📡 Follow' }),
|
|
178
|
+
isSilent && (0, jsx_runtime_1.jsx)("span", { className: "dht-badge dht-badge-silent", title: "Silent message (no notification)", children: '🔕 Silent' }),
|
|
179
|
+
activity && (0, jsx_runtime_1.jsxs)("div", { className: "dht-activity", children: [
|
|
180
|
+
(0, jsx_runtime_1.jsx)("span", { className: "dht-activity-icon", children: '🎮' }),
|
|
181
|
+
(0, jsx_runtime_1.jsxs)("span", { children: [activity.type, ' invite', activity.partyId ? ` · Party ${activity.partyId.slice(-6)}` : ''] })
|
|
182
|
+
] }),
|
|
183
|
+
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') + ')' }),
|
|
184
|
+
Array.isArray(message.editHistory) && message.editHistory.length > 0 && renderEditHistory(message.editHistory, context),
|
|
185
|
+
snapshots.length > 0 && renderSnapshots(snapshots, context),
|
|
186
|
+
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 }) })),
|
|
187
|
+
(0, jsx_runtime_1.jsx)(attachment_1.Attachments, { message: message, context: context }),
|
|
188
|
+
isVoice && renderVoiceIndicator(message, context),
|
|
189
|
+
stickers.length > 0 && renderStickers(stickers),
|
|
190
|
+
poll && renderPoll(poll, context),
|
|
191
|
+
!suppressEmbeds && message.embeds.map((embed, id) => ((0, jsx_runtime_1.jsx)(embed_1.DiscordEmbed, { embed: embed, context: Object.assign({}, context, { index: id, message }) }, id))),
|
|
192
|
+
suppressEmbeds && message.embeds.length > 0 && (0, jsx_runtime_1.jsx)("span", { className: "dht-suppressed", "data-i18n": "suppressedEmbeds", children: t(context, 'suppressedEmbeds', '(embeds hidden)') }),
|
|
193
|
+
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))) })),
|
|
194
|
+
message.reactions.cache.size > 0 && ((0, jsx_runtime_1.jsx)("discord-reactions", {
|
|
195
|
+
slot: "reactions",
|
|
196
|
+
children: Array.from(message.reactions.cache.values()).map((reaction, id) => {
|
|
197
|
+
const total = reaction.count ?? 0;
|
|
198
|
+
const burstCount = reaction.count_details?.burst || reaction.burst_count || 0;
|
|
199
|
+
const burst = burstCount > 0;
|
|
200
|
+
const burstColors = Array.isArray(reaction.burst_colors) && reaction.burst_colors.length
|
|
201
|
+
? reaction.burst_colors.filter((c) => typeof c === 'string' && /^#[0-9a-fA-F]{3,8}$/.test(c))
|
|
202
|
+
: [];
|
|
203
|
+
const title = burst
|
|
204
|
+
? `${total} reactions (${burstCount} super)`
|
|
205
|
+
: `${total} reaction${total !== 1 ? 's' : ''}`;
|
|
206
|
+
const style = burst && burstColors.length
|
|
207
|
+
? { boxShadow: `inset 0 0 0 2px ${burstColors[0]}` }
|
|
208
|
+
: undefined;
|
|
209
|
+
return ((0, jsx_runtime_1.jsx)("discord-reaction", {
|
|
210
|
+
name: reaction.emoji?.name || ':unknown:',
|
|
211
|
+
emoji: (0, utils_1.parseDiscordEmoji)(reaction.emoji) || undefined,
|
|
212
|
+
count: abbrCount(total),
|
|
213
|
+
title,
|
|
214
|
+
"data-burst": burst ? 'true' : undefined,
|
|
215
|
+
className: burst ? 'dht-reaction--burst' : undefined,
|
|
216
|
+
style,
|
|
217
|
+
}, `${message.id}r${id}`));
|
|
218
|
+
})
|
|
219
|
+
})),
|
|
220
|
+
message.hasThread && message.thread && renderThread(message.thread, context),
|
|
221
|
+
]
|
|
222
|
+
}, message.id));
|
|
223
|
+
} catch (err) {
|
|
224
|
+
console.warn('[discord-html-transcripts-fix] Render failed', message?.id, err?.message || err);
|
|
225
|
+
const safeAuthor = (message?.author?.displayName || message?.author?.username) || 'Unknown';
|
|
226
|
+
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
227
|
}
|
|
77
228
|
}
|
|
78
229
|
|
|
230
|
+
function renderThread(thread, context) {
|
|
231
|
+
const cta = thread.messageCount
|
|
232
|
+
? `${thread.messageCount} Message${thread.messageCount > 1 ? 's' : ''}`
|
|
233
|
+
: 'View Thread';
|
|
234
|
+
const badges = [];
|
|
235
|
+
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'));
|
|
236
|
+
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'));
|
|
237
|
+
return (0, jsx_runtime_1.jsxs)("discord-thread", { slot: "thread", name: thread.name, cta, children: [
|
|
238
|
+
badges,
|
|
239
|
+
thread.lastMessage
|
|
240
|
+
? (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 }) }) })
|
|
241
|
+
: 'Thread messages not saved.'
|
|
242
|
+
] });
|
|
243
|
+
}
|
|
244
|
+
|
|
79
245
|
function renderStickers(stickers) {
|
|
80
246
|
return (0, jsx_runtime_1.jsx)("div", {
|
|
81
247
|
className: "dht-stickers",
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
248
|
+
children: stickers.map((s) => {
|
|
249
|
+
// Discord sticker formats: 1=PNG, 2=APNG (animated png), 3=Lottie (JSON), 4=GIF
|
|
250
|
+
const format = s.format;
|
|
251
|
+
const baseUrl = `https://media.discordapp.net/stickers/${s.id}`;
|
|
252
|
+
if (format === 3) {
|
|
253
|
+
// Lottie — render as iframe to discord's lottie player CDN page, or fallback link
|
|
254
|
+
return (0, jsx_runtime_1.jsxs)("div", { className: "dht-sticker dht-sticker--lottie", title: s.name || '', children: [
|
|
255
|
+
(0, jsx_runtime_1.jsx)("div", { className: "dht-sticker-placeholder", children: '🎞️' }),
|
|
256
|
+
(0, jsx_runtime_1.jsxs)("span", { className: "dht-sticker-name", children: [s.name || 'sticker', ' (Lottie)'] })
|
|
257
|
+
] }, s.id);
|
|
258
|
+
}
|
|
259
|
+
if (format === 4) {
|
|
260
|
+
return (0, jsx_runtime_1.jsx)("img", { src: s.url || `${baseUrl}.gif`, alt: s.name || 'sticker', title: s.name || '', className: "dht-sticker" }, s.id);
|
|
261
|
+
}
|
|
262
|
+
if (format === 2) {
|
|
263
|
+
return (0, jsx_runtime_1.jsx)("img", { src: s.url || `${baseUrl}.png`, alt: s.name || 'sticker', title: s.name || '', className: "dht-sticker dht-sticker--apng" }, s.id);
|
|
264
|
+
}
|
|
265
|
+
return (0, jsx_runtime_1.jsx)("img", { src: s.url || `${baseUrl}.png`, alt: s.name || 'sticker', title: s.name || '', className: "dht-sticker" }, s.id);
|
|
266
|
+
})
|
|
89
267
|
});
|
|
90
268
|
}
|
|
91
269
|
|
|
92
|
-
function renderPoll(poll) {
|
|
270
|
+
function renderPoll(poll, context) {
|
|
93
271
|
let totalVotes = 0;
|
|
94
272
|
const answers = poll.answers ? Array.from(poll.answers.values()) : [];
|
|
95
273
|
for (const a of answers) totalVotes += a.voteCount || 0;
|
|
96
|
-
const
|
|
274
|
+
const locale = context?.lang === 'de' ? 'de-DE' : 'en-US';
|
|
275
|
+
const expires = poll.expiresAt instanceof Date ? poll.expiresAt.toLocaleString(locale) : '';
|
|
97
276
|
|
|
98
277
|
return (0, jsx_runtime_1.jsxs)("div", {
|
|
99
278
|
className: "dht-poll",
|
|
100
|
-
style: {
|
|
101
|
-
border: '1px solid #2b2d31', background: '#1e1f22', borderRadius: '8px',
|
|
102
|
-
padding: '12px 14px', marginTop: '6px', maxWidth: '480px'
|
|
103
|
-
},
|
|
104
279
|
children: [
|
|
105
|
-
(0, jsx_runtime_1.jsxs)("div", {
|
|
280
|
+
(0, jsx_runtime_1.jsxs)("div", { className: "dht-poll-q", children: ['📊 ', poll.question?.text || 'Poll'] }),
|
|
106
281
|
(0, jsx_runtime_1.jsx)("div", {
|
|
107
|
-
|
|
282
|
+
className: "dht-poll-answers",
|
|
108
283
|
children: answers.map((a, i) => {
|
|
109
|
-
const text =
|
|
284
|
+
const text = a.text ?? a.media?.text ?? `Option ${i + 1}`;
|
|
110
285
|
const votes = a.voteCount || 0;
|
|
111
286
|
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);
|
|
287
|
+
return (0, jsx_runtime_1.jsxs)("div", { className: "dht-poll-a", children: [
|
|
288
|
+
(0, jsx_runtime_1.jsx)("div", { className: "dht-poll-bar", style: { width: pct + '%' } }),
|
|
289
|
+
(0, jsx_runtime_1.jsxs)("div", { className: "dht-poll-line", children: [
|
|
290
|
+
(0, jsx_runtime_1.jsx)("span", { children: String(text) }),
|
|
291
|
+
(0, jsx_runtime_1.jsxs)("span", { className: "dht-poll-pct", children: [votes, ' (', pct, '%)'] })
|
|
292
|
+
] })
|
|
293
|
+
] }, i);
|
|
127
294
|
})
|
|
128
295
|
}),
|
|
129
|
-
(0, jsx_runtime_1.jsxs)("div", {
|
|
130
|
-
children: [totalVotes, ' Stimme', totalVotes !== 1 ? 'n' : '', expires ? ' · endet ' + expires : '']
|
|
131
|
-
})
|
|
296
|
+
(0, jsx_runtime_1.jsxs)("div", { className: "dht-poll-foot", children: [totalVotes, ' votes', expires ? ' · ends ' + expires : ''] })
|
|
132
297
|
]
|
|
133
298
|
});
|
|
134
299
|
}
|
|
135
|
-
|
|
300
|
+
|
|
301
|
+
function renderVoiceIndicator(message, context) {
|
|
302
|
+
// FIX: discord.js Collection.find takes a predicate but on Map it conflicts; iterate values explicitly.
|
|
303
|
+
let audio = null;
|
|
304
|
+
if (message.attachments && typeof message.attachments.values === 'function') {
|
|
305
|
+
for (const a of message.attachments.values()) {
|
|
306
|
+
if (typeof a.contentType === 'string' && a.contentType.startsWith('audio/')) { audio = a; break; }
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const duration = audio?.duration_secs || audio?.durationSecs;
|
|
310
|
+
const waveformB64 = audio?.waveform;
|
|
311
|
+
let waveformRects = null;
|
|
312
|
+
let waveformWidth = 0;
|
|
313
|
+
if (typeof waveformB64 === 'string' && waveformB64.length > 0 && waveformB64.length < 16384) {
|
|
314
|
+
try {
|
|
315
|
+
const buf = Buffer.from(waveformB64, 'base64');
|
|
316
|
+
const bars = Array.from(buf).slice(0, 100);
|
|
317
|
+
const max = bars.length ? Math.max(1, ...bars) : 1;
|
|
318
|
+
waveformWidth = bars.length * 3;
|
|
319
|
+
waveformRects = bars.map((b, i) => {
|
|
320
|
+
const h = Math.max(2, (b / max) * 20);
|
|
321
|
+
return (0, jsx_runtime_1.jsx)("rect", { x: i * 3, y: (22 - h) / 2, width: 2, height: h, rx: 1, fill: "#5865F2" }, i);
|
|
322
|
+
});
|
|
323
|
+
} catch (_e) { waveformRects = null; }
|
|
324
|
+
}
|
|
325
|
+
return (0, jsx_runtime_1.jsxs)("div", { className: "dht-voice", children: [
|
|
326
|
+
(0, jsx_runtime_1.jsx)("span", { className: "dht-voice-icon", children: '🎤' }),
|
|
327
|
+
(0, jsx_runtime_1.jsx)("span", { className: "dht-voice-label", "data-i18n": "voiceMessage", children: t(context, 'voiceMessage', 'Voice message') }),
|
|
328
|
+
waveformRects && (0, jsx_runtime_1.jsx)("svg", {
|
|
329
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
330
|
+
viewBox: `0 0 ${waveformWidth} 22`,
|
|
331
|
+
width: waveformWidth,
|
|
332
|
+
height: 22,
|
|
333
|
+
className: "dht-voice-wave",
|
|
334
|
+
children: waveformRects,
|
|
335
|
+
}),
|
|
336
|
+
duration ? (0, jsx_runtime_1.jsxs)("span", { className: "dht-voice-dur", children: [Math.round(duration), 's'] }) : null,
|
|
337
|
+
] });
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function toArray(maybeColl) {
|
|
341
|
+
if (!maybeColl) return [];
|
|
342
|
+
if (Array.isArray(maybeColl)) return maybeColl;
|
|
343
|
+
if (typeof maybeColl.values === 'function') return Array.from(maybeColl.values());
|
|
344
|
+
if (typeof maybeColl === 'object') return Object.values(maybeColl);
|
|
345
|
+
return [];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function renderSnapshots(snapshots, context, depth = 0) {
|
|
349
|
+
const arr = toArray(snapshots);
|
|
350
|
+
if (arr.length === 0 || depth >= 5) return null; // hard-cap at 5 levels
|
|
351
|
+
return (0, jsx_runtime_1.jsx)("div", {
|
|
352
|
+
className: "dht-forwarded-wrap",
|
|
353
|
+
children: arr.map((snap, i) => {
|
|
354
|
+
const m = snap.message || snap;
|
|
355
|
+
const author = m?.author?.displayName || m?.author?.username || 'Unknown';
|
|
356
|
+
const text = m?.content || '';
|
|
357
|
+
const attachments = toArray(m?.attachments);
|
|
358
|
+
const nested = toArray(m?.messageSnapshots);
|
|
359
|
+
const embeds = toArray(m?.embeds);
|
|
360
|
+
const stickers = toArray(m?.stickers).map((s) => ({
|
|
361
|
+
id: s.id,
|
|
362
|
+
name: s.name,
|
|
363
|
+
url: s.url || `https://media.discordapp.net/stickers/${s.id}.png`,
|
|
364
|
+
format: s.format,
|
|
365
|
+
}));
|
|
366
|
+
return (0, jsx_runtime_1.jsxs)("div", { className: "dht-forwarded", "data-depth": depth, children: [
|
|
367
|
+
(0, jsx_runtime_1.jsxs)("div", { className: "dht-forwarded-head", children: [
|
|
368
|
+
(0, jsx_runtime_1.jsx)("span", { className: "dht-forwarded-icon", children: '↪' }),
|
|
369
|
+
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "forwardedFrom", children: t(context, 'forwardedFrom', 'Forwarded from') }),
|
|
370
|
+
(0, jsx_runtime_1.jsx)("strong", { children: ' ' + author }),
|
|
371
|
+
] }),
|
|
372
|
+
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.NORMAL }) }) }),
|
|
373
|
+
attachments.length > 0 && (0, jsx_runtime_1.jsxs)("div", { className: "dht-forwarded-attachments", children: [
|
|
374
|
+
(0, jsx_runtime_1.jsxs)("strong", { children: ['📎 ', attachments.length, ' attachment', attachments.length !== 1 ? 's' : '', ':'] }),
|
|
375
|
+
(0, jsx_runtime_1.jsx)("ul", { children: attachments.map((a, ai) => {
|
|
376
|
+
const safeUrl = (0, utils_1.safeHref)(a.url);
|
|
377
|
+
return (0, jsx_runtime_1.jsxs)("li", { children: [
|
|
378
|
+
a.contentType?.startsWith?.('image/') && a.url
|
|
379
|
+
? (0, jsx_runtime_1.jsx)("img", { src: safeUrl !== '#' ? safeUrl : '', alt: a.name || '', style: { maxWidth: '200px', borderRadius: '4px', display: 'block', margin: '4px 0' } })
|
|
380
|
+
: (0, jsx_runtime_1.jsxs)("a", { href: safeUrl, target: "_blank", rel: "noreferrer", children: [a.name || 'attachment', a.size ? ` (${Math.round(a.size / 1024)} KB)` : ''] })
|
|
381
|
+
] }, ai);
|
|
382
|
+
}) })
|
|
383
|
+
] }),
|
|
384
|
+
stickers.length > 0 && renderStickers(stickers),
|
|
385
|
+
embeds.length > 0 && (0, jsx_runtime_1.jsxs)("div", { className: "dht-forwarded-embeds", children: ['📑 ', embeds.length, ' embed', embeds.length !== 1 ? 's' : ''] }),
|
|
386
|
+
nested.length > 0 && renderSnapshots(nested, context, depth + 1),
|
|
387
|
+
] }, i);
|
|
388
|
+
})
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function renderSlashCommandLine(message, context) {
|
|
393
|
+
const interaction = message?.interaction;
|
|
394
|
+
const metadata = message?.interactionMetadata;
|
|
395
|
+
const optsRaw = message?.interactionOptions || interaction?.options;
|
|
396
|
+
// Build command path: /cmd group? subcommand? plus key:value options
|
|
397
|
+
if (!optsRaw && !metadata) return null;
|
|
398
|
+
|
|
399
|
+
const opts = Array.isArray(optsRaw) ? optsRaw : [];
|
|
400
|
+
let cmdParts = [interaction?.commandName || '?'];
|
|
401
|
+
let valueOpts = opts;
|
|
402
|
+
|
|
403
|
+
// Detect subcommand groups: nested option structure { type:2/1 (group/subcommand), name, options }
|
|
404
|
+
for (let i = 0; i < 2; i++) {
|
|
405
|
+
if (valueOpts.length === 1 && valueOpts[0] && (valueOpts[0].type === 1 || valueOpts[0].type === 2)) {
|
|
406
|
+
cmdParts.push(valueOpts[0].name);
|
|
407
|
+
valueOpts = Array.isArray(valueOpts[0].options) ? valueOpts[0].options : [];
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
// Filter out autocomplete-only `focused` options
|
|
411
|
+
valueOpts = valueOpts.filter((o) => o && !o.focused);
|
|
412
|
+
|
|
413
|
+
if (!valueOpts.length && cmdParts.length === 1 && !metadata) return null;
|
|
414
|
+
|
|
415
|
+
const triggeredBy = metadata?.user || interaction?.user;
|
|
416
|
+
|
|
417
|
+
return (0, jsx_runtime_1.jsxs)("div", { className: "dht-slash-options", children: [
|
|
418
|
+
(0, jsx_runtime_1.jsx)("span", { className: "dht-slash-cmd-name", children: '/' + cmdParts.filter(Boolean).join(' ') }),
|
|
419
|
+
valueOpts.map((o, i) => (0, jsx_runtime_1.jsxs)("span", { className: "dht-slash-opt", children: [
|
|
420
|
+
(0, jsx_runtime_1.jsxs)("span", { className: "dht-slash-opt-name", children: [String(o.name), ':'] }),
|
|
421
|
+
' ',
|
|
422
|
+
(0, jsx_runtime_1.jsx)("span", { className: "dht-slash-opt-val", children: String(o.value ?? '') })
|
|
423
|
+
] }, i)),
|
|
424
|
+
triggeredBy && (0, jsx_runtime_1.jsxs)("span", { className: "dht-slash-opt", children: ['by ', (0, jsx_runtime_1.jsx)("strong", { children: triggeredBy.displayName || triggeredBy.username || triggeredBy.id })] })
|
|
425
|
+
] });
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function renderEditHistory(history, context) {
|
|
429
|
+
// Optional feature — host bot may attach `message.editHistory: [{content, editedAt}, …]`
|
|
430
|
+
return (0, jsx_runtime_1.jsxs)("details", { className: "dht-edit-history", children: [
|
|
431
|
+
(0, jsx_runtime_1.jsx)("summary", { "data-i18n": "editHistoryTitle", children: t(context, 'editHistoryTitle', 'Edit history') }),
|
|
432
|
+
(0, jsx_runtime_1.jsx)("ol", { children: history.map((entry, i) => {
|
|
433
|
+
const when = entry.editedAt instanceof Date ? entry.editedAt.toISOString() : (typeof entry.editedAt === 'string' ? entry.editedAt : '');
|
|
434
|
+
return (0, jsx_runtime_1.jsxs)("li", { children: [
|
|
435
|
+
when && (0, jsx_runtime_1.jsxs)("time", { dateTime: when, children: [t(context, 'editHistoryAt', 'at'), ' ', when] }),
|
|
436
|
+
(0, jsx_runtime_1.jsx)("div", { children: String(entry.content || '') })
|
|
437
|
+
] }, i);
|
|
438
|
+
}) })
|
|
439
|
+
] });
|
|
440
|
+
}
|
|
441
|
+
//# sourceMappingURL=message.js.map
|
|
@@ -1,53 +1,72 @@
|
|
|
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.default = MessageReply;
|
|
37
8
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
9
|
const discord_js_1 = require("discord.js");
|
|
39
10
|
const content_1 = __importStar(require("./content"));
|
|
11
|
+
|
|
12
|
+
function t(context, key, fallback) {
|
|
13
|
+
const dict = context?.i18n?.[context?.lang] || context?.i18n?.en || {};
|
|
14
|
+
return dict[key] || fallback || key;
|
|
15
|
+
}
|
|
16
|
+
|
|
40
17
|
async function MessageReply({ message, context }) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
18
|
+
if (!message.reference) return null;
|
|
19
|
+
|
|
20
|
+
const isCrossGuild = message.reference.guildId !== message.guild?.id;
|
|
21
|
+
|
|
22
|
+
// Cross-guild reply: show a placeholder pill instead of silently dropping.
|
|
23
|
+
if (isCrossGuild) {
|
|
24
|
+
return (0, jsx_runtime_1.jsx)("discord-reply", {
|
|
25
|
+
slot: "reply",
|
|
26
|
+
server: true,
|
|
27
|
+
children: (0, jsx_runtime_1.jsx)("em", { "data-i18n": "crossServerReply", children: t(context, 'crossServerReply', 'Message from another server') })
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
46
31
|
const referencedMessage = context.messages.find((m) => m.id === message.reference.messageId);
|
|
47
32
|
if (!referencedMessage)
|
|
48
33
|
return (0, jsx_runtime_1.jsx)("discord-reply", { slot: "reply", children: "Message could not be loaded." });
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
34
|
+
|
|
35
|
+
const isCrossPost = referencedMessage.reference && referencedMessage.reference.guildId !== message.guild?.id;
|
|
36
|
+
const isCommand = !!referencedMessage.interaction;
|
|
37
|
+
// Special-case: replying to a forwarded-only / Components-V2-only message has empty content
|
|
38
|
+
const hasContent = !!referencedMessage.content;
|
|
39
|
+
const hasSnapshot = Array.isArray(referencedMessage.messageSnapshots) && referencedMessage.messageSnapshots.length > 0;
|
|
40
|
+
const hasComponents = Array.isArray(referencedMessage.components) && referencedMessage.components.length > 0;
|
|
41
|
+
const hasSticker = referencedMessage.stickers?.size > 0;
|
|
42
|
+
const author = referencedMessage.member?.nickname ?? referencedMessage.author.displayName ?? referencedMessage.author.username;
|
|
43
|
+
const avatar = referencedMessage.author.avatarURL?.({ size: 32 }) ?? undefined;
|
|
44
|
+
const roleColor = referencedMessage.member?.displayHexColor ?? undefined;
|
|
45
|
+
const op = referencedMessage.author.id === message?.channel?.ownerId && message?.channel?.isThread?.();
|
|
46
|
+
|
|
47
|
+
return ((0, jsx_runtime_1.jsx)("discord-reply", {
|
|
48
|
+
slot: "reply",
|
|
49
|
+
edited: !isCommand && referencedMessage.editedAt !== null,
|
|
50
|
+
attachment: referencedMessage.attachments.size > 0,
|
|
51
|
+
author: author,
|
|
52
|
+
avatar: avatar,
|
|
53
|
+
roleColor: roleColor,
|
|
54
|
+
bot: !isCrossPost && referencedMessage.author.bot,
|
|
55
|
+
verified: referencedMessage.author.flags?.has?.(discord_js_1.UserFlags.VerifiedBot),
|
|
56
|
+
op: op,
|
|
57
|
+
server: isCrossPost ?? undefined,
|
|
58
|
+
command: isCommand,
|
|
59
|
+
children: hasContent
|
|
60
|
+
? (0, jsx_runtime_1.jsx)("span", { "data-goto": referencedMessage.id, className: "reply-inline", children: (0, jsx_runtime_1.jsx)(content_1.default, { content: referencedMessage.content, context: Object.assign({}, context, { type: content_1.RenderType.REPLY }) }) })
|
|
61
|
+
: isCommand
|
|
62
|
+
? (0, jsx_runtime_1.jsx)("em", { "data-goto": referencedMessage.id, children: "Click to see command." })
|
|
63
|
+
: hasSnapshot
|
|
64
|
+
? (0, jsx_runtime_1.jsx)("em", { "data-goto": referencedMessage.id, children: "Click to see forwarded message." })
|
|
65
|
+
: hasComponents
|
|
66
|
+
? (0, jsx_runtime_1.jsx)("em", { "data-goto": referencedMessage.id, children: "Click to see message." })
|
|
67
|
+
: hasSticker
|
|
68
|
+
? (0, jsx_runtime_1.jsx)("em", { "data-goto": referencedMessage.id, children: "Click to see sticker." })
|
|
69
|
+
: (0, jsx_runtime_1.jsx)("em", { "data-goto": referencedMessage.id, children: "Click to see attachment." })
|
|
70
|
+
}));
|
|
52
71
|
}
|
|
53
|
-
//# sourceMappingURL=reply.js.map
|
|
72
|
+
//# sourceMappingURL=reply.js.map
|