Skip to content
This repository has been archived by the owner on Jan 10, 2025. It is now read-only.

Commit

Permalink
feat: 🎸 增加静态资源获取接口,recvd_api 增加头像url地址
Browse files Browse the repository at this point in the history
Closes: #198
  • Loading branch information
danni-cool committed Mar 27, 2024
1 parent e6ea08e commit b85f7ba
Show file tree
Hide file tree
Showing 15 changed files with 270 additions and 23 deletions.
52 changes: 46 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
| 接收文件 || |
| 接收公众号推文链接 || |
| 接收系统通知 | ✅ 上线通知 / 掉线通知 / 异常通知 | |
| [头像获取](https://github.com/danni-cool/wechatbot-webhook?tab=readme-ov-file#1-%E5%A4%B4%E5%83%8F%E8%8E%B7%E5%8F%96-api) || |
| [快捷回复](https://github.com/danni-cool/wechatbot-webhook?tab=readme-ov-file#2-%E6%94%B6%E6%B6%88%E6%81%AF-api) |||
| **<群管理>** | | |
| **<好友管理>** | | |
Expand Down Expand Up @@ -410,12 +411,11 @@ curl --location 'https://your.recvdapi.com' \
#### token 配置说明
> 除了在 docker 启动时配置token,在默认缺省 token 的情况,会默认生成一个写入 `.env` 文件中
#### `/login?token=[YOUR_PERSONAL_TOKEN]`

- **描述**:获取登录二维码接口。
#### 3.1 获取登录二维码接口
- **地址**`/login`
- **methods**: `GET`
- **query**: token

- **example**: http://localhost:3001/login?token=[YOUR_PERSONAL_TOKEN]
**status**: `200`

##### 登录成功
Expand All @@ -430,15 +430,55 @@ curl --location 'https://your.recvdapi.com' \

展示微信登录扫码页面

#### `/healthz?token=[YOUR_PERSONAL_TOKEN]`
#### 3.2 健康检测接口

可以主动轮询该接口,检查服务是否正常运行

- **描述**健康检测接口。
- **地址**`/healthz`
- **methods**: `GET`
- **query**: token
- **status**: `200`
- **example**: http://localhost:3001/healthz?token=[YOUR_PERSONAL_TOKEN]

微信已登录, 返回纯文本 `healthy`,否则返回 `unHealthy`

#### 3.3 获取静态资源接口

从 2.8.0 版本开始,可以通过本接口访问到头像等静态资源,具体见 [recvd_api 数据结构示例的 avatar 字段](docs/recvdApi.example.md#formdatasource)

注意所有上报 recvd_api 的静态资源地址不会默认带上 token, 需要自己拼接,否则会返回 401 错误, 另外,**请确保自己微信已登录,需要通过登录态去获取资源**

- **地址**`/resouces`
- **methods**: `GET`
- **query**:
- token: 登录token
- media: encode过的相对路径,比如 `/avatar/1234567890.jpg` encode为 `avatar%2F1234567890.jpg`
- **status**: `200` `404` `401`

- **example**http://localhost:3001/resouces?media=%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxgetheadimg%3Fseq%3D83460%26username%3D%40%4086815a%26skey%3D&token=[YOUR_PERSONAL_TOKEN]

##### status: `200`

成功获取资源, 返回静态资源文件

##### status: `404`

获取资源失败

##### status: `401` 未携带登录token

```json
{"success":false, "message":"Unauthorized: Access is denied due to invalid credentials."}
```

##### status: `401` 微信登录态已过期

```json
{
"success": false, "message": "you must login first"
}
```


## 🌟 Star History

Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"form-data": "^4.0.0",
"gerror": "^1.0.16",
"hono": "^3.11.11",
"lodash.clonedeep": "^4.5.0",
"log4js": "^6.9.1",
"mime": "^3.0.0",
"node-fetch-commonjs": "^3.3.2",
Expand All @@ -64,6 +65,8 @@
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.2",
"husky": "^8.0.3",
"i": "^0.3.7",
"npm": "^10.5.0",
"prettier": "^3.1.1",
"tsc-files": "^1.1.4",
"typescript": "^5.3.3",
Expand Down
95 changes: 95 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion src/config/const.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
const path = require('path')
const { PORT } = process.env

const config = {
/**
* 上报消息的api群成员缓存多久(单位:ms)
* @type {number}
*/
roomCachedTime: 1000 * 60 * 5
roomCachedTime: 1000 * 60 * 5,
/** 服务启动地址 */
localUrl: `http://localhost:${PORT}`
}

const { homeEnvCfg, homeMemoryCardPath } = process.env
Expand Down
2 changes: 1 addition & 1 deletion src/middleware/loginCheck.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports.loginCheck = async (c, next) => {
c.status(401)
return c.json({
success: false,
message: 'you must login first before sending messages'
message: 'you must login first'
})
}

Expand Down
1 change: 1 addition & 0 deletions src/route/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ module.exports = function registerRoute({ app, bot }) {

require('./msg')({ app, bot })
require('./login')({ app, bot })
require('./resouces')({ app, bot })
}
39 changes: 39 additions & 0 deletions src/route/resouces.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const { downloadFile } = require('../utils/index')
const middleware = require('../middleware')
/**
* 注册login路由和处理上报逻辑
* @param {Object} param
* @param {import('hono').Hono} param.app
* @param {import('wechaty').Wechaty} param.bot
*/
module.exports = function registerResourceAgentRoute({ app, bot }) {
app.get(
'/resouces',
middleware.loginCheck,
/** @param {import('hono').Context} c */
async (c) => {
// 暂时不考虑其他puppet的情况
const cookie =
// @ts-ignore 私有变量
bot.__puppet._memory.payload['\rpuppet\nPUPPET-WECHAT4U'].COOKIE
const mediaUrl = c.req.query('media')
const fullResouceUrl = `https://wx2.qq.com${decodeURIComponent(
mediaUrl || ''
)}`

const { buffer, contentType } = await downloadFile(fullResouceUrl, {
Cookie: Object.entries(cookie).reduce(
(pre, next) => (pre += `${next[0]}=${next[1]};`),
''
)
})
if (buffer) {
contentType && c.header('Content-Type', contentType)
return c.body(buffer)
} else {
c.status(404)
return c.json({ success: false, message: '获取资源失败' })
}
}
)
}
35 changes: 29 additions & 6 deletions src/service/msgUploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const FormData = require('form-data')
const { LOCAL_RECVD_MSG_API, RECVD_MSG_API } = process.env
const { MSG_TYPE_ENUM } = require('../config/const')
const cacheTool = require('../service/cache')

const cloneDeep = require('lodash.cloneDeep')
/**
* 收到消息上报接受url
* @typedef {{type:'text'|'fileUrl'}} baseMsgInterface
Expand Down Expand Up @@ -39,7 +39,7 @@ async function sendMsg2RecvdApi(msg) {

// 有webhookurl才发送
if (!webhookUrl) return
/** @type {import('wechaty/impls').RoomInterface & {payload: { memberList: {id:string,name:string,alias:string|undefined}[]}}} */
/** @type {roomInfoForUpload} */
//@ts-expect-errors 强制as配合 ts-expect-errors 实用更佳
const roomInfo = msg.room()

Expand All @@ -59,6 +59,8 @@ async function sendMsg2RecvdApi(msg) {
})
}
roomInfo.payload.memberList = roomMemberInfo.map((item) => ({
// @ts-expect-error wechaty定义问题,数据在payload里
avatar: Utils.getAssetsAgentUrl(item.payload.avatar),
// @ts-expect-error wechaty定义问题,数据在payload里
id: item.payload.id,
// @ts-expect-error wechaty定义问题,数据在payload里
Expand All @@ -74,11 +76,32 @@ async function sendMsg2RecvdApi(msg) {
}

const source = {
/** room的话解析群成员信息,原始信息不会带 */
room: roomInfo ?? '',
to: msg.to() ?? '',
room: cloneDeep(roomInfo || {}),
/** @type { import('wechaty').Message['to'] } */
// @ts-ignore
to: cloneDeep(msg.to() || {}),
from: cloneDeep(msg.talker() || {})
}

// @ts-ignore
if (source.to && source.to.payload?.avatar) {
// @ts-ignore
source.to.payload.avatar = Utils.getAssetsAgentUrl(source.to.payload.avatar)
}

// @ts-ignore
if (source.from.payload?.avatar) {
// @ts-ignore
from: msg.talker() ?? ''
source.from.payload.avatar = Utils.getAssetsAgentUrl(
// @ts-ignore
source.from.payload.avatar
)
}

if (source.room.payload?.avatar) {
source.room.payload.avatar = Utils.getAssetsAgentUrl(
source.room.payload.avatar
)
}

// let passed = true
Expand Down
Loading

0 comments on commit b85f7ba

Please sign in to comment.