feat: 添加事件推送处理
- 当前不处理任何事件推送
This commit is contained in:
@ -51,6 +51,14 @@ export default defineEndpoint({
|
|||||||
let replyContent;
|
let replyContent;
|
||||||
let reply;
|
let reply;
|
||||||
|
|
||||||
|
if(MsgType === 'event') {
|
||||||
|
// 不处理事件推送,回复空内容以避免重试
|
||||||
|
console.warn("Received event push, no reply sent.");
|
||||||
|
res.set('Content-Type', 'plain/text');
|
||||||
|
res.send('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (MsgType !== 'text') {
|
if (MsgType !== 'text') {
|
||||||
replyContent = "暂不支持该消息类型";
|
replyContent = "暂不支持该消息类型";
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -31,3 +31,24 @@ export function assertWechatReceivedMessageType(value: unknown, fieldName: strin
|
|||||||
throw new Error(`Expected ${fieldName} to be one of ${WECHAT_RECEIVED_MESSAGE_TYPES.join(", ")}, but got ${typeof value === 'string' ? value : typeof value}`);
|
throw new Error(`Expected ${fieldName} to be one of ${WECHAT_RECEIVED_MESSAGE_TYPES.join(", ")}, but got ${typeof value === 'string' ? value : typeof value}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const WECHAT_RECEIVED_EVENT_TYPES = [
|
||||||
|
"subscribe",
|
||||||
|
"SCAN",
|
||||||
|
"LOCATION",
|
||||||
|
"CLICK",
|
||||||
|
"VIEW"
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type WechatReceivedEventType = typeof WECHAT_RECEIVED_EVENT_TYPES[number];
|
||||||
|
|
||||||
|
/** 断言值为微信接收推送类型字面量
|
||||||
|
*
|
||||||
|
* @param value 原始值
|
||||||
|
* @param fieldName 字段名称
|
||||||
|
*/
|
||||||
|
export function assertWechatReceivedEventType(value: unknown, fieldName: string): asserts value is WechatReceivedEventType {
|
||||||
|
if (typeof value !== 'string' || !WECHAT_RECEIVED_EVENT_TYPES.includes(value as WechatReceivedEventType)) {
|
||||||
|
throw new Error(`Expected ${fieldName} to be one of ${WECHAT_RECEIVED_EVENT_TYPES.join(", ")}, but got ${typeof value === 'string' ? value : typeof value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,7 +4,9 @@ import type { Request } from "express";
|
|||||||
import type { WechatReceivedBaseMessage, WechatReceivedImageMessage, WechatReceivedLinkMessage, WechatReceivedLocationMessage, WechatReceivedMessage, WechatReceivedTextMessage, WechatReceivedVideoMessage, WechatReceivedVoiceMessage } from "../types/received-message";
|
import type { WechatReceivedBaseMessage, WechatReceivedImageMessage, WechatReceivedLinkMessage, WechatReceivedLocationMessage, WechatReceivedMessage, WechatReceivedTextMessage, WechatReceivedVideoMessage, WechatReceivedVoiceMessage } from "../types/received-message";
|
||||||
import type { WechatEncryptMessage } from "../types/encrypted-message";
|
import type { WechatEncryptMessage } from "../types/encrypted-message";
|
||||||
import { WechatXmlObject, NormalizedWechatXml } from "../types/wechat-xml";
|
import { WechatXmlObject, NormalizedWechatXml } from "../types/wechat-xml";
|
||||||
import { assertString, assertWechatReceivedMessageType } from "./assertUtils";
|
import { assertString, assertWechatReceivedEventType, assertWechatReceivedMessageType } from "./assertUtils";
|
||||||
|
import { WechatReceivedEvent } from "../types/received-event";
|
||||||
|
import { assert } from "console";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从原始XML字符串解析为微信XML对象
|
* 从原始XML字符串解析为微信XML对象
|
||||||
@ -197,30 +199,84 @@ export async function parseWechatEncryptMessage(req: Request): Promise<WechatEnc
|
|||||||
* // }
|
* // }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export async function parseDecryptedXML(raw: string): Promise<WechatReceivedMessage> {
|
export async function parseDecryptedXML(raw: string): Promise<WechatReceivedMessage | WechatReceivedEvent> {
|
||||||
// const parser = new XMLParser({
|
|
||||||
// ignoreAttributes: false,
|
|
||||||
// parseTagValue: false,
|
|
||||||
// cdataPropName: '__cdata',
|
|
||||||
// trimValues: true,
|
|
||||||
// });
|
|
||||||
// const result = parser.parse(raw);
|
|
||||||
|
|
||||||
// const xml = result.xml as WechatXmlObject;
|
|
||||||
|
|
||||||
const xml = parseRawXml(raw);
|
const xml = parseRawXml(raw);
|
||||||
|
|
||||||
// 将可能存在的 __cdata 提取
|
// 将可能存在的 __cdata 提取
|
||||||
const normalized = normalizeWechatXml(xml);
|
const normalized = normalizeWechatXml(xml);
|
||||||
|
|
||||||
const msgType = normalized.MsgType;
|
const msgType = normalized.MsgType;
|
||||||
assertString(msgType, 'MsgType');
|
let eventType: string | undefined;
|
||||||
assertWechatReceivedMessageType(msgType, 'MsgType');
|
|
||||||
|
|
||||||
// 基础字段类型断言
|
// 基础字段类型断言
|
||||||
assertString(normalized.ToUserName, 'ToUserName');
|
assertString(normalized.ToUserName, 'ToUserName');
|
||||||
assertString(normalized.FromUserName, 'FromUserName');
|
assertString(normalized.FromUserName, 'FromUserName');
|
||||||
assertString(normalized.CreateTime, 'CreateTime');
|
assertString(normalized.CreateTime, 'CreateTime');
|
||||||
|
assertString(msgType, 'MsgType');
|
||||||
|
|
||||||
|
if (msgType === 'event') {
|
||||||
|
assertString(normalized.Event, 'Event');
|
||||||
|
eventType = normalized.Event;
|
||||||
|
assertWechatReceivedEventType(eventType, 'Event');
|
||||||
|
|
||||||
|
const baseFields: Omit<WechatReceivedEvent, 'MsgType' | 'Event'> = {
|
||||||
|
ToUserName: normalized.ToUserName,
|
||||||
|
FromUserName: normalized.FromUserName,
|
||||||
|
CreateTime: Number(normalized.CreateTime),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据 Event 进行具体事件类型的构建
|
||||||
|
switch (eventType) {
|
||||||
|
case "subscribe":
|
||||||
|
return {
|
||||||
|
...baseFields,
|
||||||
|
MsgType: "event",
|
||||||
|
Event: "subscribe",
|
||||||
|
EventKey: normalized.EventKey,
|
||||||
|
Ticket: normalized.Ticket,
|
||||||
|
} satisfies WechatReceivedEvent;
|
||||||
|
case "SCAN":
|
||||||
|
return {
|
||||||
|
...baseFields,
|
||||||
|
MsgType: "event",
|
||||||
|
Event: "SCAN",
|
||||||
|
EventKey: normalized.EventKey ?? '',
|
||||||
|
Ticket: normalized.Ticket ?? '',
|
||||||
|
} satisfies WechatReceivedEvent;
|
||||||
|
case "LOCATION":
|
||||||
|
assertString(normalized.Latitude, 'Latitude');
|
||||||
|
assertString(normalized.Longitude, 'Longitude');
|
||||||
|
assertString(normalized.Precision, 'Precision');
|
||||||
|
return {
|
||||||
|
...baseFields,
|
||||||
|
MsgType: "event",
|
||||||
|
Event: "LOCATION",
|
||||||
|
Latitude: normalized.Latitude,
|
||||||
|
Longitude: normalized.Longitude,
|
||||||
|
Precision: normalized.Precision,
|
||||||
|
} satisfies WechatReceivedEvent;
|
||||||
|
case "CLICK":
|
||||||
|
assertString(normalized.EventKey, 'EventKey');
|
||||||
|
return {
|
||||||
|
...baseFields,
|
||||||
|
MsgType: "event",
|
||||||
|
Event: "CLICK",
|
||||||
|
EventKey: normalized.EventKey,
|
||||||
|
} satisfies WechatReceivedEvent;
|
||||||
|
case "VIEW":
|
||||||
|
assertString(normalized.EventKey, 'EventKey');
|
||||||
|
return {
|
||||||
|
...baseFields,
|
||||||
|
MsgType: "event",
|
||||||
|
Event: "VIEW",
|
||||||
|
EventKey: normalized.EventKey,
|
||||||
|
} satisfies WechatReceivedEvent;
|
||||||
|
default:
|
||||||
|
const _exhaustiveCheck: never = eventType;
|
||||||
|
throw new Error(`Unsupported Event type: ${_exhaustiveCheck}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assertWechatReceivedMessageType(msgType, 'MsgType');
|
||||||
assertString(normalized.MsgId, 'MsgId');
|
assertString(normalized.MsgId, 'MsgId');
|
||||||
|
|
||||||
const baseFields: Omit<WechatReceivedBaseMessage, 'MsgType'> = {
|
const baseFields: Omit<WechatReceivedBaseMessage, 'MsgType'> = {
|
||||||
@ -308,3 +364,4 @@ export async function parseDecryptedXML(raw: string): Promise<WechatReceivedMess
|
|||||||
throw new Error(`Unsupported MsgType: ${_exhaustiveCheck}`);
|
throw new Error(`Unsupported MsgType: ${_exhaustiveCheck}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user