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

qywxbot

v1.0.1

Published

企业微信群机器人 Webhook 客户端

Downloads

272

Readme

qywxbot

企业微信群机器人 Webhook 客户端,支持文本、Markdown、图片、图文、文件、语音和模板卡片消息。

特性

  • 原生 ESM,TypeScript 类型内置。
  • 直接使用运行时全局 fetch,不额外引入请求库。
  • 提供常用消息构造函数,也可以直接通过 QywxBot 发送。
  • 文件、语音素材上传和 media_id 发送流程内置。
  • 企业微信接口错误会抛出带 errcodeerrmsg 和 HTTP 状态码的异常。

安装

pnpm add qywxbot

也可以使用 npm 或 yarn:

npm install qywxbot
yarn add qywxbot

运行环境

需要运行时提供全局 fetchBlobURLResponse。推荐 Node.js 18+,或现代浏览器 / 边缘运行时。

本包是 ESM 包:

import { QywxBot } from 'qywxbot'

快速开始

先在企业微信群里添加「群机器人」,复制完整 webhook。地址通常形如:

https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

然后创建客户端并发送消息:

import { QywxBot } from 'qywxbot'

const bot = new QywxBot(process.env.QYWX_BOT_WEBHOOK!)

await bot.sendText('广州今日天气:29度', {
  mentionedList: ['wangqing'],
  mentionedMobileList: ['13800001111'],
})

await bot.sendMarkdown('实时新增用户反馈<font color="warning">132例</font>')

也可以使用工厂函数:

import { createQywxBot } from 'qywxbot'

const bot = createQywxBot('https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx')
await bot.sendText('hello')

发送消息

文本

await bot.sendText('服务发布完成', {
  mentionedList: ['@all'],
})

mentionedList 是 userid 列表,mentionedMobileList 是手机号列表。传入 @all 可以提醒所有人。

Markdown

await bot.sendMarkdown([
  '## 发布通知',
  '> 项目 qywxbot 已发布',
  '',
  '版本:<font color="info">1.0.0</font>',
].join('\n'))

Markdown 消息如需 @ 成员,请在内容中使用企业微信支持的 <@userid> 语法。

图片

企业微信图片消息需要原始二进制内容的 base64 和 md5。本包提供 imageMessageFromBuffer 辅助生成消息体:

import { readFile } from 'node:fs/promises'
import { imageMessageFromBuffer } from 'qywxbot'

const image = imageMessageFromBuffer(await readFile('./screenshot.png'))
await bot.send(image)

如果你已经有 base64 和 md5,也可以直接发送:

await bot.sendImage(base64, md5)

图文

await bot.sendNews([
  {
    title: '发布说明',
    description: '查看本次发布的变更内容',
    url: 'https://example.com/changelog',
    picurl: 'https://example.com/cover.png',
  },
])

企业微信要求图文消息包含 1 到 8 篇文章。

文件

文件需要先上传素材获取 media_id,再发送文件消息:

import { readFile } from 'node:fs/promises'

const file = new Blob([await readFile('./report.xlsx')])
const uploaded = await bot.uploadMedia(file, {
  type: 'file',
  filename: 'report.xlsx',
})

await bot.sendFile(uploaded.media_id)

语音

语音同样需要先上传素材。企业微信语音素材通常要求 AMR 格式:

import { readFile } from 'node:fs/promises'

const voice = new Blob([await readFile('./notice.amr')], {
  type: 'audio/amr',
})

const uploaded = await bot.uploadMedia(voice, {
  type: 'voice',
  filename: 'notice.amr',
})

await bot.sendVoice(uploaded.media_id)

模板卡片

模板卡片字段随 card_type 变化,客户端会保留原始结构发送给企业微信:

await bot.sendTemplateCard({
  card_type: 'text_notice',
  main_title: {
    title: '服务告警',
    desc: '接口错误率超过阈值',
  },
  emphasis_content: {
    title: '12.5%',
    desc: '错误率',
  },
})

使用消息构造函数

除了 bot.sendText() 这类便捷方法,也可以先构造消息体,再统一调用 bot.send()

import { markdownMessage, textMessage } from 'qywxbot'

await bot.send(textMessage('任务完成'))
await bot.send(markdownMessage('**任务完成**'))

导出的构造函数包括:

  • textMessage
  • markdownMessage
  • imageMessage
  • imageMessageFromBuffer
  • newsMessage
  • fileMessage
  • voiceMessage
  • templateCardMessage

错误处理

当 HTTP 请求失败,或企业微信返回非 0 errcode 时,会抛出 QywxBotApiError

import { QywxBotApiError } from 'qywxbot'

try {
  await bot.sendText('hello')
}
catch (error) {
  if (error instanceof QywxBotApiError) {
    console.error(error.status, error.errcode, error.errmsg)
  }
  else {
    throw error
  }
}

工具函数

import { createUploadWebhook, extractWebhookKey } from 'qywxbot'

const webhook = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=robot-key'

extractWebhookKey(webhook)
// => robot-key

createUploadWebhook(webhook, 'file')
// => https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?key=robot-key&type=file

本地开发

pnpm install
pnpm run typecheck
pnpm run test
pnpm run build

项目提供了手动集成测试脚本,会依次发送文本、Markdown、图片、图文、文件和模板卡片消息:

$env:QYWX_BOT_WEBHOOK="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
pnpm run test:real

如果要测试文本消息 @ 手机号:

$env:QYWX_BOT_MENTIONED_MOBILES="13800001111,13900002222"
pnpm run test:real

语音消息需要准备真实 AMR 文件:

$env:QYWX_BOT_VOICE_FILE="D:\tmp\test.amr"
pnpm run test:real

发布

pnpm run test
pnpm publish

prepublishOnly 会在发布前自动执行构建,发布内容由 files 字段限制为 dist

注意事项

  • 请保护好机器人 webhook,不要提交到 GitHub 或公开日志。
  • 企业微信限制每个机器人每分钟最多发送 20 条消息。
  • 图片消息需要提供原始二进制的 base64 和 md5。
  • 文件素材和语音素材需要先通过 uploadMedia() 上传,再使用返回的 media_id 发送。

License

MIT License