npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wecom/aibot-node-sdk

v1.0.1

Published

企业微信智能机器人 Node.js SDK - WebSocket 长连接通道

Readme

@wecom/aibot-node-sdk

企业微信智能机器人 Node.js SDK —— 基于 WebSocket 长连接通道,提供消息收发、流式回复、模板卡片、事件回调、文件下载解密等核心能力。

✨ 特性

  • 🔗 WebSocket 长连接 — 基于 wss://openws.work.weixin.qq.com 内置默认地址,开箱即用
  • 🔐 自动认证 — 连接建立后自动发送认证帧(botId + secret)
  • 💓 心跳保活 — 自动维护心跳,连续未收到 ack 时自动判定连接异常
  • 🔄 断线重连 — 指数退避重连策略(1s → 2s → 4s → ... → 30s 上限),支持自定义最大重连次数
  • 📨 消息分发 — 自动解析消息类型并触发对应事件(text / image / mixed / voice / file)
  • 🌊 流式回复 — 内置流式回复方法,支持 Markdown 和图文混排
  • 🃏 模板卡片 — 支持回复模板卡片消息、流式+卡片组合回复、更新卡片
  • 📤 主动推送 — 支持向指定会话主动发送 Markdown 或模板卡片消息,无需依赖回调帧
  • 📡 事件回调 — 支持进入会话、模板卡片按钮点击、用户反馈等事件
  • 串行回复队列 — 同一 req_id 的回复消息串行发送,自动等待回执
  • 文件下载解密 — 内置 AES-256-CBC 文件解密,每个图片/文件消息自带独立的 aeskey
  • 🪵 可插拔日志 — 支持自定义 Logger,内置带时间戳的 DefaultLogger
  • 📦 双模块格式 — 同时输出 CJS / ESM,附带完整 TypeScript 类型声明

📦 安装

npm install @wecom/aibot-node-sdk
# 或
yarn add @wecom/aibot-node-sdk

🚀 快速开始

import AiBot from '@wecom/aibot-node-sdk';
import type { WsFrame } from '@wecom/aibot-node-sdk';
import { generateReqId } from '@wecom/aibot-node-sdk';

// 1. 创建客户端实例
const wsClient = new AiBot.WSClient({
  botId: 'your-bot-id',       // 企业微信后台获取的机器人 ID
  secret: 'your-bot-secret',  // 企业微信后台获取的机器人 Secret
});

// 2. 建立连接(支持链式调用)
wsClient.connect();

// 3. 监听认证成功
wsClient.on('authenticated', () => {
  console.log('🔐 认证成功');
});

// 4. 监听文本消息并进行流式回复
wsClient.on('message.text', (frame: WsFrame) => {
  const content = frame.body.text?.content;
  console.log(`收到文本: ${content}`);

  const streamId = generateReqId('stream');

  // 发送流式中间内容
  wsClient.replyStream(frame, streamId, '正在思考中...', false);

  // 发送最终结果
  setTimeout(() => {
    wsClient.replyStream(frame, streamId, `你好!你说的是: "${content}"`, true);
  }, 1000);
});

// 5. 监听进入会话事件(发送欢迎语)
wsClient.on('event.enter_chat', (frame: WsFrame) => {
  wsClient.replyWelcome(frame, {
    msgtype: 'text',
    text: { content: '您好!我是智能助手,有什么可以帮您的吗?' },
  });
});

// 6. 优雅退出
process.on('SIGINT', () => {
  wsClient.disconnect();
  process.exit(0);
});

📖 API 文档

WSClient

核心客户端类,继承自 EventEmitter,提供连接管理、消息收发等功能。

构造函数

const wsClient = new WSClient(options: WSClientOptions);

方法

| 方法 | 说明 | 返回值 | | --- | --- | --- | | connect() | 建立 WebSocket 连接,连接后自动认证 | this(支持链式调用) | | disconnect() | 主动断开连接 | void | | reply(frame, body, cmd?) | 通过 WebSocket 通道发送回复消息(通用方法),frame 类型为 WsFrameHeaders(即 Pick<WsFrame, 'headers'>),直接传入完整 WsFrame 对象也可兼容 | Promise<WsFrame> | | replyStream(frame, streamId, content, finish?, msgItem?, feedback?) | 发送流式文本回复(便捷方法,支持 Markdown),frame 类型同上 | Promise<WsFrame> | | replyWelcome(frame, body) | 发送欢迎语回复(支持文本或模板卡片),需在收到事件 5s 内调用 | Promise<WsFrame> | | replyTemplateCard(frame, templateCard, feedback?) | 回复模板卡片消息 | Promise<WsFrame> | | replyStreamWithCard(frame, streamId, content, finish?, options?) | 发送流式消息 + 模板卡片组合回复 | Promise<WsFrame> | | updateTemplateCard(frame, templateCard, userids?) | 更新模板卡片(响应 template_card_event),需在收到事件 5s 内调用 | Promise<WsFrame> | | sendMessage(chatid, body) | 主动发送消息(支持 Markdown 或模板卡片),无需依赖回调帧 | Promise<WsFrame> | | downloadFile(url, aesKey) | 下载文件并使用 AES 密钥解密(aesKey 取自消息体),返回 Buffer 及文件名 | Promise<{ buffer: Buffer; filename?: string }> |

属性

| 属性 | 说明 | 类型 | | --- | --- | --- | | isConnected | 当前 WebSocket 连接状态 | boolean | | api | 内部 API 客户端实例(高级用途) | WeComApiClient |

replyStream 详细说明

wsClient.replyStream(
  frame: WsFrameHeaders, // 收到的原始 WebSocket 帧(透传 req_id),也可直接传完整 WsFrame 对象
  streamId: string,     // 流式消息 ID(使用 generateReqId('stream') 生成)
  content: string,      // 回复内容(支持 Markdown)
  finish?: boolean,     // 是否结束流式消息,默认 false
  msgItem?: ReplyMsgItem[], // 图文混排项(仅 finish=true 时有效)
  feedback?: ReplyFeedback, // 反馈信息(仅首次回复时设置)
);

replyWelcome 详细说明

发送欢迎语回复,需在收到 event.enter_chat 事件 5 秒内调用,超时将无法发送。

// 文本欢迎语
wsClient.replyWelcome(frame, {
  msgtype: 'text',
  text: { content: '欢迎!' },
});

// 模板卡片欢迎语
wsClient.replyWelcome(frame, {
  msgtype: 'template_card',
  template_card: { card_type: 'text_notice', main_title: { title: '欢迎' } },
});

replyTemplateCard 详细说明

wsClient.replyTemplateCard(
  frame: WsFrameHeaders,    // 收到的原始 WebSocket 帧
  templateCard: TemplateCard, // 模板卡片内容
  feedback?: ReplyFeedback,  // 反馈信息(可选)
);

replyStreamWithCard 详细说明

wsClient.replyStreamWithCard(
  frame: WsFrameHeaders,  // 收到的原始 WebSocket 帧
  streamId: string,        // 流式消息 ID
  content: string,         // 回复内容(支持 Markdown)
  finish?: boolean,        // 是否结束流式消息,默认 false
  options?: {
    msgItem?: ReplyMsgItem[];       // 图文混排项(仅 finish=true 时有效)
    streamFeedback?: ReplyFeedback; // 流式消息反馈信息(首次回复时设置)
    templateCard?: TemplateCard;    // 模板卡片内容(同一消息只能回复一次)
    cardFeedback?: ReplyFeedback;   // 模板卡片反馈信息
  },
);

updateTemplateCard 详细说明

更新模板卡片,需在收到 event.template_card_event 事件 5 秒内调用。

wsClient.updateTemplateCard(
  frame: WsFrameHeaders,    // 对应事件的 WebSocket 帧(需包含该事件的 req_id)
  templateCard: TemplateCard, // 模板卡片内容(task_id 需与回调收到的 task_id 一致)
  userids?: string[],        // 要替换模版卡片消息的 userid 列表,不填则替换所有用户
);

sendMessage 详细说明

主动向指定会话推送消息,无需依赖收到的回调帧。

wsClient.sendMessage(
  chatid: string,  // 会话 ID,单聊填用户的 userid,群聊填对应群聊的 chatid
  body: SendMarkdownMsgBody | SendTemplateCardMsgBody, // 消息体
);

使用示例:

// 发送 Markdown 消息
await wsClient.sendMessage('userid_or_chatid', {
  msgtype: 'markdown',
  markdown: { content: '这是一条**主动推送**的消息' },
});

// 发送模板卡片消息
await wsClient.sendMessage('userid_or_chatid', {
  msgtype: 'template_card',
  template_card: { card_type: 'text_notice', main_title: { title: '通知' } },
});

downloadFile 使用示例

// aesKey 取自消息体中的 image.aeskey 或 file.aeskey
wsClient.on('message.image', async (frame: WsFrame) => {
  const body = frame.body;
  const { buffer, filename } = await wsClient.downloadFile(body.image?.url, body.image?.aeskey);
  console.log(`文件名: ${filename}, 大小: ${buffer.length} bytes`);
});

⚙️ 配置选项

WSClientOptions 完整配置:

| 参数 | 类型 | 必填 | 默认值 | 说明 | | --- | --- | --- | --- | --- | | botId | string | ✅ | — | 机器人 ID(企业微信后台获取) | | secret | string | ✅ | — | 机器人 Secret(企业微信后台获取) | | reconnectInterval | number | — | 1000 | 重连基础延迟(毫秒),实际延迟按指数退避递增(1s → 2s → 4s → ... → 30s 上限) | | maxReconnectAttempts | number | — | 10 | 最大重连次数(-1 表示无限重连) | | heartbeatInterval | number | — | 30000 | 心跳间隔(毫秒) | | requestTimeout | number | — | 10000 | HTTP 请求超时时间(毫秒) | | wsUrl | string | — | wss://openws.work.weixin.qq.com | 自定义 WebSocket 连接地址 | | logger | Logger | — | DefaultLogger | 自定义日志实例 |

📡 事件列表

所有事件均通过 wsClient.on(event, handler) 监听:

| 事件 | 回调参数 | 说明 | | --- | --- | --- | | connected | — | WebSocket 连接建立 | | authenticated | — | 认证成功 | | disconnected | reason: string | 连接断开 | | reconnecting | attempt: number | 正在重连(第 N 次) | | error | error: Error | 发生错误 | | message | frame: WsFrame<BaseMessage> | 收到消息(所有类型) | | message.text | frame: WsFrame<TextMessage> | 收到文本消息 | | message.image | frame: WsFrame<ImageMessage> | 收到图片消息 | | message.mixed | frame: WsFrame<MixedMessage> | 收到图文混排消息 | | message.voice | frame: WsFrame<VoiceMessage> | 收到语音消息 | | message.file | frame: WsFrame<FileMessage> | 收到文件消息 | | event | frame: WsFrame<EventMessage> | 收到事件回调(所有事件类型) | | event.enter_chat | frame: WsFrame<EventMessage> | 收到进入会话事件(用户当天首次进入单聊会话) | | event.template_card_event | frame: WsFrame<EventMessage> | 收到模板卡片事件(用户点击卡片按钮) | | event.feedback_event | frame: WsFrame<EventMessage> | 收到用户反馈事件 |

📋 消息类型

SDK 支持以下消息类型(MessageType 枚举):

| 类型 | 值 | 说明 | | --- | --- | --- | | Text | 'text' | 文本消息 | | Image | 'image' | 图片消息(URL 已加密,使用消息中的 image.aeskey 解密) | | Mixed | 'mixed' | 图文混排消息(包含 text / image 子项) | | Voice | 'voice' | 语音消息(已转文本) | | File | 'file' | 文件消息(URL 已加密,使用消息中的 file.aeskey 解密) |

SDK 支持以下事件类型(EventType 枚举):

| 类型 | 值 | 说明 | | --- | --- | --- | | EnterChat | 'enter_chat' | 进入会话事件:用户当天首次进入机器人单聊会话 | | TemplateCardEvent | 'template_card_event' | 模板卡片事件:用户点击模板卡片按钮 | | FeedbackEvent | 'feedback_event' | 用户反馈事件:用户对机器人回复进行反馈 |

消息帧结构(WsFrame<T>

interface WsFrame<T = any> {
  cmd?: string;              // 命令类型
  headers: {
    req_id: string;          // 请求 ID(回复时需透传)
    [key: string]: any;
  };
  body?: T;                  // 消息体(泛型,默认 any)
  errcode?: number;          // 响应错误码
  errmsg?: string;           // 响应错误信息
}

消息体结构(BaseMessage

interface BaseMessage {
  msgid: string;             // 消息唯一标识
  aibotid: string;           // 机器人 ID
  chatid?: string;           // 群聊 ID(群聊时返回)
  chattype: 'single' | 'group';  // 会话类型
  from: { userid: string };  // 发送者信息
  create_time?: number;      // 事件产生的时间戳
  response_url?: string;     // 支持主动回复消息的临时 url
  msgtype: string;           // 消息类型
  quote?: QuoteContent;      // 引用消息内容
}

事件消息结构(EventMessage

interface EventMessage {
  msgid: string;             // 本次回调的唯一性标志
  create_time: number;       // 事件产生的时间戳
  aibotid: string;           // 智能机器人 ID
  chatid?: string;           // 会话 ID(仅群聊时返回)
  chattype?: 'single' | 'group';  // 会话类型
  from: EventFrom;           // 事件触发者信息(含 userid、corpid?)
  msgtype: 'event';          // 消息类型,固定为 event
  event: EventContent;       // 事件内容
}

🪵 自定义日志

实现 Logger 接口即可自定义日志输出:

interface Logger {
  debug(message: string, ...args: any[]): void;
  info(message: string, ...args: any[]): void;
  warn(message: string, ...args: any[]): void;
  error(message: string, ...args: any[]): void;
}

使用示例:

const wsClient = new AiBot.WSClient({
  botId: 'your-bot-id',
  secret: 'your-bot-secret',
  logger: {
    debug: () => {},  // 静默 debug 日志
    info: console.log,
    warn: console.warn,
    error: console.error,
  },
});

📂 项目结构

aibot-node-sdk/
├── src/
│   ├── index.ts             # 入口文件,统一导出
│   ├── client.ts            # WSClient 核心客户端
│   ├── ws.ts                # WebSocket 长连接管理器
│   ├── message-handler.ts   # 消息解析与事件分发
│   ├── api.ts               # HTTP API 客户端(文件下载)
│   ├── crypto.ts            # AES-256-CBC 文件解密
│   ├── logger.ts            # 默认日志实现
│   ├── utils.ts             # 工具方法(generateReqId 等)
│   ├── types.ts             # 类型重导出
│   └── types/
│       ├── index.ts          # 类型统一导出
│       ├── config.ts         # 配置选项类型
│       ├── event.ts          # 事件映射类型
│       ├── message.ts        # 消息相关类型
│       ├── api.ts            # API/WebSocket 帧类型
│       └── common.ts         # 通用类型(Logger)
├── examples/
│   └── basic.ts             # 基础使用示例
├── package.json
├── tsconfig.json
├── rollup.config.mjs        # Rollup 构建配置
└── yarn.lock

🔧 开发

# 安装依赖
yarn install

# 开发模式(监听文件变化)
yarn dev

# 构建
yarn build

# 运行示例
yarn example

📄 License

MIT