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:
56
.cnb.yml
Normal file
56
.cnb.yml
Normal 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
11
.cnb/web_trigger.yml
Normal 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
15
.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.env
|
||||||
|
!.env*development
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
dist
|
||||||
|
pack-dist
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
.pnpm-store
|
||||||
|
|
||||||
|
.vite
|
||||||
|
|
||||||
|
.astro
|
||||||
2
.npmrc
Normal file
2
.npmrc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
|
||||||
|
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||||
17
bun.config.ts
Normal file
17
bun.config.ts
Normal 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
22
kevisual.json
Normal 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
47
package.json
Normal 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
1102
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
readme.md
Normal file
32
readme.md
Normal 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
3
src/core/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface Notification {
|
||||||
|
notify(message: string): void;
|
||||||
|
}
|
||||||
1
src/index.ts
Normal file
1
src/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './notiify/feishu.ts'
|
||||||
34
src/notiify/feishu.ts
Normal file
34
src/notiify/feishu.ts
Normal 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
8
test/feishu.ts
Normal 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
24
tsconfig.json
Normal 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/**/*",
|
||||||
|
],
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user