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

@starim-io/bot-sdk

v0.1.6

Published

Official Node.js SDK for StarIM Bot API and Webhooks

Readme

@starim-io/bot-sdk

npm version npm downloads license

StarIM 机器人开放平台的 官方 Node.js / TypeScript SDK,封装:

  • Bot APIAuthorization: Bearer <bot_token> 调用网关 /api/v1/bots/**
  • Webhook:校验 X-StarIM-Signature-V2(HMAC-SHA256,带 X-StarIM-Timestamp 防重放)、update_id 幂等、高阶事件路由

已发布到 npm:@starim-io/bot-sdk · npm install @starim-io/bot-sdk(版本号以 npm registry 为准)

要求

  • Node.js ≥ 18(依赖全局 fetch

安装

npm install @starim-io/bot-sdk
# 或
pnpm add @starim-io/bot-sdk

本仓库已将 botsdk/nodejs 加入根目录 pnpm workspace,在仓库根执行 pnpm install 即可安装依赖。单独构建/测试:

pnpm --filter @starim-io/bot-sdk run build
pnpm --filter @starim-io/bot-sdk run test

对外即可在任意项目中:

npm install @starim-io/bot-sdk
# 或
pnpm add @starim-io/bot-sdk

5 分钟快速上手

  1. 在 StarIM 开放平台开发者控制台提交机器人申请,由平台 Admin 审核通过后取得 sbot_... Token。
  2. 为机器人配置 HTTPS Webhook,并设置 secret_token(与下文代码中 secretToken 一致)。
  3. 编写 Webhook 服务(需保留 原始 JSON 字节 用于验签,见下文「Webhook 签名校验」)。
import { StarIMBot } from '@starim-io/bot-sdk';

const bot = new StarIMBot({
  token: process.env.STARIM_BOT_TOKEN!,
  secretToken: process.env.STARIM_WEBHOOK_SECRET!,
  baseUrl: process.env.STARIM_API_BASE // 例如 https://gateway.example/api/v1
});

bot.on('message', async ctx => {
  if (ctx.message.text) {
    await ctx.reply(`你说: ${ctx.message.text}`);
  }
});

await bot.start({ port: 8787, path: '/webhook' });

将公网 HTTPS 反代到 http://<host>:8787/webhook,在开放平台把 Webhook URL 设为 https://你的域名/webhook。在会话中 @机器人 或发 /command,即可在控制台看到 Update 并收到自动回复。

环境变量

| 变量 | 说明 | |------|------| | STARIM_API_BASE | 网关根 URL,须含 /api/v1,如 https://gw.example.com/api/v1 | | STARIM_BOT_TOKEN | 审核通过后签发的 Bot Token(sbot_ 前缀) | | STARIM_WEBHOOK_SECRET | setWebhook 时配置的 secret_token |

未设置 STARIM_API_BASE 时,客户端默认使用占位地址 https://api.starim.example/api/v1请务必改为真实网关

低阶客户端 StarIMBotClient

不跑 Webhook、仅调 Bot API 时使用:

import { StarIMBotClient } from '@starim-io/bot-sdk';

const client = new StarIMBotClient({
  token: process.env.STARIM_BOT_TOKEN!,
  baseUrl: process.env.STARIM_API_BASE
});

await client.setWebhook({
  url: 'https://example.com/webhook',
  secret_token: 'your-secret',
  allowed_updates: ['message']
});

const me = await client.getMe();
await client.sendMessage({
  chat_id: '<conversationId>',
  text: 'hello',
  reply_markup: {
    inline_keyboard: [[{ text: '收到', callback_data: 'ack' }]]
  }
});

await client.setMyCommands({
  commands: [
    { command: 'start', description: '开始使用' },
    { command: 'help', description: '帮助' }
  ],
  scope: 'all_private_chats'
});

主要方法

| 方法 | 说明 | |------|------| | getMe() | GET /bots/me | | sendMessage | 文本消息(可选 reply_markup:InlineKeyboard、ReplyKeyboard、remove_keyboard) | | setMyCommands / getMyCommands / deleteMyCommands | 命令菜单(按 scopelanguage_code 分组) | | sendPhoto / sendDocument / sendVideo / sendAudio | file_id 为上传完成后的文件 ID | | sendLocation | 经纬度 | | editMessage / editMessageReplyMarkup / deleteMessage | 仅允许操作本机器人发送的消息;editMessageReplyMarkup 可更新或清空 InlineKeyboard | | answerCallbackQuery | 回应 callback_query 按钮点击(toast / alert / url) | | answerInlineQuery | 应答 Inline Mode 查询结果 | | answerFriendRequest | 处理用户发起的 pending 好友申请(friend_request_mode = manualfriendship_id + accept/reject) | | getFriendRequests | 列出待处理好友申请(GET /bots/getFriendRequests,与 DB pending 同源) | | setMyFriendRequestMode / getMyFriendRequestMode | 配置 / 读取好友申请策略(auto_accept / auto_reject / manual) | | kickChatMember / banChatMember / unbanChatMember | 群治理(chat_id 为群会话 Mongo _id;机器人须为群主或群管理员;ban 为先踢后黑,非原子) | | setWebhook / deleteWebhook | Webhook 配置 | | issueUploadCredentials / completeUpload | S3 直传两步 | | sendPhotoFromFile / sendDocumentFromFile | 本地路径 → 上传 → 发送 | | verifyToken() | 公开接口校验 Token | | listChatVirtualMembers / simulateSendMessage(v0.10.10,增值能力) | 列群内可调度成员 / 以指定成员身份在群里发文本;默认不对外开放,需联系开放平台支持申请开通 |

调度群内成员(v0.10.10,增值能力)

该能力默认不对外开放,需联系开放平台支持申请开通后使用。 首期仅支持文本(type=text),单条 ≤ 1000 字;所有调用均会被记录用于合规追溯。

import { StarIMBotClient } from '@starim/bot-sdk';

const client = new StarIMBotClient({ token: process.env.STARIM_TOKEN! });

// 1) 列出群内可被调度的成员
const { items, pagination } = await client.listChatVirtualMembers({
  chat_id: '<conversation_id>',
  limit: 50
});
console.log('candidates', pagination.total, items.slice(0, 3));

// 2) 选一个成员让其在群里发一条文本
if (items[0]) {
  await client.simulateSendMessage({
    chat_id: '<conversation_id>',
    virtual_user_id: items[0].virtual_user_id,
    text: '大家好~'
  });
}

常见错误:

| HTTP | 触发 | |------|------| | 400 | 入参缺失 / virtual_user_id 格式不合法 / text 为空或超长 | | 403 | 能力未开通 / 机器人不在该群 / 目标成员不在该群或状态不可用 | | 404 | virtual_user_id 不存在 |

InlineKeyboard / Callback Query

bot.command('menu', async ctx => {
  await ctx.reply('请选择:', {
    reply_markup: {
      inline_keyboard: [
        [
          { text: '点赞', callback_data: 'like' },
          { text: '文档', url: 'https://open.starim.io/' }
        ]
      ]
    }
  });
});

bot.on('callback_query', async ctx => {
  await ctx.answerCallback({ text: `收到: ${ctx.callbackQuery?.data}` });
});

Webhook 签名校验原理

平台投递时(见 WebhookDeliveryWorker)下发的签名头:

| 头 | 算法 | 防 replay | 状态 | | --- | --- | --- | --- | | X-StarIM-Signature-V2 | sha256=HMAC(secret, "${ts}.${body}") | (结合 X-StarIM-Timestamp 判 5min 时窗) | 当前唯一推荐 | | X-StarIM-Timestamp | unix 秒 | — | V2 必带 | | X-StarIM-Update-Id | 透传 update.update_id | — | 用于幂等 |

SDK 的 verifyWebhookSignature / processWebhook / webhookMiddleware 默认使用 V2 校验。

服务端必须用 与计算签名时完全相同的字节 验签。若使用 express.json() 默认中间件,会丢失原始字节,签名将无法对齐。推荐之一:

方式 A:express.raw

import express from 'express';
import { StarIMBot } from '@starim-io/bot-sdk';

const app = express();
const bot = new StarIMBot({ token: '...', secretToken: '...' });

app.post('/webhook', express.raw({ type: 'application/json' }), bot.webhookCallback());

方式 B:express.jsonverify 保存 rawBody

app.use(
  express.json({
    verify: (req: express.Request & { rawBody?: Buffer }, _res, buf) => {
      req.rawBody = buf;
    }
  })
);
// 再挂载 bot.webhookCallback(),SDK 会优先读取 req.rawBody

手写验签示例(V2):

import { createHmac, timingSafeEqual } from 'node:crypto';

function verifyV2(
  secret: string,
  rawBody: Buffer,
  sigHeader: string | undefined,
  tsHeader: string | undefined
) {
  if (!sigHeader?.startsWith('sha256=') || !tsHeader) throw new Error('bad sig');
  const ts = Number(tsHeader);
  if (!Number.isFinite(ts) || Math.abs(Math.floor(Date.now() / 1000) - ts) > 300) {
    throw new Error('replay window');
  }
  const expected = Buffer.from(sigHeader.slice(7), 'hex');
  const actual = createHmac('sha256', secret)
    .update(`${ts}.`)
    .update(rawBody)
    .digest();
  if (expected.length !== actual.length || !timingSafeEqual(expected, actual)) {
    throw new Error('bad sig');
  }
}

多媒体:本地 / Buffer / URL

import {
  StarIMBotClient,
  sendPhotoFromBuffer,
  sendPhotoFromUrl,
  sendVideoFromUrl
} from '@starim-io/bot-sdk';
import { readFile } from 'node:fs/promises';

const client = new StarIMBotClient({ token: '...', baseUrl: '...' });

await client.sendPhotoFromFile(chatId, './a.png', { caption: '本地文件' });

const buf = await readFile('./a.png');
await sendPhotoFromBuffer(client, chatId, buf, { fileName: 'a.png', fileType: 'image/png' });

await sendPhotoFromUrl(client, chatId, 'https://example.com/p.png', { fileName: 'p.png' });
await sendVideoFromUrl(client, chatId, 'https://example.com/v.mp4', {
  fileName: 'v.mp4',
  fileType: 'video/mp4',
  caption: '可选说明',
  duration: 10
});

错误码说明(常见)

平台与网关可能返回 success: false 或 HTTP 4xx/5xx,SDK 统一抛出 StarIMApiError(含 statusCodemessagecode 若存在)。文档与产品侧常见语义如下(具体以网关响应为准):

| 语义 | 可能场景 | |------|----------| | invalid_token / 401 | Token 错误、吊销、格式不对 | | bot_disabled / 403 | 机器人未审核通过、已停用 | | chat_forbidden / 4xx | 无权限向目标会话发消息 | | rate_limited / 429 | 触发发送配额 | | webhook_invalid | Webhook URL 非 HTTPS 或配置无效(配置接口返回消息) |

幂等与重复投递

平台重试 Webhook 时 保持同一 update_idStarIMBot 内置 LRU(默认 1024 条)去重:重复 update_id 返回 200 ok 且不再执行业务 handler。可传入自定义 DedupeStore(见 processWebhook / 源码 webhook.ts)。

示例脚本

examples/ 目录:

  • echo-bot.ts — 回声
  • slash-command.ts/start/help
  • send-photo.ts — 本地图片上传并发送
cd botsdk/nodejs
pnpm install
npx tsx examples/echo-bot.ts

构建

pnpm run build   # 输出 dist/(ESM + CJS + 类型声明)
pnpm run test    # Vitest

License

MIT © StarIM