Nezon là thư viện NestJS giúp xây dựng bot cho nền tảng Mezon nhanh chóng, tương tự trải nghiệm của Necord với Discord.
- Decorator command: Định nghĩa text command bằng
@Command, hỗ trợ alias, prefix riêng và tự động phân tích tham số. - Decorator component: Bắt sự kiện nút bấm (và các component khác) qua
@Component, hỗ trợ pattern/regex chobutton_id, kèm@ComponentTargetđể lấy ngayMessageđã cache. Hỗ trợ named parameters (RESTful pattern) như/user/:user_id/:action. - Injection ngữ cảnh typed: Các decorator
@Message,@Channel,@Clan,@User,@MessageContent,@Args,@AutoContext… trả về đối tượng typed từmezon-sdkhoặc helper của Nezon. NamespaceNezoncung cấp alias type (Nezon.Message,Nezon.AutoContext, ...). - SmartMessage builder:
SmartMessage.text/system/image/voicegiúp dựng payload gửi tin nhắn mà không phải thao tác trực tiếp vớiChannelMessageContent. Hỗ trợ thêm buttons, images, embeds, files. - ButtonBuilder & onClick handlers: Tạo button với fluent API, hỗ trợ inline onClick handler tự động đăng ký và resolve context.
- EmbedBuilder: Tạo rich embeds với fields, images, thumbnails, và form inputs (text fields, select fields).
- Lifecycle tự động: Khởi tạo, đăng nhập bot, binding event/command/component và shutdown được xử lý trong
NezonModule. - Caching nội bộ: Hạn chế gọi API lặp lại khi truy cập channel/clan/user/message trong cùng một lần xử lý command.
Tạo một Mezon bot project mới với template sẵn:
npx create-mezon-bot my-botHoặc:
npm create mezon-bot my-botLệnh này sẽ tự động:
- Tạo cấu trúc project NestJS
- Cài đặt tất cả dependencies
- Tạo example handlers với các tính năng demo
- Cấu hình NezonModule
Sau đó chỉ cần:
cd my-bot
cp .env.example .env
# Edit .env với MEZON_TOKEN và MEZON_BOT_ID
yarn start:devTrong dự án NestJS của bạn:
yarn add @n0xgg04/nezonĐảm bảo đã cài mezon-sdk (được khai báo trong peer dependency).
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { NezonModule } from '@n0xgg04/nezon';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
NezonModule.forRoot({
token: process.env.MEZON_TOKEN ?? '',
botId: process.env.MEZON_BOT_ID ?? '',
}),
],
})
export class AppModule {}import { Injectable } from '@nestjs/common';
import {
Command,
Args,
AutoContext,
MessageContent,
SmartMessage,
} from '@n0xgg04/nezon';
import type { Nezon } from '@n0xgg04/nezon';
@Injectable()
export class PingHandler {
@Command({ name: 'ping', aliases: ['pong'] })
async onPing(
@Args() args: Nezon.Args,
@AutoContext() [message]: Nezon.AutoContext,
@MessageContent() content?: string,
) {
const suffix = args.length ? args.join(' ') : 'pong';
await message.reply(SmartMessage.text(`✅ ${suffix} (${content})`));
}
}import { Injectable } from '@nestjs/common';
import {
Command,
AutoContext,
Component,
ComponentParams,
SmartMessage,
ButtonBuilder,
ButtonStyle,
} from '@n0xgg04/nezon';
import type { Nezon } from '@n0xgg04/nezon';
@Injectable()
export class ButtonHandler {
@Command('button')
async askForConfirm(@AutoContext() [message]: Nezon.AutoContext) {
await message.reply(
SmartMessage.text('Nhấn nút để xác nhận.').addButton(
new ButtonBuilder()
.setCustomId('/demo/success/12345')
.setLabel('Confirm')
.setStyle(ButtonStyle.Success),
),
);
}
@Component({ pattern: '/demo/success/:source_id' })
async onConfirm(
@ComponentParams('source_id') sourceId: string | undefined,
@AutoContext() [message]: Nezon.AutoContext,
) {
await message.reply(
SmartMessage.text(`Đã xác nhận với source ID: ${sourceId}`),
);
}
}import { Injectable } from '@nestjs/common';
import {
Command,
AutoContext,
SmartMessage,
ButtonBuilder,
ButtonStyle,
} from '@n0xgg04/nezon';
import type { Nezon } from '@n0xgg04/nezon';
@Injectable()
export class ButtonHandler {
@Command('onclick')
async onClickDemo(@AutoContext() [message]: Nezon.AutoContext) {
await message.reply(
SmartMessage.text('Click the buttons below!')
.addButton(
new ButtonBuilder()
.setLabel('Button 1')
.setStyle(ButtonStyle.Primary)
.onClick(async (context) => {
await context.message.reply(
SmartMessage.text('Button 1 was clicked!'),
);
}),
)
.addButton(
new ButtonBuilder()
.setLabel('Button 2')
.setStyle(ButtonStyle.Success)
.onClick(async ({ message, channel, user }) => {
const channelName = channel?.name ?? 'unknown';
const userName = user?.username ?? 'unknown';
await message.reply(
SmartMessage.text(
`Button 2 was clicked by ${userName} in ${channelName}!`,
),
);
}),
),
);
}
}import { Injectable } from '@nestjs/common';
import {
Command,
AutoContext,
SmartMessage,
EmbedBuilder,
} from '@n0xgg04/nezon';
@Injectable()
export class EmbedHandler {
@Command('embed')
async onEmbedDemo(@AutoContext() [message]: Nezon.AutoContext) {
await message.reply(
SmartMessage.text('').addEmbed(
new EmbedBuilder()
.setColor('#abcdef')
.setTitle('Example Embed')
.setThumbnail('https://example.com/thumb.jpg')
.addField('Field 1', 'Value 1', true)
.addField('Field 2', 'Value 2', true)
.setImage('https://example.com/image.jpg')
.setFooter('Example footer'),
),
);
}
}await message.reply(
SmartMessage.text('').addEmbed(
new EmbedBuilder()
.setColor('#E91E63')
.setTitle('[SPECIALIZED] The basic managerial skill(s) is(are)')
.setDescriptionMarkdown(
[
'1 - business strategy, human resource practices, organisational capabilities',
'2 - marketing strategy, human resource practices, organisational capabilities',
'3 - business strategy, human resource practices, organisational structure',
'4 - marketing strategy, human resource practices, organisational structure',
'5 - to supervise',
'6 - to stimulate',
'7 - to motivate',
'8 - all of the above',
],
{ after: '(Chọn đáp án đúng tương ứng phía bên dưới!)' },
),
),
);import { Injectable } from '@nestjs/common';
import {
Command,
AutoContext,
SmartMessage,
EmbedBuilder,
ButtonBuilder,
ButtonStyle,
} from '@n0xgg04/nezon';
@Injectable()
export class FormHandler {
@Command('form')
async onFormDemo(@AutoContext() [message]: Nezon.AutoContext) {
await message.reply(
SmartMessage.build()
.addEmbed(
new EmbedBuilder()
.setColor('#E91E63')
.setTitle('POLL CREATOR')
.addTextField('Title', 'title', {
placeholder: 'Input title here',
defaultValue: '',
})
.addTextField('Option 1️⃣', 'option_1', {
placeholder: 'Input option 1 here',
})
.addSelectField(
'Type',
'type',
[
{ label: 'Single choice', value: 'SINGLE' },
{ label: 'Multiple choice', value: 'MULTIPLE' },
],
'SINGLE',
)
.setFooter('Powered by Mezon'),
)
.addButton(
new ButtonBuilder().setLabel('Create').setStyle(ButtonStyle.Success),
),
);
}
}Component hỗ trợ RESTful pattern với named parameters:
@Component({ pattern: '/user/:user_id/:action' })
async onUserAction(
@ComponentParams('user_id') userId: string | undefined,
@ComponentParams('action') action: string | undefined,
@ComponentParams() allParams: Record<string, string> | undefined,
@AutoContext() [message]: Nezon.AutoContext,
) {
console.log('User ID:', userId);
console.log('Action:', action);
console.log('All params:', allParams);
}SmartMessage.text(content)- Dựng payload text cơ bảnSmartMessage.system(content)- Áp dụng markdown triple (EMarkdownType.TRIPLE) cho toàn bộ nội dungSmartMessage.image(url, { alt, filename })- Hỗ trợ đính kèm ảnhSmartMessage.voice(url, { transcript })- Hỗ trợ đính kèm voiceSmartMessage.build()- Tạo SmartMessage rỗng.addButton(button)- Thêm button (tự động group thành action rows).addImage(url, options)- Thêm ảnh attachment.addFile(url, filename, filetype, options)- Thêm file attachment.addEmbed(embed)- Thêm embed
.setCustomId(id)- Set custom ID (không thể dùng cùng với.onClick()).setLabel(label)- Set label hiển thị.setStyle(style)- Set style (ButtonStyle.Primary, Secondary, Success, Danger, Link).setDisabled(disabled)- Set disabled state.setURL(url)- Set URL cho link button.onClick(handler)- Set inline click handler (tự động generate ID, không thể dùng cùng với.setCustomId())
.setColor(color)- Set màu embed (hex string).setTitle(title)- Set tiêu đề.setURL(url)- Set URL cho title.setAuthor(name, iconUrl, url)- Set author.setDescription(description)- Set mô tả.setDescriptionMarkdown(description, options?)- Set mô tả với markdown/code block helper.setThumbnail(url)- Set thumbnail.addField(name, value, inline?)- Thêm field.setImage(url)- Set ảnh lớn.setTimestamp(timestamp?)- Set timestamp (mặc định là now).setFooter(text, iconUrl?)- Set footer.addTextField(name, inputId, options)- Thêm text input field trong embed.addSelectField(name, inputId, options, selectedValue?)- Thêm select field trong embed
Repo đã kèm ứng dụng mẫu tại apps/mebot. Bạn có thể chạy thử:
cd apps/mebot
yarn install
yarn startĐừng quên set MEZON_TOKEN và MEZON_BOT_ID vào biến môi trường.
- Mở issue hoặc gửi PR nếu bạn muốn bổ sung decorator mới, cải thiện type, hoặc hỗ trợ thêm loại component.
- Kiểm tra
apps/mebotđể tham khảo cách kết hợp nhiều decorator.
Chúc bạn xây dựng bot Mezon thật nhanh với Nezon! 🚀