discord2html 1.0.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/LICENSE +201 -0
- package/README.md +139 -0
- package/dist/chunk-3GDQP6AS.mjs +16 -0
- package/dist/chunk-3GDQP6AS.mjs.map +1 -0
- package/dist/index.d.mts +147 -0
- package/dist/index.d.ts +147 -0
- package/dist/index.js +7961 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1365 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib-V24P5ASW.mjs +6588 -0
- package/dist/lib-V24P5ASW.mjs.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/generator/index.tsx","../src/utils/buildProfiles.ts","../src/static/client.ts","../src/generator/transcript.tsx","../src/generator/renderers/content.tsx","../src/utils/utils.ts","../src/generator/renderers/message.tsx","../src/generator/renderers/attachment.tsx","../src/generator/renderers/components.tsx","../src/generator/renderers/components/Select Menu.tsx","../src/generator/renderers/components/utils.ts","../src/generator/renderers/components/styles.ts","../src/generator/renderers/components/Container.tsx","../src/generator/renderers/components/section/Section.tsx","../src/generator/renderers/components/section/SectionContent.tsx","../src/generator/renderers/components/section/SectionAccessory.tsx","../src/generator/renderers/components/Media Gallery.tsx","../src/generator/renderers/components/Spacing.tsx","../src/generator/renderers/components/Button.tsx","../src/generator/renderers/components/Thumbnail.tsx","../src/generator/renderers/embed.tsx","../src/utils/embeds.ts","../src/generator/renderers/reply.tsx","../src/generator/renderers/systemMessage.tsx","../src/types.ts","../src/downloader/images.ts"],"sourcesContent":["import { AttachmentBuilder, version, Collection, type Channel, type Message, type TextBasedChannel } from 'discord.js';\r\nimport DiscordMessages from './generator';\r\nimport {\r\n ExportReturnType,\r\n type CreateTranscriptOptions,\r\n type GenerateFromMessagesOptions,\r\n type ObjectType,\r\n} from './types';\r\nimport { TranscriptImageDownloader, type ResolveImageCallback } from './downloader/images';\r\n\r\n// re-exports\r\nexport { default as DiscordMessages } from './generator/transcript';\r\nexport { TranscriptImageDownloader } from './downloader/images';\r\n\r\n// version check\r\nconst versionPrefix = version.split('.')[0];\r\n\r\nif (versionPrefix !== '14' && versionPrefix !== '15') {\r\n console.error(\r\n `[discord2html] discord2html requires discord.js v14.x.x or v15.x.x, but you are using v${version}.` +\r\n ` For v13.x.x support, use the original package: \"npm install discord-html-transcripts@^2\".`\r\n );\r\n process.exit(1);\r\n}\r\n\r\n/**\r\n *\r\n * @param messages The messages to generate a transcript from\r\n * @param channel The channel the messages are from (used for header and guild name)\r\n * @param options The options to use when generating the transcript\r\n * @returns The generated transcript\r\n */\r\nexport async function generateFromMessages<T extends ExportReturnType = ExportReturnType.Attachment>(\r\n messages: Message[] | Collection<string, Message>,\r\n channel: Channel,\r\n options: GenerateFromMessagesOptions<T> = {}\r\n): Promise<ObjectType<T>> {\r\n // turn messages into an array\r\n const transformedMessages = messages instanceof Collection ? Array.from(messages.values()) : messages;\r\n\r\n // figure out how the user wants images saved\r\n let resolveImageSrc: ResolveImageCallback = options.callbacks?.resolveImageSrc ?? ((attachment) => attachment.url);\r\n if (options.saveImages) {\r\n if (options.callbacks?.resolveImageSrc) {\r\n console.warn(\r\n `[discord2html] You have specified both saveImages and resolveImageSrc, please only specify one. resolveImageSrc will be used.`\r\n );\r\n } else {\r\n resolveImageSrc = new TranscriptImageDownloader().build();\r\n console.log('Using default downloader');\r\n }\r\n }\r\n\r\n // render the messages\r\n const html = await DiscordMessages({\r\n messages: transformedMessages,\r\n channel,\r\n saveImages: options.saveImages ?? false,\r\n callbacks: {\r\n resolveImageSrc,\r\n resolveChannel: async (id) => channel.client.channels.fetch(id).catch(() => null),\r\n resolveUser: async (id) => channel.client.users.fetch(id).catch(() => null),\r\n resolveRole: channel.isDMBased() ? () => null : async (id) => channel.guild?.roles.fetch(id).catch(() => null),\r\n\r\n ...(options.callbacks ?? {}),\r\n },\r\n poweredBy: options.poweredBy ?? true,\r\n footerText: options.footerText ?? 'Exported {number} message{s}.',\r\n favicon: options.favicon ?? 'guild',\r\n hydrate: options.hydrate ?? false,\r\n });\r\n\r\n // get the time it took to render the messages\r\n // const renderTime = process.hrtime(startTime);\r\n // console.log(\r\n // `[discord2html] Rendered ${transformedMessages.length} messages in ${renderTime[0]}s ${\r\n // renderTime[1] / 1000000\r\n // }ms`\r\n // );\r\n\r\n // return the html in the specified format\r\n if (options.returnType === ExportReturnType.Buffer) {\r\n return Buffer.from(html) as unknown as ObjectType<T>;\r\n }\r\n\r\n if (options.returnType === ExportReturnType.String) {\r\n return html as unknown as ObjectType<T>;\r\n }\r\n\r\n return new AttachmentBuilder(Buffer.from(html), {\r\n name: options.filename ?? `transcript-${channel.id}.html`,\r\n }) as unknown as ObjectType<T>;\r\n}\r\n\r\n/**\r\n *\r\n * @param channel The channel to create a transcript from\r\n * @param options The options to use when creating the transcript\r\n * @returns The generated transcript\r\n */\r\nexport async function createTranscript<T extends ExportReturnType = ExportReturnType.Attachment>(\r\n channel: TextBasedChannel,\r\n options: CreateTranscriptOptions<T> = {}\r\n): Promise<ObjectType<T>> {\r\n // validate type\r\n if (!channel.isTextBased()) {\r\n // @ts-expect-error(2339): run-time check\r\n throw new TypeError(`Provided channel must be text-based, received ${channel.type}`);\r\n }\r\n\r\n // fetch messages\r\n let allMessages: Message[] = [];\r\n let lastMessageId: string | undefined;\r\n const { limit, filter } = options;\r\n const resolvedLimit = typeof limit === 'undefined' || limit === -1 ? Infinity : limit;\r\n\r\n // until there are no more messages, keep fetching\r\n\r\n while (true) {\r\n // create fetch options\r\n const fetchLimitOptions = { limit: 100, before: lastMessageId };\r\n if (!lastMessageId) delete fetchLimitOptions.before;\r\n\r\n // fetch messages\r\n const messages = await channel.messages.fetch(fetchLimitOptions);\r\n const filteredMessages = typeof filter === 'function' ? messages.filter(filter) : messages;\r\n\r\n // add the messages to the array\r\n allMessages.push(...filteredMessages.values());\r\n // Get the last key of 'messages', not 'filteredMessages' because you will be refetching the same messages\r\n lastMessageId = messages.lastKey();\r\n\r\n // if there are no more messages, break\r\n if (messages.size < 100) break;\r\n\r\n // if the limit has been reached, break\r\n if (allMessages.length >= resolvedLimit) break;\r\n }\r\n\r\n if (resolvedLimit < allMessages.length) allMessages = allMessages.slice(0, limit);\r\n\r\n // generate the transcript\r\n return generateFromMessages<T>(allMessages.reverse(), channel, options);\r\n}\r\n\r\nexport default {\r\n createTranscript,\r\n generateFromMessages,\r\n};\r\nexport * from './types';\r\n","import { type Awaitable, type Channel, type Message, type Role, type User } from 'discord.js';\r\nimport { prerenderToNodeStream } from 'react-dom/static';\r\nimport React from 'react';\r\nimport { buildProfiles } from '../utils/buildProfiles';\r\nimport { revealSpoiler, scrollToMessage } from '../static/client';\r\nimport { readFileSync } from 'fs';\r\nimport path from 'path';\r\nimport { renderToString } from '@derockdev/discord-components-core/hydrate';\r\nimport DiscordMessages from './transcript';\r\nimport type { ResolveImageCallback } from '../downloader/images';\r\nimport { streamToString } from '../utils/utils';\r\n\r\n// read the package.json file and get the @derockdev/discord-components-core version\r\nlet discordComponentsVersion = '^3.6.1';\r\n\r\ntry {\r\n const packagePath = path.join(__dirname, '..', '..', 'package.json');\r\n const packageJSON = JSON.parse(readFileSync(packagePath, 'utf8'));\r\n discordComponentsVersion = packageJSON.dependencies['@derockdev/discord-components-core'] ?? discordComponentsVersion;\r\n // eslint-disable-next-line no-empty\r\n} catch {} // ignore errors\r\n\r\nexport type RenderMessageContext = {\r\n messages: Message[];\r\n channel: Channel;\r\n\r\n callbacks: {\r\n resolveImageSrc: ResolveImageCallback;\r\n resolveChannel: (channelId: string) => Awaitable<Channel | null>;\r\n resolveUser: (userId: string) => Awaitable<User | null>;\r\n resolveRole: (roleId: string) => Awaitable<Role | null>;\r\n };\r\n\r\n poweredBy?: boolean;\r\n footerText?: string;\r\n saveImages: boolean;\r\n favicon: 'guild' | string;\r\n hydrate: boolean;\r\n};\r\n\r\nexport default async function render({ messages, channel, callbacks, ...options }: RenderMessageContext) {\r\n const profiles = buildProfiles(messages);\r\n\r\n const { prelude } = await prerenderToNodeStream(\r\n <html>\r\n <head>\r\n <meta charSet=\"utf-8\" />\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\r\n\r\n {/* favicon */}\r\n <link\r\n rel=\"icon\"\r\n type=\"image/png\"\r\n href={\r\n options.favicon === 'guild'\r\n ? channel.isDMBased()\r\n ? undefined\r\n : (channel.guild.iconURL({ size: 16, extension: 'png' }) ?? undefined)\r\n : options.favicon\r\n }\r\n />\r\n\r\n {/* title */}\r\n <title>{channel.isDMBased() ? 'Direct Messages' : channel.name}</title>\r\n\r\n {/* message reference handler */}\r\n <script\r\n dangerouslySetInnerHTML={{\r\n __html: scrollToMessage,\r\n }}\r\n />\r\n\r\n {!options.hydrate && (\r\n <>\r\n {/* profiles */}\r\n <script\r\n dangerouslySetInnerHTML={{\r\n __html: `window.$discordMessage={profiles:${JSON.stringify(await profiles)}}`,\r\n }}\r\n ></script>\r\n {/* component library */}\r\n <script\r\n type=\"module\"\r\n src={`https://cdn.jsdelivr.net/npm/@derockdev/discord-components-core@${discordComponentsVersion}/dist/derockdev-discord-components-core/derockdev-discord-components-core.esm.js`}\r\n ></script>\r\n </>\r\n )}\r\n </head>\r\n\r\n <body\r\n style={{\r\n margin: 0,\r\n minHeight: '100vh',\r\n }}\r\n >\r\n <DiscordMessages messages={messages} channel={channel} callbacks={callbacks} {...options} />\r\n </body>\r\n\r\n {/* Make sure the script runs after the DOM has loaded */}\r\n {options.hydrate && <script dangerouslySetInnerHTML={{ __html: revealSpoiler }}></script>}\r\n </html>\r\n );\r\n\r\n const markup = await streamToString(prelude);\r\n\r\n if (options.hydrate) {\r\n const result = await renderToString(markup, {\r\n beforeHydrate: async (document) => {\r\n document.defaultView.$discordMessage = {\r\n profiles: await profiles,\r\n };\r\n },\r\n });\r\n\r\n return result.html;\r\n }\r\n\r\n return markup;\r\n}\r\n","import { type GuildMember, type Message, type User, UserFlags } from 'discord.js';\r\n\r\nexport type Profile = {\r\n author: string; // author of the message\r\n avatar?: string; // avatar of the author\r\n roleColor?: string; // role color of the author\r\n roleIcon?: string; // role color of the author\r\n roleName?: string; // role name of the author\r\n\r\n bot?: boolean; // is the author a bot\r\n verified?: boolean; // is the author verified\r\n};\r\n\r\nexport async function buildProfiles(messages: Message[]) {\r\n const profiles: Record<string, Profile> = {};\r\n\r\n // loop through messages\r\n for (const message of messages) {\r\n // add all users\r\n const author = message.author;\r\n if (!profiles[author.id]) {\r\n // add profile\r\n profiles[author.id] = buildProfile(message.member, author);\r\n }\r\n\r\n // add interaction users\r\n if (message.interaction) {\r\n const user = message.interaction.user;\r\n if (!profiles[user.id]) {\r\n profiles[user.id] = buildProfile(null, user);\r\n }\r\n }\r\n\r\n // threads\r\n if (message.thread && message.thread.lastMessage) {\r\n profiles[message.thread.lastMessage.author.id] = buildProfile(\r\n message.thread.lastMessage.member,\r\n message.thread.lastMessage.author\r\n );\r\n }\r\n }\r\n\r\n // return as a JSON\r\n return profiles;\r\n}\r\n\r\nfunction buildProfile(member: GuildMember | null, author: User) {\r\n return {\r\n author: member?.nickname ?? author.displayName ?? author.username,\r\n avatar: member?.displayAvatarURL({ size: 64 }) ?? author.displayAvatarURL({ size: 64 }),\r\n roleColor: member?.displayHexColor,\r\n roleIcon: member?.roles.icon?.iconURL() ?? undefined,\r\n roleName: member?.roles.hoist?.name ?? undefined,\r\n bot: author.bot,\r\n verified: author.flags?.has(UserFlags.VerifiedBot),\r\n };\r\n}\r\n","// TODO: create some sort of build system to compile this file\r\n\r\n/*\r\n// whenever user clicks on element with data-goto attribute, scroll to that message\r\ndocument.addEventListener('click', (e) => {\r\n const target = e.target;\r\n if(!target) return;\r\n\r\n const goto = target?.getAttribute('data-goto');\r\n\r\n if (goto) {\r\n const message = document.getElementById(`m-\\${goto}`);\r\n if (message) {\r\n message.scrollIntoView({ behavior: 'smooth', block: 'center' });\r\n message.style.backgroundColor = 'rgba(148, 156, 247, 0.1)';\r\n message.style.transition = 'background-color 0.5s ease';\r\n setTimeout(() => {\r\n message.style.backgroundColor = 'transparent';\r\n }, 1000);\r\n } else {\r\n console.warn(`Message \\${goto} not found.`);\r\n }\r\n }\r\n});\r\n*/\r\nexport const scrollToMessage =\r\n 'document.addEventListener(\"click\",t=>{let e=t.target;if(!e)return;let o=e?.getAttribute(\"data-goto\");if(o){let r=document.getElementById(`m-${o}`);r?(r.scrollIntoView({behavior:\"smooth\",block:\"center\"}),r.style.backgroundColor=\"rgba(148, 156, 247, 0.1)\",r.style.transition=\"background-color 0.5s ease\",setTimeout(()=>{r.style.backgroundColor=\"transparent\"},1e3)):console.warn(\"Message ${goto} not found.\")}});';\r\n\r\nexport const revealSpoiler =\r\n 'const s=document.querySelectorAll(\".discord-spoiler\");s.forEach(s=>s.addEventListener(\"click\",()=>{if(s.classList.contains(\"discord-spoiler\")){s.classList.remove(\"discord-spoiler\");s.classList.add(\"discord-spoiler--revealed\");}}));';\r\n","import { DiscordHeader, DiscordMessages as DiscordMessagesComponent } from '@derockdev/discord-components-react';\r\nimport { ChannelType } from 'discord.js';\r\nimport React from 'react';\r\nimport type { RenderMessageContext } from '.';\r\nimport MessageContent, { RenderType } from './renderers/content';\r\nimport DiscordMessage from './renderers/message';\r\nimport { globalStyles } from './renderers/components/styles';\r\n\r\n/**\r\n * The core transcript component.\r\n * Expects window.$discordMessage.profiles to be set for profile information.\r\n *\r\n * @param props Messages, channel details, callbacks, etc.\r\n * @returns\r\n */\r\nexport default async function DiscordMessages({ messages, channel, callbacks, ...options }: RenderMessageContext) {\r\n return (\r\n <DiscordMessagesComponent style={{ minHeight: '100vh' }}>\r\n <style dangerouslySetInnerHTML={{ __html: globalStyles }} />\r\n <DiscordHeader\r\n guild={channel.isDMBased() ? 'Direct Messages' : channel.guild.name}\r\n channel={\r\n channel.isDMBased()\r\n ? channel.type === ChannelType.DM\r\n ? (channel.recipient?.tag ?? 'Unknown Recipient')\r\n : 'Unknown Recipient'\r\n : channel.name\r\n }\r\n icon={channel.isDMBased() ? undefined : (channel.guild.iconURL({ size: 128 }) ?? undefined)}\r\n >\r\n {channel.isThread() ? (\r\n `Thread channel in ${channel.parent?.name ?? 'Unknown Channel'}`\r\n ) : channel.isDMBased() ? (\r\n `Direct Messages`\r\n ) : channel.isVoiceBased() ? (\r\n `Voice Text Channel for ${channel.name}`\r\n ) : channel.type === ChannelType.GuildCategory ? (\r\n `Category Channel`\r\n ) : 'topic' in channel && channel.topic ? (\r\n <MessageContent\r\n content={channel.topic}\r\n context={{ messages, channel, callbacks, type: RenderType.REPLY, ...options }}\r\n />\r\n ) : (\r\n `This is the start of #${channel.name} channel.`\r\n )}\r\n </DiscordHeader>\r\n {/* body */}\r\n {messages.map((message) => (\r\n <DiscordMessage message={message} context={{ messages, channel, callbacks, ...options }} key={message.id} />\r\n ))}\r\n {/* footer */}\r\n <div style={{ textAlign: 'center', width: '100%' }}>\r\n {options.footerText\r\n ? options.footerText\r\n .replaceAll('{number}', messages.length.toString())\r\n .replaceAll('{s}', messages.length > 1 ? 's' : '')\r\n : `Exported ${messages.length} message${messages.length > 1 ? 's' : ''}.`}{' '}\r\n {options.poweredBy ? (\r\n <span style={{ textAlign: 'center' }}>\r\n Powered by{' '}\r\n <a href=\"https://github.com/devjoseh/discord2html\" style={{ color: 'lightblue' }}>\r\n discord2html\r\n </a>\r\n .\r\n </span>\r\n ) : null}\r\n </div>\r\n </DiscordMessagesComponent>\r\n );\r\n}\r\n","import {\r\n DiscordBold,\r\n DiscordCodeBlock,\r\n DiscordCustomEmoji,\r\n DiscordInlineCode,\r\n DiscordItalic,\r\n DiscordMention,\r\n DiscordQuote,\r\n DiscordSpoiler,\r\n DiscordTime,\r\n DiscordUnderlined,\r\n} from '@derockdev/discord-components-react';\r\nimport parse, { type RuleTypesExtended } from 'discord-markdown-parser';\r\nimport { ChannelType, type APIMessageComponentEmoji } from 'discord.js';\r\nimport React from 'react';\r\nimport type { ASTNode } from 'simple-markdown';\r\nimport { ASTNode as MessageASTNodes } from 'simple-markdown';\r\nimport type { SingleASTNode } from 'simple-markdown';\r\nimport type { RenderMessageContext } from '../';\r\nimport { parseDiscordEmoji } from '../../utils/utils';\r\n\r\nexport enum RenderType {\r\n EMBED,\r\n REPLY,\r\n NORMAL,\r\n WEBHOOK,\r\n}\r\n\r\ntype RenderContentContext = RenderMessageContext & {\r\n type: RenderType;\r\n\r\n _internal?: {\r\n largeEmojis?: boolean;\r\n };\r\n};\r\n\r\n/**\r\n * Renders discord markdown content\r\n * @param content - The content to render\r\n * @param context - The context to render the content in\r\n * @returns\r\n */\r\nexport default async function MessageContent({ content, context }: { content: string; context: RenderContentContext }) {\r\n if (context.type === RenderType.REPLY && content.length > 180) content = content.slice(0, 180) + '...';\r\n\r\n // parse the markdown\r\n const parsed = parse(\r\n content,\r\n context.type === RenderType.EMBED || context.type === RenderType.WEBHOOK ? 'extended' : 'normal'\r\n );\r\n\r\n // check if the parsed content is only emojis\r\n const isOnlyEmojis = parsed.every(\r\n (node) => ['emoji', 'twemoji'].includes(node.type) || (node.type === 'text' && node.content.trim().length === 0)\r\n );\r\n if (isOnlyEmojis) {\r\n // now check if there are less than or equal to 25 emojis\r\n const emojis = parsed.filter((node) => ['emoji', 'twemoji'].includes(node.type));\r\n if (emojis.length <= 25) {\r\n context._internal = {\r\n largeEmojis: true,\r\n };\r\n }\r\n }\r\n\r\n return <MessageASTNodes nodes={parsed} context={context} />;\r\n}\r\n\r\n// This function can probably be combined into the MessageSingleASTNode function\r\nasync function MessageASTNodes({\r\n nodes,\r\n context,\r\n}: {\r\n nodes: ASTNode;\r\n context: RenderContentContext;\r\n}): Promise<React.JSX.Element> {\r\n if (Array.isArray(nodes)) {\r\n return (\r\n <>\r\n {nodes.map((node, i) => (\r\n <MessageSingleASTNode node={node} context={context} key={i} />\r\n ))}\r\n </>\r\n );\r\n } else {\r\n return <MessageSingleASTNode node={nodes} context={context} />;\r\n }\r\n}\r\n\r\nexport async function MessageSingleASTNode({ node, context }: { node: SingleASTNode; context: RenderContentContext }) {\r\n if (!node) return null;\r\n\r\n const type = node.type as RuleTypesExtended;\r\n\r\n switch (type) {\r\n case 'text':\r\n return node.content;\r\n\r\n case 'link':\r\n return (\r\n <a href={node.target}>\r\n <MessageASTNodes nodes={node.content} context={context} />\r\n </a>\r\n );\r\n\r\n case 'url':\r\n case 'autolink':\r\n return (\r\n <a href={node.target} target=\"_blank\" rel=\"noreferrer\">\r\n <MessageASTNodes nodes={node.content} context={context} />\r\n </a>\r\n );\r\n\r\n case 'blockQuote':\r\n if (context.type === RenderType.REPLY) {\r\n return <MessageASTNodes nodes={node.content} context={context} />;\r\n }\r\n\r\n return (\r\n <DiscordQuote>\r\n <MessageASTNodes nodes={node.content} context={context} />\r\n </DiscordQuote>\r\n );\r\n\r\n case 'br':\r\n case 'newline':\r\n if (context.type === RenderType.REPLY) return ' ';\r\n return <br />;\r\n\r\n case 'channel': {\r\n const id = node.id as string;\r\n const channel = await context.callbacks.resolveChannel(id);\r\n\r\n return (\r\n <DiscordMention type={channel ? (channel.isDMBased() ? 'channel' : getChannelType(channel.type)) : 'channel'}>\r\n {channel ? (channel.isDMBased() ? 'DM Channel' : channel.name) : `<#${id}>`}\r\n </DiscordMention>\r\n );\r\n }\r\n\r\n case 'role': {\r\n const id = node.id as string;\r\n const role = await context.callbacks.resolveRole(id);\r\n\r\n return (\r\n <DiscordMention type=\"role\" color={context.type === RenderType.REPLY ? undefined : role?.hexColor}>\r\n {role ? role.name : `<@&${id}>`}\r\n </DiscordMention>\r\n );\r\n }\r\n\r\n case 'user': {\r\n const id = node.id as string;\r\n const user = await context.callbacks.resolveUser(id);\r\n\r\n return <DiscordMention type=\"user\">{user ? (user.displayName ?? user.username) : `<@${id}>`}</DiscordMention>;\r\n }\r\n\r\n case 'here':\r\n case 'everyone':\r\n return (\r\n <DiscordMention type={'role'} highlight>\r\n {`@${type}`}\r\n </DiscordMention>\r\n );\r\n\r\n case 'codeBlock':\r\n if (context.type !== RenderType.REPLY) {\r\n return <DiscordCodeBlock language={node.lang} code={node.content} />;\r\n }\r\n return <DiscordInlineCode>{node.content}</DiscordInlineCode>;\r\n\r\n case 'inlineCode':\r\n return <DiscordInlineCode>{node.content}</DiscordInlineCode>;\r\n\r\n case 'em':\r\n return (\r\n <DiscordItalic>\r\n <MessageASTNodes nodes={node.content} context={context} />\r\n </DiscordItalic>\r\n );\r\n\r\n case 'strong':\r\n return (\r\n <DiscordBold>\r\n <MessageASTNodes nodes={node.content} context={context} />\r\n </DiscordBold>\r\n );\r\n\r\n case 'underline':\r\n return (\r\n <DiscordUnderlined>\r\n <MessageASTNodes nodes={node.content} context={context} />\r\n </DiscordUnderlined>\r\n );\r\n\r\n case 'strikethrough':\r\n return (\r\n <s>\r\n <MessageASTNodes nodes={node.content} context={context} />\r\n </s>\r\n );\r\n\r\n case 'emoticon':\r\n return typeof node.content === 'string' ? (\r\n node.content\r\n ) : (\r\n <MessageASTNodes nodes={node.content} context={context} />\r\n );\r\n\r\n case 'spoiler':\r\n return (\r\n <DiscordSpoiler>\r\n <MessageASTNodes nodes={node.content} context={context} />\r\n </DiscordSpoiler>\r\n );\r\n\r\n case 'emoji':\r\n case 'twemoji':\r\n return (\r\n <DiscordCustomEmoji\r\n name={node.name}\r\n url={parseDiscordEmoji(node as APIMessageComponentEmoji)}\r\n embedEmoji={context.type === RenderType.EMBED}\r\n largeEmoji={context._internal?.largeEmojis}\r\n />\r\n );\r\n\r\n case 'timestamp':\r\n return <DiscordTime timestamp={parseInt(node.timestamp) * 1000} format={node.format} />;\r\n\r\n default: {\r\n console.log(`Unknown node type: ${type}`, node);\r\n return typeof node.content === 'string' ? (\r\n node.content\r\n ) : (\r\n <MessageASTNodes nodes={node.content} context={context} />\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport function getChannelType(channelType: ChannelType): 'channel' | 'voice' | 'thread' | 'forum' {\r\n switch (channelType) {\r\n case ChannelType.GuildCategory:\r\n case ChannelType.GuildAnnouncement:\r\n case ChannelType.GuildText:\r\n case ChannelType.DM:\r\n case ChannelType.GroupDM:\r\n case ChannelType.GuildDirectory:\r\n case ChannelType.GuildMedia:\r\n return 'channel';\r\n case ChannelType.GuildVoice:\r\n case ChannelType.GuildStageVoice:\r\n return 'voice';\r\n case ChannelType.PublicThread:\r\n case ChannelType.PrivateThread:\r\n case ChannelType.AnnouncementThread:\r\n return 'thread';\r\n case ChannelType.GuildForum:\r\n return 'forum';\r\n default:\r\n return 'channel';\r\n }\r\n}\r\n","import type { APIMessageComponentEmoji, Emoji } from 'discord.js';\r\nimport twemoji from 'twemoji';\r\n\r\nexport function isDefined<T>(value: T | undefined | null): value is T {\r\n return value !== undefined && value !== null;\r\n}\r\n\r\nexport function formatBytes(bytes: number, decimals = 2) {\r\n if (bytes === 0) return '0 Bytes';\r\n\r\n const k = 1024;\r\n const dm = decimals < 0 ? 0 : decimals;\r\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];\r\n\r\n const i = Math.floor(Math.log(bytes) / Math.log(k));\r\n\r\n return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];\r\n}\r\n\r\nexport function parseDiscordEmoji(emoji: Emoji | APIMessageComponentEmoji) {\r\n if (emoji.id) {\r\n return `https://cdn.discordapp.com/emojis/${emoji.id}.${emoji.animated ? 'gif' : 'png'}`;\r\n }\r\n\r\n const codepoints = twemoji.convert\r\n .toCodePoint(\r\n emoji.name!.indexOf(String.fromCharCode(0x200d)) < 0 ? emoji.name!.replace(/\\uFE0F/g, '') : emoji.name!\r\n )\r\n .toLowerCase();\r\n\r\n return `https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/svg/${codepoints}.svg`;\r\n}\r\n\r\n/**\r\n * Converts a stream to a string\r\n * @param stream - The stream to convert\r\n */\r\nexport function streamToString(stream: NodeJS.ReadableStream) {\r\n const chunks: Buffer[] = [];\r\n\r\n return new Promise<string>((resolve, reject) => {\r\n stream.on('data', (chunk) => chunks.push(chunk));\r\n stream.on('error', reject);\r\n stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));\r\n });\r\n}\r\n","import {\r\n DiscordAttachments,\r\n DiscordCommand,\r\n DiscordMessage as DiscordMessageComponent,\r\n DiscordReaction,\r\n DiscordReactions,\r\n DiscordThread,\r\n DiscordThreadMessage,\r\n} from '@derockdev/discord-components-react';\r\nimport { type Message as MessageType } from 'discord.js';\r\nimport React from 'react';\r\nimport type { RenderMessageContext } from '..';\r\nimport { parseDiscordEmoji } from '../../utils/utils';\r\nimport { Attachments } from './attachment';\r\nimport ComponentRow from './components';\r\nimport MessageContent, { RenderType } from './content';\r\nimport { DiscordEmbed } from './embed';\r\nimport MessageReply from './reply';\r\nimport DiscordSystemMessage from './systemMessage';\r\n\r\nexport default async function DiscordMessage({\r\n message,\r\n context,\r\n}: {\r\n message: MessageType;\r\n context: RenderMessageContext;\r\n}) {\r\n if (message.system) return <DiscordSystemMessage message={message} />;\r\n\r\n const isCrosspost = message.reference && message.reference.guildId !== message.guild?.id;\r\n\r\n return (\r\n <DiscordMessageComponent\r\n id={`m-${message.id}`}\r\n timestamp={message.createdAt.toISOString()}\r\n key={message.id}\r\n edited={message.editedAt !== null}\r\n server={isCrosspost ?? undefined}\r\n highlight={message.mentions.everyone}\r\n profile={message.author.id}\r\n >\r\n {/* reply */}\r\n <MessageReply message={message} context={context} />\r\n\r\n {/* slash command */}\r\n {message.interaction && (\r\n <DiscordCommand\r\n slot=\"reply\"\r\n profile={message.interaction.user.id}\r\n command={'/' + message.interaction.commandName}\r\n />\r\n )}\r\n\r\n {/* message content */}\r\n {message.content && (\r\n <MessageContent\r\n content={message.content}\r\n context={{ ...context, type: message.webhookId ? RenderType.WEBHOOK : RenderType.NORMAL }}\r\n />\r\n )}\r\n\r\n {/* attachments */}\r\n <Attachments message={message} context={context} />\r\n\r\n {/* message embeds */}\r\n {message.embeds.map((embed, id) => (\r\n <DiscordEmbed embed={embed} context={{ ...context, index: id, message }} key={id} />\r\n ))}\r\n\r\n {/* components */}\r\n {message.components.length > 0 && (\r\n <DiscordAttachments slot=\"components\">\r\n {message.components.map((component, id) => (\r\n <ComponentRow key={id} id={id} component={component} context={context} />\r\n ))}\r\n </DiscordAttachments>\r\n )}\r\n\r\n {/* reactions */}\r\n {message.reactions.cache.size > 0 && (\r\n <DiscordReactions slot=\"reactions\">\r\n {message.reactions.cache.map((reaction, id) => (\r\n <DiscordReaction\r\n key={`${message.id}r${id}`}\r\n name={reaction.emoji.name!}\r\n emoji={parseDiscordEmoji(reaction.emoji)}\r\n count={reaction.count}\r\n />\r\n ))}\r\n </DiscordReactions>\r\n )}\r\n\r\n {/* threads */}\r\n {message.hasThread && message.thread && (\r\n <DiscordThread\r\n slot=\"thread\"\r\n name={message.thread.name}\r\n cta={\r\n message.thread.messageCount\r\n ? `${message.thread.messageCount} Message${message.thread.messageCount > 1 ? 's' : ''}`\r\n : 'View Thread'\r\n }\r\n >\r\n {message.thread.lastMessage ? (\r\n <DiscordThreadMessage profile={message.thread.lastMessage.author.id}>\r\n <MessageContent\r\n content={\r\n message.thread.lastMessage.content.length > 128\r\n ? message.thread.lastMessage.content.substring(0, 125) + '...'\r\n : message.thread.lastMessage.content\r\n }\r\n context={{ ...context, type: RenderType.REPLY }}\r\n />\r\n </DiscordThreadMessage>\r\n ) : (\r\n `Thread messages not saved.`\r\n )}\r\n </DiscordThread>\r\n )}\r\n </DiscordMessageComponent>\r\n );\r\n}\r\n","import { DiscordAttachment, DiscordAttachments } from '@derockdev/discord-components-react';\r\nimport React from 'react';\r\nimport type { APIAttachment, APIMessage, Attachment as AttachmentType, Message } from 'discord.js';\r\nimport type { RenderMessageContext } from '..';\r\nimport type { AttachmentTypes } from '../../types';\r\nimport { formatBytes } from '../../utils/utils';\r\n\r\n/**\r\n * Renders all attachments for a message\r\n * @param message\r\n * @param context\r\n * @returns\r\n */\r\nexport async function Attachments(props: { message: Message; context: RenderMessageContext }) {\r\n if (props.message.attachments.size === 0) return <></>;\r\n\r\n return (\r\n <DiscordAttachments slot=\"attachments\">\r\n {props.message.attachments.map((attachment, id) => (\r\n <Attachment attachment={attachment} message={props.message} context={props.context} key={id} />\r\n ))}\r\n </DiscordAttachments>\r\n );\r\n}\r\n\r\n// \"audio\" | \"video\" | \"image\" | \"file\"\r\nfunction getAttachmentType(attachment: AttachmentType): AttachmentTypes {\r\n const type = attachment.contentType?.split('/')?.[0] ?? 'unknown';\r\n if (['audio', 'video', 'image'].includes(type)) return type as AttachmentTypes;\r\n return 'file';\r\n}\r\n\r\n/**\r\n * Renders one Discord Attachment\r\n * @param props - the attachment and rendering context\r\n */\r\nexport async function Attachment({\r\n attachment,\r\n context,\r\n message,\r\n}: {\r\n attachment: AttachmentType;\r\n context: RenderMessageContext;\r\n message: Message;\r\n}) {\r\n let url = attachment.url;\r\n const name = attachment.name;\r\n const width = attachment.width;\r\n const height = attachment.height;\r\n\r\n const type = getAttachmentType(attachment);\r\n\r\n // if the attachment is an image, download it to a data url\r\n if (type === 'image') {\r\n const downloaded = await context.callbacks.resolveImageSrc(\r\n attachment.toJSON() as APIAttachment,\r\n message.toJSON() as APIMessage\r\n );\r\n\r\n if (downloaded !== null) {\r\n url = downloaded ?? url;\r\n }\r\n }\r\n\r\n return (\r\n <DiscordAttachment\r\n type={type}\r\n size={formatBytes(attachment.size)}\r\n key={attachment.id}\r\n slot=\"attachment\"\r\n url={url}\r\n alt={name ?? undefined}\r\n width={width ?? undefined}\r\n height={height ?? undefined}\r\n />\r\n );\r\n}\r\n","import { DiscordActionRow, DiscordAttachment, DiscordSpoiler } from '@derockdev/discord-components-react';\r\nimport {\r\n ComponentType,\r\n type ThumbnailComponent,\r\n type MessageActionRowComponent,\r\n type TopLevelComponent,\r\n} from 'discord.js';\r\nimport React from 'react';\r\nimport { parseDiscordEmoji } from '../../utils/utils';\r\nimport DiscordSelectMenu from './components/Select Menu';\r\nimport DiscordContainer from './components/Container';\r\nimport DiscordSection from './components/section/Section';\r\nimport DiscordMediaGallery from './components/Media Gallery';\r\nimport DiscordSeparator from './components/Spacing';\r\nimport DiscordButton from './components/Button';\r\nimport DiscordThumbnail from './components/Thumbnail';\r\nimport MessageContent from './content';\r\nimport { RenderType } from './content';\r\nimport type { RenderMessageContext } from '..';\r\nimport { ButtonStyleMapping } from './components/styles';\r\n\r\nexport default function ComponentRow({\r\n component,\r\n id,\r\n context,\r\n}: {\r\n component: TopLevelComponent;\r\n id: number;\r\n context: RenderMessageContext;\r\n}) {\r\n switch (component.type) {\r\n case ComponentType.ActionRow:\r\n return (\r\n <DiscordActionRow key={id}>\r\n <>\r\n {component.components.map((nestedComponent, id) => (\r\n <Component component={nestedComponent} id={id} key={id} />\r\n ))}\r\n </>\r\n </DiscordActionRow>\r\n );\r\n\r\n case ComponentType.Container:\r\n return (\r\n <DiscordContainer key={id}>\r\n <>\r\n {component.components.map((nestedComponent, id) => (\r\n <ComponentRow component={nestedComponent} id={id} key={id} context={context} />\r\n ))}\r\n </>\r\n </DiscordContainer>\r\n );\r\n\r\n case ComponentType.File:\r\n return (\r\n <>\r\n {component.spoiler ? (\r\n <DiscordSpoiler key={component.id} slot=\"attachment\">\r\n <DiscordAttachment\r\n type=\"file\"\r\n key={component.id}\r\n slot=\"attachment\"\r\n url={component.file.url}\r\n alt=\"Discord Attachment\"\r\n />\r\n </DiscordSpoiler>\r\n ) : (\r\n <DiscordAttachment\r\n type=\"file\"\r\n key={component.id}\r\n slot=\"attachment\"\r\n url={component.file.url}\r\n alt=\"Discord Attachment\"\r\n />\r\n )}\r\n </>\r\n );\r\n\r\n case ComponentType.MediaGallery:\r\n return <DiscordMediaGallery component={component} key={id} />;\r\n\r\n case ComponentType.Section:\r\n return (\r\n <DiscordSection key={id} accessory={component.accessory} id={id}>\r\n {component.components.map((nestedComponent, id) => (\r\n <ComponentRow component={nestedComponent} id={id} key={id} context={context} />\r\n ))}\r\n </DiscordSection>\r\n );\r\n\r\n case ComponentType.Separator:\r\n return <DiscordSeparator key={id} spacing={component.spacing} divider={component.divider} />;\r\n\r\n case ComponentType.TextDisplay:\r\n return <MessageContent key={id} content={component.content} context={{ ...context, type: RenderType.NORMAL }} />;\r\n\r\n default:\r\n return null;\r\n }\r\n}\r\n\r\nexport function Component({\r\n component,\r\n id,\r\n}: {\r\n component: MessageActionRowComponent | ThumbnailComponent;\r\n id: number;\r\n}) {\r\n switch (component.type) {\r\n case ComponentType.Button:\r\n return (\r\n <DiscordButton\r\n key={id}\r\n type={ButtonStyleMapping[component.style as keyof typeof ButtonStyleMapping] ?? 'secondary'}\r\n url={component.url ?? undefined}\r\n emoji={component.emoji ? parseDiscordEmoji(component.emoji) : undefined}\r\n >\r\n {component.label}\r\n </DiscordButton>\r\n );\r\n\r\n case ComponentType.StringSelect:\r\n case ComponentType.UserSelect:\r\n case ComponentType.RoleSelect:\r\n case ComponentType.MentionableSelect:\r\n case ComponentType.ChannelSelect:\r\n return <DiscordSelectMenu key={id} component={component} />;\r\n\r\n case ComponentType.Thumbnail:\r\n return <DiscordThumbnail key={id} url={component.media.url} />;\r\n\r\n default:\r\n return null;\r\n }\r\n}\r\n","import React from 'react';\r\nimport { type MessageActionRowComponent, ComponentType } from 'discord.js';\r\nimport { parseDiscordEmoji } from '../../../utils/utils';\r\nimport { getSelectTypeLabel } from './utils';\r\n\r\nfunction DiscordSelectMenu({\r\n component,\r\n}: {\r\n component: Exclude<MessageActionRowComponent, { type: ComponentType.Button }>;\r\n}) {\r\n const isStringSelect = component.type === ComponentType.StringSelect;\r\n const placeholder = component.placeholder || getSelectTypeLabel(component.type);\r\n\r\n return (\r\n <div className=\"discord-select-menu\">\r\n <div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{placeholder}</div>\r\n <div style={{ display: 'flex', alignItems: 'center', marginLeft: '8px' }}>\r\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n <path fill=\"currentColor\" d=\"M7 10L12 15L17 10H7Z\" />\r\n </svg>\r\n </div>\r\n {isStringSelect && component.options && component.options.length > 0 && (\r\n <div\r\n style={{\r\n display: 'none',\r\n position: 'absolute',\r\n top: '44px',\r\n left: '0',\r\n width: '100%',\r\n backgroundColor: '#2b2d31',\r\n borderRadius: '4px',\r\n zIndex: 10,\r\n border: '1px solid #1e1f22',\r\n maxHeight: '320px',\r\n overflowY: 'auto',\r\n }}\r\n >\r\n {component.options.map((option, idx) => (\r\n <div\r\n key={idx}\r\n style={{\r\n padding: '8px 12px',\r\n cursor: 'pointer',\r\n display: 'flex',\r\n alignItems: 'center',\r\n borderBottom: idx < component.options.length - 1 ? '1px solid #1e1f22' : 'none',\r\n }}\r\n >\r\n {option.emoji && (\r\n <img\r\n src={parseDiscordEmoji(option.emoji)}\r\n alt={option.emoji.name ?? 'emoji'}\r\n style={{ width: '16px', height: '16px', marginRight: '8px', verticalAlign: 'middle' }}\r\n />\r\n )}\r\n <span>{option.label}</span>\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n\r\nexport default DiscordSelectMenu;\r\n","import { ComponentType } from 'discord.js';\r\nimport { baseImageStyle, containerStyle } from './styles';\r\n\r\n/**\r\n * Gets the appropriate label for different select menu types\r\n */\r\nconst SELECT_LABEL_MAP = {\r\n [ComponentType.UserSelect]: 'Select User',\r\n [ComponentType.RoleSelect]: 'Select Role',\r\n [ComponentType.MentionableSelect]: 'Select Mentionable',\r\n [ComponentType.ChannelSelect]: 'Select Channel',\r\n [ComponentType.StringSelect]: 'Make a Selection',\r\n} as const;\r\n\r\nexport function getSelectTypeLabel(type: ComponentType): string {\r\n return SELECT_LABEL_MAP[type as keyof typeof SELECT_LABEL_MAP] ?? 'Select Option';\r\n}\r\n\r\n/**\r\n * Gets the grid layout for media galleries based on count\r\n */\r\nexport function getGalleryLayout(count: number) {\r\n switch (count) {\r\n case 1:\r\n return {\r\n ...containerStyle,\r\n gridTemplateColumns: '1fr',\r\n gridTemplateRows: 'auto',\r\n };\r\n case 2:\r\n return {\r\n ...containerStyle,\r\n gridTemplateColumns: '1fr 1fr',\r\n gridTemplateRows: 'auto',\r\n };\r\n case 3:\r\n case 4:\r\n return {\r\n ...containerStyle,\r\n gridTemplateColumns: '1fr 1fr',\r\n gridTemplateRows: '1fr 1fr',\r\n };\r\n case 5:\r\n return {\r\n ...containerStyle,\r\n gridTemplateColumns: '1fr 1fr 1fr',\r\n gridTemplateRows: 'auto auto',\r\n };\r\n default:\r\n if (count >= 7) {\r\n return {\r\n ...containerStyle,\r\n gridTemplateColumns: '1fr 1fr 1fr',\r\n gridTemplateRows: 'auto auto auto auto',\r\n };\r\n } else {\r\n return {\r\n ...containerStyle,\r\n gridTemplateColumns: '1fr 1fr 1fr',\r\n gridTemplateRows: 'auto',\r\n };\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Gets the style for an individual image based on its position and total count\r\n */\r\nexport function getImageStyle(idx: number, count: number) {\r\n switch (count) {\r\n case 3:\r\n if (idx === 0) {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '1 / span 2',\r\n gridColumn: '1',\r\n aspectRatio: '1/2',\r\n };\r\n }\r\n break;\r\n\r\n case 5:\r\n if (idx < 2) {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '1',\r\n gridColumn: idx === 0 ? '1 / span 2' : '3',\r\n };\r\n } else {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '2',\r\n gridColumn: `${idx - 2 + 1}`,\r\n };\r\n }\r\n\r\n case 7:\r\n if (idx === 0) {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '1',\r\n gridColumn: '1 / span 3',\r\n };\r\n } else if (idx <= 3) {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '2',\r\n gridColumn: `${idx - 0}`,\r\n };\r\n } else {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '3',\r\n gridColumn: `${idx - 3}`,\r\n };\r\n }\r\n\r\n case 8:\r\n if (idx < 2) {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '1',\r\n gridColumn: idx === 0 ? '1 / span 2' : '3',\r\n };\r\n } else if (idx < 5) {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '2',\r\n gridColumn: `${idx - 2 + 1}`,\r\n };\r\n } else {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '3',\r\n gridColumn: `${idx - 5 + 1}`,\r\n };\r\n }\r\n\r\n case 10:\r\n if (idx === 0) {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '1',\r\n gridColumn: '1 / span 3',\r\n };\r\n } else if (idx <= 3) {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '2',\r\n gridColumn: `${idx - 0}`,\r\n };\r\n } else if (idx <= 6) {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '3',\r\n gridColumn: `${idx - 3}`,\r\n };\r\n } else {\r\n return {\r\n ...baseImageStyle,\r\n gridRow: '4',\r\n gridColumn: `${idx - 6}`,\r\n };\r\n }\r\n }\r\n\r\n return baseImageStyle;\r\n}\r\n","import type { CSSProperties } from 'react';\r\nimport { ButtonStyle } from 'discord.js';\r\n\r\n// Container styles\r\nexport const containerStyle = {\r\n display: 'grid',\r\n gap: '4px',\r\n width: '100%',\r\n maxWidth: '500px',\r\n borderRadius: '8px',\r\n overflow: 'hidden',\r\n} satisfies CSSProperties;\r\n\r\n// Base image style\r\nexport const baseImageStyle = {\r\n overflow: 'hidden',\r\n position: 'relative',\r\n background: '#2b2d31',\r\n} satisfies CSSProperties;\r\n\r\n// Button style mapping\r\nexport const ButtonStyleMapping = {\r\n [ButtonStyle.Primary]: 'primary',\r\n [ButtonStyle.Secondary]: 'secondary',\r\n [ButtonStyle.Success]: 'success',\r\n [ButtonStyle.Danger]: 'destructive',\r\n [ButtonStyle.Link]: 'secondary',\r\n [ButtonStyle.Premium]: 'primary',\r\n} as const;\r\n\r\nexport const globalStyles = `\r\n .discord-container {\r\n display: grid;\r\n gap: 4px;\r\n width: 100%;\r\n max-width: 500px;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n }\r\n\r\n .discord-base-image {\r\n overflow: hidden;\r\n position: relative;\r\n background: #2b2d31;\r\n }\r\n\r\n .discord-button {\r\n color: #ffffff !important;\r\n padding: 2px 16px;\r\n border-radius: 8px;\r\n text-decoration: none !important;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 14px;\r\n font-weight: 500;\r\n height: 32px;\r\n min-height: 32px;\r\n min-width: 60px;\r\n cursor: pointer;\r\n font-family: Whitney, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\r\n text-align: center;\r\n box-sizing: border-box;\r\n border: none;\r\n outline: none;\r\n transition: background-color 0.2s ease;\r\n }\r\n\r\n .discord-button-primary {\r\n background-color: hsl(234.935 calc(1*85.556%) 64.706% /1);\r\n }\r\n\r\n .discord-button-secondary {\r\n background-color: hsl(240 calc(1*4%) 60.784% /0.12156862745098039);\r\n }\r\n\r\n .discord-button-success {\r\n background-color: hsl(145.97 calc(1*100%) 26.275% /1);\r\n }\r\n\r\n .discord-button-destructive {\r\n background-color: hsl(355.636 calc(1*64.706%) 50% /1);\r\n }\r\n\r\n .discord-select-menu {\r\n margin-top: 2px;\r\n margin-bottom: 2px;\r\n position: relative;\r\n width: 100%;\r\n max-width: 500px;\r\n height: 40px;\r\n background-color: #2b2d31;\r\n border-radius: 4px;\r\n color: #b5bac1;\r\n cursor: pointer;\r\n font-family: Whitney, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\r\n font-size: 14px;\r\n display: flex;\r\n align-items: center;\r\n padding: 0 8px;\r\n justify-content: space-between;\r\n box-sizing: border-box;\r\n border: 1px solid #1e1f22;\r\n }\r\n`;\r\n","import React from 'react';\r\n\r\nfunction DiscordContainer({ children }: { children: React.ReactNode }) {\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n width: '500px',\r\n flexDirection: 'column',\r\n backgroundColor: '#3f4248',\r\n padding: '16px',\r\n border: '1px solid #4f5359',\r\n marginTop: '2px',\r\n marginBottom: '2px',\r\n borderRadius: '10px',\r\n gap: '8px',\r\n }}\r\n >\r\n {children}\r\n </div>\r\n );\r\n}\r\n\r\nexport default DiscordContainer;\r\n","import React from 'react';\r\nimport type { ButtonComponent, ThumbnailComponent } from 'discord.js';\r\nimport { Component } from '../../components';\r\nimport SectionContent from './SectionContent';\r\nimport SectionAccessory from './SectionAccessory';\r\n\r\ninterface DiscordSectionProps {\r\n children: React.ReactNode;\r\n accessory?: ButtonComponent | ThumbnailComponent;\r\n id: number;\r\n}\r\n\r\nfunction DiscordSection({ children, accessory, id }: DiscordSectionProps) {\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n flexDirection: 'row',\r\n width: '100%',\r\n maxWidth: '500px',\r\n }}\r\n >\r\n <SectionContent>{children}</SectionContent>\r\n <SectionAccessory>{accessory && <Component component={accessory} id={id} />}</SectionAccessory>\r\n </div>\r\n );\r\n}\r\n\r\nexport default DiscordSection;\r\n","import React from 'react';\r\n\r\ninterface SectionContentProps {\r\n children: React.ReactNode;\r\n}\r\n\r\nfunction SectionContent({ children }: SectionContentProps) {\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n flexDirection: 'column',\r\n width: '100%',\r\n }}\r\n >\r\n {children}\r\n </div>\r\n );\r\n}\r\n\r\nexport default SectionContent;\r\n","import React from 'react';\r\n\r\ninterface SectionAccessoryProps {\r\n children?: React.ReactNode;\r\n}\r\n\r\nfunction SectionAccessory({ children }: SectionAccessoryProps) {\r\n if (!children) return null;\r\n\r\n return (\r\n <div\r\n style={{\r\n display: 'flex',\r\n width: '100%',\r\n maxWidth: '500px',\r\n justifyContent: 'flex-end',\r\n alignItems: 'center',\r\n }}\r\n >\r\n {children}\r\n </div>\r\n );\r\n}\r\n\r\nexport default SectionAccessory;\r\n","import React from 'react';\r\nimport type { MediaGalleryComponent } from 'discord.js';\r\nimport { getGalleryLayout, getImageStyle } from './utils';\r\n\r\nfunction DiscordMediaGallery({ component }: { component: MediaGalleryComponent }) {\r\n if (!component.items || component.items.length === 0) {\r\n return null;\r\n }\r\n\r\n const count = component.items.length;\r\n const imagesToShow = component.items.slice(0, 10);\r\n const hasMore = component.items.length > 10;\r\n\r\n return (\r\n <div style={getGalleryLayout(count)}>\r\n {imagesToShow.map((media, idx) => (\r\n <div key={idx} style={getImageStyle(idx, count)}>\r\n <img\r\n src={media.media.url}\r\n alt={media.description || 'Media content'}\r\n style={{\r\n width: '100%',\r\n height: '100%',\r\n objectFit: 'cover',\r\n }}\r\n />\r\n {hasMore && idx === imagesToShow.length - 1 && (\r\n <div\r\n style={{\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n width: '100%',\r\n height: '100%',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n backgroundColor: 'rgba(0, 0, 0, 0.7)',\r\n color: 'white',\r\n fontSize: '20px',\r\n fontWeight: 'bold',\r\n }}\r\n >\r\n +{component.items.length - 10}\r\n </div>\r\n )}\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n}\r\n\r\nexport default DiscordMediaGallery;\r\n","import React from 'react';\r\nimport { SeparatorSpacingSize } from 'discord.js';\r\n\r\nfunction DiscordSeparator({ divider, spacing }: { divider: boolean; spacing: SeparatorSpacingSize }) {\r\n return (\r\n <div\r\n style={{\r\n width: '100%',\r\n height: divider ? '1px' : '0px',\r\n backgroundColor: '#4f5359',\r\n margin: spacing === SeparatorSpacingSize.Large ? '8px 0' : '0',\r\n }}\r\n />\r\n );\r\n}\r\n\r\nexport default DiscordSeparator;\r\n","import React from 'react';\r\n\r\ninterface DiscordButtonProps {\r\n type: string;\r\n url?: string;\r\n emoji?: string;\r\n children: React.ReactNode;\r\n}\r\n\r\nexport function DiscordButton({ type, url, emoji, children }: DiscordButtonProps) {\r\n return (\r\n <a href={url} target=\"_blank\" className={`discord-button discord-button-${type}`}>\r\n {emoji && (\r\n <span style={{ display: 'flex', alignItems: 'center' }}>\r\n <img src={emoji} alt=\"emoji\" style={{ width: '16px', height: '16px', marginRight: '8px' }} />\r\n </span>\r\n )}\r\n <span style={{ display: 'flex', alignItems: 'center' }}>{children}</span>\r\n {url && (\r\n <span style={{ marginLeft: '8px', display: 'flex', alignItems: 'center' }}>\r\n <svg role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"none\" viewBox=\"0 0 24 24\">\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M15 2a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v6a1 1 0 1 1-2 0V4.41l-4.3 4.3a1 1 0 1 1-1.4-1.42L19.58 3H16a1 1 0 0 1-1-1Z\"\r\n />\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M5 2a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-6a1 1 0 1 0-2 0v6a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h6a1 1 0 1 0 0-2H5Z\"\r\n />\r\n </svg>\r\n </span>\r\n )}\r\n </a>\r\n );\r\n}\r\n\r\nexport default DiscordButton;\r\n","import React from 'react';\r\n\r\nfunction DiscordThumbnail({ url }: { url: string }) {\r\n return (\r\n <img\r\n src={url}\r\n alt=\"Thumbnail\"\r\n style={{\r\n width: '85px',\r\n height: '85px',\r\n objectFit: 'cover',\r\n borderRadius: '8px',\r\n }}\r\n />\r\n );\r\n}\r\n\r\nexport default DiscordThumbnail;\r\n","import {\r\n DiscordEmbed as DiscordEmbedComponent,\r\n DiscordEmbedDescription,\r\n DiscordEmbedField,\r\n DiscordEmbedFields,\r\n DiscordEmbedFooter,\r\n} from '@derockdev/discord-components-react';\r\nimport type { Embed, Message } from 'discord.js';\r\nimport React from 'react';\r\nimport type { RenderMessageContext } from '..';\r\nimport { calculateInlineIndex } from '../../utils/embeds';\r\nimport MessageContent, { RenderType } from './content';\r\n\r\ntype RenderEmbedContext = RenderMessageContext & {\r\n index: number;\r\n message: Message;\r\n};\r\n\r\nexport async function DiscordEmbed({ embed, context }: { embed: Embed; context: RenderEmbedContext }) {\r\n return (\r\n <DiscordEmbedComponent\r\n embedTitle={embed.title ?? undefined}\r\n slot=\"embeds\"\r\n key={`${context.message.id}-e-${context.index}`}\r\n authorImage={embed.author?.proxyIconURL ?? embed.author?.iconURL}\r\n authorName={embed.author?.name}\r\n authorUrl={embed.author?.url}\r\n color={embed.hexColor ?? undefined}\r\n image={embed.image?.proxyURL ?? embed.image?.url}\r\n thumbnail={embed.thumbnail?.proxyURL ?? embed.thumbnail?.url}\r\n url={embed.url ?? undefined}\r\n >\r\n {/* Description */}\r\n {embed.description && (\r\n <DiscordEmbedDescription slot=\"description\">\r\n <MessageContent content={embed.description} context={{ ...context, type: RenderType.EMBED }} />\r\n </DiscordEmbedDescription>\r\n )}\r\n\r\n {/* Fields */}\r\n {embed.fields.length > 0 && (\r\n <DiscordEmbedFields slot=\"fields\">\r\n {embed.fields.map(async (field, id) => (\r\n <DiscordEmbedField\r\n key={`${context.message.id}-e-${context.index}-f-${id}`}\r\n fieldTitle={field.name}\r\n inline={field.inline}\r\n inlineIndex={calculateInlineIndex(embed.fields, id)}\r\n >\r\n <MessageContent content={field.value} context={{ ...context, type: RenderType.EMBED }} />\r\n </DiscordEmbedField>\r\n ))}\r\n </DiscordEmbedFields>\r\n )}\r\n\r\n {/* Footer */}\r\n {embed.footer && (\r\n <DiscordEmbedFooter\r\n slot=\"footer\"\r\n footerImage={embed.footer.proxyIconURL ?? embed.footer.iconURL}\r\n timestamp={embed.timestamp ?? undefined}\r\n >\r\n {embed.footer.text}\r\n </DiscordEmbedFooter>\r\n )}\r\n </DiscordEmbedComponent>\r\n );\r\n}\r\n","import type { APIEmbedField } from 'discord.js';\r\n\r\nexport function calculateInlineIndex(fields: APIEmbedField[], currentFieldIndex: number) {\r\n const startIndex = currentFieldIndex - 1;\r\n\r\n for (let i = startIndex; i >= 0; i--) {\r\n const field = fields[i];\r\n if (!field) continue;\r\n\r\n if (field.inline === false) {\r\n const amount = startIndex - i;\r\n return (amount % 3) + 1;\r\n }\r\n }\r\n\r\n return (currentFieldIndex % 3) + 1;\r\n}\r\n","import { DiscordReply } from '@derockdev/discord-components-react';\r\nimport { type Message, UserFlags } from 'discord.js';\r\nimport type { RenderMessageContext } from '..';\r\nimport React from 'react';\r\nimport MessageContent, { RenderType } from './content';\r\n\r\nexport default async function MessageReply({ message, context }: { message: Message; context: RenderMessageContext }) {\r\n if (!message.reference) return null;\r\n if (message.reference.guildId !== message.guild?.id) return null;\r\n\r\n const referencedMessage = context.messages.find((m) => m.id === message.reference!.messageId);\r\n\r\n if (!referencedMessage) return <DiscordReply slot=\"reply\">Message could not be loaded.</DiscordReply>;\r\n\r\n const isCrossPost = referencedMessage.reference && referencedMessage.reference.guildId !== message.guild?.id;\r\n const isCommand = referencedMessage.interaction !== null;\r\n\r\n return (\r\n <DiscordReply\r\n slot=\"reply\"\r\n edited={!isCommand && referencedMessage.editedAt !== null}\r\n attachment={referencedMessage.attachments.size > 0}\r\n author={\r\n referencedMessage.member?.nickname ?? referencedMessage.author.displayName ?? referencedMessage.author.username\r\n }\r\n avatar={referencedMessage.author.avatarURL({ size: 32 }) ?? undefined}\r\n roleColor={referencedMessage.member?.displayHexColor ?? undefined}\r\n bot={!isCrossPost && referencedMessage.author.bot}\r\n verified={referencedMessage.author.flags?.has(UserFlags.VerifiedBot)}\r\n op={message?.channel?.isThread?.() && referencedMessage.author.id === message?.channel?.ownerId}\r\n server={isCrossPost ?? undefined}\r\n command={isCommand}\r\n >\r\n {referencedMessage.content ? (\r\n <span data-goto={referencedMessage.id}>\r\n <MessageContent content={referencedMessage.content} context={{ ...context, type: RenderType.REPLY }} />\r\n </span>\r\n ) : isCommand ? (\r\n <em data-goto={referencedMessage.id}>Click to see command.</em>\r\n ) : (\r\n <em data-goto={referencedMessage.id}>Click to see attachment.</em>\r\n )}\r\n </DiscordReply>\r\n );\r\n}\r\n","import { DiscordReaction, DiscordReactions, DiscordSystemMessage } from '@derockdev/discord-components-react';\r\nimport { MessageType, type GuildMember, type Message, type User } from 'discord.js';\r\nimport React from 'react';\r\nimport { parseDiscordEmoji } from '../../utils/utils';\r\n\r\nexport default async function SystemMessage({ message }: { message: Message }) {\r\n switch (message.type) {\r\n case MessageType.RecipientAdd:\r\n case MessageType.UserJoin:\r\n return (\r\n <DiscordSystemMessage id={`m-${message.id}`} key={message.id} type=\"join\">\r\n <JoinMessage member={message.member} fallbackUser={message.author} />\r\n </DiscordSystemMessage>\r\n );\r\n\r\n case MessageType.ChannelPinnedMessage:\r\n return (\r\n <DiscordSystemMessage id={`m-${message.id}`} key={message.id} type=\"pin\">\r\n <Highlight color={message.member?.roles.color?.hexColor}>\r\n {message.author.displayName ?? message.author.username}\r\n </Highlight>{' '}\r\n pinned <i data-goto={message.reference?.messageId}>a message</i> to this channel.\r\n {/* reactions */}\r\n {message.reactions.cache.size > 0 && (\r\n <DiscordReactions slot=\"reactions\">\r\n {message.reactions.cache.map((reaction, id) => (\r\n <DiscordReaction\r\n key={`${message.id}r${id}`}\r\n name={reaction.emoji.name!}\r\n emoji={parseDiscordEmoji(reaction.emoji)}\r\n count={reaction.count}\r\n />\r\n ))}\r\n </DiscordReactions>\r\n )}\r\n </DiscordSystemMessage>\r\n );\r\n\r\n case MessageType.GuildBoost:\r\n case MessageType.GuildBoostTier1:\r\n case MessageType.GuildBoostTier2:\r\n case MessageType.GuildBoostTier3:\r\n return (\r\n <DiscordSystemMessage id={`m-${message.id}`} key={message.id} type=\"boost\">\r\n <Highlight color={message.member?.roles.color?.hexColor}>\r\n {message.author.displayName ?? message.author.username}\r\n </Highlight>{' '}\r\n boosted the server!\r\n </DiscordSystemMessage>\r\n );\r\n\r\n case MessageType.ThreadStarterMessage:\r\n return (\r\n <DiscordSystemMessage id={`ms-${message.id}`} key={message.id} type=\"thread\">\r\n <Highlight color={message.member?.roles.color?.hexColor}>\r\n {message.author.displayName ?? message.author.username}\r\n </Highlight>{' '}\r\n started a thread: <i data-goto={message.reference?.messageId}>{message.content}</i>\r\n </DiscordSystemMessage>\r\n );\r\n\r\n // TODO: implement support for these:\r\n case MessageType.Default:\r\n case MessageType.RecipientRemove:\r\n case MessageType.Call:\r\n case MessageType.ChannelNameChange:\r\n case MessageType.ChannelIconChange:\r\n case MessageType.ChannelFollowAdd:\r\n case MessageType.GuildDiscoveryDisqualified:\r\n case MessageType.GuildDiscoveryRequalified:\r\n case MessageType.GuildDiscoveryGracePeriodInitialWarning:\r\n case MessageType.GuildDiscoveryGracePeriodFinalWarning:\r\n case MessageType.ThreadCreated:\r\n case MessageType.Reply:\r\n case MessageType.ChatInputCommand:\r\n case MessageType.GuildInviteReminder:\r\n case MessageType.ContextMenuCommand:\r\n case MessageType.AutoModerationAction:\r\n case MessageType.RoleSubscriptionPurchase:\r\n case MessageType.InteractionPremiumUpsell:\r\n case MessageType.StageStart:\r\n case MessageType.StageEnd:\r\n case MessageType.StageSpeaker:\r\n case MessageType.StageRaiseHand:\r\n case MessageType.StageTopic:\r\n case MessageType.GuildApplicationPremiumSubscription:\r\n case MessageType.GuildIncidentAlertModeEnabled:\r\n case MessageType.GuildIncidentAlertModeDisabled:\r\n case MessageType.GuildIncidentReportRaid:\r\n case MessageType.GuildIncidentReportFalseAlarm:\r\n case MessageType.PurchaseNotification:\r\n case MessageType.PollResult:\r\n return undefined;\r\n\r\n default:\r\n return undefined;\r\n }\r\n}\r\n\r\nexport function Highlight({ children, color }: { children: React.ReactNode; color?: string }) {\r\n return <i style={{ color: color ?? 'white' }}>{children}</i>;\r\n}\r\n\r\nconst allJoinMessages = [\r\n '{user} just joined the server - glhf!',\r\n '{user} just joined. Everyone, look busy!',\r\n '{user} just joined. Can I get a heal?',\r\n '{user} joined your party.',\r\n '{user} joined. You must construct additional pylons.',\r\n 'Ermagherd. {user} is here.',\r\n 'Welcome, {user}. Stay awhile and listen.',\r\n 'Welcome, {user}. We were expecting you ( ͡° ͜ʖ ͡°)',\r\n 'Welcome, {user}. We hope you brought pizza.',\r\n 'Welcome {user}. Leave your weapons by the door.',\r\n 'A wild {user} appeared.',\r\n 'Swoooosh. {user} just landed.',\r\n 'Brace yourselves {user} just joined the server.',\r\n '{user} just joined. Hide your bananas.',\r\n '{user} just arrived. Seems OP - please nerf.',\r\n '{user} just slid into the server.',\r\n 'A {user} has spawned in the server.',\r\n 'Big {user} showed up!',\r\n \"Where's {user}? In the server!\",\r\n '{user} hopped into the server. Kangaroo!!',\r\n '{user} just showed up. Hold my beer.',\r\n 'Challenger approaching - {user} has appeared!',\r\n \"It's a bird! It's a plane! Nevermind, it's just {user}.\",\r\n \"It's {user}! Praise the sun! \\\\\\\\[T]/\",\r\n 'Never gonna give {user} up. Never gonna let {user} down.',\r\n 'Ha! {user} has joined! You activated my trap card!',\r\n 'Cheers, love! {user} is here!',\r\n 'Hey! Listen! {user} has joined!',\r\n \"We've been expecting you {user}\",\r\n \"It's dangerous to go alone, take {user}!\",\r\n \"{user} has joined the server! It's super effective!\",\r\n 'Cheers, love! {user} is here!',\r\n '{user} is here, as the prophecy foretold.',\r\n \"{user} has arrived. Party's over.\",\r\n 'Ready player {user}',\r\n '{user} is here to kick butt and chew bubblegum. And {user} is all out of gum.',\r\n \"Hello. Is it {user} you're looking for?\",\r\n];\r\n\r\nexport function JoinMessage({ member, fallbackUser }: { member: GuildMember | null; fallbackUser: User }) {\r\n const randomMessage = allJoinMessages[Math.floor(Math.random() * allJoinMessages.length)];\r\n\r\n return randomMessage\r\n .split('{user}')\r\n .flatMap((item, i) => [\r\n item,\r\n <Highlight color={member?.roles.color?.hexColor} key={i}>\r\n {member?.nickname ?? fallbackUser.displayName ?? fallbackUser.username}\r\n </Highlight>,\r\n ])\r\n .slice(0, -1);\r\n}\r\n","import type { AttachmentBuilder, Message } from 'discord.js';\r\nimport type { RenderMessageContext } from './generator';\r\n\r\nexport type AttachmentTypes = 'audio' | 'video' | 'image' | 'file';\r\n\r\nexport enum ExportReturnType {\r\n Buffer = 'buffer',\r\n String = 'string',\r\n Attachment = 'attachment',\r\n}\r\n\r\nexport type ObjectType<T extends ExportReturnType> = T extends ExportReturnType.Buffer\r\n ? Buffer\r\n : T extends ExportReturnType.String\r\n ? string\r\n : AttachmentBuilder;\r\n\r\nexport type GenerateFromMessagesOptions<T extends ExportReturnType> = Partial<{\r\n /**\r\n * The type of object to return\r\n * @default ExportReturnType.ATTACHMENT\r\n */\r\n returnType: T;\r\n\r\n /**\r\n * Downloads images and encodes them as base64 data urls\r\n * @default false\r\n */\r\n saveImages: boolean;\r\n\r\n /**\r\n * Callbacks for resolving channels, users, and roles\r\n */\r\n callbacks: Partial<RenderMessageContext['callbacks']>;\r\n\r\n /**\r\n * The name of the file to return if returnType is ExportReturnType.ATTACHMENT\r\n * @default 'transcript-{channel-id}.html'\r\n */\r\n filename: string;\r\n\r\n /**\r\n * Whether to include the \"Powered by discord-html-transcripts\" footer\r\n * @default true\r\n */\r\n poweredBy: boolean;\r\n\r\n /**\r\n * The message right before \"Powered by\" text. Remember to put the {s}\r\n * @default 'Exported {number} message{s}.'\r\n */\r\n footerText: string;\r\n\r\n /**\r\n * Whether to show the guild icon or a custom icon as the favicon\r\n * 'guild' - use the guild icon\r\n * or pass in a url to use a custom icon\r\n * @default \"guild\"\r\n */\r\n favicon: 'guild' | string;\r\n\r\n /**\r\n * Whether to hydrate the html server-side\r\n * @default false - the returned html will be hydrated client-side\r\n */\r\n hydrate: boolean;\r\n}>;\r\n\r\nexport type CreateTranscriptOptions<T extends ExportReturnType> = Partial<\r\n GenerateFromMessagesOptions<T> & {\r\n /**\r\n * The max amount of messages to fetch. Use `-1` to recursively fetch.\r\n */\r\n limit: number;\r\n\r\n /**\r\n * Filter messages of the channel\r\n * @default (() => true)\r\n */\r\n filter: (message: Message<boolean>) => boolean;\r\n }\r\n>;\r\n","import type { APIAttachment, APIMessage, Awaitable } from 'discord.js';\r\nimport type { WebpOptions } from 'sharp';\r\nimport { request } from 'undici';\r\nimport debug from 'debug';\r\n\r\n/**\r\n * Callback used to save an image attachment.\r\n * The returned string is the URL that will be used in the transcript.\r\n *\r\n * `undefined` indicates to use the original attachment URL.\r\n * `null` indicates to not include the attachment in the transcript.\r\n * `string` indicates to use the returned URL as the attachment URL (base64 or remote image).\r\n */\r\nexport type ResolveImageCallback = (\r\n attachment: APIAttachment,\r\n message: APIMessage\r\n) => Awaitable<string | null | undefined>;\r\n\r\n/**\r\n * Builder to build a image saving callback.\r\n */\r\nexport class TranscriptImageDownloader {\r\n private static log = debug('discord2html:TranscriptImageDownloader');\r\n private log = TranscriptImageDownloader.log;\r\n\r\n private maxFileSize?: number; // in kilobytes\r\n private compression?: {\r\n quality: number; // 1-100\r\n convertToWebP: boolean;\r\n options: Omit<WebpOptions, 'quality' | 'force'>;\r\n };\r\n\r\n /**\r\n * Sets the maximum file size for *each* individual image.\r\n * @param size The maximum file size in kilobytes\r\n */\r\n withMaxSize(size: number) {\r\n this.maxFileSize = size;\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets the compression quality for each image. This requires `sharp` to be installed.\r\n * Optionally, images can be converted to WebP format which is smaller in size.\r\n * @param quality The quality of the image (1 lowest - 100 highest). Lower quality means smaller file size.\r\n * @param convertToWebP Whether to convert the image to WebP format\r\n */\r\n withCompression(quality = 80, convertToWebP = false, options: Omit<WebpOptions, 'quality' | 'force'> = {}) {\r\n if (quality < 1 || quality > 100) throw new Error('Quality must be between 1 and 100');\r\n\r\n // try and import sharp\r\n import('sharp').catch((err) => {\r\n console.error(err);\r\n console.error(\r\n `[discord2html] Failed to import 'sharp'. Image compression requires the 'sharp' package to be installed. Either install sharp or remove the compression options.`\r\n );\r\n });\r\n\r\n this.compression = { quality, convertToWebP, options };\r\n return this;\r\n }\r\n\r\n /**\r\n * Builds the image saving callback.\r\n */\r\n build(): ResolveImageCallback {\r\n return async (attachment) => {\r\n // if the attachment is not an image, return null\r\n if (!attachment.width || !attachment.height) return undefined;\r\n\r\n // if the max file size is set, check if the file size is within the limit\r\n if (this.maxFileSize && attachment.size > this.maxFileSize * 1024) return undefined;\r\n\r\n // fetch the image\r\n this.log(`Fetching attachment ${attachment.id}: ${attachment.url}`);\r\n const response = await request(attachment.url).catch((err) => {\r\n console.error(`[discord2html] Failed to download image for transcript: `, err);\r\n return null;\r\n });\r\n\r\n if (!response) return undefined;\r\n\r\n const mimetype = response.headers['content-type'];\r\n const buffer = await response.body.arrayBuffer().then((res) => Buffer.from(res));\r\n this.log(`Finished fetching ${attachment.id} (${buffer.length} bytes)`);\r\n\r\n // if the compression options are set, compress the image\r\n if (this.compression) {\r\n const sharp = await import('sharp');\r\n\r\n this.log(`Compressing ${attachment.id} with 'sharp'`);\r\n const sharpbuf = await sharp\r\n .default(buffer)\r\n .webp({\r\n quality: this.compression.quality,\r\n force: this.compression.convertToWebP,\r\n effort: 2,\r\n ...this.compression.options,\r\n })\r\n .toBuffer({ resolveWithObject: true });\r\n this.log(`Finished compressing ${attachment.id} (${sharpbuf.info.size} bytes)`);\r\n\r\n return `data:image/${sharpbuf.info.format};base64,${sharpbuf.data.toString('base64')}`;\r\n }\r\n\r\n // return the base64 string\r\n return `data:${mimetype};base64,${buffer.toString('base64')}`;\r\n };\r\n }\r\n}\r\n"],"mappings":"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","names":["React","ChannelType","React","DiscordAttachments","DiscordReaction","DiscordReactions","React","React","React","DiscordAttachment","DiscordSpoiler","ComponentType","React","React","ComponentType","ComponentType","React","React","React","React","React","React","React","React","React","React","React","ComponentType","React","id","DiscordSpoiler","DiscordAttachment","React","React","UserFlags","React","React","UserFlags","React","React","React","DiscordAttachments","DiscordReactions","DiscordReaction","React","ChannelType","React","ExportReturnType"]}
|