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

@seayoo-web/messenger

v3.0.2

Published

agent for messenger

Readme

@seayoo-web/messenger

消息系统 messenger 的 Node.js SDK。

两种消息:

  • 定向消息(PersonalMessage)—— 发给指定的人,用 API Key 鉴权。
  • 主题消息(TopicMessage)—— 发到一个主题,由主题的关注者收取,用主题密钥鉴权。
pnpm add @seayoo-web/messenger

要求 Node.js >= 22。这是服务端 SDK,在浏览器里 import 会直接抛错。


定向消息

API Key 在 messenger.seayoo.com 的「我的密钥」里签发,签发时填写的「来源」会记进调用统计。

import { PersonalMessage } from "@seayoo-web/messenger";

const messenger = new PersonalMessage(process.env.MESSENGER_API_KEY!, "order-service", "1.2.3");

await messenger.send({
  to: "[email protected]",
  title: "订单异常",
  content: {
    订单号: "SO-20260920-001",
    金额: 128,
    状态: "支付超时",
  },
  payload: "请尽快处理",
});

send(options)

| 字段 | 类型 | 说明 | | --------- | --------------------------------------------------------------------------- | -------------------------------------------------------------- | | to | string \| string[] | 接收人邮箱,必填 | | title | string | 消息标题 | | content | string \| string[] \| Record<string, string \| number \| boolean \| null> | 消息内容,见下文 | | payload | string? | 附加内容,显示在正文下方 | | style | "default" \| "warning" \| "info" \| "error" | 消息样式 | | enhance | "key" \| "value" \| "kv" \| "none" | 加粗强调哪一部分,仅当 content 不是字符串时有效 | | sender | string? | 信使的 AppId(向系统管理员查询),不填或找不到时由默认信使发送 |


主题消息

主题 ID 和主题密钥在主题的管理页面里获取。

import { TopicMessage } from "@seayoo-web/messenger";

const topic = new TopicMessage(topicId, topicSecretKey, "order-service", "1.2.3");

await topic.publish({
  title: "每日对账完成",
  content: ["共 1024 笔", "差异 0 笔"],
  keywords: ["对账"],
});

publish(options)

字段与 send 相同,但没有 to 和 sender,多一个 keywords:

| 字段 | 类型 | 说明 | | ---------- | ----------- | -------------------------------------------- | | keywords | string[]? | 消息关键词。关注者可以只订阅自己关心的关键词 |

管理关注者

// 查询关注列表(不含群聊)
const followers = await topic.queryFollower();
// [{ email, name, followTime, status: "wait" | "follow", keywords }]

// 添加关注
await topic.addFollower({ email: "[email protected]", name: "张三", keywords: ["对账"] });

// 移除关注
await topic.removeFollower("[email protected]");

queryFollower 失败时返回空数组,不抛错;其余方法见下面的「错误处理」。


消息内容的三种写法

content 支持三种形式:

// 一段纯文本
content: "今天的对账已完成";

// 多行文本
content: ["共 1024 笔", "差异 0 笔"];

// 键值对,会渲染成两列;值不是字符串时自动转成字符串
content: { 订单号: "SO-001", 金额: 128, 已支付: true };

键值对里,把键或值写成 "-" 会渲染成一条分隔线:

content: { 订单号: "SO-001", "-": "-", 金额: 128 };

声明调用方

两个客户端的构造函数都接受可选的调用方名字和版本,会带进 User-Agent:

new PersonalMessage(apiKey, "order-service");
new PersonalMessage(apiKey, "order-service", "1.2.3");
new TopicMessage(topicId, topicSecretKey, "order-service", "1.2.3");

发出的 User-Agent 形如:

messenger-node-sdk/3.0.2 (order-service/1.2.3; node22.13.0; linux/x64)

服务端每个请求都会记一行日志,userAgent 就在里面,排查问题时可以直接按调用方筛。不传则只带 SDK 自己的信息。

SDK 版本也单独导出,方便打进自己的启动日志:

import { sdkVersion } from "@seayoo-web/messenger";

错误处理

send / publish / addFollower / removeFollower 成功时 resolve true,失败一律抛错,不会返回 false。

参数不合法时抛普通 Error(MissingApiKey、MissingTarget、MissingContent、EmailFormatError);请求失败时抛 MessengerError,它继承自 Error,额外带着服务端的错误码、这次请求的追踪编号和 HTTP 状态码:

import { traceIdOf } from "@seayoo-web/messenger";

try {
  await messenger.send({ to, title, content });
} catch (err) {
  // 反馈问题时带上 traceId,服务端按它能查到这次请求的完整日志
  logger.error("发送失败", { err, traceId: traceIdOf(err) });
}

traceIdOf 按字段取值而不是用 instanceof——ESM 与 CJS 两份构建混用时 instanceof 不成立。请求没发出去(连不上、超时)时 status 为 0。

按错误码分支

message 是给人看的描述,要分辨错误类型请用 code,取值见 errorCode:ApiKeyMissing、ApiKeyInvalid、ApiKeyRevoked、ParamInvalid、ParamMissing、Forbidden、FollowLimitReached、UnexpectedError。响应体不是 { code, message } 结构时(网关等中间环节)code 是空串。

API Key 没通过校验时服务端返回 401,三种原因分开给。它们都得人来处置,重试没有意义:

import { isApiKeyRejected, codeOf, errorCode } from "@seayoo-web/messenger";

try {
  await messenger.send({ to, title, content });
} catch (err) {
  if (isApiKeyRejected(err)) {
    // 缺失、无效或者已被吊销
    alert.page("messenger 的 API Key 不可用", { code: codeOf(err) });
  }
  if (codeOf(err) === errorCode.apiKeyRevoked) {
    // 只想区分「被管理员吊销」时
  }
}

codeOf 和 traceIdOf 一样按字段取值。注意 Node 自己的错误也常带 code(ECONNREFUSED 这类),拿到非空值不代表它来自 messenger 服务端,判断时要和 errorCode 里的值比对。


追踪编号

SDK 每个请求都会带上 X-Trace-Id,服务端沿用这个编号记日志并在响应头里带回来,两边的日志就能按同一个编号串起来。

默认每个请求自己生成一个。如果你的服务已经有请求上下文,设置一次钩子,SDK 就会从你的上下文里取:

import { setTraceIdSource } from "@seayoo-web/messenger";

// 比如用 AsyncLocalStorage 保存请求上下文
setTraceIdSource(() => store.getStore()?.traceId);

取不到或格式不合规(服务端要求 16–64 位的字母、数字、下划线或短横线)时,SDK 自己生成一个;钩子抛错不影响发消息。传 null 取消。

编号格式可以用 isValidTraceId 自行校验。


从 2.x 升级

3.0.0 跟着服务端统一了 API Key 的用词,两处不兼容:

  • API Key 没通过校验时的判别标识从 message 挪到了 code。 2.x 里服务端返回的 code 恒为 Unauthorized,标识写在 message 里,叫 ServiceTokenMissing / ServiceTokenError / ServiceTokenRevoked;现在 code 就是 ApiKeyMissing / ApiKeyInvalid / ApiKeyRevoked,message 换成了中文描述。原先按 message 匹配的代码要改成 isApiKeyRejected(err) 或者比对 codeOf(err)。状态码仍是 401。
  • 没传 API Key 时抛的 MissingToken 改名为 MissingApiKey。

请求头仍然是 x-msg-token,构造函数的签名也没变,只是参数名在文档里改叫 apiKey。

MessengerError 新增了 code 字段,另外新增 errorCode、codeOf、isApiKeyRejected 三个导出——这些都是加法,不影响现有代码。


文档:https://messenger.seayoo.com/docs/sdk