feat: 支持 HTTP 回调和 WebSocket 长连接两种模式
新增功能: - 通过 FEISHU_MODE 配置切换模式 (http/websocket) - 安装飞书 SDK (@larksuiteoapi/node-sdk) - WebSocket 模式支持内网部署(无需公网 IP) - 新增 WEBSOCKET.md 配置指南 更新: - README.md 添加两种模式说明 - .env.example 添加 FEISHU_MODE 配置 - 健康检查返回当前模式信息
This commit is contained in:
@@ -4,6 +4,11 @@ FEISHU_APP_SECRET=xxxxxxxxxxxxxx
|
||||
FEISHU_VERIFICATION_TOKEN=xxxxxxxxxxxxxx
|
||||
FEISHU_ENCRYPT_KEY=xxxxxxxxxxxxxx
|
||||
|
||||
# 飞书事件接收模式:http (HTTP 回调) 或 websocket (WebSocket 长连接)
|
||||
# HTTP 回调:需要公网 IP/域名,配置简单
|
||||
# WebSocket:不需要公网 IP,内网可用,需要飞书 SDK
|
||||
FEISHU_MODE=http
|
||||
|
||||
# 七牛云配置(可选,也可通过卡片配置)
|
||||
QINIU_ACCESS_KEY=YOUR_ACCESS_KEY
|
||||
QINIU_SECRET_KEY=YOUR_SECRET_KEY
|
||||
|
||||
30
README.md
30
README.md
@@ -46,13 +46,29 @@
|
||||
|
||||
### 3. 配置事件订阅
|
||||
|
||||
**方式一:HTTP 回调(默认)**
|
||||
|
||||
1. 进入"事件订阅"页面
|
||||
2. 开启"启用事件订阅"
|
||||
3. 填写请求地址:`https://your-domain.com/feishu/event`
|
||||
2. 选择 **"HTTP 回调"** 方式
|
||||
3. 开启"启用事件订阅"
|
||||
4. 填写请求地址:`https://your-domain.com/feishu/event`
|
||||
5. 配置订阅事件:
|
||||
- `im.message.receive_v1` - 接收消息
|
||||
6. 保存后复制 Verification Token 和 Encrypt Key
|
||||
|
||||
**方式二:WebSocket 长连接(内网推荐)**
|
||||
|
||||
1. 进入"事件订阅"页面
|
||||
2. 选择 **"WebSocket 长连接"** 方式
|
||||
3. 开启"启用事件订阅"
|
||||
4. 配置订阅事件:
|
||||
- `im.message.receive_v1` - 接收消息
|
||||
- `im.message.group_at_msg.receive_v1` - 群组 @ 消息(可选)
|
||||
5. 保存后复制 Verification Token 和 Encrypt Key
|
||||
6. 在 `.env` 中设置 `FEISHU_MODE=websocket`
|
||||
|
||||
> **💡 提示:** WebSocket 模式不需要公网 IP,适合内网部署。
|
||||
>
|
||||
> 详细配置请查看 [`WEBSOCKET.md`](./WEBSOCKET.md)
|
||||
|
||||
### 4. 配置机器人
|
||||
|
||||
@@ -168,6 +184,11 @@ FEISHU_APP_SECRET=xxxxxxxxxxxxxx
|
||||
FEISHU_VERIFICATION_TOKEN=xxxxxxxxxxxxxx
|
||||
FEISHU_ENCRYPT_KEY=xxxxxxxxxxxxxx
|
||||
|
||||
# 飞书事件接收模式:http (HTTP 回调) 或 websocket (WebSocket 长连接)
|
||||
# - http: 需要公网 IP/域名,配置简单
|
||||
# - websocket: 不需要公网 IP,内网可用
|
||||
FEISHU_MODE=http
|
||||
|
||||
# 七牛云配置(可选,也可通过卡片配置)
|
||||
QINIU_ACCESS_KEY=xxxxxxxxxxxxxx
|
||||
QINIU_SECRET_KEY=xxxxxxxxxxxxxx
|
||||
@@ -241,7 +262,8 @@ qiniu-feishu-bot/
|
||||
├── README.md # 项目说明
|
||||
├── DEPLOY.md # 详细部署指南(Linux/macOS/Windows)
|
||||
├── WINDOWS.md # Windows 专用指南
|
||||
└── NGINX.md # Nginx 反向代理部署指南
|
||||
├── NGINX.md # Nginx 反向代理部署指南
|
||||
└── WEBSOCKET.md # WebSocket 长连接模式配置指南
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
213
WEBSOCKET.md
Normal file
213
WEBSOCKET.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# WebSocket 长连接模式配置指南
|
||||
|
||||
## 📡 两种模式对比
|
||||
|
||||
| 特性 | HTTP 回调 | WebSocket 长连接 |
|
||||
|------|----------|-----------------|
|
||||
| 公网 IP | ✅ 需要 | ❌ 不需要 |
|
||||
| 域名 | ✅ 需要 | ❌ 不需要 |
|
||||
| HTTPS | ✅ 推荐 | ❌ 不需要 |
|
||||
| 内网部署 | ❌ 困难 | ✅ 简单 |
|
||||
| 实时性 | 好 | 更好 |
|
||||
| 配置复杂度 | 简单 | 中等 |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 配置 WebSocket 模式
|
||||
|
||||
### 1️⃣ 飞书开放平台配置
|
||||
|
||||
1. 访问 https://open.feishu.cn/
|
||||
2. 进入你的应用管理页面
|
||||
3. 点击"事件订阅"
|
||||
4. **选择"WebSocket 长连接"方式**
|
||||
5. 开启"启用事件订阅"
|
||||
6. 添加订阅事件:
|
||||
- `im.message.receive_v1` - 接收消息
|
||||
7. 保存并复制:
|
||||
- Verification Token
|
||||
- Encrypt Key
|
||||
|
||||
### 2️⃣ 修改配置文件
|
||||
|
||||
编辑 `.env` 文件:
|
||||
|
||||
```env
|
||||
# 飞书配置
|
||||
FEISHU_APP_ID=cli_xxxxxxxxxx
|
||||
FEISHU_APP_SECRET=xxxxxxxxxxxxxx
|
||||
FEISHU_VERIFICATION_TOKEN=xxxxxxxxxxxxxx
|
||||
FEISHU_ENCRYPT_KEY=xxxxxxxxxxxxxx
|
||||
|
||||
# 设置为 WebSocket 模式
|
||||
FEISHU_MODE=websocket
|
||||
|
||||
# 七牛云配置
|
||||
QINIU_ACCESS_KEY=xxxxxxxxxxxxxx
|
||||
QINIU_SECRET_KEY=xxxxxxxxxxxxxx
|
||||
QINIU_BUCKET=your-bucket-name
|
||||
QINIU_REGION=z0
|
||||
QINIU_DOMAIN=https://your-cdn.com
|
||||
|
||||
# 服务配置
|
||||
PORT=3030
|
||||
NODE_ENV=production
|
||||
```
|
||||
|
||||
### 3️⃣ 安装依赖
|
||||
|
||||
```bash
|
||||
cd /path/to/qiniu-feishu-bot
|
||||
npm install
|
||||
```
|
||||
|
||||
### 4️⃣ 启动服务
|
||||
|
||||
```bash
|
||||
# 使用 PM2
|
||||
pm2 restart qiniu-bot
|
||||
|
||||
# 或直接启动
|
||||
npm start
|
||||
```
|
||||
|
||||
### 5️⃣ 查看日志
|
||||
|
||||
```bash
|
||||
# PM2 日志
|
||||
pm2 logs qiniu-bot
|
||||
|
||||
# 应该看到:
|
||||
# 🚀 七牛云上传机器人启动 (WebSocket 长连接模式)
|
||||
# 📡 WebSocket 已启动
|
||||
# ✅ WebSocket 连接成功
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 故障排查
|
||||
|
||||
### WebSocket 连接失败
|
||||
|
||||
**检查配置:**
|
||||
```bash
|
||||
# 验证 .env 配置
|
||||
cat .env | grep FEISHU
|
||||
|
||||
# 确认 FEISHU_MODE=websocket
|
||||
```
|
||||
|
||||
**查看日志:**
|
||||
```bash
|
||||
pm2 logs qiniu-bot --lines 100
|
||||
```
|
||||
|
||||
**常见错误:**
|
||||
|
||||
1. **验证失败**
|
||||
- 检查 Verification Token 是否正确
|
||||
- 检查 Encrypt Key 是否正确
|
||||
|
||||
2. **连接被拒绝**
|
||||
- 检查防火墙是否允许出站连接
|
||||
- 确认服务器能访问外网
|
||||
|
||||
3. **认证失败**
|
||||
- 检查 App ID 和 App Secret
|
||||
|
||||
---
|
||||
|
||||
## 📊 监控 WebSocket 状态
|
||||
|
||||
### 健康检查
|
||||
|
||||
```bash
|
||||
curl http://localhost:3030/health
|
||||
```
|
||||
|
||||
返回:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"timestamp": "2026-03-05T08:00:00.000Z",
|
||||
"mode": "websocket",
|
||||
"port": 3030
|
||||
}
|
||||
```
|
||||
|
||||
### 连接状态
|
||||
|
||||
查看 PM2 日志中的连接状态:
|
||||
|
||||
```bash
|
||||
pm2 logs qiniu-bot | grep -E "(WebSocket|连接|open|close)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 切换模式
|
||||
|
||||
### 从 HTTP 切换到 WebSocket
|
||||
|
||||
```bash
|
||||
# 1. 修改 .env
|
||||
nano .env
|
||||
# 设置 FEISHU_MODE=websocket
|
||||
|
||||
# 2. 重启服务
|
||||
pm2 restart qiniu-bot
|
||||
|
||||
# 3. 验证
|
||||
pm2 logs qiniu-bot
|
||||
```
|
||||
|
||||
### 从 WebSocket 切换到 HTTP
|
||||
|
||||
```bash
|
||||
# 1. 修改 .env
|
||||
nano .env
|
||||
# 设置 FEISHU_MODE=http
|
||||
|
||||
# 2. 重启服务
|
||||
pm2 restart qiniu-bot
|
||||
|
||||
# 3. 在飞书开放平台改回 HTTP 回调方式
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践
|
||||
|
||||
### WebSocket 模式适用场景
|
||||
|
||||
- ✅ 内网服务器(无公网 IP)
|
||||
- ✅ 开发测试环境
|
||||
- ✅ 不想配置域名和 HTTPS
|
||||
- ✅ 需要更好的实时性
|
||||
|
||||
### HTTP 回调模式适用场景
|
||||
|
||||
- ✅ 云服务器(有公网 IP)
|
||||
- ✅ 生产环境
|
||||
- ✅ 已有域名和 HTTPS
|
||||
- ✅ 需要更可控的连接管理
|
||||
|
||||
---
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. **WebSocket 长连接会保持在线状态**
|
||||
- 确保服务器网络稳定
|
||||
- 断线会自动重连(5 秒间隔)
|
||||
|
||||
2. **两种方式不能同时使用**
|
||||
- 通过 `FEISHU_MODE` 配置选择
|
||||
- 飞书开放平台也要对应配置
|
||||
|
||||
3. **健康检查始终可用**
|
||||
- 无论哪种模式,`/health` 端点都工作
|
||||
- 可用于监控服务状态
|
||||
|
||||
---
|
||||
|
||||
**🍙 祝你使用愉快!**
|
||||
164
package-lock.json
generated
164
package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@larksuiteoapi/node-sdk": "^1.5.0",
|
||||
"axios": "^1.6.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2"
|
||||
@@ -17,6 +18,94 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@larksuiteoapi/node-sdk": {
|
||||
"version": "1.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@larksuiteoapi/node-sdk/-/node-sdk-1.59.0.tgz",
|
||||
"integrity": "sha512-sBpkruTvZDOxnVtoTbepWKRX0j1Y1ZElQYu0x7+v088sI9pcpbVp6ZzCGn62dhrKPatzNyCJyzYCPXPYQWccrA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "~1.13.3",
|
||||
"lodash.identity": "^3.0.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"lodash.pickby": "^4.6.0",
|
||||
"protobufjs": "^7.2.6",
|
||||
"qs": "^6.14.2",
|
||||
"ws": "^8.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/base64": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
||||
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/codegen": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
|
||||
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/eventemitter": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
|
||||
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/fetch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
|
||||
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.1",
|
||||
"@protobufjs/inquire": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/float": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
|
||||
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/inquire": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
|
||||
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/path": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
|
||||
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/pool": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
|
||||
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/utf8": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz",
|
||||
"integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
@@ -563,6 +652,30 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.identity": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.identity/-/lodash.identity-3.0.0.tgz",
|
||||
"integrity": "sha512-AupTIzdLQxJS5wIYUQlgGyk2XRTfGXA+MCghDHqZk0pzUNYvd3EESS6dkChNauNYVIutcb0dfHw1ri9Q1yPV8Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.pickby": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz",
|
||||
"integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
||||
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@@ -686,6 +799,30 @@
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
|
||||
"integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.2",
|
||||
"@protobufjs/base64": "^1.1.2",
|
||||
"@protobufjs/codegen": "^2.0.4",
|
||||
"@protobufjs/eventemitter": "^1.1.0",
|
||||
"@protobufjs/fetch": "^1.1.0",
|
||||
"@protobufjs/float": "^1.0.2",
|
||||
"@protobufjs/inquire": "^1.1.0",
|
||||
"@protobufjs/path": "^1.1.2",
|
||||
"@protobufjs/pool": "^1.1.0",
|
||||
"@protobufjs/utf8": "^1.1.0",
|
||||
"@types/node": ">=13.7.0",
|
||||
"long": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
@@ -924,6 +1061,12 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.18.2",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
@@ -950,6 +1093,27 @@
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.19.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"author": "饭团 🍙",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@larksuiteoapi/node-sdk": "^1.5.0",
|
||||
"axios": "^1.6.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2"
|
||||
|
||||
97
src/index.js
97
src/index.js
@@ -8,6 +8,7 @@
|
||||
* 2. 支持交互式卡片上传
|
||||
* 3. 支持命令触发上传
|
||||
* 4. 配置管理
|
||||
* 5. 支持 HTTP 回调和 WebSocket 长连接两种模式
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
@@ -21,8 +22,14 @@ const { QiniuUploader } = require('./qiniu-uploader');
|
||||
const { UploadCard } = require('./cards/upload-card');
|
||||
const { ConfigCard } = require('./cards/config-card');
|
||||
|
||||
// 飞书 SDK(WebSocket 模式)
|
||||
const { Api, eventSubscription } = require('@larksuiteoapi/node-sdk');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
const PORT = process.env.PORT || 3030;
|
||||
|
||||
// 运行模式:'http' 或 'websocket'
|
||||
const MODE = (process.env.FEISHU_MODE || 'http').toLowerCase();
|
||||
|
||||
// 中间件
|
||||
app.use(express.json());
|
||||
@@ -299,13 +306,89 @@ app.post('/feishu/event', handleFeishuEvent);
|
||||
|
||||
// 健康检查
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||
res.json({
|
||||
status: 'ok',
|
||||
timestamp: new Date().toISOString(),
|
||||
mode: MODE,
|
||||
port: PORT
|
||||
});
|
||||
});
|
||||
|
||||
// ============ 启动服务 ============
|
||||
|
||||
app.listen(PORT, () => {
|
||||
log(`🚀 七牛云上传机器人启动`);
|
||||
log(`📍 端口:${PORT}`);
|
||||
log(`🔗 事件地址:https://your-domain.com/feishu/event`);
|
||||
});
|
||||
function startHTTPMode() {
|
||||
app.listen(PORT, () => {
|
||||
log(`🚀 七牛云上传机器人启动 (HTTP 回调模式)`);
|
||||
log(`📍 端口:${PORT}`);
|
||||
log(`🔗 事件地址:http://your-domain.com:${PORT}/feishu/event`);
|
||||
log(`💡 提示:在飞书开放平台配置事件订阅地址为上述地址`);
|
||||
});
|
||||
}
|
||||
|
||||
function startWebSocketMode() {
|
||||
log(`🚀 七牛云上传机器人启动 (WebSocket 长连接模式)`);
|
||||
log(`💡 提示:在飞书开放平台选择 "WebSocket 长连接" 方式`);
|
||||
|
||||
// 创建飞书客户端
|
||||
const client = new Api({
|
||||
appId: process.env.FEISHU_APP_ID,
|
||||
appSecret: process.env.FEISHU_APP_SECRET,
|
||||
});
|
||||
|
||||
// 创建 WebSocket 长连接
|
||||
const ws = eventSubscription({
|
||||
appId: process.env.FEISHU_APP_ID,
|
||||
appSecret: process.env.FEISHU_APP_SECRET,
|
||||
encryptKey: process.env.FEISHU_ENCRYPT_KEY,
|
||||
verificationToken: process.env.FEISHU_VERIFICATION_TOKEN,
|
||||
logLevel: 'info',
|
||||
});
|
||||
|
||||
// 监听消息事件
|
||||
ws.on('im.message.receive_v1', async (data) => {
|
||||
log('收到消息事件');
|
||||
await handleMessage(data);
|
||||
});
|
||||
|
||||
// 监听连接状态
|
||||
ws.on('open', () => {
|
||||
log('✅ WebSocket 连接成功');
|
||||
});
|
||||
|
||||
ws.on('close', () => {
|
||||
log('❌ WebSocket 连接关闭,5 秒后重连...');
|
||||
setTimeout(() => {
|
||||
try {
|
||||
ws.start();
|
||||
} catch (e) {
|
||||
log('重连失败:', e.message);
|
||||
}
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
ws.on('error', (error) => {
|
||||
log('❌ WebSocket 错误:', error.message);
|
||||
});
|
||||
|
||||
// 启动 WebSocket 连接
|
||||
try {
|
||||
ws.start();
|
||||
log('📡 WebSocket 已启动');
|
||||
} catch (error) {
|
||||
log('❌ WebSocket 启动失败:', error.message);
|
||||
log('💡 请检查飞书配置是否正确');
|
||||
}
|
||||
|
||||
// HTTP 服务器仍然运行(用于健康检查)
|
||||
app.listen(PORT, () => {
|
||||
log(`📍 健康检查端口:${PORT}`);
|
||||
log(`🔗 健康检查地址:http://localhost:${PORT}/health`);
|
||||
});
|
||||
}
|
||||
|
||||
// 根据配置启动对应模式
|
||||
if (MODE === 'websocket') {
|
||||
startWebSocketMode();
|
||||
} else {
|
||||
startHTTPMode();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user