buncord-transcript 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/.gitattributes +2 -0
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/bun.lock +31 -0
- package/index.ts +1 -0
- package/package.json +38 -0
- package/src/generator.ts +245 -0
- package/src/index.ts +6 -0
- package/src/mustache.d.ts +8 -0
- package/src/template.ts +383 -0
- package/src/types.ts +173 -0
- package/test_mentions.html +381 -0
- package/test_transcript.html +695 -0
- package/tests/test_gen.ts +192 -0
- package/tests/test_mentions.ts +78 -0
- package/tsconfig.json +29 -0
package/.gitattributes
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Luigi Colantuono
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Buncord-Transcript
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<img src="https://github.com/user-attachments/assets/70e8758e-f363-478a-a013-fd46ca3cf3ec" alt="Buncord Logo" width="180"/>
|
|
5
|
+
<p><b>The fastest, lightest, and most faithful Discord HTML transcript generator.</b></p>
|
|
6
|
+
<p><i>Built exclusively for the Bun ecosystem.</i></p>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Stop simulating browsers to generate simple text logs. **Buncord-Transcript** purges the bloat of JSDOM and React, replacing them with a high-performance, string-based rendering engine powered by Bun and a specialized fork of Mustache.
|
|
12
|
+
|
|
13
|
+
## ⚡ Blazingly Fast
|
|
14
|
+
|
|
15
|
+
* **Zero Node Dependencies**: No `ws`, no `http` legacy, no `JSDOM`. Pure Bun-native execution.
|
|
16
|
+
* **Mustache Powered**: Generates complex transcripts in milliseconds using optimized string templates instead of heavy, recursive DOM manipulation.
|
|
17
|
+
* **Zero Memory Overhead**: While other libraries require hundreds of MBs to "render" a virtual DOM, Buncord processes messages through a stream-like logic that keeps your RAM footprint invisible.
|
|
18
|
+
|
|
19
|
+
## 🎨 Absolute Cinema UI
|
|
20
|
+
|
|
21
|
+
* **Discord v2 Native**: First-class support for modern components: **Buttons**, **Select Menus**, and the new **Containers**.
|
|
22
|
+
* **1:1 Visual Fidelity**: Unlike libraries with hardcoded styles, Buncord uses a dynamic CSS variable system mirrored directly from the official Discord client.
|
|
23
|
+
* **Media-First**: Native support for **Multi-image Media Galleries**, high-res avatars, and custom emoji rendering.
|
|
24
|
+
* **Smart Mentions**: Intelligently resolves user mentions and relative timestamps within the transcript context.
|
|
25
|
+
|
|
26
|
+
## 📦 Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bun add github:LuigiColantuono/buncord-transcript
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 🚀 Quick Start
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { createTranscript } from 'buncord-transcript';
|
|
36
|
+
|
|
37
|
+
const messages = [...]; // Your Discord.js / Buncord messages
|
|
38
|
+
const channel = { name: 'ticket-001' };
|
|
39
|
+
|
|
40
|
+
const html = await createTranscript(messages, channel);
|
|
41
|
+
// Output is a high-performance HTML buffer/string ready to be served or saved.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 🛠️ The Philosophy
|
|
45
|
+
|
|
46
|
+
Built out of frustration with outdated, bloated libraries that fail to render modern Discord components. Buncord-Transcript is a **"Performance Tier 1"** tool for developers who prioritize speed, code purity, and production stability.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
> This project was created using `bun init` in bun v1.3.6. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
package/bun.lock
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "buncord-transcript",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"mustache": "luigicolantuono/mustache",
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/bun": "latest",
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"typescript": "^5",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
"packages": {
|
|
19
|
+
"@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
|
|
20
|
+
|
|
21
|
+
"@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
|
|
22
|
+
|
|
23
|
+
"bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
|
|
24
|
+
|
|
25
|
+
"mustache": ["mustache@github:luigicolantuono/mustache#9a14e96", { "bin": { "mustache": "./bin/mustache" } }, "LuigiColantuono-mustache-9a14e96"],
|
|
26
|
+
|
|
27
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
28
|
+
|
|
29
|
+
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
30
|
+
}
|
|
31
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.log("Hello via Bun!");
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "buncord-transcript",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A high-performance Discord transcript generator built for Bun.",
|
|
5
|
+
"module": "src/index.ts",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Luigi Colantuono",
|
|
10
|
+
"url": "https://github.com/LuigiColantuono"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/LuigiColantuono",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"funding": {
|
|
15
|
+
"type": "individual",
|
|
16
|
+
"url": "https://paypal.me/l0g4n7"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/LuigiColantuono/Buncord-Transcript.git"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"discord",
|
|
24
|
+
"transcript",
|
|
25
|
+
"bun",
|
|
26
|
+
"html",
|
|
27
|
+
"generator"
|
|
28
|
+
],
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/bun": "latest"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"typescript": "5.9.3"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"mustache": "github:LuigiColantuono/mustache"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/generator.ts
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import mustache from 'mustache/mustache.js';
|
|
2
|
+
import { htmlTemplate, css } from './template';
|
|
3
|
+
import type { Message, TranscriptOptions, ChannelInfo, Embed, Button, SelectMenu, AnyComponent, ContainerComponent, TextDisplayComponent, SeparatorComponent, ActionRow } from './types';
|
|
4
|
+
// Helper to format Date
|
|
5
|
+
function formatDate(dateString: string): string {
|
|
6
|
+
const date = new Date(dateString);
|
|
7
|
+
return date.toLocaleString('en-US', {
|
|
8
|
+
year: 'numeric',
|
|
9
|
+
month: '2-digit',
|
|
10
|
+
day: '2-digit',
|
|
11
|
+
hour: '2-digit',
|
|
12
|
+
minute: '2-digit',
|
|
13
|
+
hour12: true
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
// Simple Markdown Formatter (Zero-dependency)
|
|
17
|
+
function formatContent(content: string, userMap?: Map<string, string>): string {
|
|
18
|
+
if (!content) return '';
|
|
19
|
+
let html = content
|
|
20
|
+
// Escape HTML
|
|
21
|
+
.replace(/&/g, '&')
|
|
22
|
+
.replace(/</g, '<')
|
|
23
|
+
.replace(/>/g, '>')
|
|
24
|
+
|
|
25
|
+
// Headers (must be at start of line or after newline)
|
|
26
|
+
.replace(/^### (.*$)/gm, '<h3>$1</h3>')
|
|
27
|
+
.replace(/^## (.*$)/gm, '<h2>$1</h2>')
|
|
28
|
+
.replace(/^# (.*$)/gm, '<h1>$1</h1>')
|
|
29
|
+
|
|
30
|
+
// Code Blocks (multiline)
|
|
31
|
+
.replace(/```(\w+)?\n([\s\S]*?)```/g, '<pre><code class="language-$1">$2</code></pre>')
|
|
32
|
+
// Code Blocks (simple)
|
|
33
|
+
.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>')
|
|
34
|
+
|
|
35
|
+
// Inline Code
|
|
36
|
+
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
|
37
|
+
|
|
38
|
+
// Links [text](url)
|
|
39
|
+
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>')
|
|
40
|
+
// Bold
|
|
41
|
+
.replace(/\*\*([^*]+)\*\*/g, '<b>$1</b>')
|
|
42
|
+
|
|
43
|
+
// Italic
|
|
44
|
+
.replace(/\*([^*]+)\*/g, '<i>$1</i>')
|
|
45
|
+
.replace(/_([^_]+)_/g, '<i>$1</i>')
|
|
46
|
+
|
|
47
|
+
// Underline
|
|
48
|
+
.replace(/__([^_]+)__/g, '<u>$1</u>')
|
|
49
|
+
|
|
50
|
+
// Strikethrough
|
|
51
|
+
.replace(/~~([^~]+)~~/g, '<s>$1</s>')
|
|
52
|
+
|
|
53
|
+
// Newlines
|
|
54
|
+
.replace(/\n/g, '<br>');
|
|
55
|
+
// Mentions (User) <@123456>
|
|
56
|
+
html = html.replace(/<@!?(\d+)>/g, (match, id) => {
|
|
57
|
+
const username = userMap?.get(id) || 'User';
|
|
58
|
+
return `<span class="mention">@${username}</span>`;
|
|
59
|
+
});
|
|
60
|
+
// Mentions (Channel) <#123456>
|
|
61
|
+
html = html.replace(/<#(\d+)>/g, '<span class="mention">#channel</span>');
|
|
62
|
+
// Mentions (Role) <@&123456>
|
|
63
|
+
html = html.replace(/<@&(\d+)>/g, '<span class="mention">@role</span>');
|
|
64
|
+
// Timestamps <t:123456:R>
|
|
65
|
+
html = html.replace(/<t:(\d+):?([A-Z])?>/g, (match, timestamp, style) => {
|
|
66
|
+
const date = new Date(parseInt(timestamp) * 1000);
|
|
67
|
+
return `<span class="timestamp">${date.toLocaleString()}</span>`;
|
|
68
|
+
});
|
|
69
|
+
return html;
|
|
70
|
+
}
|
|
71
|
+
// Helper to render V2 Components to HTML string
|
|
72
|
+
function renderComponent(component: AnyComponent, userMap?: Map<string, string>): string {
|
|
73
|
+
if (component.type === 17) { // Container
|
|
74
|
+
const container = component as ContainerComponent;
|
|
75
|
+
const children = container.components.map(c => renderComponent(c, userMap)).join('');
|
|
76
|
+
return children;
|
|
77
|
+
}
|
|
78
|
+
if (component.type === 10) { // Text Display
|
|
79
|
+
const text = component as TextDisplayComponent;
|
|
80
|
+
const content = formatContent(text.content, userMap);
|
|
81
|
+
return `<div class="discord-section"><div class="discord-section-content">${content}</div></div>`;
|
|
82
|
+
}
|
|
83
|
+
if (component.type === 14) { // Separator
|
|
84
|
+
const sep = component as SeparatorComponent;
|
|
85
|
+
const style = sep.divider ? 'height: 1px;' : 'height: 0px;';
|
|
86
|
+
const margin = sep.spacing === 3 ? 'margin: 8px 0;' : sep.spacing === 2 ? 'margin: 4px 0;' : 'margin: 0;';
|
|
87
|
+
return `<div class="discord-separator" style="${style} ${margin}"></div>`;
|
|
88
|
+
}
|
|
89
|
+
if (component.type === 1) { // Action Row
|
|
90
|
+
const row = component as ActionRow;
|
|
91
|
+
const children = row.components.map(c => renderComponent(c, userMap)).join('');
|
|
92
|
+
return `<div class="message-component-group">${children}</div>`;
|
|
93
|
+
}
|
|
94
|
+
if (component.type === 2) { // Button
|
|
95
|
+
const btn = component as Button;
|
|
96
|
+
const styleClass = btn.style === 1 ? 'primary' :
|
|
97
|
+
btn.style === 2 ? 'secondary' :
|
|
98
|
+
btn.style === 3 ? 'success' :
|
|
99
|
+
btn.style === 4 ? 'destructive' :
|
|
100
|
+
btn.style === 5 ? 'secondary' : 'primary';
|
|
101
|
+
|
|
102
|
+
let content = '';
|
|
103
|
+
if (btn.emoji) {
|
|
104
|
+
if (btn.emoji.id) {
|
|
105
|
+
content += `<span style="display: flex; align-items: center;"><img src="https://cdn.discordapp.com/emojis/${btn.emoji.id}.webp?size=44&quality=lossless" alt="${btn.emoji.name}" style="width: 16px; height: 16px; margin-right: 8px;"></span>`;
|
|
106
|
+
} else if (btn.emoji.name) {
|
|
107
|
+
content += `<span style="display: flex; align-items: center; margin-right: 8px;">${btn.emoji.name}</span>`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (btn.label) {
|
|
111
|
+
content += `<span style="display: flex; align-items: center;">${btn.label}</span>`;
|
|
112
|
+
}
|
|
113
|
+
if (btn.style === 5) {
|
|
114
|
+
content += `<span style="margin-left: 8px; display: flex; align-items: center;"><svg role="img" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 24 24"><path fill="currentColor" 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"/><path fill="currentColor" 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"/></svg></span>`;
|
|
115
|
+
}
|
|
116
|
+
const disabledAttr = btn.disabled ? 'disabled' : '';
|
|
117
|
+
if (btn.style === 5 && btn.url) {
|
|
118
|
+
return `<a class="discord-button discord-button-secondary" href="${btn.url}" target="_blank" ${disabledAttr}>${content}</a>`;
|
|
119
|
+
}
|
|
120
|
+
return `<button class="discord-button discord-button-${styleClass}" type="button" ${disabledAttr}>${content}</button>`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (component.type === 3 || component.type === 5 || component.type === 6 || component.type === 7 || component.type === 8) {
|
|
124
|
+
const menu = component as SelectMenu;
|
|
125
|
+
return `<div class="discord-select-menu">
|
|
126
|
+
<div style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${menu.placeholder || 'Select...'}</div>
|
|
127
|
+
<div style="display: flex; align-items: center; margin-left: 8px;">
|
|
128
|
+
<svg width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M7 10L12 15L17 10H7Z" /></svg>
|
|
129
|
+
</div>
|
|
130
|
+
</div>`;
|
|
131
|
+
}
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
export async function generateTranscript(messages: Message[], channel: ChannelInfo, options: TranscriptOptions = {}) {
|
|
135
|
+
// Build user map
|
|
136
|
+
const userMap = new Map<string, string>();
|
|
137
|
+
for (const msg of messages) {
|
|
138
|
+
if (msg.author && msg.author.id && msg.author.username) {
|
|
139
|
+
userMap.set(msg.author.id, msg.author.username);
|
|
140
|
+
}
|
|
141
|
+
// Also check replyTo author
|
|
142
|
+
if (msg.replyTo?.author?.id && msg.replyTo?.author?.username) {
|
|
143
|
+
userMap.set(msg.replyTo.author.id, msg.replyTo.author.username);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const processedMessages = messages.map(msg => {
|
|
148
|
+
return {
|
|
149
|
+
...msg,
|
|
150
|
+
timestamp: formatDate(msg.timestamp),
|
|
151
|
+
content: formatContent(msg.content, userMap),
|
|
152
|
+
embeds: msg.embeds?.map(embed => ({
|
|
153
|
+
...embed,
|
|
154
|
+
description: embed.description ? formatContent(embed.description, userMap) : undefined,
|
|
155
|
+
fields: embed.fields?.map(field => ({
|
|
156
|
+
...field,
|
|
157
|
+
value: formatContent(field.value, userMap)
|
|
158
|
+
})),
|
|
159
|
+
hexColor: embed.color ? '#' + embed.color.toString(16).padStart(6, '0') : undefined,
|
|
160
|
+
})),
|
|
161
|
+
containers: [
|
|
162
|
+
...(msg.containers?.map(container => ({
|
|
163
|
+
...container,
|
|
164
|
+
content: container.content
|
|
165
|
+
})) || []),
|
|
166
|
+
...(msg.components?.filter(c => c.type === 17).map(container => ({
|
|
167
|
+
content: renderComponent(container, userMap)
|
|
168
|
+
})) || [])
|
|
169
|
+
],
|
|
170
|
+
components: [
|
|
171
|
+
...(msg.components?.filter(c => c.type === 1).map(c => {
|
|
172
|
+
const row = c as ActionRow;
|
|
173
|
+
return {
|
|
174
|
+
...row,
|
|
175
|
+
components: row.components.map(component => {
|
|
176
|
+
if (component.type === 2) {
|
|
177
|
+
const btn = component as Button;
|
|
178
|
+
return {
|
|
179
|
+
...btn,
|
|
180
|
+
isButton: true,
|
|
181
|
+
styleClass: btn.style === 1 ? 'primary' :
|
|
182
|
+
btn.style === 2 ? 'secondary' :
|
|
183
|
+
btn.style === 3 ? 'success' :
|
|
184
|
+
btn.style === 4 ? 'destructive' :
|
|
185
|
+
btn.style === 5 ? 'secondary' : 'primary',
|
|
186
|
+
isLink: btn.style === 5,
|
|
187
|
+
emoji: btn.emoji
|
|
188
|
+
};
|
|
189
|
+
} else {
|
|
190
|
+
return {
|
|
191
|
+
...component,
|
|
192
|
+
isSelectMenu: true
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
};
|
|
197
|
+
}) || []),
|
|
198
|
+
...(msg.components?.some(c => c.type !== 1 && c.type !== 17) ? [{
|
|
199
|
+
type: 1,
|
|
200
|
+
components: msg.components.filter(c => c.type !== 1 && c.type !== 17).map(component => {
|
|
201
|
+
if (component.type === 2) {
|
|
202
|
+
const btn = component as Button;
|
|
203
|
+
return {
|
|
204
|
+
...btn,
|
|
205
|
+
isButton: true,
|
|
206
|
+
styleClass: btn.style === 1 ? 'primary' :
|
|
207
|
+
btn.style === 2 ? 'secondary' :
|
|
208
|
+
btn.style === 3 ? 'success' :
|
|
209
|
+
btn.style === 4 ? 'destructive' :
|
|
210
|
+
btn.style === 5 ? 'secondary' : 'primary',
|
|
211
|
+
isLink: btn.style === 5,
|
|
212
|
+
emoji: btn.emoji
|
|
213
|
+
};
|
|
214
|
+
} else if (component.type === 3 || component.type === 5 || component.type === 6 || component.type === 7 || component.type === 8) {
|
|
215
|
+
return {
|
|
216
|
+
...component,
|
|
217
|
+
isSelectMenu: true
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return component;
|
|
221
|
+
})
|
|
222
|
+
}] : [])
|
|
223
|
+
],
|
|
224
|
+
mediaGalleries: msg.mediaGalleries,
|
|
225
|
+
separators: msg.separators?.map(sep => ({
|
|
226
|
+
...sep,
|
|
227
|
+
isLarge: sep.spacing === 3
|
|
228
|
+
})),
|
|
229
|
+
replyTo: msg.replyTo ? {
|
|
230
|
+
...msg.replyTo,
|
|
231
|
+
contentSnippet: msg.replyTo.content.substring(0, 50) + (msg.replyTo.content.length > 50 ? '...' : '')
|
|
232
|
+
} : undefined
|
|
233
|
+
};
|
|
234
|
+
});
|
|
235
|
+
const view = {
|
|
236
|
+
channel,
|
|
237
|
+
messages: processedMessages,
|
|
238
|
+
css
|
|
239
|
+
};
|
|
240
|
+
const output = (mustache as any).render(htmlTemplate, view);
|
|
241
|
+
if (options.returnType === 'buffer') {
|
|
242
|
+
return Buffer.from(output);
|
|
243
|
+
}
|
|
244
|
+
return output;
|
|
245
|
+
}
|
package/src/index.ts
ADDED