数据格式
使用相同的 API 构建消息内容,由各平台包负责将其转换为平台原生格式。
消息发送的返回值为 Result[],即 [res1, res2, ...]。数组中的每一项对应一次实际的平台 API 调用。
核心原则:
- 不支持的数据格式,一律逐步降级到 Text。
- 媒体消息无法向后合并时,优先发送媒体,再发后续内容。
- Markdown 与 Text 互相转换:平台支持哪个,就合并到哪个格式发送,始终只产出一条消息。
Text
src/response/**/*/res.ts
import { useMessage, Format } from 'alemonjs'
export default () => {
// 创建
const [message] = useMessage()
const format = Format.create().addText('hello').addText(' ').addText('word')
message.send({ format })
}
Image
src/response/**/*/res.ts
import { useMessage, Format } from 'alemonjs'
import JPEG_PATH from '@src/assets/test.jpeg'
import { readFileSync } from 'node:fs'
export default () => {
const [message] = useMessage()
// 支持 Buffer、base64://、https://、file://
message.send({
format: Format.create().addImage(JPEG_PATH)
})
}
Image&Text
💡 不推荐使用的操作
import { useMessage, Format } from 'alemonjs'
import JPEG_PATH from '@src/assets/test.jpeg'
export default () => {
const [message] = useMessage()
const format = Format.create().addText('hello word').addImage(JPEG_PATH)
message.send({ format: format })
}
Mention&Text
response/**/*/res.ts
import { useEvent, useMessage, Format } from 'alemonjs'
export default () => {
const [event] = useEvent()
const [message] = useMessage()
// 发送多种类型的消息
message.send({
format: Format.create()
.addText('hello ')
.addMention(event.current.UserId)
.addText(', How are things going?')
})
// @ all
message.send({
format: Format.create().addMention()
})
// @ channel
message.send({
format: Format.create().addMention(event.current.ChannelId, {
belong: 'channel'
})
})
}
Audio
src/response/**/*/res.ts
import { useMessage, Format } from 'alemonjs'
import MP3_PATH from '@src/assets/test.mp3'
export default () => {
const [message] = useMessage()
// 支持 base64://、https://、file://
message.send({
format: Format.create().addAudio(MP3_PATH)
})
}
Video
src/response/**/*/res.ts
import { useMessage, Format } from 'alemonjs'
import MP4_PATH from '@src/assets/test.mp4'
export default () => {
const [message] = useMessage()
// 支持 base64://、https://、file://
message.send({
format: Format.create().addVideo(MP4_PATH)
})
}
Attachment
💡 即File,为避免和js全局对象冲突,用Attachment代替
src/response/**/*/res.ts
import { useMessage, Format } from 'alemonjs'
import DOCS_PATH from '@src/assets/test.docs'
export default () => {
const [message] = useMessage()
// 支持 base64://、https://、file://
message.send({
format: Format.create().addAttachment(DOCS_PATH)
})
}
ButtonGroup
⚠️ 框架把按钮视为一组5*5的排列,超过将不确保有效发送或渲染
import { useMessage, Format } from 'alemonjs'
export default () => {
const [message] = useMessage()
// 一行多个
message.send({
format: Format.create().addButtonGroup(
Format.createButtonGroup()
.addRow()
.addButton('开始', '/开始游戏')
.addButton('结束', '/结束游戏')
)
})
// 多行多个
message.send({
format: Format.create().addButtonGroup(
Format.createButtonGroup()
.addRow()
.addButton('开始', '/开始游戏')
.addButton('结束', '/结束游戏')
.addRow()
.addButton('退出', '/退出游戏')
.addButton('注销', '/注销账户')
)
})
// 更多类型
message.send({
format: Format.create().addButtonGroup(
Format.createButtonGroup()
// link
.addRow()
.addButton('访问文档', 'https://alemonjs.com/', { type: 'link' })
// call
.addRow()
.addButton('是否同意', '/同意', { type: 'call' })
// 自动发送 + 显示子频道list + 禁用提示
.addRow()
.addButton('哈哈', '/哈哈', {
autoEnter: false,
showList: true,
toolTip: '不支持'
})
)
})
}
- absorb
const format = Format.create()
const bt = Format.createButtonGroup()
const bt2 = Format.createButtonGroup()
// 吸收bt2的按钮
bt.absorb(bt2)
format.addButtonGroup(md)
MarkDown
import { useEvent, useMessage, Format } from 'alemonjs'
export default () => {
const [event] = useEvent()
const [message] = useMessage()
const format = Format.create()
const md = Format.createMarkdown()
md
// @UserId
.addMention(event.current.UserId)
// 换行
.addNewline()
// Button,⚠️部分平台不支持
.addButton('你好', { data: '/你好' })
// 标题
.addTitle('标题!!')
// 副标题
.addSubtitle('子标题')
// 普通文本
.addText('普通文本')
// 加粗
.addBold('加粗')
// 斜体
.addItalic('斜体')
// 星号斜体
.addItalicStar('星号斜体')
// 删除线
.addStrikethrough('删除线')
// 裸链接
.addLink('https://www.baidu.com')
// 标准链接,⚠️需要配置白名单
.addLink('链接', 'https://www.baidu.com')
// 图片
.addImage('https://www.baidu.com/img/bd_logo1.png', {
width: 100,
height: 100
})
// 有序列表
.addList(
{ index: 1, text: '有序列表' },
{ index: 2, text: '有序列表' },
{ index: 3, text: '有序列表' },
{ index: 4, text: '有序列表' }
)
// 无序列表
.addList('无序列表', '无序列表', '无序列表', '无序列表', '无序列表')
// 块引用 \n>XXX
.addBlockquote('块引用')
// \n\n 结束
.addNewline(2)
// 水平分割线
.addDivider()
format.addMarkdown(md)
message.send({ format })
}
- absorb
const format = Format.create()
const md = Format.createMarkdown()
md.addText('hello')
md.addText(' ')
const md2 = Format.createMarkdown()
md2.addText('word')
// md对md2进行吸收
md.absorb(md2)
format.addMarkdown(md) // hello word
MarkdownOriginal
import { Format } from 'alemonjs'
const format = Format.create()
format.addMarkdownOriginal(`
# 标题
## 子标题
这是一个不推荐使用的数据格式,用于不考虑兼容性,直接最大可能的写md
`)
三级优先级
消息中可能同时包含多种数据类型。平台包按照以下优先级从高到低处理:
优先级 1(媒体) ── Image / ImageURL / ImageFile / Audio / Video / Attachment
↓
优先级 2(富文本)── Markdown / ButtonGroup / MarkdownOriginal
↓
优先级 3(基础) ── Text / Link / Mention
发送规则
高优先级优先发送
当存在优先级 1 的媒体内容时,先发媒体。如果平台支持图文合并(如 caption),则低优先级内容随媒体一起发出,只产出一次调用。如果不支持合并,则拆分为多次:先发媒体,再发后续内容。
MD 与 Text 互为转换
Markdown 和 Text 是同一层信息的两种表达。调用发送时:
- 平台支持 Markdown → Text 内容合并进 Markdown,只发一条
[res(md)] - 平台不支持 Markdown → Markdown 降级为纯文本,合并到 Text,只发一条
[res(text)]
不论平台是否支持,MD 和 Text 不会分成两次 API 调用。它们必须合并为一个格式输出。
不支持的格式逐步降级到 Text
任何平台不支持的数据类型,都会先尝试转为同优先级的替代格式,最终兜底到 Text:
ButtonGroup → 降级为 "[按钮名1] [按钮名2]" 文本
Markdown → 降级为 "【标题】\n内容..." 文本
Audio/Video → 降级为 "[音频: url]" / "[视频: url]" 文本
Attachment → 降级为 "[附件: url]" 文本
无高优先级内容时依次降级
如果消息中没有媒体,直接检查优先级 2;如果也没有,发送优先级 3 纯文本。全空则返回 []。
发送流程
DataEnums[] 输入
│
├─ 1. 提取优先级 1:Image / Audio / Video / Attachment
│ 有媒体?
│ ├── 是 → 发送媒体消息
│ │ ├── 支持图文合并 → 低优先级内容作为 caption → [res1]
│ │ └── 不支持合并 → 先发媒体 [res1],剩余内容继续向下 ↓
│ └── 否 → 继续向下 ↓
│
├─ 2. 合并优先级 2 + 3:MD / Buttons / Text / Link / Mention
│ 有 Markdown?
│ ├── 支持 MD → Text 合并进 MD → [res(md)]
│ └── 不支持 MD → MD 降级为文本,合并到 Text → [res(text)]
│ 有 ButtonGroup?
│ ├── 支持按钮 → 随 MD/Text 一起发送
│ └── 不支持按钮 → 降级为文本,合并到内容中
│
├─ 3. 仅有 Text / Link / Mention?
│ └── 发送纯文本 → [res(text)]
│
└─ 4. 无内容 → []