chatboty
v0.2.1
Published
chatboty — 企业微信(WeCom)IM 自动化 SDK。自研全生态框架, 收发消息/群管理/群事件/联系人, 经云网关连接你的探针实例。
Maintainers
Readme
chatboty
企业微信(WeCom)IM 自动化 SDK —— 自研全生态框架,零外部框架依赖。
用一个订阅 token,即可在 Node 里收发消息、管理群、监听群事件、读联系人。SDK 自动向平台发证、连接到分配给你的探针实例。
概念(Contact / Room / Message / 事件)类似业界常见的聊天机器人框架,但 chatboty 为完全自研实现。
安装
npm install chatboty要求 Node.js >= 18。
快速上手
import { Chatboty } from 'chatboty'
const bot = new Chatboty({
token: 'cbt_你的订阅token', // 来自 chatboty 控制台
// atlasUrl: 'https://api.chatboty.ai', // 平台地址(默认本地开发地址)
})
bot.on('message', async (m) => {
if (m.self()) return // 跳过自己发的
console.log(`[${m.id}] ${m.talker()?.name()}: ${m.text()}`)
if (m.text() === 'ping') await m.say('pong')
})
bot.on('room-join', (e) => {
const names = e.contactList.map(c => c.name()).join('、')
e.room.say(`欢迎 ${names} 加入!`)
})
bot.on('login', (self) => console.log('已登录:', self.name())) // self 是 typed Contact
bot.on('error', (err) => console.error('探针错误:', err.message)) // err 是真正的 Error
await bot.start()就这样——token 决定连到哪个企业微信实例,平台负责路由与隔离。
同步 vs 异步约定
一条简单规则贯穿所有实体:
实体已经持有的数据(来自事件 payload / 缓存)用同步读取; 任何需要回连接器 / 企微的操作(下载、拉通讯录、发送、建群、改名…)一律
async。
- 同步:
m.id、m.text()、m.type()、m.self()、m.talker()、m.room()、m.mentionSelf();c.id、c.name()、c.alias()、c.title()、c.phone();r.id、r.topic()、r.owner()。 - 异步:
m.say()、m.toFileBox()、m.download()、m.mentionList();r.say()、r.rename()、r.memberAll();bot.currentUser()、bot.contactFind*()、bot.roomFind*()、bot.roomCreate()、bot.sayTo()。
id是只读属性(m.id,不是m.id())——Message / Contact / Room 皆然。
事件
| 事件 | 回调参数 | 说明 |
|---|---|---|
| message | Message | 收到消息(文本/图片/文件/链接/小程序/系统) |
| room-join | { room, contactList, idList, timestamp } | 有人进群(含外部客户完整信息) |
| room-leave | { room, contactList, idList, timestamp } | 有人退群/被移除 |
| room-topic | { room, topic, oldTopic, timestamp } | 群改名 |
| friendship | Friendship | 加好友/删好友 |
| login | Contact | 登录成功, 回调收到 typed 的当前登录者 Contact |
| logout | — | 登出 |
| scan | { qrcode, status } | 登录二维码 |
| error | Error | 探针/代理侧错误信号(不中断运行, 仅上报; 回调收到真正的 Error 对象) |
| heartbeat | { state } | 探针心跳(~30s), 供存活/掉线判定 |
实体 API(节选)
// Message —— id 是只读属性; 其余同步读取; 下载/发送为 async
m.id; m.text(); m.type(); m.self(); m.talker(); m.room(); m.file()
await m.say(sayable)
m.mentionSelf(); // 本条是否 @ 了我/@所有人(布尔, 纯文本判定)
await m.mentionList(); // 被 @ 的 Contact[](best-effort: 解析正文@名→就近解析通讯录)
const f = await m.toFileBox(); // → ReceivedFile{dataUrl,mime,name,size} + toBuffer()/save(); 别名 m.download()
if (f) await f.save('./photo.png'); // 或 const buf = f.toBuffer()
// Contact
c.id; c.name(); c.alias(); c.avatar(); c.title(); c.phone(); c.type(); await c.say(sayable)
// 注: 企微通讯录未采集性别 → 不提供 gender()
// Room
r.id; r.topic(); r.owner(); // 同步(缓存)
await r.say(sayable); await r.rename(topic); await r.memberAll()
// Bot —— 集合方法对称: contactFind/contactFindAll, roomFind/roomFindAll, roomCreate
bot.roomCreate(memberIds, topic)
await bot.roomFind({ topic: '项目群' }); // 或 roomFind('关键词') → Room | undefined
await bot.roomFindAll(); // → Room[](全部已加载群; 传 query 则本地过滤)
await bot.contactFind({ name: '张三' }); // 或 contactFind('关键词') → Contact | undefined
await bot.contactFindAll('关键词'); // → Contact[](本地通讯录过滤)
await bot.currentUser(); bot.self; // 当前登录者(Contact) / 缓存的 self getter
await bot.logout(); // 干净登出(回扫码页, 不重启探针)发送内容(Sayable)
await contact.say('纯文本')
await room.say({ image: 'https://.../pic.png' }) // 图片(URL/本地路径/base64)
await room.say({ file: '/path/to/doc.pdf' }) // 文件
await room.say({ url: 'https://...', title: '标题', description: '摘要', thumbUrl: '...' }) // 链接卡片
await room.say({ miniProgram: { appid: '...', title: '...', path: '...', thumb: '...' } }) // 小程序卡片服务发现接入(推荐)
除 token 直连外,也可用服务令牌 + 服务发现端点接入。SDK 会先向发现端点换取一个短 TTL 的接入凭据,再连网关,并在过期前自动刷新:
const bot = new Chatboty({
serviceToken: '你的服务令牌', // 长期令牌
serviceAuthority: 'https://discovery.chatboty.ai', // 发现端点(权威 URL)
})
await bot.start()流程:POST {serviceAuthority}(Authorization: Bearer {serviceToken})→ { endpoint, accessToken, expiresIn } → 连 wss://{endpoint}/client?access={accessToken}(网关验 RS256 → seatId)。accessToken 到期前约 30s 自动重新发现刷新;断线按指数退避自愈重连。仅给 { token, endpoint } 时维持既有 ?token= 直连行为不变。
仅用传输层(高级)
不想要框架封装,只要底层连接?
import { ChatbotyClient } from 'chatboty/client'
const c = new ChatbotyClient({ token: 'cbt_...' })
await c.start()
const rooms = await c.roomList()License
Apache-2.0
