Skip to main content

SmartMessage - Builder thông minh cho Message Payload

SmartMessage là fluent builder giúp tạo message payload cho Mezon SDK một cách dễ dàng và type-safe. Thay vì phải tạo ChannelMessageContent thủ công, bạn có thể sử dụng SmartMessage để build message với cú pháp rõ ràng và dễ đọc.

Tổng quan​

SmartMessage là một wrapper thông minh trên ChannelMessageContent của Mezon SDK, cung cấp:

  • Fluent API: Chain methods để build message phức tạp
  • Type Safety: Đảm bảo type correctness với TypeScript
  • Tự động chuyển đổi: Tự động convert sang format Mezon SDK khi gửi
  • Hỗ trợ đầy đủ: Text, System, Image, Voice, Attachments, Buttons, Embeds

Kiến trúc và cách hoạt động​

SmartMessage vs Mezon SDK​

SmartMessage hoạt động như một layer abstraction trên Mezon SDK:

SmartMessage (Nezon)
↓ toJSON() / toContent()
ChannelMessageContent (Mezon SDK)
↓ send()
Mezon API

Quá trình chuyển đổi​

Khi bạn gọi message.reply(SmartMessage.text('Hello')), Nezon sẽ:

  1. Normalize: Chuyển SmartMessage thành NormalizedSmartMessage
  2. Extract: Tách content và attachments
  3. Send: Gọi Mezon SDK với payload đã chuẩn hóa
// Bên trong Nezon
const payload = smartMessage.toJSON();
// payload = {
// content: { t: 'Hello', components: [...], embed: [...] },
// attachments: [...]
// }

await channel.send(payload.content, undefined, payload.attachments);

Các phương thức tạo SmartMessage​

Static Methods​

SmartMessage.text()​

Tạo text message cơ bản.

SmartMessage.text(content: string): SmartMessage

Ví dụ:

const message = SmartMessage.text("Hello, World!");

Tương đương Mezon SDK:

const content: ChannelMessageContent = { t: "Hello, World!" };

SmartMessage.system()​

Tạo system message với markdown triple.

SmartMessage.system(content: string): SmartMessage

Ví dụ:

const message = SmartMessage.system("⚠️ Thông báo quan trọng!");

Tương đương Mezon SDK:

const content: ChannelMessageContent = {
t: "⚠️ Thông báo quan trọng!",
mk: [
{
type: EMarkdownType.PRE,
s: 0,
e: content.length,
},
],
};

SmartMessage.image()​

Tạo message với ảnh đính kèm.

SmartMessage.image(
url: string,
options?: {
alt?: string;
filename?: string;
width?: number;
height?: number;
}
): SmartMessage

Ví dụ:

const message = SmartMessage.image("https://example.com/image.jpg", {
alt: "A beautiful image",
filename: "image.jpg",
width: 1920,
height: 1080,
});

Tương đương Mezon SDK:

const content: ChannelMessageContent = { t: "A beautiful image" };
const attachments: ApiMessageAttachment[] = [
{
url: "https://example.com/image.jpg",
filetype: "image",
filename: "image.jpg",
width: 1920,
height: 1080,
},
];

SmartMessage.voice()​

Tạo voice message với audio đính kèm.

SmartMessage.voice(
url: string,
options?: {
transcript?: string;
}
): SmartMessage

Ví dụ:

const message = SmartMessage.voice("https://example.com/audio.mp3", {
transcript: "This is a voice message",
});

Tương đương Mezon SDK:

const content: ChannelMessageContent = { t: "This is a voice message" };
const attachments: ApiMessageAttachment[] = [
{
url: "https://example.com/audio.mp3",
filetype: "audio",
},
];

SmartMessage.build()​

Tạo SmartMessage rỗng (không có content).

SmartMessage.build(): SmartMessage

Ví dụ:

const message = SmartMessage.build()
.addEmbed(new EmbedBuilder().setTitle("Title"))
.addButton(new ButtonBuilder().setLabel("Click"));

Tương đương Mezon SDK:

const content: ChannelMessageContent = {
embed: [{ title: "Title" }],
components: [{ components: [{ label: "Click" }] }],
};

SmartMessage.raw()​

Tạo SmartMessage từ ChannelMessageContent có sẵn (advanced).

SmartMessage.raw(
content: ChannelMessageContent,
attachments?: ApiMessageAttachment[]
): SmartMessage

Ví dụ:

const customContent: ChannelMessageContent = {
t: "Custom message",
mk: [
/* custom markdown */
],
};

const message = SmartMessage.raw(customContent, [
{ url: "https://example.com/file.pdf", filetype: "application/pdf" },
]);

Instance Methods (Chainable)​

Tất cả các instance methods đều trả về this để hỗ trợ method chaining.

.addButton()​

Thêm button vào message. Buttons tự động được nhóm thành action rows (tối đa 5 buttons/row).

addButton(button: ButtonBuilder | ButtonComponent): this

Ví dụ:

const message = SmartMessage.text("Choose an option:")
.addButton(
new ButtonBuilder()
.setLabel("Option 1")
.setStyle(ButtonStyle.Primary)
.setCustomId("option/1")
)
.addButton(
new ButtonBuilder()
.setLabel("Option 2")
.setStyle(ButtonStyle.Secondary)
.setCustomId("option/2")
);

Tương đương Mezon SDK:

const content: ChannelMessageContent = {
t: "Choose an option:",
components: [
{
components: [
{ label: "Option 1", style: 1, custom_id: "option/1" },
{ label: "Option 2", style: 2, custom_id: "option/2" },
],
},
],
};

.addImage()​

Thêm ảnh vào message.

addImage(
url: string,
options?: {
filename?: string;
width?: number;
height?: number;
}
): this

Ví dụ:

const message = SmartMessage.text("Check out these images!")
.addImage("https://example.com/img1.jpg", { filename: "img1.jpg" })
.addImage("https://example.com/img2.jpg", { filename: "img2.jpg" });

.addFile()​

Thêm file đính kèm vào message.

addFile(
url: string,
filename: string,
filetype: string,
options?: {
size?: number;
}
): this

addGIF(
url: string,
options?: {
filename?: string;
width?: number;
height?: number;
size?: number;
}
): this

Ví dụ:

const message = SmartMessage.text("Download the file:").addFile(
"https://example.com/document.pdf",
"document.pdf",
"application/pdf",
{ size: 1024000 }
);

// Thêm GIF
SmartMessage.text("Reaction time!").addGIF(
"https://media.tenor.com/2ES7YijqoOwAAAAC/kiss.gif",
{ filename: "kiss.gif" }
);

.addEmbed()​

Thêm embed vào message.

addEmbed(embed: EmbedBuilder | IInteractiveMessageProps): this

Ví dụ:

const message = SmartMessage.text("Rich embed:").addEmbed(
new EmbedBuilder()
.setTitle("Title")
.setDescription("Description")
.setColor("#f0a8da")
.addField("Field 1", "Value 1", true)
.addField("Field 2", "Value 2", true)
);

.addMention()​

Map placeholder → user_id để SmartMessage tự render mention với @username và metadata mentions.

addMention(
key: string,
value: string | { user_id?: string; role_id?: string; role_name?: string }
): this
addMention(
mentions: Record<
string,
string | { user_id?: string; role_id?: string; role_name?: string }
>
): this

Cách sử dụng

  1. Trong nội dung text, đặt {{placeholder_name}} tại vị trí cần mention.
  2. Gọi .addMention({ placeholder_name: 'USER_ID' }) hoặc .addMention('placeholder_name', 'USER_ID') với user, hoặc truyền object { role_name: '...' } / { role_id: '...' } đối với role.
  3. Khi gửi qua ManagedMessage/DMHelper, SDK sẽ tìm username hoặc role theo clan.listRoles(), thay thế bằng @ phù hợp và tự động truyền mentions với s/e.

Ví dụ

@Command("hi")
async function onHi(@AutoContext() [managedMessage]: Nezon.AutoContext) {
await managedMessage.reply(
SmartMessage.text("Hello {{user_id_anh_luongtuan}} 👋")
.addMention({ user_id_anh_luongtuan: "12132124214" })
);
}

Output thực tế:

  • Nội dung: Hello @anh.luongtuan 👋.
  • Mentions: [{ user_id: "12132124214", username: "anh.luongtuan", s: 6, e: 20 }].

Nếu không tìm được username, SDK fallback về user_id.

Role mention

await managedMessage.reply(
SmartMessage.text("Gửi thông báo tới {{target_role}} 🛎️").addMention({
target_role: { role_name: "GÔ LĂNG" },
})
);
  • role_name tự động bỏ ký tự @ đầu chuỗi và so khớp case-insensitive với clan.listRoles(). Có thể truyền trực tiếp role_id ({ role_id: "1840..." }) nếu đã biết ID.
  • Khi tìm thấy role, nội dung sẽ trở thành @TênRole và payload chứa role_id, rolename, s, e tương ứng.
  • Chỉ khi bot xác định được role_id (qua role_id truyền vào hoặc lookup theo tên) thì mới thêm entry vào mentions. Nếu không match được, SDK chỉ thay thế text nhưng không thêm mention.
  • Với user mentions, bạn có thể truyền thêm username hoặc display_name trong object { user_id, username } để làm fallback khi bot chưa fetch được profile (tránh hiển thị ID thô).

Chuyển đổi sang Mezon SDK​

SmartMessage cung cấp các methods để chuyển đổi sang format Mezon SDK:

.toJSON()​

Chuyển đổi thành NormalizedSmartMessage (bao gồm cả attachments).

toJSON(): NormalizedSmartMessage

interface NormalizedSmartMessage {
content: ChannelMessageContent;
attachments?: ApiMessageAttachment[];
mentions?: ApiMessageMention[];
mentionPlaceholders?: Record<
string,
| { kind: "user"; userId: string }
| { kind: "role"; roleId?: string; roleName?: string }
>;
}

Ví dụ:

const smartMessage = SmartMessage.text("Hello")
.addImage("https://example.com/img.jpg")
.addButton(new ButtonBuilder().setLabel("Click"));

const payload = smartMessage.toJSON();
// payload = {
// content: {
// t: 'Hello',
// components: [{ components: [...] }]
// },
// attachments: [{ url: '...', filetype: 'image' }]
// }

await channel.send(payload.content, payload.mentions, payload.attachments);

.toContent()​

Chỉ lấy ChannelMessageContent (không có attachments).

toContent(): ChannelMessageContent

Ví dụ:

const smartMessage = SmartMessage.text("Hello").addButton(
new ButtonBuilder().setLabel("Click")
);

const content = smartMessage.toContent();
// content = {
// t: 'Hello',
// components: [{ components: [...] }]
// }

await channel.send(content);

.toAttachments()​

Chỉ lấy attachments.

toAttachments(): ApiMessageAttachment[] | undefined

Ví dụ:

const smartMessage = SmartMessage.text("Files:")
.addFile("https://example.com/file1.pdf", "file1.pdf", "application/pdf")
.addFile("https://example.com/file2.zip", "file2.zip", "application/zip");

const attachments = smartMessage.toAttachments();
// attachments = [
// { url: '...', filename: 'file1.pdf', filetype: 'application/pdf' },
// { url: '...', filename: 'file2.zip', filetype: 'application/zip' }
// ]

Kết hợp với Mezon SDK​

Sử dụng trực tiếp với Mezon Client​

Bạn có thể sử dụng SmartMessage với Mezon SDK trực tiếp:

import { MezonClient } from "mezon-sdk";
import { SmartMessage, ButtonBuilder, ButtonStyle } from "@n0xgg04/nezon";

const client = new MezonClient({ token: "YOUR_TOKEN" });

async function sendMessage() {
const channel = await client.channels.fetch("CHANNEL_ID");

const smartMessage = SmartMessage.text("Hello from Mezon SDK!").addButton(
new ButtonBuilder().setLabel("Click Me").setStyle(ButtonStyle.Primary)
);

const payload = smartMessage.toJSON();

await channel.send(payload.content, undefined, payload.attachments);
}

Tích hợp với Mezon SDK Events​

Bạn có thể sử dụng SmartMessage trong Mezon SDK event handlers:

import { MezonClient } from "mezon-sdk";
import { SmartMessage, ButtonBuilder, ButtonStyle } from "@n0xgg04/nezon";

const client = new MezonClient({ token: "YOUR_TOKEN" });

client.on("message", async (message) => {
if (message.content?.t === "ping") {
const reply = SmartMessage.text("pong!").addButton(
new ButtonBuilder()
.setLabel("Ping Again")
.setStyle(ButtonStyle.Primary)
.setCustomId("ping/again")
);

const payload = reply.toJSON();
await message.reply(payload.content, undefined, payload.attachments);
}
});

Tạo payload động với Mezon SDK​

Kết hợp SmartMessage với logic Mezon SDK để tạo payload phức tạp:

import { MezonClient, User, Channel } from "mezon-sdk";
import { SmartMessage, EmbedBuilder } from "@n0xgg04/nezon";

async function createDynamicMessage(user: User, channel: Channel) {
const embed = new EmbedBuilder()
.setTitle(`Welcome to ${channel.name}!`)
.setDescription(`Hello ${user.display_name}`)
.setColor("#00ff00");

const message = SmartMessage.system(`User ${user.user_id} joined`).addEmbed(
embed
);

if (user.avatar_url) {
message.addImage(user.avatar_url, { filename: "avatar.jpg" });
}

return message.toJSON();
}

const client = new MezonClient({ token: "YOUR_TOKEN" });
client.on("userJoined", async (event) => {
const user = await client.users.fetch(event.user_id);
const channel = await client.channels.fetch(event.channel_id);

const payload = await createDynamicMessage(user, channel);
const targetChannel = await client.channels.fetch("TARGET_CHANNEL_ID");

await targetChannel.send(payload.content, undefined, payload.attachments);
});

Sử dụng với Mezon SDK Webhooks​

SmartMessage cũng có thể được sử dụng với webhook responses:

import express from "express";
import { SmartMessage, ButtonBuilder, ButtonStyle } from "@n0xgg04/nezon";

const app = express();

app.post("/webhook", async (req, res) => {
const { message } = req.body;

if (message.content?.t === "hello") {
const reply = SmartMessage.text("Hello from webhook!").addButton(
new ButtonBuilder().setLabel("Respond").setStyle(ButtonStyle.Primary)
);

const payload = reply.toJSON();

res.json({
content: payload.content,
attachments: payload.attachments,
});
}
});

SmartMessageLike - Flexible Input​

Nezon hỗ trợ nhiều kiểu input cho message, tự động normalize:

type SmartMessageLike =
| SmartMessage
| NormalizedSmartMessage
| ChannelMessageContent
| string;

Ví dụ các cách sử dụng:

await message.reply(SmartMessage.text("Hello"));
await message.reply("Hello");
await message.reply({ t: "Hello" });
await message.reply({
content: { t: "Hello" },
attachments: [{ url: "...", filetype: "image" }],
});

Tất cả đều được normalize thành NormalizedSmartMessage trước khi gửi.

Ví dụ thực tế​

Ví dụ 1: Menu với nhiều buttons​

import { Command, AutoContext, SmartMessage, ButtonBuilder, ButtonStyle } from '@n0xgg04/nezon';

@Command('menu')
async onMenu(@AutoContext() [message]: Nezon.AutoContext) {
await message.reply(
SmartMessage.text('Chọn một tùy chọn:')
.addButton(
new ButtonBuilder()
.setLabel('Thông tin')
.setStyle(ButtonStyle.Primary)
.setCustomId('menu/info')
)
.addButton(
new ButtonBuilder()
.setLabel('Cài đặt')
.setStyle(ButtonStyle.Secondary)
.setCustomId('menu/settings')
)
.addButton(
new ButtonBuilder()
.setLabel('Hỗ trợ')
.setStyle(ButtonStyle.Success)
.setCustomId('menu/support')
)
);
}

Ví dụ 2: Rich message với embed và attachments​

@Command('product')
async onProduct(@AutoContext() [message]: Nezon.AutoContext) {
await message.reply(
SmartMessage.text('Sản phẩm mới:')
.addEmbed(
new EmbedBuilder()
.setTitle('iPhone 15 Pro')
.setDescription('Flagship smartphone mới nhất')
.setColor('#007AFF')
.addField('Giá', '29.990.000 VNĐ', true)
.addField('Màu sắc', 'Titanium Blue', true)
.setThumbnail('https://example.com/iphone-thumb.jpg')
)
.addImage('https://example.com/iphone-full.jpg', {
filename: 'iphone.jpg'
})
.addButton(
new ButtonBuilder()
.setLabel('Mua ngay')
.setStyle(ButtonStyle.Primary)
.setCustomId('buy/iphone15')
)
);
}

Ví dụ 3: Message với file đính kèm​

@Command('download')
async onDownload(@AutoContext() [message]: Nezon.AutoContext) {
await message.reply(
SmartMessage.text('Tài liệu hướng dẫn:')
.addFile(
'https://example.com/guide.pdf',
'huong-dan.pdf',
'application/pdf',
{ size: 2048000 }
)
.addFile(
'https://example.com/sample.zip',
'mau.zip',
'application/x-zip-compressed',
{ size: 5120000 }
)
);
}

Ví dụ 4: Kết hợp với Mezon SDK để tạo message động​

import { MezonClient, User, Clan } from "mezon-sdk";
import { SmartMessage, EmbedBuilder } from "@n0xgg04/nezon";

async function createUserProfileMessage(client: MezonClient, userId: string) {
const user: User = await client.users.fetch(userId);
const clan: Clan | null = user.clan_id
? await client.clans.fetch(user.clan_id)
: null;

const embed = new EmbedBuilder()
.setTitle(`${user.display_name}'s Profile`)
.setDescription(user.bio || "No bio")
.addField("User ID", user.user_id, true)
.addField("Clan", clan?.name || "None", true);

const message = SmartMessage.text(
`Thông tin user ${user.display_name}`
).addEmbed(embed);

if (user.avatar_url) {
message.addImage(user.avatar_url, { filename: "avatar.jpg" });
}

return message.toJSON();
}

Best Practices​

1. Sử dụng method chaining​

const message = SmartMessage.text('Hello')
.addButton(...)
.addEmbed(...)
.addImage(...);

2. Tách logic phức tạp thành functions​

function createWelcomeMessage(user: User): SmartMessage {
return SmartMessage.system(`Welcome ${user.display_name}!`)
.addEmbed(
new EmbedBuilder()
.setTitle("Getting Started")
.setDescription("Chào mừng bạn đến với server!")
)
.addButton(
new ButtonBuilder()
.setLabel("Xem hướng dẫn")
.setStyle(ButtonStyle.Primary)
);
}

3. Sử dụng .toJSON() khi cần tích hợp với Mezon SDK​

const smartMessage = SmartMessage.text("Hello");
const payload = smartMessage.toJSON();

await mezonChannel.send(payload.content, undefined, payload.attachments);

4. Tận dụng type safety​

const message: SmartMessage = SmartMessage.text('Hello');
message.addButton(...);

5. Sử dụng SmartMessage.build() cho message không có text​

const message = SmartMessage.build()
.addEmbed(new EmbedBuilder().setTitle("Title Only"))
.addButton(new ButtonBuilder().setLabel("Click"));

So sánh với Mezon SDK thuần​

Tính năngSmartMessageMezon SDK thuần
Cú phápFluent, dễ đọcObject literal phức tạp
Type Safety✅ Full TypeScript support⚠️ Manual typing
Method Chaining✅ Hỗ trợ❌ Không hỗ trợ
Auto Grouping✅ Buttons tự động nhóm❌ Phải tự nhóm
Attachments✅ Dễ thêm⚠️ Phải quản lý riêng
Embeds✅ Builder pattern⚠️ Object literal
Learning Curve✅ Dễ học⚠️ Cần hiểu API structure

API Reference​

Static Methods​

  • SmartMessage.text(content: string): SmartMessage
  • SmartMessage.system(content: string): SmartMessage
  • SmartMessage.image(url: string, options?): SmartMessage
  • SmartMessage.voice(url: string, options?): SmartMessage
  • SmartMessage.build(): SmartMessage
  • SmartMessage.raw(content: ChannelMessageContent, attachments?): SmartMessage

Instance Methods​

  • .addButton(button: ButtonBuilder | ButtonComponent): this
  • .addImage(url: string, options?): this
  • .addFile(url: string, filename: string, filetype: string, options?): this
  • .addEmbed(embed: EmbedBuilder | IInteractiveMessageProps): this
  • .toJSON(): NormalizedSmartMessage
  • .toContent(): ChannelMessageContent
  • .toAttachments(): ApiMessageAttachment[] | undefined

Xem thêm​