feat: add Feishu notifier for message notifications

- Implemented a lightweight notification library with support for multiple channels.
- Added FeishuNotifier class to send messages via Feishu webhook.
- Created README documentation for usage and configuration of Feishu notifier.
- Added TypeScript configuration for the project.
- Included a test script for verifying Feishu message notifications.
This commit is contained in:
2026-01-08 16:43:53 +08:00
commit 6cf949bcd1
14 changed files with 1374 additions and 0 deletions

56
.cnb.yml Normal file
View File

@@ -0,0 +1,56 @@
# # .cnb.yml
# $:
# vscode:
# - docker:
# image: docker.cnb.cool/kevisual/dev-env:latest
# services:
# - vscode
# - docker
# imports: https://cnb.cool/kevisual/env/-/blob/main/env.yml
# # 开发环境启动后会执行的任务
# # stages:
# # - name: pnpm install
# # script: pnpm install
# main:
# web_trigger_sync_to_gitea:
# - services:
# - docker
# imports:
# - https://cnb.cool/kevisual/env/-/blob/main/env.yml
# stages:
# - name: 'show username'
# script: echo "GITEA_USERNAME is ${GITEA_USERNAME} and GITEA_PASSWORD is ${GITEA_PASSWORD}"
# - name: sync to gitea
# image: tencentcom/git-sync
# settings:
# target_url: https://git.xiongxiao.me/template/simple-bun.git
# auth_type: https
# username: "oauth2"
# password: ${GITEA_TOKEN}
# git_user: "abearxiong"
# git_email: "xiongxiao@xiongxiao.me"
# sync_mode: rebase
# branch: main
# web_trigger_sync_from_gitea:
# - services:
# - docker
# imports:
# - https://cnb.cool/kevisual/env/-/blob/main/env.yml
# stages:
# - name: '添加 gitea的origin'
# script: |
# git remote remove gitea 2>/dev/null || true
# git remote add gitea https://oauth2:${GITEA_TOKEN}@git.xiongxiao.me/template/simple-bun.git
# - name: '同步gitea代码到当前仓库'
# script: git pull gitea main
# - name: '提交到原本的origin'
# script: git push origin main
# "**":
# web_trigger_test:
# - stages:
# - name: 执行任务
# script: echo "job"

11
.cnb/web_trigger.yml Normal file
View File

@@ -0,0 +1,11 @@
# .cnb/web_trigger.yml
branch:
# 如下按钮在分支名以 release 开头的分支详情页面显示
- reg: "^main"
buttons:
- name: 同步代码到gitea
desc: 同步代码到gitea
event: web_trigger_sync_to_gitea
- name: 同步gitea代码到当前仓库
desc: 同步gitea代码到当前仓库
event: web_trigger_sync_from_gitea

15
.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
.env
!.env*development
node_modules
dist
pack-dist
.DS_Store
.pnpm-store
.vite
.astro

2
.npmrc Normal file
View File

@@ -0,0 +1,2 @@
//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

17
bun.config.ts Normal file
View File

@@ -0,0 +1,17 @@
// @ts-check
import { resolvePath } from '@kevisual/use-config';
import { execSync } from 'node:child_process';
const entry = 'src/index.ts';
const naming = 'app';
const external = ['pm2'];
await Bun.build({
target: 'node',
format: 'esm',
entrypoints: [resolvePath(entry, { meta: import.meta })],
outdir: resolvePath('./dist', { meta: import.meta }),
naming: {
entry: `${naming}.js`,
},
external,
});

22
kevisual.json Normal file
View File

@@ -0,0 +1,22 @@
{
"metadata": {
"name": "kevisual",
"share": "public"
},
"registry": "https://kevisual.cn/root/ai/kevisual/backend/simple-bun",
"clone": {
".": {
"enabled": true
}
},
"syncd": [
{
"files": [
"**/*"
],
"registry": ""
}
],
"sync": {
}
}

47
package.json Normal file
View File

@@ -0,0 +1,47 @@
{
"name": "@kevisual/notifier",
"version": "0.0.2",
"description": "",
"main": "index.js",
"basename": "/root/notifier",
"app": {
"type": "system-app",
"entry": "app.js",
"runtime": [
"server"
]
},
"scripts": {
"dev": "bun --watch src/main.ts ",
"build": "pnpm run clean && bun run bun.config.mjs",
"postbuild": "ev pack",
"compile": "bun build --compile ./src/main.ts --outfile router-template",
"compile:win": "bun build --compile ./src/main.ts --target=bun-windows-x64 --outfile router-template.exe",
"clean": "rm -rf dist && rimraf pack-dist",
"pub": "envision pack -p -u"
},
"files": [
"dist",
"src"
],
"keywords": [],
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
"license": "MIT",
"packageManager": "pnpm@10.27.0",
"type": "module",
"dependencies": {
"dayjs": "^1.11.19",
"es-toolkit": "^1.43.0"
},
"devDependencies": {
"@kevisual/types": "^0.0.10",
"@types/bun": "^1.3.5",
"@types/node": "^25.0.3"
},
"publishConfig": {
"access": "public"
},
"exports": {
".": "./src/index.ts"
}
}

1102
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

32
readme.md Normal file
View File

@@ -0,0 +1,32 @@
# @kevisual/notifier
一个轻量级的消息通知库,支持多种推送渠道。
## 支持的推送渠道
### 飞书 (Feishu)
通过 Webhook 将消息推送到飞书群机器人。
#### 配置
```typescript
import { FeishuNotifier } from '@kevisual/notifier';
const feishu = new FeishuNotifier({
webhook: 'https://open.feishu.cn/open-apis/bot/v2/hook/xxx',
});
```
#### 使用
```typescript
await feishu.notify('这是一条测试消息');
```
#### 创建飞书群机器人 Webhook
1. 打开飞书群设置,点击「群机器人」
2. 添加「自定义机器人」
3. 复制 Webhook URL
4. (可选) 设置安全策略:签名校验或 IP 白名单

3
src/core/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export interface Notification {
notify(message: string): void;
}

1
src/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './notiify/feishu.ts'

34
src/notiify/feishu.ts Normal file
View File

@@ -0,0 +1,34 @@
import { Notification } from '../core/index.ts';
interface FeishuConfig {
webhook: string;
}
export class FeishuNotifier implements Notification {
private webhook: string;
constructor(config: FeishuConfig) {
this.webhook = config.webhook;
}
async notify(message: string): Promise<void> {
const payload = {
msg_type: 'text',
content: {
text: message,
},
};
const response = await fetch(this.webhook, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`飞书推送失败: ${response.statusText}`);
}
}
}

8
test/feishu.ts Normal file
View File

@@ -0,0 +1,8 @@
import { FeishuNotifier } from '../src/notiify/feishu.ts';
const webhook = 'https://open.feishu.cn/open-apis/bot/v2/hook/c1c32e36-ddc6-4965-8943-fc826f4f5060';
const feishu = new FeishuNotifier({ webhook });
await feishu.notify('测试飞书推送消息');
console.log('推送成功');

24
tsconfig.json Normal file
View File

@@ -0,0 +1,24 @@
{
"extends": "@kevisual/types/json/backend.json",
"compilerOptions": {
"module": "NodeNext",
"target": "esnext",
"baseUrl": ".",
"typeRoots": [
"./node_modules/@types",
"./node_modules/@kevisual/types/index.d.ts"
],
"paths": {
"@/*": [
"src/*"
],
"@agent/*": [
"agent/*"
]
},
},
"include": [
"src/**/*",
"agent/**/*",
],
}