feat: 接入lint 和 husky
This commit is contained in:
parent
61919e0155
commit
6e65581bbf
@ -1,33 +1,27 @@
|
||||
{
|
||||
"name": "egg_server",
|
||||
"image": "micr.cloud.mioffice.cn/zhaoyingbo/dev:bun",
|
||||
"remoteUser": "bun",
|
||||
"containerUser": "bun",
|
||||
"forwardPorts": [3000],
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"files.autoSave": "afterDelay",
|
||||
"editor.guides.bracketPairs": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"github.copilot.chat.localeOverride": "zh-CN"
|
||||
},
|
||||
"extensions": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"eamodio.gitlens",
|
||||
"unifiedjs.vscode-mdx",
|
||||
"litiany4.umijs-plugin-model",
|
||||
"oderwat.indent-rainbow",
|
||||
"jock.svg",
|
||||
"ChakrounAnas.turbo-console-log",
|
||||
"Gruntfuggly.todo-tree",
|
||||
"MS-CEINTL.vscode-language-pack-zh-hans",
|
||||
"GitHub.copilot",
|
||||
"GitHub.copilot-chat"
|
||||
]
|
||||
}
|
||||
},
|
||||
"postCreateCommand": "bash -i /workspaces/egg_server/.devcontainer/initial.bash"
|
||||
}
|
||||
{
|
||||
"name": "ci_monitor",
|
||||
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"files.autoSave": "afterDelay",
|
||||
"editor.guides.bracketPairs": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "always"
|
||||
}
|
||||
},
|
||||
"extensions": [
|
||||
"eamodio.gitlens",
|
||||
"Gruntfuggly.todo-tree",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"ChakrounAnas.turbo-console-log",
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"MS-CEINTL.vscode-language-pack-zh-hans"
|
||||
]
|
||||
}
|
||||
},
|
||||
"onCreateCommand": "curl -fsSL https://bun.sh/install | bash"
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
echo "alias dev=\"cd /workspaces/egg_server && bun run dev\"" >> /home/bun/.bashrc
|
17
.devcontainer/readme.md
Normal file
17
.devcontainer/readme.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Dev Container
|
||||
|
||||
这是 Bun + Node.js 的开发容器,基于`mcr.microsoft.com/devcontainers/typescript-node:20`镜像
|
||||
|
||||
在宿主机上设置`.gitconfig`文件,以及`.ssh`文件夹,以便容器可以访问 git 仓库。
|
||||
|
||||
> 详见[官方文档](https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials)
|
||||
|
||||
# 资源
|
||||
|
||||
[devcontainer.json 字段定义](https://containers.dev/implementors/json_reference/)
|
||||
|
||||
[devcontainer 官方文档](https://code.visualstudio.com/docs/remote/containers)
|
||||
|
||||
[images](https://github.com/devcontainers/images)
|
||||
|
||||
[features](https://github.com/devcontainers/features)
|
@ -1,57 +1,57 @@
|
||||
name: Egg CI/CD
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-latest
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: git.yingbo.im:333
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
push: true
|
||||
tags: git.yingbo.im:333/zhaoyingbo/egg_server:${{ github.sha }}
|
||||
|
||||
deploy:
|
||||
needs: build-image
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-latest
|
||||
steps:
|
||||
# 检出代码
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
# 使用scp命令将docker-compose.yml文件上传到服务器
|
||||
- name: Upload docker-compose.yml to server
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: ${{ secrets.SERVER_HOST }}
|
||||
username: ${{ secrets.SERVER_USERNAME }}
|
||||
key: ${{ secrets.SERVER_KEY }}
|
||||
port: ${{ secrets.SERVER_PORT }}
|
||||
source: docker-compose.yml
|
||||
target: /home/${{ secrets.SERVER_USERNAME }}/docker/egg_server
|
||||
# 登录服务器,执行docker-compose命令
|
||||
- name: Login to the server and execute docker-compose command
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.SERVER_HOST }}
|
||||
username: ${{ secrets.SERVER_USERNAME }}
|
||||
key: ${{ secrets.SERVER_KEY }}
|
||||
port: ${{ secrets.SERVER_PORT }}
|
||||
script: |
|
||||
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} git.yingbo.im:333
|
||||
cd /home/${{ secrets.SERVER_USERNAME }}/docker/egg_server
|
||||
sed -i "s/sha/${{ github.sha }}/g" docker-compose.yml
|
||||
docker compose up -d --force-recreate --no-deps egg_server
|
||||
name: Egg CI/CD
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-latest
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: git.yingbo.im:333
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
push: true
|
||||
tags: git.yingbo.im:333/zhaoyingbo/egg_server:${{ github.sha }}
|
||||
|
||||
deploy:
|
||||
needs: build-image
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-latest
|
||||
steps:
|
||||
# 检出代码
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
# 使用scp命令将docker-compose.yml文件上传到服务器
|
||||
- name: Upload docker-compose.yml to server
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: ${{ secrets.SERVER_HOST }}
|
||||
username: ${{ secrets.SERVER_USERNAME }}
|
||||
key: ${{ secrets.SERVER_KEY }}
|
||||
port: ${{ secrets.SERVER_PORT }}
|
||||
source: docker-compose.yml
|
||||
target: /home/${{ secrets.SERVER_USERNAME }}/docker/egg_server
|
||||
# 登录服务器,执行docker-compose命令
|
||||
- name: Login to the server and execute docker-compose command
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.SERVER_HOST }}
|
||||
username: ${{ secrets.SERVER_USERNAME }}
|
||||
key: ${{ secrets.SERVER_KEY }}
|
||||
port: ${{ secrets.SERVER_PORT }}
|
||||
script: |
|
||||
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} git.yingbo.im:333
|
||||
cd /home/${{ secrets.SERVER_USERNAME }}/docker/egg_server
|
||||
sed -i "s/sha/${{ github.sha }}/g" docker-compose.yml
|
||||
docker compose up -d --force-recreate --no-deps egg_server
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -49,7 +49,7 @@ profile-*
|
||||
.idea
|
||||
|
||||
# vscode
|
||||
.vscode
|
||||
# .vscode
|
||||
*code-workspace
|
||||
|
||||
# clinic
|
||||
|
1
.husky/commit-msg
Normal file
1
.husky/commit-msg
Normal file
@ -0,0 +1 @@
|
||||
npx --no -- commitlint --edit $1
|
1
.husky/pre-commit
Normal file
1
.husky/pre-commit
Normal file
@ -0,0 +1 @@
|
||||
lint-staged
|
15
.vscode/settings.json
vendored
Normal file
15
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"bunx",
|
||||
"CEINTL",
|
||||
"Chakroun",
|
||||
"commitlint",
|
||||
"dbaeumer",
|
||||
"devcontainers",
|
||||
"eamodio",
|
||||
"esbenp",
|
||||
"Gruntfuggly",
|
||||
"tseslint",
|
||||
"wlpbbgiky"
|
||||
]
|
||||
}
|
1
commitlint.config.js
Normal file
1
commitlint.config.js
Normal file
@ -0,0 +1 @@
|
||||
export default { extends: ["@commitlint/config-conventional"] }
|
@ -1,16 +1,16 @@
|
||||
import { DB } from "../../types";
|
||||
import { managePbError } from "../../utils/pbTools";
|
||||
import pbClient from "../pbClient";
|
||||
import { DB } from "../../types"
|
||||
import { managePbError } from "../../utils/pbTools"
|
||||
import pbClient from "../pbClient"
|
||||
|
||||
const getOne = (id: string) =>
|
||||
managePbError<DB.MessageGroup>(() =>
|
||||
pbClient.collection("api_key").getOne(id, {
|
||||
expand: "app",
|
||||
})
|
||||
);
|
||||
)
|
||||
|
||||
const apiKey = {
|
||||
getOne,
|
||||
};
|
||||
}
|
||||
|
||||
export default apiKey;
|
||||
export default apiKey
|
||||
|
@ -1,6 +1,6 @@
|
||||
import pbClient from "../pbClient";
|
||||
import { managePb404 } from "../../utils/pbTools";
|
||||
import { DB } from "../../types";
|
||||
import { DB } from "../../types"
|
||||
import { managePb404 } from "../../utils/pbTools"
|
||||
import pbClient from "../pbClient"
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
@ -10,13 +10,13 @@ import { DB } from "../../types";
|
||||
const get = async (appName: string) =>
|
||||
managePb404<DB.AppInfo>(() =>
|
||||
pbClient.collection("app_info").getFirstListItem(`name='${appName}'`)
|
||||
);
|
||||
)
|
||||
|
||||
/**
|
||||
* 获取所有配置
|
||||
* @returns
|
||||
*/
|
||||
const getFullList = () => pbClient.collection("app_info").getFullList();
|
||||
const getFullList = () => pbClient.collection("app_info").getFullList()
|
||||
|
||||
/**
|
||||
* 获取配置的某个值
|
||||
@ -25,15 +25,15 @@ const getFullList = () => pbClient.collection("app_info").getFullList();
|
||||
* @returns
|
||||
*/
|
||||
const getVal = async (appName: string, key: string) => {
|
||||
const config = await get(appName);
|
||||
if (!config) return "";
|
||||
return config[key] || "";
|
||||
};
|
||||
const config = await get(appName)
|
||||
if (!config) return ""
|
||||
return config[key] || ""
|
||||
}
|
||||
|
||||
const appInfo = {
|
||||
get,
|
||||
getVal,
|
||||
getFullList,
|
||||
};
|
||||
}
|
||||
|
||||
export default appInfo;
|
||||
export default appInfo
|
||||
|
30
db/index.ts
30
db/index.ts
@ -1,15 +1,15 @@
|
||||
import messageGroup from "./messageGroup";
|
||||
import tenantAccessToken from "./tenantAccessToken";
|
||||
import appInfo from "./appInfo";
|
||||
import log from "./log";
|
||||
import apiKey from "./apiKey";
|
||||
|
||||
const db = {
|
||||
apiKey,
|
||||
appInfo,
|
||||
messageGroup,
|
||||
log,
|
||||
tenantAccessToken,
|
||||
};
|
||||
|
||||
export default db;
|
||||
import apiKey from "./apiKey"
|
||||
import appInfo from "./appInfo"
|
||||
import log from "./log"
|
||||
import messageGroup from "./messageGroup"
|
||||
import tenantAccessToken from "./tenantAccessToken"
|
||||
|
||||
const db = {
|
||||
apiKey,
|
||||
appInfo,
|
||||
messageGroup,
|
||||
log,
|
||||
tenantAccessToken,
|
||||
}
|
||||
|
||||
export default db
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { DB } from "../../types";
|
||||
import { managePbError } from "../../utils/pbTools";
|
||||
import pbClient from "../pbClient";
|
||||
import { DB } from "../../types"
|
||||
import { managePbError } from "../../utils/pbTools"
|
||||
import pbClient from "../pbClient"
|
||||
|
||||
const create = (collection: DB.LogCollection, log: DB.Log) =>
|
||||
managePbError(() => pbClient.collection(collection).create(log));
|
||||
managePbError(() => pbClient.collection(collection).create(log))
|
||||
|
||||
const log = {
|
||||
create,
|
||||
};
|
||||
}
|
||||
|
||||
export default log;
|
||||
export default log
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { DB } from "../../types";
|
||||
import { managePbError } from "../../utils/pbTools";
|
||||
import pbClient from "../pbClient";
|
||||
|
||||
const getOne = (groupId: string) =>
|
||||
managePbError<DB.MessageGroup>(() =>
|
||||
pbClient.collection("message_group").getOne(groupId)
|
||||
);
|
||||
|
||||
const messageGroup = {
|
||||
getOne,
|
||||
};
|
||||
|
||||
export default messageGroup;
|
||||
import { DB } from "../../types"
|
||||
import { managePbError } from "../../utils/pbTools"
|
||||
import pbClient from "../pbClient"
|
||||
|
||||
const getOne = (groupId: string) =>
|
||||
managePbError<DB.MessageGroup>(() =>
|
||||
pbClient.collection("message_group").getOne(groupId)
|
||||
)
|
||||
|
||||
const messageGroup = {
|
||||
getOne,
|
||||
}
|
||||
|
||||
export default messageGroup
|
||||
|
@ -1,7 +1,7 @@
|
||||
import PocketBase from "pocketbase";
|
||||
|
||||
const pbClient = new PocketBase("https://eggpb.imoaix.cn");
|
||||
|
||||
pbClient.autoCancellation(false);
|
||||
|
||||
export default pbClient;
|
||||
import PocketBase from "pocketbase"
|
||||
|
||||
const pbClient = new PocketBase("https://eggpb.imoaix.cn")
|
||||
|
||||
pbClient.autoCancellation(false)
|
||||
|
||||
export default pbClient
|
||||
|
@ -1,40 +1,42 @@
|
||||
import appInfo from "../appInfo";
|
||||
import pbClient from "../pbClient";
|
||||
|
||||
const tokenCache = {} as Record<string, string>;
|
||||
|
||||
/**
|
||||
* 更新租户的token
|
||||
* @param {string} id 记录id
|
||||
* @param {string} appName 租户名称
|
||||
* @param {string} value 新的token
|
||||
*/
|
||||
const update = async (id: string, appName: string, value: string) => {
|
||||
try {
|
||||
await pbClient
|
||||
.collection("app_info")
|
||||
.update(id, { tenant_access_token: value });
|
||||
} catch {}
|
||||
|
||||
tokenCache[appName] = value;
|
||||
console.log(`reset ${appName} access token success`, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取租户的token
|
||||
* @param {string} appName 租户名称
|
||||
* @returns {string} 租户的token
|
||||
*/
|
||||
const get = async (appName: string) => {
|
||||
if (tokenCache[appName]) return tokenCache[appName];
|
||||
const config = await appInfo.getVal(appName, "tenant_access_token");
|
||||
tokenCache[appName] = config;
|
||||
return config;
|
||||
};
|
||||
|
||||
const tenantAccessToken = {
|
||||
get,
|
||||
update,
|
||||
};
|
||||
|
||||
export default tenantAccessToken;
|
||||
import appInfo from "../appInfo"
|
||||
import pbClient from "../pbClient"
|
||||
|
||||
const tokenCache = {} as Record<string, string>
|
||||
|
||||
/**
|
||||
* 更新租户的token
|
||||
* @param {string} id 记录id
|
||||
* @param {string} appName 租户名称
|
||||
* @param {string} value 新的token
|
||||
*/
|
||||
const update = async (id: string, appName: string, value: string) => {
|
||||
try {
|
||||
await pbClient
|
||||
.collection("app_info")
|
||||
.update(id, { tenant_access_token: value })
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
tokenCache[appName] = value
|
||||
console.log(`reset ${appName} access token success`, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户的token
|
||||
* @param {string} appName 租户名称
|
||||
* @returns {string} 租户的token
|
||||
*/
|
||||
const get = async (appName: string) => {
|
||||
if (tokenCache[appName]) return tokenCache[appName]
|
||||
const config = await appInfo.getVal(appName, "tenant_access_token")
|
||||
tokenCache[appName] = config
|
||||
return config
|
||||
}
|
||||
|
||||
const tenantAccessToken = {
|
||||
get,
|
||||
update,
|
||||
}
|
||||
|
||||
export default tenantAccessToken
|
||||
|
@ -1,9 +1,9 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
egg_server:
|
||||
image: git.yingbo.im:333/zhaoyingbo/egg_server:sha
|
||||
container_name: egg_server
|
||||
restart: always
|
||||
ports:
|
||||
- 3003:3000
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
egg_server:
|
||||
image: git.yingbo.im:333/zhaoyingbo/egg_server:sha
|
||||
container_name: egg_server
|
||||
restart: always
|
||||
ports:
|
||||
- 3003:3000
|
||||
|
22
eslint.config.js
Normal file
22
eslint.config.js
Normal file
@ -0,0 +1,22 @@
|
||||
import pluginJs from "@eslint/js"
|
||||
import simpleImportSort from "eslint-plugin-simple-import-sort"
|
||||
import globals from "globals"
|
||||
import tseslint from "typescript-eslint"
|
||||
|
||||
export default [
|
||||
{ files: ["**/*.{js,mjs,cjs,ts}"] },
|
||||
{ languageOptions: { globals: globals.browser } },
|
||||
pluginJs.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
plugins: {
|
||||
"simple-import-sort": simpleImportSort,
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-namespace": "off",
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error",
|
||||
},
|
||||
},
|
||||
]
|
72
index.ts
72
index.ts
@ -1,36 +1,36 @@
|
||||
import { manageBotReq } from "./routes/bot";
|
||||
import { manageMessageReq } from "./routes/message";
|
||||
import { manageMicroAppReq } from "./routes/microApp";
|
||||
import { manageSheetReq } from "./routes/sheet";
|
||||
import { initSchedule } from "./schedule";
|
||||
import netTool from "./services/netTool";
|
||||
|
||||
initSchedule();
|
||||
|
||||
const server = Bun.serve({
|
||||
async fetch(req) {
|
||||
try {
|
||||
const url = new URL(req.url);
|
||||
// 根路由
|
||||
if (url.pathname === "/") return netTool.ok("hello, glade to see you!");
|
||||
// 机器人
|
||||
if (url.pathname === "/bot") return await manageBotReq(req);
|
||||
// 消息代理发送
|
||||
if (url.pathname === "/message") return await manageMessageReq(req);
|
||||
// 表格代理操作
|
||||
if (url.pathname === "/sheet") return await manageSheetReq(req);
|
||||
// 小程序
|
||||
if (url.pathname.startsWith("/micro_app"))
|
||||
return await manageMicroAppReq(req);
|
||||
// 其他
|
||||
return netTool.ok("hello, glade to see you!");
|
||||
} catch (error: any) {
|
||||
// 错误处理
|
||||
console.error("🚀 ~ serve ~ error", error);
|
||||
return netTool.serverError(error.message || "server error");
|
||||
}
|
||||
},
|
||||
port: 3000,
|
||||
});
|
||||
|
||||
console.log(`Listening on ${server.hostname}:${server.port}`);
|
||||
import { manageBotReq } from "./routes/bot"
|
||||
import { manageMessageReq } from "./routes/message"
|
||||
import { manageMicroAppReq } from "./routes/microApp"
|
||||
import { manageSheetReq } from "./routes/sheet"
|
||||
import { initSchedule } from "./schedule"
|
||||
import netTool from "./services/netTool"
|
||||
|
||||
initSchedule()
|
||||
|
||||
const server = Bun.serve({
|
||||
async fetch(req) {
|
||||
try {
|
||||
const url = new URL(req.url)
|
||||
// 根路由
|
||||
if (url.pathname === "/") return netTool.ok("hello, glade to see you!")
|
||||
// 机器人
|
||||
if (url.pathname === "/bot") return await manageBotReq(req)
|
||||
// 消息代理发送
|
||||
if (url.pathname === "/message") return await manageMessageReq(req)
|
||||
// 表格代理操作
|
||||
if (url.pathname === "/sheet") return await manageSheetReq(req)
|
||||
// 小程序
|
||||
if (url.pathname.startsWith("/micro_app"))
|
||||
return await manageMicroAppReq(req)
|
||||
// 其他
|
||||
return netTool.ok("hello, glade to see you!")
|
||||
} catch (error: any) {
|
||||
// 错误处理
|
||||
console.error("🚀 ~ serve ~ error", error)
|
||||
return netTool.serverError(error.message || "server error")
|
||||
}
|
||||
},
|
||||
port: 3000,
|
||||
})
|
||||
|
||||
console.log(`Listening on ${server.hostname}:${server.port}`)
|
||||
|
27
package.json
27
package.json
@ -3,17 +3,36 @@
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "bun run index.ts"
|
||||
"start": "bun run index.ts",
|
||||
"lint": "eslint --fix .",
|
||||
"prepare": "husky",
|
||||
"prettier": "prettier --write ."
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"eslint --fix",
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"bun-types": "latest"
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@eslint/js": "^9.7.0",
|
||||
"@types/node-schedule": "^2.1.7",
|
||||
"bun-types": "latest",
|
||||
"eslint": "^9.7.0",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"husky": "^9.1.1",
|
||||
"lint-staged": "^15.2.7",
|
||||
"prettier": "^3.3.3",
|
||||
"typescript-eslint": "^7.17.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node-schedule": "^2.1.6",
|
||||
"node-schedule": "^2.1.1",
|
||||
"pocketbase": "^0.21.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
6
prettier.config.js
Normal file
6
prettier.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
trailingComma: "es5",
|
||||
tabWidth: 2,
|
||||
semi: false,
|
||||
singleQuote: false,
|
||||
}
|
@ -1,63 +1,63 @@
|
||||
import { sleep } from "bun";
|
||||
import { getActionType, getIsActionMsg } from "../../utils/msgTools";
|
||||
|
||||
import service from "../../services";
|
||||
import { LarkAction } from "../../types";
|
||||
|
||||
/**
|
||||
* 返回ChatId卡片
|
||||
* @param {LarkAction.Data} body
|
||||
*/
|
||||
const makeChatIdCard = async (body: LarkAction.Data) => {
|
||||
await sleep(500);
|
||||
return JSON.stringify({
|
||||
type: "template",
|
||||
data: {
|
||||
config: {
|
||||
update_multi: true,
|
||||
},
|
||||
template_id: "ctp_AAi3NnHb6zgK",
|
||||
template_variable: {
|
||||
chat_id: body.open_chat_id,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const ACTION_MAP = {
|
||||
chat_id: makeChatIdCard,
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理按钮点击事件
|
||||
* @param {LarkAction.Data} body
|
||||
*/
|
||||
const manageBtnClick = async (body: LarkAction.Data) => {
|
||||
const { action } = body?.action?.value as {
|
||||
action: keyof typeof ACTION_MAP;
|
||||
};
|
||||
if (!action) return;
|
||||
const func = ACTION_MAP[action];
|
||||
if (!func) return;
|
||||
const card = await func(body);
|
||||
if (!card) return;
|
||||
// 更新飞书的卡片
|
||||
await service.lark.message.update()(body.open_message_id, card);
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理Action消息
|
||||
* @param {LarkAction.Data} body
|
||||
* @returns {boolean} 是否在本函数中处理了消息
|
||||
*/
|
||||
export const manageActionMsg = (body: LarkAction.Data) => {
|
||||
// 过滤非Action消息
|
||||
if (!getIsActionMsg(body)) {
|
||||
return false;
|
||||
}
|
||||
const actionType = getActionType(body);
|
||||
if (actionType === "button") {
|
||||
manageBtnClick(body);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
import { sleep } from "bun"
|
||||
|
||||
import service from "../../services"
|
||||
import { LarkAction } from "../../types"
|
||||
import { getActionType, getIsActionMsg } from "../../utils/msgTools"
|
||||
|
||||
/**
|
||||
* 返回ChatId卡片
|
||||
* @param {LarkAction.Data} body
|
||||
*/
|
||||
const makeChatIdCard = async (body: LarkAction.Data) => {
|
||||
await sleep(500)
|
||||
return JSON.stringify({
|
||||
type: "template",
|
||||
data: {
|
||||
config: {
|
||||
update_multi: true,
|
||||
},
|
||||
template_id: "ctp_AAi3NnHb6zgK",
|
||||
template_variable: {
|
||||
chat_id: body.open_chat_id,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const ACTION_MAP = {
|
||||
chat_id: makeChatIdCard,
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理按钮点击事件
|
||||
* @param {LarkAction.Data} body
|
||||
*/
|
||||
const manageBtnClick = async (body: LarkAction.Data) => {
|
||||
const { action } = body?.action?.value as {
|
||||
action: keyof typeof ACTION_MAP
|
||||
}
|
||||
if (!action) return
|
||||
const func = ACTION_MAP[action]
|
||||
if (!func) return
|
||||
const card = await func(body)
|
||||
if (!card) return
|
||||
// 更新飞书的卡片
|
||||
await service.lark.message.update()(body.open_message_id, card)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Action消息
|
||||
* @param {LarkAction.Data} body
|
||||
* @returns {boolean} 是否在本函数中处理了消息
|
||||
*/
|
||||
export const manageActionMsg = (body: LarkAction.Data) => {
|
||||
// 过滤非Action消息
|
||||
if (!getIsActionMsg(body)) {
|
||||
return false
|
||||
}
|
||||
const actionType = getActionType(body)
|
||||
if (actionType === "button") {
|
||||
manageBtnClick(body)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -1,158 +1,158 @@
|
||||
import service from "../../services";
|
||||
import { LarkEvent } from "../../types";
|
||||
import {
|
||||
getChatId,
|
||||
getChatType,
|
||||
getIsEventMsg,
|
||||
getMentions,
|
||||
getMsgText,
|
||||
getMsgType,
|
||||
} from "../../utils/msgTools";
|
||||
|
||||
/**
|
||||
* 是否为P2P或者群聊并且艾特了小煎蛋
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns {boolean} 是否为P2P或者群聊并且艾特了小煎蛋
|
||||
*/
|
||||
const getIsP2pOrGroupAtBot = (body: LarkEvent.Data) => {
|
||||
const isP2p = getChatType(body) === "p2p";
|
||||
const isAtBot = getMentions(body)?.some?.(
|
||||
(mention) => mention.name === "小煎蛋"
|
||||
);
|
||||
return isP2p || isAtBot;
|
||||
};
|
||||
|
||||
/**
|
||||
* 过滤出非法消息,如果发表情包就直接发回去
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns {boolean} 是否为非法消息
|
||||
*/
|
||||
const filterIllegalMsg = (body: LarkEvent.Data) => {
|
||||
// 没有chatId的消息不处理
|
||||
const chatId = getChatId(body);
|
||||
if (!chatId) return true;
|
||||
|
||||
// 获取msgType
|
||||
const msgType = getMsgType(body);
|
||||
|
||||
// 放行纯文本消息
|
||||
if (msgType === "text") {
|
||||
// 过滤艾特全体成员的消息
|
||||
if (getMsgText(body).includes("@_all")) {
|
||||
return true;
|
||||
}
|
||||
// 放行
|
||||
return false;
|
||||
}
|
||||
|
||||
// 发表情包就直接发回去
|
||||
if (msgType === "sticker") {
|
||||
const content = body?.event?.message?.content;
|
||||
service.lark.message.send()("chat_id", chatId, "sticker", content);
|
||||
}
|
||||
|
||||
// 非表情包只在私聊或者群聊中艾特小煎蛋时才回复
|
||||
else if (getIsP2pOrGroupAtBot(body)) {
|
||||
const content = JSON.stringify({
|
||||
text: "哇!这是什么东东?我只懂普通文本啦![可爱]",
|
||||
});
|
||||
service.lark.message.send()("chat_id", chatId, "text", content);
|
||||
}
|
||||
|
||||
// 非纯文本,全不放行
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 发送ID消息
|
||||
* @param chatId - 发送消息的chatId
|
||||
*/
|
||||
const manageIdMsg = async (chatId: string) => {
|
||||
const content = JSON.stringify({
|
||||
type: "template",
|
||||
data: {
|
||||
config: {
|
||||
update_multi: true,
|
||||
},
|
||||
template_id: "ctp_AAi3NnHb6zgK",
|
||||
template_variable: {
|
||||
chat_id: chatId,
|
||||
},
|
||||
},
|
||||
});
|
||||
service.lark.message.send()("chat_id", chatId, "interactive", content);
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理命令消息
|
||||
* @param body - 消息体
|
||||
* @returns
|
||||
*/
|
||||
const manageCMDMsg = (body: LarkEvent.Data) => {
|
||||
const text = getMsgText(body);
|
||||
console.log("🚀 ~ manageCMDMsg ~ text:", text);
|
||||
const chatId = getChatId(body);
|
||||
if (text.trim() === "/id") {
|
||||
manageIdMsg(chatId);
|
||||
return true;
|
||||
}
|
||||
if (text.trim() === "/ci") {
|
||||
service.attach.ciMonitor(chatId);
|
||||
return true;
|
||||
}
|
||||
if (text.includes("share") && text.includes("简报")) {
|
||||
service.attach.reportCollector(body);
|
||||
// 这个用时比较久,先发一条提醒用户收到了请求
|
||||
const content = JSON.stringify({
|
||||
text: "正在为您收集简报,请稍等片刻~",
|
||||
});
|
||||
service.lark.message.send()("chat_id", chatId, "text", content);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* 回复引导消息
|
||||
* @param {LarkEvent.Data} body
|
||||
*/
|
||||
const replyGuideMsg = async (body: LarkEvent.Data) => {
|
||||
const chatId = getChatId(body);
|
||||
const content = JSON.stringify({
|
||||
type: "template",
|
||||
data: {
|
||||
config: {
|
||||
enable_forward: false,
|
||||
update_multi: true,
|
||||
},
|
||||
template_id: "ctp_AAyVx5R39xU9",
|
||||
template_variable: {
|
||||
chat_id: chatId,
|
||||
},
|
||||
},
|
||||
});
|
||||
await service.lark.message.send()("chat_id", chatId, "interactive", content);
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理Event消息
|
||||
* @param {LarkUserAction} body
|
||||
* @returns {boolean} 是否在本函数中处理了消息
|
||||
*/
|
||||
export const manageEventMsg = (body: LarkEvent.Data) => {
|
||||
// 过滤非Event消息
|
||||
if (!getIsEventMsg(body)) {
|
||||
return false;
|
||||
}
|
||||
// 过滤非法消息
|
||||
if (filterIllegalMsg(body)) {
|
||||
return true;
|
||||
}
|
||||
// 处理命令消息
|
||||
if (manageCMDMsg(body)) {
|
||||
return true;
|
||||
}
|
||||
// 返回引导消息
|
||||
replyGuideMsg(body);
|
||||
return true;
|
||||
};
|
||||
import service from "../../services"
|
||||
import { LarkEvent } from "../../types"
|
||||
import {
|
||||
getChatId,
|
||||
getChatType,
|
||||
getIsEventMsg,
|
||||
getMentions,
|
||||
getMsgText,
|
||||
getMsgType,
|
||||
} from "../../utils/msgTools"
|
||||
|
||||
/**
|
||||
* 是否为P2P或者群聊并且艾特了小煎蛋
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns {boolean} 是否为P2P或者群聊并且艾特了小煎蛋
|
||||
*/
|
||||
const getIsP2pOrGroupAtBot = (body: LarkEvent.Data) => {
|
||||
const isP2p = getChatType(body) === "p2p"
|
||||
const isAtBot = getMentions(body)?.some?.(
|
||||
(mention) => mention.name === "小煎蛋"
|
||||
)
|
||||
return isP2p || isAtBot
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤出非法消息,如果发表情包就直接发回去
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns {boolean} 是否为非法消息
|
||||
*/
|
||||
const filterIllegalMsg = (body: LarkEvent.Data) => {
|
||||
// 没有chatId的消息不处理
|
||||
const chatId = getChatId(body)
|
||||
if (!chatId) return true
|
||||
|
||||
// 获取msgType
|
||||
const msgType = getMsgType(body)
|
||||
|
||||
// 放行纯文本消息
|
||||
if (msgType === "text") {
|
||||
// 过滤艾特全体成员的消息
|
||||
if (getMsgText(body).includes("@_all")) {
|
||||
return true
|
||||
}
|
||||
// 放行
|
||||
return false
|
||||
}
|
||||
|
||||
// 发表情包就直接发回去
|
||||
if (msgType === "sticker") {
|
||||
const content = body?.event?.message?.content
|
||||
service.lark.message.send()("chat_id", chatId, "sticker", content)
|
||||
}
|
||||
|
||||
// 非表情包只在私聊或者群聊中艾特小煎蛋时才回复
|
||||
else if (getIsP2pOrGroupAtBot(body)) {
|
||||
const content = JSON.stringify({
|
||||
text: "哇!这是什么东东?我只懂普通文本啦![可爱]",
|
||||
})
|
||||
service.lark.message.send()("chat_id", chatId, "text", content)
|
||||
}
|
||||
|
||||
// 非纯文本,全不放行
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送ID消息
|
||||
* @param chatId - 发送消息的chatId
|
||||
*/
|
||||
const manageIdMsg = async (chatId: string) => {
|
||||
const content = JSON.stringify({
|
||||
type: "template",
|
||||
data: {
|
||||
config: {
|
||||
update_multi: true,
|
||||
},
|
||||
template_id: "ctp_AAi3NnHb6zgK",
|
||||
template_variable: {
|
||||
chat_id: chatId,
|
||||
},
|
||||
},
|
||||
})
|
||||
service.lark.message.send()("chat_id", chatId, "interactive", content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理命令消息
|
||||
* @param body - 消息体
|
||||
* @returns
|
||||
*/
|
||||
const manageCMDMsg = (body: LarkEvent.Data) => {
|
||||
const text = getMsgText(body)
|
||||
console.log("🚀 ~ manageCMDMsg ~ text:", text)
|
||||
const chatId = getChatId(body)
|
||||
if (text.trim() === "/id") {
|
||||
manageIdMsg(chatId)
|
||||
return true
|
||||
}
|
||||
if (text.trim() === "/ci") {
|
||||
service.attach.ciMonitor(chatId)
|
||||
return true
|
||||
}
|
||||
if (text.includes("share") && text.includes("简报")) {
|
||||
service.attach.reportCollector(body)
|
||||
// 这个用时比较久,先发一条提醒用户收到了请求
|
||||
const content = JSON.stringify({
|
||||
text: "正在为您收集简报,请稍等片刻~",
|
||||
})
|
||||
service.lark.message.send()("chat_id", chatId, "text", content)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复引导消息
|
||||
* @param {LarkEvent.Data} body
|
||||
*/
|
||||
const replyGuideMsg = async (body: LarkEvent.Data) => {
|
||||
const chatId = getChatId(body)
|
||||
const content = JSON.stringify({
|
||||
type: "template",
|
||||
data: {
|
||||
config: {
|
||||
enable_forward: false,
|
||||
update_multi: true,
|
||||
},
|
||||
template_id: "ctp_AAyVx5R39xU9",
|
||||
template_variable: {
|
||||
chat_id: chatId,
|
||||
},
|
||||
},
|
||||
})
|
||||
await service.lark.message.send()("chat_id", chatId, "interactive", content)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Event消息
|
||||
* @param {LarkUserAction} body
|
||||
* @returns {boolean} 是否在本函数中处理了消息
|
||||
*/
|
||||
export const manageEventMsg = (body: LarkEvent.Data) => {
|
||||
// 过滤非Event消息
|
||||
if (!getIsEventMsg(body)) {
|
||||
return false
|
||||
}
|
||||
// 过滤非法消息
|
||||
if (filterIllegalMsg(body)) {
|
||||
return true
|
||||
}
|
||||
// 处理命令消息
|
||||
if (manageCMDMsg(body)) {
|
||||
return true
|
||||
}
|
||||
// 返回引导消息
|
||||
replyGuideMsg(body)
|
||||
return true
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import netTool from "../../services/netTool";
|
||||
import { manageActionMsg } from "./actionMsg";
|
||||
import { manageEventMsg } from "./eventMsg";
|
||||
|
||||
export const manageBotReq = async (req: Request) => {
|
||||
const body = (await req.json()) as any;
|
||||
console.log("🚀 ~ manageBotReq ~ body:", body);
|
||||
// 验证机器人
|
||||
if (body?.type === "url_verification") {
|
||||
return Response.json({ challenge: body?.challenge });
|
||||
}
|
||||
// 处理Event消息
|
||||
if (manageEventMsg(body)) return netTool.ok();
|
||||
// 处理Action消息
|
||||
if (manageActionMsg(body)) return netTool.ok();
|
||||
// 其他
|
||||
return netTool.ok();
|
||||
};
|
||||
import netTool from "../../services/netTool"
|
||||
import { manageActionMsg } from "./actionMsg"
|
||||
import { manageEventMsg } from "./eventMsg"
|
||||
|
||||
export const manageBotReq = async (req: Request) => {
|
||||
const body = (await req.json()) as any
|
||||
console.log("🚀 ~ manageBotReq ~ body:", body)
|
||||
// 验证机器人
|
||||
if (body?.type === "url_verification") {
|
||||
return Response.json({ challenge: body?.challenge })
|
||||
}
|
||||
// 处理Event消息
|
||||
if (manageEventMsg(body)) return netTool.ok()
|
||||
// 处理Action消息
|
||||
if (manageActionMsg(body)) return netTool.ok()
|
||||
// 其他
|
||||
return netTool.ok()
|
||||
}
|
||||
|
@ -1,138 +1,138 @@
|
||||
import db from "../../db";
|
||||
import service from "../../services";
|
||||
import netTool from "../../services/netTool";
|
||||
import { DB, LarkServer, MsgProxy } from "../../types";
|
||||
import { safeJsonStringify } from "../../utils/pathTools";
|
||||
|
||||
const LOG_COLLECTION = "message_log";
|
||||
|
||||
const validateMessageReq = (body: MsgProxy.Body) => {
|
||||
if (!body.api_key) {
|
||||
return netTool.badRequest("api_key is required");
|
||||
}
|
||||
if (!body.group_id && !body.receive_id) {
|
||||
return netTool.badRequest("group_id or receive_id is required");
|
||||
}
|
||||
if (body.receive_id && !body.receive_id_type) {
|
||||
return netTool.badRequest("receive_id_type is required");
|
||||
}
|
||||
if (!body.msg_type) {
|
||||
return netTool.badRequest("msg_type is required");
|
||||
}
|
||||
if (!body.content) {
|
||||
return netTool.badRequest("content is required");
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const manageMessageReq = async (req: Request) => {
|
||||
const body = (await req.json()) as MsgProxy.Body;
|
||||
// 校验参数
|
||||
const validateRes = validateMessageReq(body);
|
||||
if (validateRes) return validateRes;
|
||||
|
||||
// 处理消息内容
|
||||
const finalContent =
|
||||
typeof body.content !== "string"
|
||||
? safeJsonStringify(body.content)
|
||||
: body.content;
|
||||
|
||||
// 遍历所有id发送消息,保存所有对应的messageId
|
||||
const sendRes = {
|
||||
chat_id: {} as Record<string, any>,
|
||||
open_id: {} as Record<string, any>,
|
||||
union_id: {} as Record<string, any>,
|
||||
user_id: {} as Record<string, any>,
|
||||
email: {} as Record<string, any>,
|
||||
};
|
||||
|
||||
// 发送消息列表
|
||||
const sendList = [] as Promise<any>[];
|
||||
|
||||
// 构造消息记录
|
||||
const baseLog: DB.MessageLogCreate = {
|
||||
...body,
|
||||
final_content: finalContent,
|
||||
};
|
||||
|
||||
// 校验api_key
|
||||
const apiKeyInfo = await db.apiKey.getOne(body.api_key);
|
||||
if (!apiKeyInfo) {
|
||||
const error = "api key not found";
|
||||
db.log.create(LOG_COLLECTION, { ...baseLog, error });
|
||||
return netTool.notFound(error);
|
||||
}
|
||||
|
||||
// 获取app name
|
||||
const appName = apiKeyInfo.expand?.app?.name;
|
||||
if (!appName) {
|
||||
const error = "app name not found";
|
||||
db.log.create(LOG_COLLECTION, { ...baseLog, error });
|
||||
return netTool.notFound(error);
|
||||
}
|
||||
|
||||
// 如果有group_id,则发送给所有group_id中的人
|
||||
if (body.group_id) {
|
||||
// 获取所有接收者
|
||||
const group = await db.messageGroup.getOne(body.group_id!);
|
||||
if (!group) {
|
||||
const error = "message group not found";
|
||||
db.log.create(LOG_COLLECTION, { ...baseLog, error });
|
||||
return netTool.notFound(error);
|
||||
}
|
||||
|
||||
const { chat_id, open_id, union_id, user_id, email } = group;
|
||||
|
||||
// 构造发送消息函数
|
||||
const makeSendFunc = (receive_id_type: LarkServer.ReceiveIDType) => {
|
||||
return (receive_id: string) => {
|
||||
sendList.push(
|
||||
service.lark.message
|
||||
.send(appName)(
|
||||
receive_id_type,
|
||||
receive_id,
|
||||
body.msg_type,
|
||||
finalContent
|
||||
)
|
||||
.then((res) => {
|
||||
sendRes[receive_id_type][receive_id] = res;
|
||||
})
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
// 创建消息列表
|
||||
if (chat_id) chat_id.map(makeSendFunc("chat_id"));
|
||||
if (open_id) open_id.map(makeSendFunc("open_id"));
|
||||
if (union_id) union_id.map(makeSendFunc("union_id"));
|
||||
if (user_id) user_id.map(makeSendFunc("user_id"));
|
||||
if (email) email.map(makeSendFunc("email"));
|
||||
}
|
||||
|
||||
if (body.receive_id && body.receive_id_type) {
|
||||
sendList.push(
|
||||
service.lark.message
|
||||
.send(appName)(
|
||||
body.receive_id_type,
|
||||
body.receive_id,
|
||||
body.msg_type,
|
||||
finalContent
|
||||
)
|
||||
.then((res) => {
|
||||
sendRes[body.receive_id_type][body.receive_id] = res;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// 里边有错误处理,这里不用担心执行不完
|
||||
await Promise.all(sendList);
|
||||
// 保存消息记录
|
||||
db.log.create(LOG_COLLECTION, { ...baseLog, send_result: sendRes });
|
||||
return netTool.ok(sendRes);
|
||||
} catch {
|
||||
const error = "send msg failed";
|
||||
db.log.create(LOG_COLLECTION, { ...baseLog, error });
|
||||
return netTool.serverError(error, sendRes);
|
||||
}
|
||||
};
|
||||
import db from "../../db"
|
||||
import service from "../../services"
|
||||
import netTool from "../../services/netTool"
|
||||
import { DB, LarkServer, MsgProxy } from "../../types"
|
||||
import { safeJsonStringify } from "../../utils/pathTools"
|
||||
|
||||
const LOG_COLLECTION = "message_log"
|
||||
|
||||
const validateMessageReq = (body: MsgProxy.Body) => {
|
||||
if (!body.api_key) {
|
||||
return netTool.badRequest("api_key is required")
|
||||
}
|
||||
if (!body.group_id && !body.receive_id) {
|
||||
return netTool.badRequest("group_id or receive_id is required")
|
||||
}
|
||||
if (body.receive_id && !body.receive_id_type) {
|
||||
return netTool.badRequest("receive_id_type is required")
|
||||
}
|
||||
if (!body.msg_type) {
|
||||
return netTool.badRequest("msg_type is required")
|
||||
}
|
||||
if (!body.content) {
|
||||
return netTool.badRequest("content is required")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const manageMessageReq = async (req: Request) => {
|
||||
const body = (await req.json()) as MsgProxy.Body
|
||||
// 校验参数
|
||||
const validateRes = validateMessageReq(body)
|
||||
if (validateRes) return validateRes
|
||||
|
||||
// 处理消息内容
|
||||
const finalContent =
|
||||
typeof body.content !== "string"
|
||||
? safeJsonStringify(body.content)
|
||||
: body.content
|
||||
|
||||
// 遍历所有id发送消息,保存所有对应的messageId
|
||||
const sendRes = {
|
||||
chat_id: {} as Record<string, any>,
|
||||
open_id: {} as Record<string, any>,
|
||||
union_id: {} as Record<string, any>,
|
||||
user_id: {} as Record<string, any>,
|
||||
email: {} as Record<string, any>,
|
||||
}
|
||||
|
||||
// 发送消息列表
|
||||
const sendList = [] as Promise<any>[]
|
||||
|
||||
// 构造消息记录
|
||||
const baseLog: DB.MessageLogCreate = {
|
||||
...body,
|
||||
final_content: finalContent,
|
||||
}
|
||||
|
||||
// 校验api_key
|
||||
const apiKeyInfo = await db.apiKey.getOne(body.api_key)
|
||||
if (!apiKeyInfo) {
|
||||
const error = "api key not found"
|
||||
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
||||
return netTool.notFound(error)
|
||||
}
|
||||
|
||||
// 获取app name
|
||||
const appName = apiKeyInfo.expand?.app?.name
|
||||
if (!appName) {
|
||||
const error = "app name not found"
|
||||
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
||||
return netTool.notFound(error)
|
||||
}
|
||||
|
||||
// 如果有group_id,则发送给所有group_id中的人
|
||||
if (body.group_id) {
|
||||
// 获取所有接收者
|
||||
const group = await db.messageGroup.getOne(body.group_id!)
|
||||
if (!group) {
|
||||
const error = "message group not found"
|
||||
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
||||
return netTool.notFound(error)
|
||||
}
|
||||
|
||||
const { chat_id, open_id, union_id, user_id, email } = group
|
||||
|
||||
// 构造发送消息函数
|
||||
const makeSendFunc = (receive_id_type: LarkServer.ReceiveIDType) => {
|
||||
return (receive_id: string) => {
|
||||
sendList.push(
|
||||
service.lark.message
|
||||
.send(appName)(
|
||||
receive_id_type,
|
||||
receive_id,
|
||||
body.msg_type,
|
||||
finalContent
|
||||
)
|
||||
.then((res) => {
|
||||
sendRes[receive_id_type][receive_id] = res
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建消息列表
|
||||
if (chat_id) chat_id.map(makeSendFunc("chat_id"))
|
||||
if (open_id) open_id.map(makeSendFunc("open_id"))
|
||||
if (union_id) union_id.map(makeSendFunc("union_id"))
|
||||
if (user_id) user_id.map(makeSendFunc("user_id"))
|
||||
if (email) email.map(makeSendFunc("email"))
|
||||
}
|
||||
|
||||
if (body.receive_id && body.receive_id_type) {
|
||||
sendList.push(
|
||||
service.lark.message
|
||||
.send(appName)(
|
||||
body.receive_id_type,
|
||||
body.receive_id,
|
||||
body.msg_type,
|
||||
finalContent
|
||||
)
|
||||
.then((res) => {
|
||||
sendRes[body.receive_id_type][body.receive_id] = res
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
// 里边有错误处理,这里不用担心执行不完
|
||||
await Promise.all(sendList)
|
||||
// 保存消息记录
|
||||
db.log.create(LOG_COLLECTION, { ...baseLog, send_result: sendRes })
|
||||
return netTool.ok(sendRes)
|
||||
} catch {
|
||||
const error = "send msg failed"
|
||||
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
||||
return netTool.serverError(error, sendRes)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import service from "../../services";
|
||||
import netTool from "../../services/netTool";
|
||||
import { trimPathPrefix } from "../../utils/pathTools";
|
||||
import service from "../../services"
|
||||
import netTool from "../../services/netTool"
|
||||
import { trimPathPrefix } from "../../utils/pathTools"
|
||||
|
||||
/**
|
||||
* 处理登录请求
|
||||
@ -8,34 +8,34 @@ import { trimPathPrefix } from "../../utils/pathTools";
|
||||
* @returns
|
||||
*/
|
||||
const manageLogin = async (req: Request) => {
|
||||
const url = new URL(req.url);
|
||||
const code = url.searchParams.get("code");
|
||||
const appName = url.searchParams.get("app_name") || undefined;
|
||||
const url = new URL(req.url)
|
||||
const code = url.searchParams.get("code")
|
||||
const appName = url.searchParams.get("app_name") || undefined
|
||||
if (!code) {
|
||||
return netTool.badRequest("code not found");
|
||||
return netTool.badRequest("code not found")
|
||||
}
|
||||
const {
|
||||
code: resCode,
|
||||
data,
|
||||
msg,
|
||||
} = await service.lark.user.code2Login(appName)(code);
|
||||
} = await service.lark.user.code2Login(appName)(code)
|
||||
|
||||
console.log("🚀 ~ manageLogin:", resCode, data, msg);
|
||||
console.log("🚀 ~ manageLogin:", resCode, data, msg)
|
||||
|
||||
if (resCode !== 0) {
|
||||
return Response.json({
|
||||
code: resCode,
|
||||
message: msg,
|
||||
data: null,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
code: 0,
|
||||
message: "success",
|
||||
data,
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理批量获取用户信息请求
|
||||
@ -43,35 +43,35 @@ const manageLogin = async (req: Request) => {
|
||||
* @returns
|
||||
*/
|
||||
const manageBatchUser = async (req: Request) => {
|
||||
const body = (await req.json()) as any;
|
||||
console.log("🚀 ~ manageBatchUser ~ body:", body);
|
||||
const { user_ids, user_id_type, app_name } = body;
|
||||
const body = (await req.json()) as any
|
||||
console.log("🚀 ~ manageBatchUser ~ body:", body)
|
||||
const { user_ids, user_id_type, app_name } = body
|
||||
if (!user_ids) {
|
||||
return netTool.badRequest("user_ids not found");
|
||||
return netTool.badRequest("user_ids not found")
|
||||
}
|
||||
if (!user_id_type) {
|
||||
return netTool.badRequest("user_id_type not found");
|
||||
return netTool.badRequest("user_id_type not found")
|
||||
}
|
||||
|
||||
const { code, data, msg } = await service.lark.user.batchGet(app_name)(
|
||||
user_ids,
|
||||
user_id_type
|
||||
);
|
||||
)
|
||||
|
||||
console.log("🚀 ~ manageBatchUser:", code, data, msg);
|
||||
console.log("🚀 ~ manageBatchUser:", code, data, msg)
|
||||
if (code !== 0) {
|
||||
return Response.json({
|
||||
code,
|
||||
message: msg,
|
||||
data: null,
|
||||
});
|
||||
})
|
||||
}
|
||||
return Response.json({
|
||||
code,
|
||||
message: "success",
|
||||
data: data.items,
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理小程序请求
|
||||
@ -79,15 +79,15 @@ const manageBatchUser = async (req: Request) => {
|
||||
* @returns
|
||||
*/
|
||||
export const manageMicroAppReq = async (req: Request) => {
|
||||
const url = new URL(req.url);
|
||||
const withoutPrefix = trimPathPrefix(url.pathname, "/micro_app");
|
||||
const url = new URL(req.url)
|
||||
const withoutPrefix = trimPathPrefix(url.pathname, "/micro_app")
|
||||
// 处理登录请求
|
||||
if (withoutPrefix === "/login") {
|
||||
return manageLogin(req);
|
||||
return manageLogin(req)
|
||||
}
|
||||
// 处理批量获取用户信息请求
|
||||
if (withoutPrefix === "/batch_user") {
|
||||
return manageBatchUser(req);
|
||||
return manageBatchUser(req)
|
||||
}
|
||||
return netTool.ok();
|
||||
};
|
||||
return netTool.ok()
|
||||
}
|
||||
|
@ -1,59 +1,59 @@
|
||||
import db from "../../db";
|
||||
import service from "../../services";
|
||||
import netTool from "../../services/netTool";
|
||||
import { SheetProxy } from "../../types/sheetProxy";
|
||||
import db from "../../db"
|
||||
import service from "../../services"
|
||||
import netTool from "../../services/netTool"
|
||||
import { SheetProxy } from "../../types/sheetProxy"
|
||||
|
||||
const validateSheetReq = async (body: SheetProxy.Body) => {
|
||||
if (!body.api_key) {
|
||||
return netTool.badRequest("api_key is required");
|
||||
return netTool.badRequest("api_key is required")
|
||||
}
|
||||
if (!body.sheet_token) {
|
||||
return netTool.badRequest("sheet_token is required");
|
||||
return netTool.badRequest("sheet_token is required")
|
||||
}
|
||||
if (!body.range) {
|
||||
return netTool.badRequest("range is required");
|
||||
return netTool.badRequest("range is required")
|
||||
}
|
||||
if (!body.values) {
|
||||
return netTool.badRequest("values is required");
|
||||
return netTool.badRequest("values is required")
|
||||
}
|
||||
|
||||
if (!SheetProxy.isType(body.type)) {
|
||||
return netTool.badRequest("type is invalid");
|
||||
return netTool.badRequest("type is invalid")
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return false
|
||||
}
|
||||
|
||||
export const manageSheetReq = async (req: Request) => {
|
||||
const body = (await req.json()) as SheetProxy.Body;
|
||||
const body = (await req.json()) as SheetProxy.Body
|
||||
// 校验参数
|
||||
const validateRes = await validateSheetReq(body);
|
||||
if (validateRes) return validateRes;
|
||||
const validateRes = await validateSheetReq(body)
|
||||
if (validateRes) return validateRes
|
||||
|
||||
// 校验api_key
|
||||
const apiKeyInfo = await db.apiKey.getOne(body.api_key);
|
||||
const apiKeyInfo = await db.apiKey.getOne(body.api_key)
|
||||
if (!apiKeyInfo) {
|
||||
return netTool.notFound("api key not found");
|
||||
return netTool.notFound("api key not found")
|
||||
}
|
||||
|
||||
// 获取 app name
|
||||
const appName = apiKeyInfo.expand?.app?.name;
|
||||
const appName = apiKeyInfo.expand?.app?.name
|
||||
if (!appName) {
|
||||
return netTool.notFound("app name not found");
|
||||
return netTool.notFound("app name not found")
|
||||
}
|
||||
|
||||
if (body.type === "insert") {
|
||||
// 插入行
|
||||
const insertRes = await service.lark.sheet.insterRows(appName)(
|
||||
const insertRes = await service.lark.sheet.insertRows(appName)(
|
||||
body.sheet_token,
|
||||
body.range,
|
||||
body.values
|
||||
);
|
||||
)
|
||||
if (insertRes?.code !== 0) {
|
||||
return netTool.serverError(insertRes?.msg, insertRes?.data);
|
||||
return netTool.serverError(insertRes?.msg, insertRes?.data)
|
||||
}
|
||||
// 返回
|
||||
return netTool.ok(insertRes?.data);
|
||||
return netTool.ok(insertRes?.data)
|
||||
}
|
||||
|
||||
return netTool.ok();
|
||||
};
|
||||
return netTool.ok()
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
import db from "../db";
|
||||
import netTool from "../services/netTool";
|
||||
|
||||
const URL =
|
||||
"https://open.f.mioffice.cn/open-apis/auth/v3/tenant_access_token/internal";
|
||||
|
||||
export const resetAccessToken = async () => {
|
||||
try {
|
||||
const appList = await db.appInfo.getFullList();
|
||||
for (const app of appList) {
|
||||
const { tenant_access_token } = await netTool.post(URL, {
|
||||
app_id: app.app_id,
|
||||
app_secret: app.app_secret,
|
||||
});
|
||||
await db.tenantAccessToken.update(app.id, app.name, tenant_access_token);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("🚀 ~ resetAccessToken ~ error", error);
|
||||
}
|
||||
};
|
||||
import db from "../db"
|
||||
import netTool from "../services/netTool"
|
||||
|
||||
const URL =
|
||||
"https://open.f.mioffice.cn/open-apis/auth/v3/tenant_access_token/internal"
|
||||
|
||||
export const resetAccessToken = async () => {
|
||||
try {
|
||||
const appList = await db.appInfo.getFullList()
|
||||
for (const app of appList) {
|
||||
const { tenant_access_token } = await netTool.post(URL, {
|
||||
app_id: app.app_id,
|
||||
app_secret: app.app_secret,
|
||||
})
|
||||
await db.tenantAccessToken.update(app.id, app.name, tenant_access_token)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("🚀 ~ resetAccessToken ~ error", error)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { resetAccessToken } from "./accessToken";
|
||||
import schedule from "node-schedule";
|
||||
|
||||
export const initSchedule = async () => {
|
||||
// 定时任务,每15分钟刷新一次token
|
||||
schedule.scheduleJob("*/15 * * * *", resetAccessToken);
|
||||
// 立即执行一次
|
||||
resetAccessToken();
|
||||
};
|
||||
import schedule from "node-schedule"
|
||||
|
||||
import { resetAccessToken } from "./accessToken"
|
||||
|
||||
export const initSchedule = async () => {
|
||||
// 定时任务,每15分钟刷新一次token
|
||||
schedule.scheduleJob("*/15 * * * *", resetAccessToken)
|
||||
// 立即执行一次
|
||||
resetAccessToken()
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { LarkEvent } from "../../types";
|
||||
import netTool from "../netTool";
|
||||
import { LarkEvent } from "../../types"
|
||||
import netTool from "../netTool"
|
||||
|
||||
/**
|
||||
* 请求 CI 监控
|
||||
*/
|
||||
const ciMonitor = async (chat_id: string) => {
|
||||
const URL = `https://ci-monitor.xiaomiwh.cn/ci?chat_id=${chat_id}`;
|
||||
const URL = `https://ci-monitor.xiaomiwh.cn/ci?chat_id=${chat_id}`
|
||||
try {
|
||||
const res = await netTool.get(URL);
|
||||
return (res as string) || "";
|
||||
const res = await netTool.get(URL)
|
||||
return (res as string) || ""
|
||||
} catch {
|
||||
return "";
|
||||
return ""
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求简报收集器
|
||||
@ -20,18 +20,18 @@ const ciMonitor = async (chat_id: string) => {
|
||||
* @returns
|
||||
*/
|
||||
const reportCollector = async (body: LarkEvent.Data) => {
|
||||
const URL = "https://report.imoaix.cn/report";
|
||||
const URL = "https://report.imoaix.cn/report"
|
||||
try {
|
||||
const res = await netTool.post(URL, body);
|
||||
return (res as string) || "";
|
||||
const res = await netTool.post(URL, body)
|
||||
return (res as string) || ""
|
||||
} catch {
|
||||
return "";
|
||||
return ""
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const attach = {
|
||||
ciMonitor,
|
||||
reportCollector,
|
||||
};
|
||||
}
|
||||
|
||||
export default attach;
|
||||
export default attach
|
||||
|
@ -1,9 +1,9 @@
|
||||
import attach from "./attach";
|
||||
import lark from "./lark";
|
||||
|
||||
const service = {
|
||||
attach,
|
||||
lark,
|
||||
};
|
||||
|
||||
export default service;
|
||||
import attach from "./attach"
|
||||
import lark from "./lark"
|
||||
|
||||
const service = {
|
||||
attach,
|
||||
lark,
|
||||
}
|
||||
|
||||
export default service
|
||||
|
@ -1,42 +1,42 @@
|
||||
import { LarkServer } from "../../types";
|
||||
import larkNetTool from "./larkNetTool";
|
||||
import { LarkServer } from "../../types"
|
||||
import larkNetTool from "./larkNetTool"
|
||||
|
||||
const batchGetMeta =
|
||||
(appName?: string) =>
|
||||
async (docTokens: string[], doc_type = "doc", user_id_type = "user_id") => {
|
||||
const URL =
|
||||
"https://open.f.mioffice.cn/open-apis/drive/v1/metas/batch_query";
|
||||
"https://open.f.mioffice.cn/open-apis/drive/v1/metas/batch_query"
|
||||
// 如果docTokens长度超出150,需要分批请求
|
||||
const docTokensLen = docTokens.length;
|
||||
const maxLen = 150;
|
||||
const docTokensLen = docTokens.length
|
||||
const maxLen = 150
|
||||
const requestMap = Array.from(
|
||||
{ length: Math.ceil(docTokensLen / maxLen) },
|
||||
(_, index) => {
|
||||
const start = index * maxLen;
|
||||
const docTokensSlice = docTokens.slice(start, start + maxLen);
|
||||
const start = index * maxLen
|
||||
const docTokensSlice = docTokens.slice(start, start + maxLen)
|
||||
const data = {
|
||||
request_docs: docTokensSlice.map((id) => ({
|
||||
doc_token: id,
|
||||
doc_type,
|
||||
})),
|
||||
};
|
||||
}
|
||||
return larkNetTool.post(appName)<LarkServer.BatchDocMetaRes>(
|
||||
URL,
|
||||
data,
|
||||
{
|
||||
user_id_type,
|
||||
}
|
||||
);
|
||||
)
|
||||
}
|
||||
);
|
||||
const responses = await Promise.all(requestMap);
|
||||
)
|
||||
const responses = await Promise.all(requestMap)
|
||||
const metas = responses.flatMap((res) => {
|
||||
return res.data?.metas || [];
|
||||
});
|
||||
return res.data?.metas || []
|
||||
})
|
||||
|
||||
const failed_list = responses.flatMap((res) => {
|
||||
return res.data?.failed_list || [];
|
||||
});
|
||||
return res.data?.failed_list || []
|
||||
})
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
@ -45,11 +45,11 @@ const batchGetMeta =
|
||||
failed_list,
|
||||
},
|
||||
msg: "success",
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const drive = {
|
||||
batchGetMeta,
|
||||
};
|
||||
}
|
||||
|
||||
export default drive;
|
||||
export default drive
|
||||
|
@ -1,13 +1,13 @@
|
||||
import message from "./message";
|
||||
import user from "./user";
|
||||
import drive from "./drive";
|
||||
import sheet from "./sheet";
|
||||
import drive from "./drive"
|
||||
import message from "./message"
|
||||
import sheet from "./sheet"
|
||||
import user from "./user"
|
||||
|
||||
const lark = {
|
||||
message,
|
||||
user,
|
||||
drive,
|
||||
sheet,
|
||||
};
|
||||
}
|
||||
|
||||
export default lark;
|
||||
export default lark
|
||||
|
@ -1,6 +1,6 @@
|
||||
import db from "../../db";
|
||||
import { DB, LarkServer } from "../../types";
|
||||
import netTool from "../netTool";
|
||||
import db from "../../db"
|
||||
import { LarkServer } from "../../types"
|
||||
import netTool from "../netTool"
|
||||
|
||||
/**
|
||||
* 发送网络请求并返回一个解析为响应数据的Promise。
|
||||
@ -21,17 +21,17 @@ const larkNetTool = async <T = LarkServer.BaseRes>({
|
||||
additionalHeaders,
|
||||
appName = "egg",
|
||||
}: {
|
||||
url: string;
|
||||
method: string;
|
||||
queryParams?: any;
|
||||
payload?: any;
|
||||
additionalHeaders?: any;
|
||||
appName?: string;
|
||||
url: string
|
||||
method: string
|
||||
queryParams?: any
|
||||
payload?: any
|
||||
additionalHeaders?: any
|
||||
appName?: string
|
||||
}): Promise<T> => {
|
||||
const headersWithAuth = {
|
||||
Authorization: `Bearer ${await db.tenantAccessToken.get(appName)}`,
|
||||
...additionalHeaders,
|
||||
};
|
||||
}
|
||||
return netTool<T>({
|
||||
url,
|
||||
method,
|
||||
@ -39,14 +39,14 @@ const larkNetTool = async <T = LarkServer.BaseRes>({
|
||||
payload,
|
||||
additionalHeaders: headersWithAuth,
|
||||
}).catch((error) => {
|
||||
console.error("larkNetTool catch error: ", error);
|
||||
console.error("larkNetTool catch error: ", error)
|
||||
return {
|
||||
code: 1,
|
||||
data: null,
|
||||
msg: error.message || "网络请求异常",
|
||||
} as T;
|
||||
});
|
||||
};
|
||||
} as T
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送GET请求并返回一个解析为响应数据的Promise。
|
||||
@ -67,7 +67,7 @@ larkNetTool.get =
|
||||
queryParams,
|
||||
additionalHeaders,
|
||||
appName,
|
||||
});
|
||||
})
|
||||
|
||||
/**
|
||||
* 发送POST请求并返回一个解析为响应数据的Promise。
|
||||
@ -90,7 +90,7 @@ larkNetTool.post =
|
||||
queryParams,
|
||||
additionalHeaders,
|
||||
appName,
|
||||
});
|
||||
})
|
||||
|
||||
/**
|
||||
* 发送DELETE请求并返回一个解析为响应数据的Promise。
|
||||
@ -105,7 +105,7 @@ larkNetTool.del =
|
||||
payload: any,
|
||||
additionalHeaders?: any
|
||||
): Promise<T> =>
|
||||
larkNetTool({ url, method: "delete", payload, additionalHeaders, appName });
|
||||
larkNetTool({ url, method: "delete", payload, additionalHeaders, appName })
|
||||
|
||||
/**
|
||||
* 发送PATCH请求并返回一个解析为响应数据的Promise。
|
||||
@ -120,6 +120,6 @@ larkNetTool.patch =
|
||||
payload: any,
|
||||
additionalHeaders?: any
|
||||
): Promise<T> =>
|
||||
larkNetTool({ url, method: "patch", payload, additionalHeaders, appName });
|
||||
larkNetTool({ url, method: "patch", payload, additionalHeaders, appName })
|
||||
|
||||
export default larkNetTool;
|
||||
export default larkNetTool
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { LarkServer } from "../../types/larkServer";
|
||||
import larkNetTool from "./larkNetTool";
|
||||
import { LarkServer } from "../../types/larkServer"
|
||||
import larkNetTool from "./larkNetTool"
|
||||
|
||||
/**
|
||||
* 发送卡片
|
||||
@ -16,16 +16,16 @@ const send =
|
||||
msg_type: LarkServer.MsgType,
|
||||
content: string
|
||||
) => {
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/im/v1/messages?receive_id_type=${receive_id_type}`;
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/im/v1/messages?receive_id_type=${receive_id_type}`
|
||||
if (msg_type === "text" && !content.includes('"text"')) {
|
||||
content = JSON.stringify({ text: content });
|
||||
content = JSON.stringify({ text: content })
|
||||
}
|
||||
return larkNetTool.post(appName)<LarkServer.BaseRes>(URL, {
|
||||
receive_id,
|
||||
msg_type,
|
||||
content,
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新卡片
|
||||
@ -34,13 +34,13 @@ const send =
|
||||
*/
|
||||
const update =
|
||||
(appName?: string) => async (message_id: string, content: string) => {
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/im/v1/messages/${message_id}`;
|
||||
return larkNetTool.patch(appName)<LarkServer.BaseRes>(URL, { content });
|
||||
};
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/im/v1/messages/${message_id}`
|
||||
return larkNetTool.patch(appName)<LarkServer.BaseRes>(URL, { content })
|
||||
}
|
||||
|
||||
const message = {
|
||||
send,
|
||||
update,
|
||||
};
|
||||
}
|
||||
|
||||
export default message;
|
||||
export default message
|
||||
|
@ -1,25 +1,25 @@
|
||||
import { LarkServer } from "../../types/larkServer";
|
||||
import larkNetTool from "./larkNetTool";
|
||||
import { LarkServer } from "../../types/larkServer"
|
||||
import larkNetTool from "./larkNetTool"
|
||||
|
||||
/**
|
||||
* 向电子表格中插入行。
|
||||
* @param appName - 应用程序的名称(可选)。
|
||||
* @returns 一个函数,该函数接受表格令牌、范围和要插入的值。
|
||||
*/
|
||||
const insterRows =
|
||||
const insertRows =
|
||||
(appName?: string) =>
|
||||
async (sheetToken: string, range: string, values: string[][]) => {
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/sheets/v2/spreadsheets/${sheetToken}/values_append?insertDataOption=INSERT_ROWS`;
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/sheets/v2/spreadsheets/${sheetToken}/values_append?insertDataOption=INSERT_ROWS`
|
||||
return larkNetTool.post(appName)<LarkServer.BaseRes>(URL, {
|
||||
valueRange: {
|
||||
range,
|
||||
values,
|
||||
},
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
const sheet = {
|
||||
insterRows,
|
||||
};
|
||||
insertRows,
|
||||
}
|
||||
|
||||
export default sheet;
|
||||
export default sheet
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { LarkServer } from "../../types/larkServer";
|
||||
import larkNetTool from "./larkNetTool";
|
||||
import { LarkServer } from "../../types/larkServer"
|
||||
import larkNetTool from "./larkNetTool"
|
||||
|
||||
/**
|
||||
* 登录凭证校验
|
||||
@ -7,9 +7,9 @@ import larkNetTool from "./larkNetTool";
|
||||
* @returns
|
||||
*/
|
||||
const code2Login = (appName?: string) => async (code: string) => {
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/mina/v2/tokenLoginValidate`;
|
||||
return larkNetTool.post(appName)<LarkServer.UserSessionRes>(URL, { code });
|
||||
};
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/mina/v2/tokenLoginValidate`
|
||||
return larkNetTool.post(appName)<LarkServer.UserSessionRes>(URL, { code })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
@ -19,11 +19,11 @@ const code2Login = (appName?: string) => async (code: string) => {
|
||||
const get =
|
||||
(appName?: string) =>
|
||||
async (user_id: string, user_id_type: "open_id" | "user_id") => {
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/contact/v3/users/${user_id}`;
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/contact/v3/users/${user_id}`
|
||||
return larkNetTool.get(appName)<LarkServer.UserInfoRes>(URL, {
|
||||
user_id_type,
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取用户信息
|
||||
@ -33,32 +33,32 @@ const get =
|
||||
const batchGet =
|
||||
(appName?: string) =>
|
||||
async (user_ids: string[], user_id_type: "open_id" | "user_id") => {
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/contact/v3/users/batch`;
|
||||
const URL = `https://open.f.mioffice.cn/open-apis/contact/v3/users/batch`
|
||||
|
||||
// 如果user_id长度超出50,需要分批请求,
|
||||
const userCount = user_ids.length;
|
||||
const maxLen = 50;
|
||||
const userCount = user_ids.length
|
||||
const maxLen = 50
|
||||
|
||||
const requestMap = Array.from(
|
||||
{ length: Math.ceil(userCount / maxLen) },
|
||||
(_, index) => {
|
||||
const start = index * maxLen;
|
||||
const user_idsSlice = user_ids.slice(start, start + maxLen);
|
||||
const start = index * maxLen
|
||||
const user_idsSlice = user_ids.slice(start, start + maxLen)
|
||||
const getParams = `${user_idsSlice
|
||||
.map((id) => `user_ids=${id}`)
|
||||
.join("&")}&user_id_type=${user_id_type}`;
|
||||
.join("&")}&user_id_type=${user_id_type}`
|
||||
return larkNetTool.get(appName)<LarkServer.BatchUserInfoRes>(
|
||||
URL,
|
||||
getParams
|
||||
);
|
||||
)
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
const responses = await Promise.all(requestMap);
|
||||
const responses = await Promise.all(requestMap)
|
||||
|
||||
const items = responses.flatMap((res) => {
|
||||
return res.data?.items || [];
|
||||
});
|
||||
return res.data?.items || []
|
||||
})
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
@ -66,13 +66,13 @@ const batchGet =
|
||||
items,
|
||||
},
|
||||
msg: "success",
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const user = {
|
||||
code2Login,
|
||||
batchGet,
|
||||
get,
|
||||
};
|
||||
}
|
||||
|
||||
export default user;
|
||||
export default user
|
||||
|
@ -1,9 +1,9 @@
|
||||
interface NetRequestParams {
|
||||
url: string;
|
||||
method: string;
|
||||
queryParams?: any;
|
||||
payload?: any;
|
||||
additionalHeaders?: any;
|
||||
url: string
|
||||
method: string
|
||||
queryParams?: any
|
||||
payload?: any
|
||||
additionalHeaders?: any
|
||||
}
|
||||
|
||||
/**
|
||||
@ -32,10 +32,10 @@ const logResponse = (
|
||||
responseHeaders: response.headers,
|
||||
requestBody,
|
||||
responseBody,
|
||||
};
|
||||
console.log("🚀 ~ responseLog:", JSON.stringify(responseLog, null, 2));
|
||||
return responseLog;
|
||||
};
|
||||
}
|
||||
console.log("🚀 ~ responseLog:", JSON.stringify(responseLog, null, 2))
|
||||
return responseLog
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送网络请求并返回一个解析为响应数据的Promise。
|
||||
@ -55,13 +55,13 @@ const netTool = async <T = any>({
|
||||
additionalHeaders,
|
||||
}: NetRequestParams): Promise<T> => {
|
||||
// 拼接完整的URL
|
||||
let fullUrl = url;
|
||||
let fullUrl = url
|
||||
if (queryParams) {
|
||||
if (typeof queryParams === "string") {
|
||||
fullUrl = `${url}?${queryParams}`;
|
||||
fullUrl = `${url}?${queryParams}`
|
||||
} else {
|
||||
const queryString = new URLSearchParams(queryParams).toString();
|
||||
if (queryString) fullUrl = `${url}?${queryString}`;
|
||||
const queryString = new URLSearchParams(queryParams).toString()
|
||||
if (queryString) fullUrl = `${url}?${queryString}`
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,40 +69,42 @@ const netTool = async <T = any>({
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
...additionalHeaders,
|
||||
};
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
const res = await fetch(fullUrl, {
|
||||
method,
|
||||
body: JSON.stringify(payload),
|
||||
headers,
|
||||
});
|
||||
})
|
||||
// 获取响应数据
|
||||
let resData: any = null;
|
||||
let resText: string = "";
|
||||
let resData: any = null
|
||||
let resText: string = ""
|
||||
|
||||
try {
|
||||
resText = await res.text();
|
||||
resData = JSON.parse(resText);
|
||||
} catch {}
|
||||
resText = await res.text()
|
||||
resData = JSON.parse(resText)
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
// 记录响应
|
||||
logResponse(res, method, headers, payload, resData || resText);
|
||||
logResponse(res, method, headers, payload, resData || resText)
|
||||
if (!res.ok) {
|
||||
if (resData?.msg) {
|
||||
throw new Error(resData.msg);
|
||||
throw new Error(resData.msg)
|
||||
}
|
||||
if (resText) {
|
||||
throw new Error(resText);
|
||||
throw new Error(resText)
|
||||
}
|
||||
throw new Error("网络响应异常");
|
||||
throw new Error("网络响应异常")
|
||||
}
|
||||
// http 错误码正常,但解析异常
|
||||
if (!resData) {
|
||||
throw new Error("解析响应数据异常");
|
||||
throw new Error("解析响应数据异常")
|
||||
}
|
||||
return resData as T;
|
||||
};
|
||||
return resData as T
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送GET请求并返回一个解析为响应数据的Promise。
|
||||
@ -116,8 +118,7 @@ netTool.get = <T = any>(
|
||||
url: string,
|
||||
queryParams?: any,
|
||||
additionalHeaders?: any
|
||||
): Promise<T> =>
|
||||
netTool({ url, method: "get", queryParams, additionalHeaders });
|
||||
): Promise<T> => netTool({ url, method: "get", queryParams, additionalHeaders })
|
||||
|
||||
/**
|
||||
* 发送POST请求并返回一个解析为响应数据的Promise。
|
||||
@ -134,7 +135,7 @@ netTool.post = <T = any>(
|
||||
queryParams?: any,
|
||||
additionalHeaders?: any
|
||||
): Promise<T> =>
|
||||
netTool({ url, method: "post", payload, queryParams, additionalHeaders });
|
||||
netTool({ url, method: "post", payload, queryParams, additionalHeaders })
|
||||
|
||||
/**
|
||||
* 发送PUT请求并返回一个解析为响应数据的Promise。
|
||||
@ -151,7 +152,7 @@ netTool.put = <T = any>(
|
||||
queryParams?: any,
|
||||
additionalHeaders?: any
|
||||
): Promise<T> =>
|
||||
netTool({ url, method: "put", payload, queryParams, additionalHeaders });
|
||||
netTool({ url, method: "put", payload, queryParams, additionalHeaders })
|
||||
|
||||
/**
|
||||
* 发送DELETE请求并返回一个解析为响应数据的Promise。
|
||||
@ -168,7 +169,7 @@ netTool.del = <T = any>(
|
||||
queryParams?: any,
|
||||
additionalHeaders?: any
|
||||
): Promise<T> =>
|
||||
netTool({ url, method: "delete", payload, queryParams, additionalHeaders });
|
||||
netTool({ url, method: "delete", payload, queryParams, additionalHeaders })
|
||||
|
||||
/**
|
||||
* 发送PATCH请求并返回一个解析为响应数据的Promise。
|
||||
@ -185,7 +186,7 @@ netTool.patch = <T = any>(
|
||||
queryParams?: any,
|
||||
additionalHeaders?: any
|
||||
): Promise<T> =>
|
||||
netTool({ url, method: "patch", payload, queryParams, additionalHeaders });
|
||||
netTool({ url, method: "patch", payload, queryParams, additionalHeaders })
|
||||
|
||||
/**
|
||||
* 创建一个表示400 Bad Request的响应对象。
|
||||
@ -195,7 +196,7 @@ netTool.patch = <T = any>(
|
||||
* @returns 一个表示400 Bad Request的响应对象。
|
||||
*/
|
||||
netTool.badRequest = (msg: string, requestId?: string) =>
|
||||
Response.json({ code: 400, msg, requestId }, { status: 400 });
|
||||
Response.json({ code: 400, msg, requestId }, { status: 400 })
|
||||
|
||||
/**
|
||||
* 创建一个表示404 Not Found的响应对象。
|
||||
@ -205,7 +206,7 @@ netTool.badRequest = (msg: string, requestId?: string) =>
|
||||
* @returns 一个表示404 Not Found的响应对象。
|
||||
*/
|
||||
netTool.notFound = (msg: string, requestId?: string) =>
|
||||
Response.json({ code: 404, msg, requestId }, { status: 404 });
|
||||
Response.json({ code: 404, msg, requestId }, { status: 404 })
|
||||
|
||||
/**
|
||||
* 创建一个表示500 Internal Server Error的响应对象。
|
||||
@ -216,7 +217,7 @@ netTool.notFound = (msg: string, requestId?: string) =>
|
||||
* @returns 一个表示500 Internal Server Error的响应对象。
|
||||
*/
|
||||
netTool.serverError = (msg: string, data?: any, requestId?: string) =>
|
||||
Response.json({ code: 500, msg, data, requestId }, { status: 500 });
|
||||
Response.json({ code: 500, msg, data, requestId }, { status: 500 })
|
||||
|
||||
/**
|
||||
* 创建一个表示200 OK的响应对象。
|
||||
@ -226,6 +227,6 @@ netTool.serverError = (msg: string, data?: any, requestId?: string) =>
|
||||
* @returns 一个表示200 OK的响应对象。
|
||||
*/
|
||||
netTool.ok = (data?: any, requestId?: string) =>
|
||||
Response.json({ code: 0, msg: "success", data, requestId });
|
||||
Response.json({ code: 0, msg: "success", data, requestId })
|
||||
|
||||
export default netTool;
|
||||
export default netTool
|
||||
|
@ -1,5 +1,5 @@
|
||||
const localUrl = "http://localhost:3000/micro_app/batch_user";
|
||||
const prodUrl = "https://egg.imoaix.cn/micro_app/batch_user";
|
||||
const localUrl = "http://localhost:3000/micro_app/batch_user"
|
||||
// const prodUrl = "https://egg.imoaix.cn/micro_app/batch_user";
|
||||
|
||||
const res = await fetch(localUrl, {
|
||||
method: "POST",
|
||||
@ -10,6 +10,6 @@ const res = await fetch(localUrl, {
|
||||
user_ids: ["zhaoyingbo"],
|
||||
user_id_type: "user_id",
|
||||
}),
|
||||
});
|
||||
})
|
||||
|
||||
console.log(JSON.stringify(await res.json()));
|
||||
console.log(JSON.stringify(await res.json()))
|
||||
|
@ -1,5 +1,5 @@
|
||||
import db from "../db";
|
||||
import db from "../db"
|
||||
|
||||
const res = await db.apiKey.getOne("uwnpzb9hvoft28h");
|
||||
const res = await db.apiKey.getOne("uwnpzb9hvoft28h")
|
||||
|
||||
console.log("🚀 ~ res", res);
|
||||
console.log("🚀 ~ res", res)
|
||||
|
@ -1,5 +1,5 @@
|
||||
// const URL = "https://egg.imoaix.cn/sheet";
|
||||
const URL = "http://localhost:3000/sheet";
|
||||
const URL = "http://localhost:3000/sheet"
|
||||
|
||||
const res = await fetch(URL, {
|
||||
method: "POST",
|
||||
@ -10,6 +10,6 @@ const res = await fetch(URL, {
|
||||
range: "a2YJxa",
|
||||
values: [["hello", "world"]],
|
||||
}),
|
||||
});
|
||||
})
|
||||
|
||||
console.log(JSON.stringify(await res.json(), null, 2));
|
||||
console.log(JSON.stringify(await res.json(), null, 2))
|
||||
|
@ -1,5 +1,5 @@
|
||||
// const URL = "https://egg.imoaix.cn/message";
|
||||
const URL = "http://localhost:3000/message";
|
||||
const URL = "http://localhost:3000/message"
|
||||
|
||||
const res = await fetch(URL, {
|
||||
method: "POST",
|
||||
@ -10,6 +10,6 @@ const res = await fetch(URL, {
|
||||
msg_type: "text",
|
||||
content: "hello, world!",
|
||||
}),
|
||||
});
|
||||
})
|
||||
|
||||
console.log(JSON.stringify(await res.text()));
|
||||
console.log(JSON.stringify(await res.text()))
|
||||
|
@ -1,21 +1,21 @@
|
||||
[
|
||||
{
|
||||
"_id": "a5cb7b58-0c22-41fb-bc00-af460ef90c1b",
|
||||
"colId": "history",
|
||||
"containerId": "",
|
||||
"name": "https://self.imoaix.cn/bot",
|
||||
"url": "https://self.imoaix.cn/bot",
|
||||
"method": "POST",
|
||||
"sortNum": 0,
|
||||
"created": "2023-08-15T07:47:05.439Z",
|
||||
"modified": "2023-08-15T07:47:34.510Z",
|
||||
"headers": [],
|
||||
"params": [],
|
||||
"body": {
|
||||
"type": "json",
|
||||
"raw": "{\r\n \"challenge\": \"c14206ad-5b5f-4a77-82b6-b39b7365392b\",\r\n \"token\": \"tV9djUKSjzVnekV7xTg2Od06NFTcsBnj\",\r\n \"type\": \"url_verification\"\r\n}",
|
||||
"form": []
|
||||
},
|
||||
"tests": []
|
||||
}
|
||||
]
|
||||
[
|
||||
{
|
||||
"_id": "a5cb7b58-0c22-41fb-bc00-af460ef90c1b",
|
||||
"colId": "history",
|
||||
"containerId": "",
|
||||
"name": "https://self.imoaix.cn/bot",
|
||||
"url": "https://self.imoaix.cn/bot",
|
||||
"method": "POST",
|
||||
"sortNum": 0,
|
||||
"created": "2023-08-15T07:47:05.439Z",
|
||||
"modified": "2023-08-15T07:47:34.510Z",
|
||||
"headers": [],
|
||||
"params": [],
|
||||
"body": {
|
||||
"type": "json",
|
||||
"raw": "{\r\n \"challenge\": \"c14206ad-5b5f-4a77-82b6-b39b7365392b\",\r\n \"token\": \"tV9djUKSjzVnekV7xTg2Od06NFTcsBnj\",\r\n \"type\": \"url_verification\"\r\n}",
|
||||
"form": []
|
||||
},
|
||||
"tests": []
|
||||
}
|
||||
]
|
||||
|
@ -1,22 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
"composite": true,
|
||||
"strict": true,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "react-jsx",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowJs": true,
|
||||
"types": [
|
||||
"bun-types" // add Bun global
|
||||
]
|
||||
}
|
||||
}
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleDetection": "force",
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
"composite": true,
|
||||
"strict": true,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "react-jsx",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowJs": true,
|
||||
"types": [
|
||||
"bun-types" // add Bun global
|
||||
]
|
||||
}
|
||||
}
|
||||
|
64
types/db.ts
64
types/db.ts
@ -1,44 +1,44 @@
|
||||
import { RecordModel } from "pocketbase";
|
||||
import { RecordModel } from "pocketbase"
|
||||
|
||||
export namespace DB {
|
||||
export interface AppInfo extends RecordModel {
|
||||
name: string;
|
||||
app_id: string;
|
||||
app_secret: string;
|
||||
app_name: string;
|
||||
avatar_url: string;
|
||||
description: string;
|
||||
tenant_access_token: string;
|
||||
name: string
|
||||
app_id: string
|
||||
app_secret: string
|
||||
app_name: string
|
||||
avatar_url: string
|
||||
description: string
|
||||
tenant_access_token: string
|
||||
}
|
||||
|
||||
export interface ApiKey extends RecordModel {
|
||||
name: string;
|
||||
user: string;
|
||||
app: string;
|
||||
apply_reason: string;
|
||||
name: string
|
||||
user: string
|
||||
app: string
|
||||
apply_reason: string
|
||||
}
|
||||
|
||||
export interface MessageGroup extends RecordModel {
|
||||
desc: string;
|
||||
id: string;
|
||||
name: string;
|
||||
email?: string[];
|
||||
chat_id?: string[];
|
||||
open_id?: string[];
|
||||
union_id?: string[];
|
||||
user_id?: string[];
|
||||
desc: string
|
||||
id: string
|
||||
name: string
|
||||
email?: string[]
|
||||
chat_id?: string[]
|
||||
open_id?: string[]
|
||||
union_id?: string[]
|
||||
user_id?: string[]
|
||||
}
|
||||
|
||||
export interface MessageLog extends RecordModel {
|
||||
api_key: string;
|
||||
group_id?: string;
|
||||
receive_id?: string;
|
||||
receive_id_type?: string;
|
||||
msg_type: string;
|
||||
content: string;
|
||||
final_content?: string;
|
||||
send_result?: any;
|
||||
error?: string;
|
||||
api_key: string
|
||||
group_id?: string
|
||||
receive_id?: string
|
||||
receive_id_type?: string
|
||||
msg_type: string
|
||||
content: string
|
||||
final_content?: string
|
||||
send_result?: any
|
||||
error?: string
|
||||
}
|
||||
|
||||
export type MessageLogCreate = Pick<
|
||||
@ -52,8 +52,8 @@ export namespace DB {
|
||||
| "final_content"
|
||||
| "send_result"
|
||||
| "error"
|
||||
>;
|
||||
>
|
||||
|
||||
export type Log = MessageLogCreate;
|
||||
export type LogCollection = "message_log";
|
||||
export type Log = MessageLogCreate
|
||||
export type LogCollection = "message_log"
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { DB } from "./db";
|
||||
import type { LarkAction } from "./larkAction";
|
||||
import type { LarkEvent } from "./larkEvent";
|
||||
import type { LarkServer } from "./larkServer";
|
||||
import type { MsgProxy } from "./msgProxy";
|
||||
import type { DB } from "./db"
|
||||
import type { LarkAction } from "./larkAction"
|
||||
import type { LarkEvent } from "./larkEvent"
|
||||
import type { LarkServer } from "./larkServer"
|
||||
import type { MsgProxy } from "./msgProxy"
|
||||
|
||||
export { DB, LarkAction, LarkEvent, LarkServer, MsgProxy };
|
||||
export { DB, LarkAction, LarkEvent, LarkServer, MsgProxy }
|
||||
|
@ -3,32 +3,32 @@ export namespace LarkAction {
|
||||
/**
|
||||
* open_id
|
||||
*/
|
||||
open_id: string;
|
||||
open_id: string
|
||||
/**
|
||||
* 用户名
|
||||
* @example zhaoyingbo
|
||||
*/
|
||||
user_id: string;
|
||||
user_id: string
|
||||
/**
|
||||
* 当前消息的ID
|
||||
* @example om_038fc0eceed6224a1abc1cdaa4266405
|
||||
*/
|
||||
open_message_id: string;
|
||||
open_message_id: string
|
||||
/**
|
||||
* 对话流ID
|
||||
* @example oc_433b1cb7a9dbb7ebe70a4e1a59cb8bb1
|
||||
*/
|
||||
open_chat_id: string;
|
||||
open_chat_id: string
|
||||
/**
|
||||
* 应用ID
|
||||
* @example 2ee61fe50f4f1657
|
||||
*/
|
||||
tenant_key: string;
|
||||
tenant_key: string
|
||||
/**
|
||||
* token
|
||||
* @example tV9djUKSjzVnekV7xTg2Od06NFTcsBnj
|
||||
*/
|
||||
token: string;
|
||||
token: string
|
||||
/**
|
||||
* 事件结果
|
||||
*/
|
||||
@ -36,21 +36,21 @@ export namespace LarkAction {
|
||||
/**
|
||||
* 传的参数
|
||||
*/
|
||||
value: any;
|
||||
value: any
|
||||
/**
|
||||
* 标签名
|
||||
* @example picker_datetime
|
||||
*/
|
||||
tag: string;
|
||||
tag: string
|
||||
/**
|
||||
* 选择的事件
|
||||
* @example 2023-09-03 10:35 +0800
|
||||
*/
|
||||
option: string;
|
||||
option: string
|
||||
/**
|
||||
* 时区
|
||||
*/
|
||||
timezone: string;
|
||||
};
|
||||
timezone: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,32 +7,32 @@ export namespace LarkEvent {
|
||||
* 事件ID
|
||||
* @example 0f8ab23b60993cf8dd15c8cde4d7b0f5
|
||||
*/
|
||||
event_id: string;
|
||||
event_id: string
|
||||
/**
|
||||
* token
|
||||
* @example tV9djUKSjzVnekV7xTg2Od06NFTcsBnj
|
||||
*/
|
||||
token: string;
|
||||
token: string
|
||||
/**
|
||||
* 创建时间戳
|
||||
* @example 1693565712117
|
||||
*/
|
||||
create_time: string;
|
||||
create_time: string
|
||||
/**
|
||||
* 事件类型
|
||||
* @example im.message.receive_v1
|
||||
*/
|
||||
event_type: string;
|
||||
event_type: string
|
||||
/**
|
||||
* tenant_key
|
||||
* @example 2ee61fe50f4f1657
|
||||
*/
|
||||
tenant_key: string;
|
||||
tenant_key: string
|
||||
/**
|
||||
* app_id
|
||||
* @example cli_a1eff35b43b89063
|
||||
*/
|
||||
app_id: string;
|
||||
app_id: string
|
||||
}
|
||||
/**
|
||||
* 被AT的人的信息
|
||||
@ -41,22 +41,22 @@ export namespace LarkEvent {
|
||||
/**
|
||||
* 被艾特的人的ID信息
|
||||
*/
|
||||
id: UserIdInfo;
|
||||
id: UserIdInfo
|
||||
/**
|
||||
* 对应到文本内的内容
|
||||
* @example "@_user_1"
|
||||
*/
|
||||
key: string;
|
||||
key: string
|
||||
/**
|
||||
* 用户名
|
||||
* @example 小煎蛋
|
||||
*/
|
||||
name: string;
|
||||
name: string
|
||||
/**
|
||||
* 应用ID
|
||||
* @example 2ee61fe50f4f1657
|
||||
*/
|
||||
tenant_key: string;
|
||||
tenant_key: string
|
||||
}
|
||||
/**
|
||||
* 消息内容信息
|
||||
@ -66,36 +66,36 @@ export namespace LarkEvent {
|
||||
* 对话流ID
|
||||
* @example oc_433b1cb7a9dbb7ebe70a4e1a59cb8bb1
|
||||
*/
|
||||
chat_id: string;
|
||||
chat_id: string
|
||||
/**
|
||||
* 消息类型
|
||||
* @example group | p2p
|
||||
*/
|
||||
chat_type: "group" | "p2p";
|
||||
chat_type: "group" | "p2p"
|
||||
/**
|
||||
* JSON字符串文本内容
|
||||
* @example "{\"text\":\"@_user_1 测试\"}"
|
||||
*/
|
||||
content: string;
|
||||
content: string
|
||||
/**
|
||||
* 消息发送时间戳
|
||||
* @example 1693565711996
|
||||
*/
|
||||
create_time: string;
|
||||
create_time: string
|
||||
/**
|
||||
* 被艾特的人信息
|
||||
*/
|
||||
mentions?: Mention[];
|
||||
mentions?: Mention[]
|
||||
/**
|
||||
* 当前消息的ID
|
||||
* @example om_038fc0eceed6224a1abc1cdaa4266405
|
||||
*/
|
||||
message_id: string;
|
||||
message_id: string
|
||||
/**
|
||||
* 消息类型
|
||||
* @example text、post、image、file、audio、media、sticker、interactive、share_chat、share_user
|
||||
*/
|
||||
message_type: string;
|
||||
message_type: string
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,17 +106,17 @@ export namespace LarkEvent {
|
||||
* 用户标记
|
||||
* @example ou_032f507d08f9a7f28b042fcd086daef5
|
||||
*/
|
||||
open_id: string;
|
||||
open_id: string
|
||||
/**
|
||||
* 用户标记
|
||||
* @example on_7111660fddd8302ce47bf1999147c011
|
||||
*/
|
||||
union_id: string;
|
||||
union_id: string
|
||||
/**
|
||||
* 用户名
|
||||
* @example zhaoyingbo
|
||||
*/
|
||||
user_id: string;
|
||||
user_id: string
|
||||
}
|
||||
/**
|
||||
* 消息发送者信息
|
||||
@ -125,24 +125,24 @@ export namespace LarkEvent {
|
||||
/**
|
||||
* id 相关信息
|
||||
*/
|
||||
sender_id: UserIdInfo;
|
||||
sender_id: UserIdInfo
|
||||
/**
|
||||
* 发送者类型
|
||||
* @example user
|
||||
*/
|
||||
sender_type: string;
|
||||
sender_type: string
|
||||
/**
|
||||
* 应用ID
|
||||
* @example 2ee61fe50f4f1657
|
||||
*/
|
||||
tenant_key: string;
|
||||
tenant_key: string
|
||||
}
|
||||
/**
|
||||
* 事件详情
|
||||
*/
|
||||
export interface Event {
|
||||
message: Message;
|
||||
sender: Sender;
|
||||
message: Message
|
||||
sender: Sender
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
@ -150,14 +150,14 @@ export namespace LarkEvent {
|
||||
* 协议版本
|
||||
* @example 2.0
|
||||
*/
|
||||
schema: string;
|
||||
schema: string
|
||||
/**
|
||||
* 事件头
|
||||
*/
|
||||
header: Header;
|
||||
header: Header
|
||||
/**
|
||||
* 事件详情
|
||||
*/
|
||||
event: Event;
|
||||
event: Event
|
||||
}
|
||||
}
|
||||
|
@ -3,88 +3,88 @@ export namespace LarkServer {
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
access_token: string;
|
||||
access_token: string
|
||||
|
||||
/**
|
||||
* 员工ID
|
||||
*/
|
||||
employee_id: string;
|
||||
employee_id: string
|
||||
|
||||
/**
|
||||
* 令牌过期时间
|
||||
*/
|
||||
expires_in: number;
|
||||
expires_in: number
|
||||
|
||||
/**
|
||||
* 开放ID
|
||||
*/
|
||||
open_id: string;
|
||||
open_id: string
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
refresh_token: string;
|
||||
refresh_token: string
|
||||
|
||||
/**
|
||||
* 会话密钥
|
||||
*/
|
||||
session_key: string;
|
||||
session_key: string
|
||||
|
||||
/**
|
||||
* 租户密钥
|
||||
*/
|
||||
tenant_key: string;
|
||||
tenant_key: string
|
||||
|
||||
/**
|
||||
* 联盟ID
|
||||
*/
|
||||
union_id: string;
|
||||
union_id: string
|
||||
}
|
||||
|
||||
export interface SuccessDocMeta {
|
||||
doc_token: string;
|
||||
doc_type: string;
|
||||
title: string;
|
||||
owner_id: string;
|
||||
create_time: string;
|
||||
latest_modify_user: string;
|
||||
latest_modify_time: string;
|
||||
url: string;
|
||||
sec_label_name: string;
|
||||
doc_token: string
|
||||
doc_type: string
|
||||
title: string
|
||||
owner_id: string
|
||||
create_time: string
|
||||
latest_modify_user: string
|
||||
latest_modify_time: string
|
||||
url: string
|
||||
sec_label_name: string
|
||||
}
|
||||
|
||||
export interface FailedDocMeta {
|
||||
token: string;
|
||||
code: number;
|
||||
token: string
|
||||
code: number
|
||||
}
|
||||
|
||||
export interface BaseRes {
|
||||
code: number;
|
||||
data: any;
|
||||
msg: string;
|
||||
code: number
|
||||
data: any
|
||||
msg: string
|
||||
}
|
||||
|
||||
export interface UserSessionRes extends BaseRes {
|
||||
data: UserSession;
|
||||
data: UserSession
|
||||
}
|
||||
|
||||
export interface UserInfoRes extends BaseRes {
|
||||
data: {
|
||||
user: any;
|
||||
};
|
||||
user: any
|
||||
}
|
||||
}
|
||||
|
||||
export interface BatchUserInfoRes extends BaseRes {
|
||||
data: {
|
||||
items: any[];
|
||||
};
|
||||
items: any[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface BatchDocMetaRes extends BaseRes {
|
||||
data: {
|
||||
metas: SuccessDocMeta[];
|
||||
failed_list: FailedDocMeta[];
|
||||
};
|
||||
metas: SuccessDocMeta[]
|
||||
failed_list: FailedDocMeta[]
|
||||
}
|
||||
}
|
||||
|
||||
export type ReceiveIDType =
|
||||
@ -92,7 +92,7 @@ export namespace LarkServer {
|
||||
| "user_id"
|
||||
| "union_id"
|
||||
| "email"
|
||||
| "chat_id";
|
||||
| "chat_id"
|
||||
|
||||
export type MsgType =
|
||||
| "text"
|
||||
@ -104,5 +104,5 @@ export namespace LarkServer {
|
||||
| "sticker"
|
||||
| "interactive"
|
||||
| "share_chat"
|
||||
| "share_user";
|
||||
| "share_user"
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
import { LarkServer } from "./larkServer";
|
||||
import { LarkServer } from "./larkServer"
|
||||
|
||||
export namespace MsgProxy {
|
||||
export interface BaseBody {
|
||||
msg_type: LarkServer.MsgType;
|
||||
content: string;
|
||||
api_key: string;
|
||||
msg_type: LarkServer.MsgType
|
||||
content: string
|
||||
api_key: string
|
||||
}
|
||||
export interface GroupBody extends BaseBody {
|
||||
group_id: string;
|
||||
group_id: string
|
||||
}
|
||||
export interface NormalBody extends BaseBody {
|
||||
receive_id: string;
|
||||
receive_id_type: LarkServer.ReceiveIDType;
|
||||
receive_id: string
|
||||
receive_id_type: LarkServer.ReceiveIDType
|
||||
}
|
||||
export type Body = GroupBody & NormalBody;
|
||||
export type Body = GroupBody & NormalBody
|
||||
}
|
||||
|
@ -5,20 +5,20 @@ export interface User {
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
id: string;
|
||||
id: string
|
||||
/**
|
||||
* 用户名
|
||||
* @example zhaoyingbo
|
||||
*/
|
||||
userId: string;
|
||||
userId: string
|
||||
/**
|
||||
* open_id
|
||||
*/
|
||||
openId: string;
|
||||
openId: string
|
||||
/**
|
||||
* 提醒列表
|
||||
*/
|
||||
remindList: string[];
|
||||
remindList: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
@ -28,31 +28,31 @@ export interface Remind {
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
id: string;
|
||||
id: string
|
||||
/**
|
||||
* 所有者信息,绑定用户表的id
|
||||
*/
|
||||
owner: string;
|
||||
owner: string
|
||||
/**
|
||||
* 消息Id
|
||||
*/
|
||||
messageId: string;
|
||||
messageId: string
|
||||
/**
|
||||
* 接收者类型
|
||||
*/
|
||||
subscriberType: "open_id" | "user_id" | "union_id" | "email" | "chat_id";
|
||||
subscriberType: "open_id" | "user_id" | "union_id" | "email" | "chat_id"
|
||||
/**
|
||||
* 接收者Id
|
||||
*/
|
||||
subscriberId: string;
|
||||
subscriberId: string
|
||||
/**
|
||||
* 是否需要回复,不需要回复的也不会重复提醒
|
||||
*/
|
||||
needReply: boolean;
|
||||
needReply: boolean
|
||||
/**
|
||||
* 延迟时间
|
||||
*/
|
||||
delayTime: number;
|
||||
delayTime: number
|
||||
/**
|
||||
* 卡片信息,用于绘制初始卡片、确认卡片、取消卡片、延迟卡片
|
||||
*/
|
||||
@ -60,28 +60,28 @@ export interface Remind {
|
||||
/**
|
||||
* 提醒标题,必须要有
|
||||
*/
|
||||
title: string;
|
||||
title: string
|
||||
/**
|
||||
* 插图key
|
||||
*/
|
||||
imageKey?: string;
|
||||
imageKey?: string
|
||||
/**
|
||||
* 提醒内容,为空不显示
|
||||
*/
|
||||
content?: string;
|
||||
content?: string
|
||||
/**
|
||||
* 确认文本,为空不显示,为需要回复卡片时,如果为空则默认为“完成”
|
||||
*/
|
||||
confirmText?: string;
|
||||
confirmText?: string
|
||||
/**
|
||||
* 取消文本,为空不显示
|
||||
*/
|
||||
cancelText?: string;
|
||||
cancelText?: string
|
||||
/**
|
||||
* 延迟文本,为空不显示
|
||||
*/
|
||||
delayText?: string;
|
||||
} | null;
|
||||
delayText?: string
|
||||
} | null
|
||||
/**
|
||||
* 卡片模板信息
|
||||
*/
|
||||
@ -91,7 +91,7 @@ export interface Remind {
|
||||
* ${owner} 所有者
|
||||
* ${remindTime} 提醒时间
|
||||
*/
|
||||
pendingTemplateId: string;
|
||||
pendingTemplateId: string
|
||||
/**
|
||||
* 交互之后的卡片模板ID,如果有这个就不会用下边三个但是都会注入变量
|
||||
* ${owner} 所有者
|
||||
@ -99,38 +99,38 @@ export interface Remind {
|
||||
* ${result} 交互结果,会读卡片按钮绑定的变量text,如果没有则是绑定的result对应的 已确认、已取消、已延迟
|
||||
* ${interactTime} 交互时间
|
||||
*/
|
||||
interactedTemplateId: string;
|
||||
interactedTemplateId: string
|
||||
/**
|
||||
* 确认之后的卡片模板ID
|
||||
*/
|
||||
confirmedTemplateId: string;
|
||||
confirmedTemplateId: string
|
||||
/**
|
||||
* 取消之后的卡片模板ID
|
||||
*/
|
||||
cancelededTemplateId: string;
|
||||
cancelededTemplateId: string
|
||||
/**
|
||||
* 延迟之后的卡片模板ID
|
||||
*/
|
||||
delayedTemplateId: string;
|
||||
} | null;
|
||||
delayedTemplateId: string
|
||||
} | null
|
||||
|
||||
/**
|
||||
* 提醒时间
|
||||
*/
|
||||
remindTimes: RemindTime[];
|
||||
remindTimes: RemindTime[]
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
enabled: boolean;
|
||||
enabled: boolean
|
||||
/**
|
||||
* 下次提醒的时间,格式为yyyy-MM-dd HH:mm
|
||||
*/
|
||||
nextRemindTime: string;
|
||||
nextRemindTime: string
|
||||
/**
|
||||
* 下次提醒时间的中文
|
||||
*/
|
||||
nextRemindTimeCHS: string;
|
||||
nextRemindTimeCHS: string
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,23 +155,23 @@ export interface RemindTime {
|
||||
| "monthly"
|
||||
| "yearly"
|
||||
| "workday"
|
||||
| "holiday";
|
||||
| "holiday"
|
||||
/**
|
||||
* 提醒时间,格式为HH:mm, single类型时仅作展示用,类型为yyyy-MM-dd HH:mm
|
||||
*/
|
||||
time: string;
|
||||
time: string
|
||||
/**
|
||||
* 星期几[1-7],当frequency为weekly时有效
|
||||
*/
|
||||
daysOfWeek: number[];
|
||||
daysOfWeek: number[]
|
||||
/**
|
||||
* 每月的几号[1-31],当frequency为monthly时有效
|
||||
*/
|
||||
daysOfMonth: number[];
|
||||
daysOfMonth: number[]
|
||||
/**
|
||||
* 每年的哪天提醒,当frequency为 yearly 时有效,格式为MM-dd
|
||||
*/
|
||||
dayOfYear: string;
|
||||
dayOfYear: string
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,15 +182,15 @@ export interface RemindRecord {
|
||||
/**
|
||||
* 记录Id
|
||||
*/
|
||||
id: string;
|
||||
id: string
|
||||
/**
|
||||
* 关联的提醒Id
|
||||
*/
|
||||
remindId: string;
|
||||
remindId: string
|
||||
/**
|
||||
* 发送的卡片Id
|
||||
*/
|
||||
messageId: string;
|
||||
messageId: string
|
||||
/**
|
||||
* 提醒状态
|
||||
* pending: 待确认
|
||||
@ -198,17 +198,17 @@ export interface RemindRecord {
|
||||
* confirmed: 已确认
|
||||
* canceled: 已取消
|
||||
*/
|
||||
status: "pending" | "delayed" | "confirmed" | "canceled";
|
||||
status: "pending" | "delayed" | "confirmed" | "canceled"
|
||||
/**
|
||||
* 本次提醒时间,格式为yyyy-MM-dd HH:mm
|
||||
*/
|
||||
remindTime: string;
|
||||
remindTime: string
|
||||
/**
|
||||
* 用户交互的时间,格式为yyyy-MM-dd HH:mm
|
||||
*/
|
||||
interactTime: string;
|
||||
interactTime: string
|
||||
/**
|
||||
* 用户回答的结果,类似每天 07:00
|
||||
*/
|
||||
result: object;
|
||||
result: object
|
||||
}
|
||||
|
@ -4,15 +4,15 @@ export namespace SheetProxy {
|
||||
}
|
||||
|
||||
export interface Body {
|
||||
api_key: string;
|
||||
sheet_token: string;
|
||||
type: "insert";
|
||||
range: string;
|
||||
values: string[][]; // 二维数组
|
||||
api_key: string
|
||||
sheet_token: string
|
||||
type: "insert"
|
||||
range: string
|
||||
values: string[][] // 二维数组
|
||||
}
|
||||
|
||||
// 判断一个值是否是枚举中的值
|
||||
export const isType = (value: any): value is Type => {
|
||||
return Object.values(Type).includes(value);
|
||||
};
|
||||
return Object.values(Type).includes(value)
|
||||
}
|
||||
}
|
||||
|
@ -1,91 +1,89 @@
|
||||
import { LarkAction, LarkEvent } from "../types";
|
||||
|
||||
/**
|
||||
* 是否为事件消息
|
||||
* @param {LarkEvent.Data} body
|
||||
*/
|
||||
export const getIsEventMsg = (body: LarkEvent.Data) => {
|
||||
return body?.header?.event_type === "im.message.receive_v1";
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取事件文本类型
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns
|
||||
*/
|
||||
export const getMsgType = (body: LarkEvent.Data) => {
|
||||
return body?.event?.message?.message_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取对话流Id
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns
|
||||
*/
|
||||
export const getChatId = (body: LarkEvent.Data) => {
|
||||
return body?.event?.message?.chat_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取用户Id
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns
|
||||
*/
|
||||
export const getUserId = (body: LarkEvent.Data) => {
|
||||
return body?.event?.sender?.sender_id?.user_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否为Action消息
|
||||
* @param {LarkAction.Data} body
|
||||
*/
|
||||
export const getIsActionMsg = (body: LarkAction.Data) => {
|
||||
return body?.action;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取Action类型
|
||||
* @param {LarkAction.Data} body
|
||||
* @returns {string} Action类型
|
||||
*/
|
||||
export const getActionType = (body: LarkAction.Data) => {
|
||||
return body?.action?.tag;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取文本内容并剔除艾特信息
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns {string} 文本内容
|
||||
*/
|
||||
export const getMsgText = (body: LarkEvent.Data) => {
|
||||
try {
|
||||
const { text }: { text: string } = JSON.parse(
|
||||
body?.event?.message?.content
|
||||
);
|
||||
// 去掉@_user_1相关的内容,例如 '@_user_1 测试' -> '测试'
|
||||
const textWithoutAt = text.replace(/@_user_\d+/g, "");
|
||||
// // 去除空格和换行
|
||||
// const textWithoutSpace = textWithoutAt.replace(/[\s\n]/g, "");
|
||||
return textWithoutAt;
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取聊天类型
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns {string} 聊天类型
|
||||
*/
|
||||
export const getChatType = (body: LarkEvent.Data) => {
|
||||
return body?.event?.message?.chat_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取艾特信息
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns {Array} 艾特信息
|
||||
*/
|
||||
export const getMentions = (body: LarkEvent.Data) => {
|
||||
return body?.event?.message?.mentions;
|
||||
};
|
||||
import { LarkAction, LarkEvent } from "../types"
|
||||
|
||||
/**
|
||||
* 是否为事件消息
|
||||
* @param {LarkEvent.Data} body
|
||||
*/
|
||||
export const getIsEventMsg = (body: LarkEvent.Data) => {
|
||||
return body?.header?.event_type === "im.message.receive_v1"
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取事件文本类型
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns
|
||||
*/
|
||||
export const getMsgType = (body: LarkEvent.Data) => {
|
||||
return body?.event?.message?.message_type
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对话流Id
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns
|
||||
*/
|
||||
export const getChatId = (body: LarkEvent.Data) => {
|
||||
return body?.event?.message?.chat_id
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户Id
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns
|
||||
*/
|
||||
export const getUserId = (body: LarkEvent.Data) => {
|
||||
return body?.event?.sender?.sender_id?.user_id
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为Action消息
|
||||
* @param {LarkAction.Data} body
|
||||
*/
|
||||
export const getIsActionMsg = (body: LarkAction.Data) => {
|
||||
return body?.action
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Action类型
|
||||
* @param {LarkAction.Data} body
|
||||
* @returns {string} Action类型
|
||||
*/
|
||||
export const getActionType = (body: LarkAction.Data) => {
|
||||
return body?.action?.tag
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文本内容并剔除艾特信息
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns {string} 文本内容
|
||||
*/
|
||||
export const getMsgText = (body: LarkEvent.Data) => {
|
||||
try {
|
||||
const { text }: { text: string } = JSON.parse(body?.event?.message?.content)
|
||||
// 去掉@_user_1相关的内容,例如 '@_user_1 测试' -> '测试'
|
||||
const textWithoutAt = text.replace(/@_user_\d+/g, "")
|
||||
// // 去除空格和换行
|
||||
// const textWithoutSpace = textWithoutAt.replace(/[\s\n]/g, "");
|
||||
return textWithoutAt
|
||||
} catch (e) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取聊天类型
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns {string} 聊天类型
|
||||
*/
|
||||
export const getChatType = (body: LarkEvent.Data) => {
|
||||
return body?.event?.message?.chat_type
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取艾特信息
|
||||
* @param {LarkEvent.Data} body
|
||||
* @returns {Array} 艾特信息
|
||||
*/
|
||||
export const getMentions = (body: LarkEvent.Data) => {
|
||||
return body?.event?.message?.mentions
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
// 裁剪指定prefix路径
|
||||
export function trimPathPrefix(path: string, prefix: string): string {
|
||||
return path.startsWith(prefix) ? path.slice(prefix.length) : path;
|
||||
return path.startsWith(prefix) ? path.slice(prefix.length) : path
|
||||
}
|
||||
|
||||
export const safeJsonStringify = (obj: any) => {
|
||||
try {
|
||||
return JSON.stringify(obj);
|
||||
return JSON.stringify(obj)
|
||||
} catch (e) {
|
||||
return String(obj);
|
||||
return String(obj)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,18 +1,22 @@
|
||||
export const managePb404 = async <T>(dbFunc: Function): Promise<T | null> => {
|
||||
try {
|
||||
return await dbFunc();
|
||||
} catch (err: any) {
|
||||
if (err?.message === "The requested resource wasn't found.") {
|
||||
return null;
|
||||
} else throw err;
|
||||
}
|
||||
};
|
||||
|
||||
export const managePbError = async <T>(dbFunc: Function): Promise<T | null> => {
|
||||
try {
|
||||
return await dbFunc();
|
||||
} catch (err: any) {
|
||||
console.log("🚀 ~ managePbError ~ err:", err);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
export const managePb404 = async <T>(
|
||||
dbFunc: () => Promise<T>
|
||||
): Promise<T | null> => {
|
||||
try {
|
||||
return await dbFunc()
|
||||
} catch (err: any) {
|
||||
if (err?.message === "The requested resource wasn't found.") {
|
||||
return null
|
||||
} else throw err
|
||||
}
|
||||
}
|
||||
|
||||
export const managePbError = async <T>(
|
||||
dbFunc: () => Promise<T>
|
||||
): Promise<T | null> => {
|
||||
try {
|
||||
return await dbFunc()
|
||||
} catch (err: any) {
|
||||
console.log("🚀 ~ managePbError ~ err:", err)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user