discord-html-transcripts-fix
Version:
A nicely formatted html transcript generator for discord.js. Bugfix fork with support for the latest discord.js and Components v2.
297 lines (274 loc) • 17.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = SystemMessage;
exports.Highlight = Highlight;
exports.JoinMessage = JoinMessage;
const jsx_runtime_1 = require("react/jsx-runtime");
const discord_js_1 = require("discord.js");
const utils_1 = require("../../utils/utils");
function t(context, key, fallback) {
const dict = context?.i18n?.[context?.lang] || context?.i18n?.en || {};
return dict[key] || fallback || key;
}
async function SystemMessage({ message, context }) {
const author = message.author;
const authorName = author?.displayName ?? author?.username ?? 'Unknown';
const color = message.member?.roles?.color?.hexColor;
const authorId = author?.id;
const ctx = context || {};
// FIX: pass message.createdAt as ISO string so skyra renders the original
// timestamp instead of falling back to "now" (= transcript creation time).
const ts = message.createdAt instanceof Date
? message.createdAt.toISOString()
: (typeof message.createdAt === 'string' ? message.createdAt : undefined);
switch (message.type) {
case discord_js_1.MessageType.RecipientAdd:
case discord_js_1.MessageType.UserJoin:
return ((0, jsx_runtime_1.jsx)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "join", children: (0, jsx_runtime_1.jsx)(JoinMessage, { member: message.member, fallbackUser: author, seed: message.id, context: ctx }) }, message.id));
case discord_js_1.MessageType.ChannelPinnedMessage: {
const refId = message.reference?.messageId;
// Discord renders this as: "{user} pinned {a message} to this channel."
// where "a message" is the clickable jump link. The i18n key `pinned`
// accepts a `{message}` placeholder; if absent (legacy dict), we fall
// back to flat text + a trailing " (jump)".
const template = t(ctx, 'pinned', 'pinned {message} to this channel.');
const linkLabel = t(ctx, 'pinnedLink', 'a message');
const linkEl = refId
? (0, jsx_runtime_1.jsx)("i", { "data-goto": refId, className: "dht-jump", role: "button", tabIndex: 0, children: linkLabel })
: (0, jsx_runtime_1.jsx)("strong", { children: linkLabel });
const parts = template.includes('{message}') ? template.split('{message}') : null;
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "pin", children: parts
? [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ',
parts[0],
linkEl,
parts[1],
]
: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ',
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "pinned", children: template }),
refId ? (0, jsx_runtime_1.jsx)("i", { "data-goto": refId, className: "dht-jump", role: "button", tabIndex: 0, children: " (jump)" }) : null,
]
}, message.id));
}
case discord_js_1.MessageType.GuildBoost:
case discord_js_1.MessageType.GuildBoostTier1:
case discord_js_1.MessageType.GuildBoostTier2:
case discord_js_1.MessageType.GuildBoostTier3:
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "boost", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ',
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "boosted", children: t(ctx, 'boosted', 'boosted the server!') }),
] }, message.id));
case discord_js_1.MessageType.ThreadStarterMessage:
case discord_js_1.MessageType.ThreadCreated: {
const refId = message.reference?.messageId;
const label = message.content || 'New thread';
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "thread", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ',
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "threadStarted", children: t(ctx, 'threadStarted', 'started a thread:') }),
' ',
refId
? (0, jsx_runtime_1.jsx)("i", { "data-goto": refId, className: "dht-jump", role: "button", tabIndex: 0, children: label })
: (0, jsx_runtime_1.jsx)("i", { children: label }),
] }, message.id));
}
case discord_js_1.MessageType.ChannelNameChange:
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "edit", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ',
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "changedChannelName", children: t(ctx, 'changedChannelName', 'changed the channel name:') }),
' ',
(0, jsx_runtime_1.jsx)("strong", { children: message.content }),
] }, message.id));
case discord_js_1.MessageType.ChannelIconChange:
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "edit", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ',
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "changedChannelIcon", children: t(ctx, 'changedChannelIcon', 'changed the channel icon.') }),
] }, message.id));
case discord_js_1.MessageType.ChatInputCommand:
case discord_js_1.MessageType.ContextMenuCommand: {
const cmd = message.interaction?.commandName ? '/' + message.interaction.commandName : 'a command';
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "call", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ',
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "usedCommand", children: t(ctx, 'usedCommand', 'used') }),
' ',
(0, jsx_runtime_1.jsx)("strong", { children: cmd }),
] }, message.id));
}
case discord_js_1.MessageType.Call:
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "call", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ',
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "startedCall", children: t(ctx, 'startedCall', 'started a call.') }),
] }, message.id));
case discord_js_1.MessageType.ChannelFollowAdd: {
const followText = t(ctx, 'addedFollow', 'added {target} to follow this channel.');
const parts = followText.split('{target}');
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "edit", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ',
parts[0],
(0, jsx_runtime_1.jsxs)("span", { className: "dht-mention dht-mention--channel", children: ['#', message.content || 'channel'] }),
parts[1] || '',
] }, message.id));
}
case discord_js_1.MessageType.RecipientRemove:
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "leave", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ',
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "left", children: t(ctx, 'left', 'left.') }),
] }, message.id));
case discord_js_1.MessageType.AutoModerationAction: {
// Discord packs detail into the first embed: rule_name, matched_keyword, action_type, channel_id
const e = message.embeds?.[0];
const fields = Array.isArray(e?.fields) ? e.fields : [];
const ruleName = fields.find((f) => /rule_name|rule name/i.test(f.name))?.value || e?.title || '';
const matchedKeyword = fields.find((f) => /matched_keyword|keyword/i.test(f.name))?.value || '';
const action = fields.find((f) => /action_type|action/i.test(f.name))?.value || '';
const blocked = e?.description || '';
const channelId = fields.find((f) => /channel/i.test(f.name))?.value;
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "boost", children: [
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "autoModBlocked", children: t(ctx, 'autoModBlocked', 'AutoMod blocked a message.') }),
ruleName && (0, jsx_runtime_1.jsxs)("span", { className: "dht-automod-rule", children: [' · Rule: ', (0, jsx_runtime_1.jsx)("strong", { children: ruleName })] }),
action && (0, jsx_runtime_1.jsxs)("span", { className: "dht-automod-rule", children: [' · Action: ', action] }),
matchedKeyword && (0, jsx_runtime_1.jsxs)("span", { className: "dht-automod-rule", children: [' · Matched: ', (0, jsx_runtime_1.jsx)("code", { children: matchedKeyword })] }),
blocked && (0, jsx_runtime_1.jsx)("blockquote", { className: "dht-automod-content", children: blocked }),
channelId && (0, jsx_runtime_1.jsxs)("span", { className: "dht-automod-rule", children: [' · In <#', channelId, '>'] }),
] }, message.id));
}
case discord_js_1.MessageType.StageStart:
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "call", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' started a stage: ',
(0, jsx_runtime_1.jsx)("strong", { children: message.content || '' })
] }, message.id));
case discord_js_1.MessageType.StageEnd:
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "leave", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ended the stage.'
] }, message.id));
case discord_js_1.MessageType.StageSpeaker:
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "call", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' is now a speaker.'
] }, message.id));
case discord_js_1.MessageType.StageRaiseHand:
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "call", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' requested to speak.'
] }, message.id));
case discord_js_1.MessageType.StageTopic:
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "edit", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' changed the stage topic: ',
(0, jsx_runtime_1.jsx)("strong", { children: message.content || '' })
] }, message.id));
case discord_js_1.MessageType.GuildApplicationPremiumSubscription:
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "boost", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' subscribed ',
(0, jsx_runtime_1.jsx)("strong", { children: message.applicationId ? `app ${message.applicationId}` : 'an app' }),
' to this server.'
] }, message.id));
case discord_js_1.MessageType.RoleSubscriptionPurchase: {
const roleName = message.roleSubscriptionData?.tierName || message.content || 'a role';
return ((0, jsx_runtime_1.jsxs)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "boost", children: [
(0, jsx_runtime_1.jsx)(Highlight, { color, userId: authorId, children: authorName }),
' ',
t(ctx, 'roleSubPurchased', 'subscribed to {role}!').replace('{role}', roleName),
] }, message.id));
}
case discord_js_1.MessageType.GuildIncidentReportRaid:
case discord_js_1.MessageType.GuildIncidentReportFalseAlarm:
case discord_js_1.MessageType.GuildIncidentAlertModeEnabled:
case discord_js_1.MessageType.GuildIncidentAlertModeDisabled:
return ((0, jsx_runtime_1.jsx)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "boost", children: (0, jsx_runtime_1.jsx)("span", { "data-i18n": "serverAlert", children: t(ctx, 'serverAlert', 'Server safety alert.') }) }, message.id));
case discord_js_1.MessageType.PollResult:
return ((0, jsx_runtime_1.jsx)("discord-system-message", { id: `m-${message.id}`, timestamp: ts, type: "edit", children: (0, jsx_runtime_1.jsx)("span", { "data-i18n": "pollEnded", children: t(ctx, 'pollEnded', 'A poll ended.') }) }, message.id));
// These render through the normal pipeline
case discord_js_1.MessageType.Default:
case discord_js_1.MessageType.Reply:
return undefined;
default:
return undefined;
}
}
function Highlight({ children, color, userId }) {
const safe = typeof color === 'string' && /^#[0-9a-fA-F]{3,8}$/.test(color) ? color : 'white';
const props = { style: { color: safe } };
// Wire up the user popup the same way mentions and message authors do.
if (typeof userId === 'string' && userId) {
props.className = 'dht-sys-author';
props['data-mention-type'] = 'user';
props['data-mention-id'] = userId;
}
return (0, jsx_runtime_1.jsx)("i", Object.assign({}, props, { children }));
}
const allJoinMessages = [
'{user} just joined the server - glhf!',
'{user} just joined. Everyone, look busy!',
'{user} just joined. Can I get a heal?',
'{user} joined your party.',
'{user} joined. You must construct additional pylons.',
'Ermagherd. {user} is here.',
'Welcome, {user}. Stay awhile and listen.',
'Welcome, {user}. We were expecting you ( ͡° ͜ʖ ͡°)',
'Welcome, {user}. We hope you brought pizza.',
'Welcome {user}. Leave your weapons by the door.',
'A wild {user} appeared.',
'Swoooosh. {user} just landed.',
'Brace yourselves {user} just joined the server.',
'{user} just joined. Hide your bananas.',
'{user} just arrived. Seems OP - please nerf.',
'{user} just slid into the server.',
'A {user} has spawned in the server.',
'Big {user} showed up!',
"Where's {user}? In the server!",
'{user} hopped into the server. Kangaroo!!',
'{user} just showed up. Hold my beer.',
'Challenger approaching - {user} has appeared!',
"It's a bird! It's a plane! Nevermind, it's just {user}.",
"It's {user}! Praise the sun! \\\\[T]/",
'Never gonna give {user} up. Never gonna let {user} down.',
'Ha! {user} has joined! You activated my trap card!',
'Cheers, love! {user} is here!',
'Hey! Listen! {user} has joined!',
"We've been expecting you {user}",
"It's dangerous to go alone, take {user}!",
"{user} has joined the server! It's super effective!",
'{user} is here, as the prophecy foretold.',
"{user} has arrived. Party's over.",
'Ready player {user}',
'{user} is here to kick butt and chew bubblegum. And {user} is all out of gum.',
"Hello. Is it {user} you're looking for?",
];
// Deterministic — seed off message.id so the message text stays stable across re-renders.
function pickJoinMessage(seed) {
if (!seed) return allJoinMessages[0];
try {
// Hash snowflake → index. BigInt safe for snowflakes.
let n = 0;
for (let i = 0; i < seed.length; i++) n = (n * 31 + seed.charCodeAt(i)) >>> 0;
return allJoinMessages[n % allJoinMessages.length];
} catch (_e) {
return allJoinMessages[0];
}
}
function JoinMessage({ member, fallbackUser, seed, context }) {
const template = pickJoinMessage(seed);
const uid = member?.id || fallbackUser?.id;
return template
.split('{user}')
.flatMap((item, i) => [
item,
(0, jsx_runtime_1.jsx)(Highlight, { color: member?.roles?.color?.hexColor, userId: uid, children: member?.nickname ?? fallbackUser.displayName ?? fallbackUser.username }, i),
])
.slice(0, -1);
}
//# sourceMappingURL=systemMessage.js.map