feat: 优化请求处理 & 拆分Type
All checks were successful
Egg CI/CD / build-image (push) Successful in 49s
Egg CI/CD / deploy (push) Successful in 23s

This commit is contained in:
zhaoyingbo 2024-06-08 09:15:14 +00:00
parent ae199d661e
commit b7437f47e4
27 changed files with 884 additions and 720 deletions

View File

@ -1,10 +1,6 @@
import { RecordModel } from "pocketbase";
import pbClient from "../pbClient";
import { managePb404 } from "../../utils/pbTools";
export interface AppConfigRecordModel extends RecordModel {
value: string;
}
import { DB } from "../../types";
/**
*
@ -12,7 +8,7 @@ export interface AppConfigRecordModel extends RecordModel {
* @returns
*/
const get = async (key: string) => {
const config = await managePb404<AppConfigRecordModel>(
const config = await managePb404<DB.AppConfig>(
async () =>
await await pbClient.collection("config").getFirstListItem(`key='${key}'`)
);

View File

@ -1,23 +1,9 @@
import { DB } from "../../types";
import { managePb404 } from "../../utils/pbTools";
import pbClient from "../pbClient";
export interface PBMessageGroup {
collectionId: string;
collectionName: string;
updated: string;
created: string;
desc: string;
id: string;
name: string;
email?: string[];
chat_id?: string[];
open_id?: string[];
union_id?: string[];
user_id?: string[];
}
const getOne = (groupId: string) =>
managePb404<PBMessageGroup>(
managePb404<DB.MessageGroup>(
async () => await pbClient.collection("message_group").getOne(groupId)
);

View File

@ -1,5 +1,6 @@
import { manageBotReq } from "./routes/bot";
import { manageMessageReq } from "./routes/message";
import { manageMicroAppReq } from "./routes/microApp";
import { initSchedule } from "./schedule";
initSchedule();
@ -14,6 +15,8 @@ Bun.serve({
if (url.pathname === "/bot") return await manageBotReq(req);
// 消息代理发送
if (url.pathname === "/message") return await manageMessageReq(req);
// 小程序
if (url.pathname.startsWith("/micro_app")) return await manageMicroAppReq(req);
// 其他
return new Response("hello, glade to see you!");
} catch (error: any) {

View File

@ -1,9 +1,14 @@
import { sleep } from "bun";
import { getActionType, getIsActionMsg } from "../../utils/msgTools";
import { LarkUserAction } from "../../types";
import lark from "../../service/lark";
const makeChatIdCard = async (body: LarkUserAction) => {
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",
@ -25,9 +30,9 @@ const ACTION_MAP = {
/**
*
* @param {LarkUserAction} body
* @param {LarkAction.Data} body
*/
const manageBtnClick = async (body: LarkUserAction) => {
const manageBtnClick = async (body: LarkAction.Data) => {
const { action } = body?.action?.value as {
action: keyof typeof ACTION_MAP;
};
@ -37,15 +42,15 @@ const manageBtnClick = async (body: LarkUserAction) => {
const card = await func(body);
if (!card) return;
// 更新飞书的卡片
await lark.updateCard(body.open_message_id, card);
await service.lark.message.update(body.open_message_id, card);
};
/**
* Action消息
* @param {LarkUserAction} body
* @param {LarkAction.Data} body
* @returns {boolean}
*/
export const manageActionMsg = (body: LarkUserAction) => {
export const manageActionMsg = (body: LarkAction.Data) => {
// 过滤非Action消息
if (!getIsActionMsg(body)) {
return false;

View File

@ -1,6 +1,5 @@
import { fetchCIMonitor, fetchReportCollector } from "../../service";
import lark from "../../service/lark";
import { LarkMessageEvent } from "../../types";
import service from "../../services";
import { LarkEvent } from "../../types";
import {
getChatId,
getChatType,
@ -12,10 +11,10 @@ import {
/**
* P2P或者群聊并且艾特了小煎蛋
* @param {LarkMessageEvent} body
* @param {LarkEvent.Data} body
* @returns {boolean} P2P或者群聊并且艾特了小煎蛋
*/
const getIsP2pOrGroupAtBot = (body: LarkMessageEvent) => {
const getIsP2pOrGroupAtBot = (body: LarkEvent.Data) => {
const isP2p = getChatType(body) === "p2p";
const isAtBot = getMentions(body)?.some?.(
(mention) => mention.name === "小煎蛋"
@ -25,10 +24,10 @@ const getIsP2pOrGroupAtBot = (body: LarkMessageEvent) => {
/**
*
* @param {LarkMessageEvent} body
* @param {LarkEvent.Data} body
* @returns {boolean}
*/
const filterIllegalMsg = (body: LarkMessageEvent) => {
const filterIllegalMsg = (body: LarkEvent.Data) => {
// 没有chatId的消息不处理
const chatId = getChatId(body);
if (!chatId) return true;
@ -49,7 +48,7 @@ const filterIllegalMsg = (body: LarkMessageEvent) => {
// 发表情包就直接发回去
if (msgType === "sticker") {
const content = body?.event?.message?.content;
lark.sendMsg("chat_id", chatId, "sticker", content);
service.lark.message.send("chat_id", chatId, "sticker", content);
}
// 非表情包只在私聊或者群聊中艾特小煎蛋时才回复
@ -57,7 +56,7 @@ const filterIllegalMsg = (body: LarkMessageEvent) => {
const content = JSON.stringify({
text: "哇!这是什么东东?我只懂普通文本啦![可爱]",
});
lark.sendMsg("chat_id", chatId, "text", content);
service.lark.message.send("chat_id", chatId, "text", content);
}
// 非纯文本,全不放行
@ -81,7 +80,7 @@ const manageIdMsg = async (chatId: string) => {
},
},
});
lark.sendMsg("chat_id", chatId, "interactive", content);
service.lark.message.send("chat_id", chatId, "interactive", content);
};
/**
@ -89,7 +88,7 @@ const manageIdMsg = async (chatId: string) => {
* @param body -
* @returns
*/
const manageCMDMsg = (body: LarkMessageEvent) => {
const manageCMDMsg = (body: LarkEvent.Data) => {
const text = getMsgText(body);
const chatId = getChatId(body);
if (text === "/id") {
@ -97,16 +96,16 @@ const manageCMDMsg = (body: LarkMessageEvent) => {
return true;
}
if (text === "/ci") {
fetchCIMonitor(chatId);
service.attach.ciMonitor(chatId);
return true;
}
if (text.includes("share") && text.includes("简报")) {
fetchReportCollector(body);
service.attach.reportCollector(body);
// 这个用时比较久,先发一条提醒用户收到了请求
const content = JSON.stringify({
text: "正在为您收集简报,请稍等片刻~",
});
lark.sendMsg("chat_id", chatId, "text", content);
service.lark.message.send("chat_id", chatId, "text", content);
return true;
}
return false;
@ -114,9 +113,9 @@ const manageCMDMsg = (body: LarkMessageEvent) => {
/**
*
* @param {LarkMessageEvent} body
* @param {LarkEvent.Data} body
*/
const replyGuideMsg = async (body: LarkMessageEvent) => {
const replyGuideMsg = async (body: LarkEvent.Data) => {
const chatId = getChatId(body);
const content = JSON.stringify({
type: "template",
@ -131,7 +130,7 @@ const replyGuideMsg = async (body: LarkMessageEvent) => {
},
},
});
await lark.sendMsg("chat_id", chatId, "interactive", content);
await service.lark.message.send("chat_id", chatId, "interactive", content);
};
/**
@ -139,7 +138,7 @@ const replyGuideMsg = async (body: LarkMessageEvent) => {
* @param {LarkUserAction} body
* @returns {boolean}
*/
export const manageEventMsg = (body: LarkMessageEvent) => {
export const manageEventMsg = (body: LarkEvent.Data) => {
// 过滤非Event消息
if (!getIsEventMsg(body)) {
return false;

View File

@ -1,4 +1,4 @@
import { manageActionMsg } from "./activeMsg";
import { manageActionMsg } from "./actionMsg";
import { manageEventMsg } from "./eventMsg";
export const manageBotReq = async (req: Request) => {

View File

@ -1,24 +1,9 @@
import db from "../../db";
import lark from "../../service/lark";
import { MsgType, ReceiveIDType } from "../../types";
import service from "../../services";
import { LarkServer, MsgProxy } from "../../types";
interface BaseMsg {
msg_type: MsgType;
content: string;
}
interface GroupMsg extends BaseMsg {
group_id: string;
}
interface NormalMsg extends BaseMsg {
receive_id: string;
receive_id_type: ReceiveIDType;
}
type MessageReqJson = GroupMsg & NormalMsg;
const validateMessageReq = (body: MessageReqJson) => {
const validateMessageReq = (body: MsgProxy.Body) => {
if (!body.group_id && !body.receive_id) {
return new Response("group_id or receive_id is required", { status: 400 });
}
@ -35,7 +20,7 @@ const validateMessageReq = (body: MessageReqJson) => {
};
export const manageMessageReq = async (req: Request) => {
const body = (await req.json()) as MessageReqJson;
const body = (await req.json()) as MsgProxy.Body;
// 校验参数
const validateRes = validateMessageReq(body);
if (validateRes) return validateRes;
@ -68,11 +53,11 @@ export const manageMessageReq = async (req: Request) => {
const { chat_id, open_id, union_id, user_id, email } = group;
// 构造发送消息函数
const makeSendFunc = (receive_id_type: ReceiveIDType) => {
const makeSendFunc = (receive_id_type: LarkServer.ReceiveIDType) => {
return (receive_id: string) => {
sendList.push(
lark
.sendMsg(receive_id_type, receive_id, body.msg_type, finalContent)
service.lark.message
.send(receive_id_type, receive_id, body.msg_type, finalContent)
.then((res) => {
sendRes[receive_id_type][receive_id] = res;
})
@ -90,8 +75,8 @@ export const manageMessageReq = async (req: Request) => {
if (body.receive_id && body.receive_id_type) {
sendList.push(
lark
.sendMsg(
service.lark.message
.send(
body.receive_id_type,
body.receive_id,
body.msg_type,

View File

@ -1 +0,0 @@
# 批量发送消息,给已经订阅的用户和群组发送消息

7
routes/microApp/index.ts Normal file
View File

@ -0,0 +1,7 @@
export const manageMicroAppReq = async (req: Request) => {
const url = new URL(req.url);
const body = (await req.json()) as any;
console.log("🚀 ~ manageMicroAppReq ~ body:", body);
return new Response("hello, glade to see you!");
};

0
routes/microApp/login.ts Normal file
View File

View File

@ -1,37 +0,0 @@
import { LarkMessageEvent } from "../types";
/**
* CI
*/
export const fetchCIMonitor = async (chat_id: string) => {
try {
const res = await fetch(
`https://ci-monitor.xiaomiwh.cn/ci?chat_id=${chat_id}`
);
return ((await res.json()) as string) || "";
} catch {
return "";
}
};
/**
*
* @param body
* @returns
*/
export const fetchReportCollector = async (body: LarkMessageEvent) => {
const url = "https://report.imoaix.cn/report";
// 将body作为请求体post到url
try {
const res = await fetch(url, {
method: "POST",
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
},
});
return ((await res.json()) as string) || "";
} catch {
return "";
}
};

View File

@ -1,125 +0,0 @@
import db from "../db";
import { MsgType, ReceiveIDType, ServerResponse } from "../types";
/**
*
* @param func fetch
* @returns
*/
const manageFetch = async (func: Function) => {
try {
const res = await func();
const data = (await res.json()) as ServerResponse;
console.log("🚀 ~ manageFetch ~ data:", data);
return data;
} catch (error) {
console.log("🚀 ~ manageFetch ~ error:", error);
return {
code: 1,
data: null,
msg: "sendMsg fetch error",
};
}
};
/**
* header
* @returns header
*/
const getHeaders = async () => {
const tenant_access_token = await db.tenantAccessToken.get();
return {
"Content-Type": "application/json",
Authorization: `Bearer ${tenant_access_token}`,
};
};
/**
*
* @param {ReceiveIDType} receive_id_type id类型 open_id/user_id/union_id/email/chat_id
* @param {string} receive_id IDID类型应与查询参数receive_id_type
* @param {MsgType} msg_type textpostimagefileaudiomediastickerinteractiveshare_chatshare_user
* @param {string} content JSON结构序列化后的字符串msg_type对应不同内容
*/
const sendMsg = async (
receive_id_type: ReceiveIDType,
receive_id: string,
msg_type: MsgType,
content: string
) => {
const URL = `https://open.f.mioffice.cn/open-apis/im/v1/messages?receive_id_type=${receive_id_type}`;
const headers = await getHeaders();
return await manageFetch(() =>
fetch(URL, {
method: "POST",
headers,
body: JSON.stringify({ receive_id, msg_type, content }),
})
);
};
/**
*
* @param {string} message_id id
* @param {string} content JSON结构序列化后的字符串msg_type对应不同内容
*/
const updateCard = async (message_id: string, content: string) => {
const URL = `https://open.f.mioffice.cn/open-apis/im/v1/messages/${message_id}`;
const headers = await getHeaders();
return await manageFetch(() =>
fetch(URL, {
method: "PATCH",
headers,
body: JSON.stringify({ content }),
})
);
};
/**
*
* @param {string} chat_id ID
* @param {string} open_id ID
* @param {MsgType} msg_type textpostimagefileaudiomediastickerinteractiveshare_chatshare_user
* @param {*} card String
*/
const sendEphemeralMsg = async (
chat_id: string,
open_id: string,
msg_type: MsgType,
card: any
) => {
const URL = `https://open.f.mioffice.cn/open-apis/ephemeral/v1/send`;
const headers = await getHeaders();
return await manageFetch(() =>
fetch(URL, {
method: "POST",
headers,
body: JSON.stringify({ chat_id, open_id, msg_type, card }),
})
);
};
/**
*
* @param message_id id
*/
const delEphemeralMsg = async (message_id: string) => {
const URL = `https://open.f.mioffice.cn/open-apis/ephemeral/v1/delete`;
const headers = await getHeaders();
return await manageFetch(() =>
fetch(URL, {
method: "POST",
headers,
body: JSON.stringify({ message_id }),
})
);
};
const lark = {
sendMsg,
updateCard,
sendEphemeralMsg,
delEphemeralMsg,
};
export default lark;

37
services/attach/index.ts Normal file
View File

@ -0,0 +1,37 @@
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}`;
try {
const res = await netTool.get(URL);
return (res as string) || "";
} catch {
return "";
}
};
/**
*
* @param body
* @returns
*/
const reportCollector = async (body: LarkEvent.Data) => {
const URL = "https://report.imoaix.cn/report";
try {
const res = await netTool.post(URL, body);
return (res as string) || "";
} catch {
return "";
}
};
const attach = {
ciMonitor,
reportCollector,
};
export default attach;

9
services/index.ts Normal file
View File

@ -0,0 +1,9 @@
import attach from "./attach";
import lark from "./lark";
const service = {
attach,
lark,
};
export default service;

10
services/lark/index.ts Normal file
View File

@ -0,0 +1,10 @@
import message from "./message";
import user from "./user";
const lark = {
message,
user,
};
export default lark;

View File

@ -0,0 +1,63 @@
import db from "../../db";
import { LarkServer } from "../../types";
import netTool from "../netTool";
const larkNetTool = async <T = LarkServer.BaseRes>({
url,
method,
params,
data,
headers,
}: {
url: string;
method: string;
params?: any;
data?: any;
headers?: any;
}): Promise<T> => {
const headersWithAuth = {
Authorization: `Bearer ${await db.tenantAccessToken.get()}`,
...headers,
};
return netTool<T>({
url,
method,
params,
data,
headers: headersWithAuth,
}).catch((error) => {
console.error("网络请求异常", error);
return {
code: 1,
data: null,
msg: "网络请求异常",
} as T;
});
};
larkNetTool.get = <T = LarkServer.BaseRes>(
url: string,
params?: any,
headers?: any
): Promise<T> => larkNetTool({ url, method: "get", params, headers });
larkNetTool.post = <T = LarkServer.BaseRes>(
url: string,
data?: any,
params?: any,
headers?: any
): Promise<T> => larkNetTool({ url, method: "post", data, params, headers });
larkNetTool.del = <T = LarkServer.BaseRes>(
url: string,
data: any,
headers?: any
): Promise<T> => larkNetTool({ url, method: "delete", data, headers });
larkNetTool.patch = <T = LarkServer.BaseRes>(
url: string,
data: any,
headers?: any
): Promise<T> => larkNetTool({ url, method: "patch", data, headers });
export default larkNetTool;

40
services/lark/message.ts Normal file
View File

@ -0,0 +1,40 @@
import { LarkServer } from "../../types/larkServer";
import larkNetTool from "./larkNetTool";
/**
*
* @param {LarkServer.ReceiveIDType} receive_id_type id类型 open_id/user_id/union_id/email/chat_id
* @param {string} receive_id IDID类型应与查询参数receive_id_type
* @param {MsgType} msg_type textpostimagefileaudiomediastickerinteractiveshare_chatshare_user
* @param {string} content JSON结构序列化后的字符串msg_type对应不同内容
*/
const send = async (
receive_id_type: LarkServer.ReceiveIDType,
receive_id: string,
msg_type: LarkServer.MsgType,
content: string
) => {
const URL = `https://open.f.mioffice.cn/open-apis/im/v1/messages?receive_id_type=${receive_id_type}`;
return larkNetTool.post<LarkServer.BaseRes>(URL, {
receive_id,
msg_type,
content,
});
};
/**
*
* @param {string} message_id id
* @param {string} content JSON结构序列化后的字符串msg_type对应不同内容
*/
const update = async (message_id: string, content: string) => {
const URL = `https://open.f.mioffice.cn/open-apis/im/v1/messages/${message_id}`;
return larkNetTool.patch<LarkServer.BaseRes>(URL, { content });
};
const message = {
send,
update,
};
export default message;

45
services/lark/user.ts Normal file
View File

@ -0,0 +1,45 @@
import { LarkServer } from "../../types/larkServer";
import larkNetTool from "./larkNetTool";
/**
*
* @param code
* @returns
*/
const code2Session = async (code: string) => {
const URL = `https://open.f.mioffice.cn/open-apis/mina/v2/tokenLoginValidate`;
return larkNetTool.post<LarkServer.UserSessionRes>(URL, { code });
};
/**
*
* @param user_id
* @returns
*/
const getUser = async (user_id: string) => {
const URL = `https://open.f.mioffice.cn/open-apis/contact/v3/users/${user_id}`;
return larkNetTool.get<LarkServer.UserInfoRes>(URL, {
user_id_type: "user_id",
});
};
/**
*
* @param user_ids
* @returns
*/
const getMultiUser = async (user_ids: string[]) => {
const URL = `https://open.f.mioffice.cn/open-apis/user/v1/batch_get`;
return larkNetTool.get<LarkServer.UserInfoRes>(URL, {
user_ids,
user_id_type: "user_id",
});
};
const user = {
code2Session,
getMultiUser,
getUser,
};
export default user;

53
services/netTool.ts Normal file
View File

@ -0,0 +1,53 @@
interface NetGetParams {
url: string;
method: string;
params?: any;
data?: any;
headers?: any;
}
const netTool = <T = any>({
url,
method,
params,
data,
headers,
}: NetGetParams): Promise<T> => {
let fullUrl = url;
if (params) {
const queryString = new URLSearchParams(params).toString();
fullUrl = `${url}?${queryString}`;
}
return fetch(fullUrl, {
method,
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
...headers,
},
}).then((response) => {
if (!response.ok) {
throw new Error("网络响应异常");
}
return response.json() as Promise<T>;
});
};
netTool.get = <T = any>(url: string, params?: any, headers?: any): Promise<T> =>
netTool({ url, method: "get", params, headers });
netTool.post = <T = any>(
url: string,
data?: any,
params?: any,
headers?: any
): Promise<T> => netTool({ url, method: "post", data, params, headers });
netTool.del = <T = any>(url: string, data: any, headers?: any): Promise<T> =>
netTool({ url, method: "delete", data, headers });
netTool.patch = <T = any>(url: string, data: any, headers?: any): Promise<T> =>
netTool({ url, method: "patch", data, headers });
export default netTool;

21
types/db.ts Normal file
View File

@ -0,0 +1,21 @@
import { RecordModel } from "pocketbase";
export namespace DB {
export interface AppConfig extends RecordModel {
value: string;
}
export interface MessageGroup extends RecordModel {
collectionId: string;
collectionName: string;
updated: string;
created: string;
desc: string;
id: string;
name: string;
email?: string[];
chat_id?: string[];
open_id?: string[];
union_id?: string[];
user_id?: string[];
}
}

View File

@ -1,466 +1,7 @@
/**
*
*/
export interface User {
/**
* id
*/
id: string;
/**
*
* @example zhaoyingbo
*/
userId: string;
/**
* open_id
*/
openId: string;
/**
*
*/
remindList: string[];
}
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 interface Remind {
/**
* id
*/
id: string;
/**
* id
*/
owner: string;
/**
* Id
*/
messageId: string;
/**
*
*/
subscriberType: "open_id" | "user_id" | "union_id" | "email" | "chat_id";
/**
* Id
*/
subscriberId: string;
/**
*
*/
needReply: boolean;
/**
*
*/
delayTime: number;
/**
*
*/
cardInfo: {
/**
*
*/
title: string;
/**
* key
*/
imageKey?: string;
/**
*
*/
content?: string;
/**
*
*/
confirmText?: string;
/**
*
*/
cancelText?: string;
/**
*
*/
delayText?: string;
} | null;
/**
*
*/
templateInfo: {
/**
* ID
* ${owner}
* ${remindTime}
*/
pendingTemplateId: string;
/**
* ID
* ${owner}
* ${remindTime}
* ${result} textresult对应的
* ${interactTime}
*/
interactedTemplateId: string;
/**
* ID
*/
confirmedTemplateId: string;
/**
* ID
*/
cancelededTemplateId: string;
/**
* ID
*/
delayedTemplateId: string;
} | null;
/**
*
*/
remindTimes: RemindTime[];
/**
*
*/
enabled: boolean;
/**
* yyyy-MM-dd HH:mm
*/
nextRemindTime: string;
/**
*
*/
nextRemindTimeCHS: string;
}
/**
*
*
*/
export interface RemindTime {
/**
*
* single: 一次性
* daily: 每天
* weekly: 每周
* monthly: 每月
* yearly: 每年
* workday: 工作日
* holiday: 节假日
*/
frequency:
| "single"
| "daily"
| "weekly"
| "monthly"
| "yearly"
| "workday"
| "holiday";
/**
* 格式为HH:mm single类型时仅作展示用yyyy-MM-dd HH:mm
*/
time: string;
/**
* [1-7]frequency为weekly时有效
*/
daysOfWeek: number[];
/**
* [1-31]frequency为monthly时有效
*/
daysOfMonth: number[];
/**
* frequency为 yearly MM-dd
*/
dayOfYear: string;
}
/**
*
*
*/
export interface RemindRecord {
/**
* Id
*/
id: string;
/**
* Id
*/
remindId: string;
/**
* Id
*/
messageId: string;
/**
*
* pending: 待确认
* delay: 已延迟
* confirmed: 已确认
* canceled: 已取消
*/
status: "pending" | "delayed" | "confirmed" | "canceled";
/**
* yyyy-MM-dd HH:mm
*/
remindTime: string;
/**
* yyyy-MM-dd HH:mm
*/
interactTime: string;
/**
* 07:00
*/
result: object;
}
/**
*
*/
export interface Header {
/**
* ID
* @example 0f8ab23b60993cf8dd15c8cde4d7b0f5
*/
event_id: string;
/**
* token
* @example tV9djUKSjzVnekV7xTg2Od06NFTcsBnj
*/
token: string;
/**
*
* @example 1693565712117
*/
create_time: string;
/**
*
* @example im.message.receive_v1
*/
event_type: string;
/**
* tenant_key
* @example 2ee61fe50f4f1657
*/
tenant_key: string;
/**
* app_id
* @example cli_a1eff35b43b89063
*/
app_id: string;
}
/**
* ID信息
*/
export interface UserIdInfo {
/**
*
* @example ou_032f507d08f9a7f28b042fcd086daef5
*/
open_id: string;
/**
*
* @example on_7111660fddd8302ce47bf1999147c011
*/
union_id: string;
/**
*
* @example zhaoyingbo
*/
user_id: string;
}
/**
* AT的人的信息
*/
export interface Mention {
/**
* ID信息
*/
id: UserIdInfo;
/**
*
* @example "@_user_1"
*/
key: string;
/**
*
* @example
*/
name: string;
/**
* ID
* @example 2ee61fe50f4f1657
*/
tenant_key: string;
}
/**
*
*/
export interface Message {
/**
* ID
* @example oc_433b1cb7a9dbb7ebe70a4e1a59cb8bb1
*/
chat_id: string;
/**
*
* @example group | p2p
*/
chat_type: "group" | "p2p";
/**
* JSON字符串文本内容
* @example "{\"text\":\"@_user_1 测试\"}"
*/
content: string;
/**
*
* @example 1693565711996
*/
create_time: string;
/**
*
*/
mentions?: Mention[];
/**
* ID
* @example om_038fc0eceed6224a1abc1cdaa4266405
*/
message_id: string;
/**
*
* @example textpostimagefileaudiomediastickerinteractiveshare_chatshare_user
*/
message_type: string;
}
/**
*
*/
export interface Sender {
/**
* id
*/
sender_id: UserIdInfo;
/**
*
* @example user
*/
sender_type: string;
/**
* ID
* @example 2ee61fe50f4f1657
*/
tenant_key: string;
}
/**
*
*/
export interface Event {
message: Message;
sender: Sender;
}
/**
*
*/
export interface LarkMessageEvent {
/**
*
* @example 2.0
*/
schema: string;
/**
*
*/
header: Header;
/**
*
*/
event: Event;
}
/**
* Action信息
*/
export interface LarkUserAction {
/**
* open_id
*/
open_id: string;
/**
*
* @example zhaoyingbo
*/
user_id: string;
/**
* ID
* @example om_038fc0eceed6224a1abc1cdaa4266405
*/
open_message_id: string;
/**
* ID
* @example oc_433b1cb7a9dbb7ebe70a4e1a59cb8bb1
*/
open_chat_id: string;
/**
* ID
* @example 2ee61fe50f4f1657
*/
tenant_key: string;
/**
* token
* @example tV9djUKSjzVnekV7xTg2Od06NFTcsBnj
*/
token: string;
/**
*
*/
action: {
/**
*
*/
value: any;
/**
*
* @example picker_datetime
*/
tag: string;
/**
*
* @example 2023-09-03 10:35 +0800
*/
option: string;
/**
*
*/
timezone: string;
};
}
export type ReceiveIDType =
| "open_id"
| "user_id"
| "union_id"
| "email"
| "chat_id";
export type MsgType =
| "text"
| "post"
| "image"
| "file"
| "audio"
| "media"
| "sticker"
| "interactive"
| "share_chat"
| "share_user";
export interface ServerResponse {
code: number;
data: any;
msg: string;
}
export { DB, LarkAction, LarkEvent, LarkServer, MsgProxy };

56
types/larkAction.ts Normal file
View File

@ -0,0 +1,56 @@
export namespace LarkAction {
export interface Data {
/**
* open_id
*/
open_id: string;
/**
*
* @example zhaoyingbo
*/
user_id: string;
/**
* ID
* @example om_038fc0eceed6224a1abc1cdaa4266405
*/
open_message_id: string;
/**
* ID
* @example oc_433b1cb7a9dbb7ebe70a4e1a59cb8bb1
*/
open_chat_id: string;
/**
* ID
* @example 2ee61fe50f4f1657
*/
tenant_key: string;
/**
* token
* @example tV9djUKSjzVnekV7xTg2Od06NFTcsBnj
*/
token: string;
/**
*
*/
action: {
/**
*
*/
value: any;
/**
*
* @example picker_datetime
*/
tag: string;
/**
*
* @example 2023-09-03 10:35 +0800
*/
option: string;
/**
*
*/
timezone: string;
};
}
}

163
types/larkEvent.ts Normal file
View File

@ -0,0 +1,163 @@
export namespace LarkEvent {
/**
*
*/
export interface Header {
/**
* ID
* @example 0f8ab23b60993cf8dd15c8cde4d7b0f5
*/
event_id: string;
/**
* token
* @example tV9djUKSjzVnekV7xTg2Od06NFTcsBnj
*/
token: string;
/**
*
* @example 1693565712117
*/
create_time: string;
/**
*
* @example im.message.receive_v1
*/
event_type: string;
/**
* tenant_key
* @example 2ee61fe50f4f1657
*/
tenant_key: string;
/**
* app_id
* @example cli_a1eff35b43b89063
*/
app_id: string;
}
/**
* AT的人的信息
*/
export interface Mention {
/**
* ID信息
*/
id: UserIdInfo;
/**
*
* @example "@_user_1"
*/
key: string;
/**
*
* @example
*/
name: string;
/**
* ID
* @example 2ee61fe50f4f1657
*/
tenant_key: string;
}
/**
*
*/
export interface Message {
/**
* ID
* @example oc_433b1cb7a9dbb7ebe70a4e1a59cb8bb1
*/
chat_id: string;
/**
*
* @example group | p2p
*/
chat_type: "group" | "p2p";
/**
* JSON字符串文本内容
* @example "{\"text\":\"@_user_1 测试\"}"
*/
content: string;
/**
*
* @example 1693565711996
*/
create_time: string;
/**
*
*/
mentions?: Mention[];
/**
* ID
* @example om_038fc0eceed6224a1abc1cdaa4266405
*/
message_id: string;
/**
*
* @example textpostimagefileaudiomediastickerinteractiveshare_chatshare_user
*/
message_type: string;
}
/**
* ID信息
*/
export interface UserIdInfo {
/**
*
* @example ou_032f507d08f9a7f28b042fcd086daef5
*/
open_id: string;
/**
*
* @example on_7111660fddd8302ce47bf1999147c011
*/
union_id: string;
/**
*
* @example zhaoyingbo
*/
user_id: string;
}
/**
*
*/
export interface Sender {
/**
* id
*/
sender_id: UserIdInfo;
/**
*
* @example user
*/
sender_type: string;
/**
* ID
* @example 2ee61fe50f4f1657
*/
tenant_key: string;
}
/**
*
*/
export interface Event {
message: Message;
sender: Sender;
}
export interface Data {
/**
*
* @example 2.0
*/
schema: string;
/**
*
*/
header: Header;
/**
*
*/
event: Event;
}
}

78
types/larkServer.ts Normal file
View File

@ -0,0 +1,78 @@
export namespace LarkServer {
export interface UserSession {
/**
* 访
*/
access_token: string;
/**
* ID
*/
employee_id: string;
/**
*
*/
expires_in: number;
/**
* ID
*/
open_id: string;
/**
*
*/
refresh_token: string;
/**
*
*/
session_key: string;
/**
*
*/
tenant_key: string;
/**
* ID
*/
union_id: string;
}
export interface BaseRes {
code: number;
data: any;
msg: string;
}
export interface UserSessionRes extends BaseRes {
data: UserSession;
}
export interface UserInfoRes extends BaseRes {
data: {
user: any;
};
}
export type ReceiveIDType =
| "open_id"
| "user_id"
| "union_id"
| "email"
| "chat_id";
export type MsgType =
| "text"
| "post"
| "image"
| "file"
| "audio"
| "media"
| "sticker"
| "interactive"
| "share_chat"
| "share_user";
}

16
types/msgProxy.ts Normal file
View File

@ -0,0 +1,16 @@
import { LarkServer } from "./larkServer";
export namespace MsgProxy {
export interface BaseBody {
msg_type: LarkServer.MsgType;
content: string;
}
export interface GroupBody extends BaseBody {
group_id: string;
}
export interface NormalBody extends BaseBody {
receive_id: string;
receive_id_type: LarkServer.ReceiveIDType;
}
export type Body = GroupBody & NormalBody;
}

214
types/remind.ts Normal file
View File

@ -0,0 +1,214 @@
/**
*
*/
export interface User {
/**
* id
*/
id: string;
/**
*
* @example zhaoyingbo
*/
userId: string;
/**
* open_id
*/
openId: string;
/**
*
*/
remindList: string[];
}
/**
*
*/
export interface Remind {
/**
* id
*/
id: string;
/**
* id
*/
owner: string;
/**
* Id
*/
messageId: string;
/**
*
*/
subscriberType: "open_id" | "user_id" | "union_id" | "email" | "chat_id";
/**
* Id
*/
subscriberId: string;
/**
*
*/
needReply: boolean;
/**
*
*/
delayTime: number;
/**
*
*/
cardInfo: {
/**
*
*/
title: string;
/**
* key
*/
imageKey?: string;
/**
*
*/
content?: string;
/**
*
*/
confirmText?: string;
/**
*
*/
cancelText?: string;
/**
*
*/
delayText?: string;
} | null;
/**
*
*/
templateInfo: {
/**
* ID
* ${owner}
* ${remindTime}
*/
pendingTemplateId: string;
/**
* ID
* ${owner}
* ${remindTime}
* ${result} textresult对应的
* ${interactTime}
*/
interactedTemplateId: string;
/**
* ID
*/
confirmedTemplateId: string;
/**
* ID
*/
cancelededTemplateId: string;
/**
* ID
*/
delayedTemplateId: string;
} | null;
/**
*
*/
remindTimes: RemindTime[];
/**
*
*/
enabled: boolean;
/**
* yyyy-MM-dd HH:mm
*/
nextRemindTime: string;
/**
*
*/
nextRemindTimeCHS: string;
}
/**
*
*
*/
export interface RemindTime {
/**
*
* single: 一次性
* daily: 每天
* weekly: 每周
* monthly: 每月
* yearly: 每年
* workday: 工作日
* holiday: 节假日
*/
frequency:
| "single"
| "daily"
| "weekly"
| "monthly"
| "yearly"
| "workday"
| "holiday";
/**
* 格式为HH:mm single类型时仅作展示用yyyy-MM-dd HH:mm
*/
time: string;
/**
* [1-7]frequency为weekly时有效
*/
daysOfWeek: number[];
/**
* [1-31]frequency为monthly时有效
*/
daysOfMonth: number[];
/**
* frequency为 yearly MM-dd
*/
dayOfYear: string;
}
/**
*
*
*/
export interface RemindRecord {
/**
* Id
*/
id: string;
/**
* Id
*/
remindId: string;
/**
* Id
*/
messageId: string;
/**
*
* pending: 待确认
* delay: 已延迟
* confirmed: 已确认
* canceled: 已取消
*/
status: "pending" | "delayed" | "confirmed" | "canceled";
/**
* yyyy-MM-dd HH:mm
*/
remindTime: string;
/**
* yyyy-MM-dd HH:mm
*/
interactTime: string;
/**
* 07:00
*/
result: object;
}

View File

@ -1,63 +1,63 @@
import { LarkMessageEvent, LarkUserAction } from "../types";
import { LarkAction, LarkEvent } from "../types";
/**
*
* @param {LarkMessageEvent} body
* @param {LarkEvent.Data} body
*/
export const getIsEventMsg = (body: LarkMessageEvent) => {
export const getIsEventMsg = (body: LarkEvent.Data) => {
return body?.header?.event_type === "im.message.receive_v1";
};
/**
*
* @param {LarkMessageEvent} body
* @param {LarkEvent.Data} body
* @returns
*/
export const getMsgType = (body: LarkMessageEvent) => {
export const getMsgType = (body: LarkEvent.Data) => {
return body?.event?.message?.message_type;
};
/**
* Id
* @param {LarkMessageEvent} body
* @param {LarkEvent.Data} body
* @returns
*/
export const getChatId = (body: LarkMessageEvent) => {
export const getChatId = (body: LarkEvent.Data) => {
return body?.event?.message?.chat_id;
};
/**
* Id
* @param {LarkMessageEvent} body
* @param {LarkEvent.Data} body
* @returns
*/
export const getUserId = (body: LarkMessageEvent) => {
export const getUserId = (body: LarkEvent.Data) => {
return body?.event?.sender?.sender_id?.user_id;
};
/**
* Action消息
* @param {LarkUserAction} body
* @param {LarkAction.Data} body
*/
export const getIsActionMsg = (body: LarkUserAction) => {
export const getIsActionMsg = (body: LarkAction.Data) => {
return body?.action;
};
/**
* Action类型
* @param {LarkUserAction} body
* @param {LarkAction.Data} body
* @returns {string} Action类型
*/
export const getActionType = (body: LarkUserAction) => {
export const getActionType = (body: LarkAction.Data) => {
return body?.action?.tag;
};
/**
*
* @param {LarkMessageEvent} body
* @param {LarkEvent.Data} body
* @returns {string}
*/
export const getMsgText = (body: LarkMessageEvent) => {
export const getMsgText = (body: LarkEvent.Data) => {
try {
const { text }: { text: string } = JSON.parse(
body?.event?.message?.content
@ -74,18 +74,18 @@ export const getMsgText = (body: LarkMessageEvent) => {
/**
*
* @param {LarkMessageEvent} body
* @param {LarkEvent.Data} body
* @returns {string}
*/
export const getChatType = (body: LarkMessageEvent) => {
export const getChatType = (body: LarkEvent.Data) => {
return body?.event?.message?.chat_type;
};
/**
*
* @param {LarkMessageEvent} body
* @param {LarkEvent.Data} body
* @returns {Array}
*/
export const getMentions = (body: LarkMessageEvent) => {
export const getMentions = (body: LarkEvent.Data) => {
return body?.event?.message?.mentions;
};