gewe-openclaw 2026.3.23 → 2026.3.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/inbound.ts +65 -14
- package/src/monitor.ts +8 -1
- package/src/types.ts +4 -0
- package/src/xml.ts +18 -0
package/package.json
CHANGED
package/src/inbound.ts
CHANGED
|
@@ -113,6 +113,25 @@ function resolveAppMsgPlaceholder(appType?: number): string {
|
|
|
113
113
|
return typeof appType === "number" ? `<appmsg:${appType}>` : "<appmsg>";
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
function summarizeUnsupportedInboundMessage(message: GeweInboundMessage): string {
|
|
117
|
+
const preview = message.text.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
118
|
+
const parts = [
|
|
119
|
+
`msgType=${message.msgType}`,
|
|
120
|
+
`from=${message.fromId}`,
|
|
121
|
+
`to=${message.toId}`,
|
|
122
|
+
`sender=${message.senderId}`,
|
|
123
|
+
`messageId=${message.messageId}`,
|
|
124
|
+
`newMessageId=${message.newMessageId}`,
|
|
125
|
+
preview ? `text=${JSON.stringify(preview)}` : undefined,
|
|
126
|
+
];
|
|
127
|
+
return parts.filter(Boolean).join(" ");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function summarizeTextPreview(text: string): string | undefined {
|
|
131
|
+
const preview = text.replace(/\s+/g, " ").trim().slice(0, 160);
|
|
132
|
+
return preview ? JSON.stringify(preview) : undefined;
|
|
133
|
+
}
|
|
134
|
+
|
|
116
135
|
function resolveGewePairCodeCandidate(rawBody: string): string | null {
|
|
117
136
|
const trimmed = rawBody.trim();
|
|
118
137
|
if (!trimmed) return null;
|
|
@@ -342,7 +361,7 @@ function normalizeInboundEntry(params: {
|
|
|
342
361
|
const { message, runtime } = params;
|
|
343
362
|
const msgType = message.msgType;
|
|
344
363
|
if (![1, 3, 34, 43, 49].includes(msgType)) {
|
|
345
|
-
runtime.log?.(`gewe: skip unsupported
|
|
364
|
+
runtime.log?.(`gewe: skip unsupported ${summarizeUnsupportedInboundMessage(message)}`);
|
|
346
365
|
return null;
|
|
347
366
|
}
|
|
348
367
|
|
|
@@ -834,10 +853,33 @@ export async function handleGeweInboundBatch(params: {
|
|
|
834
853
|
return;
|
|
835
854
|
}
|
|
836
855
|
|
|
837
|
-
const
|
|
838
|
-
|
|
856
|
+
const route = core.channel.routing.resolveAgentRoute({
|
|
857
|
+
cfg: config as OpenClawConfig,
|
|
858
|
+
channel: CHANNEL_ID,
|
|
859
|
+
accountId: account.accountId,
|
|
860
|
+
peer: {
|
|
861
|
+
kind: isGroup ? "group" : "direct",
|
|
862
|
+
id: isGroup ? groupId ?? "" : senderId,
|
|
863
|
+
},
|
|
864
|
+
});
|
|
865
|
+
const mentionRegexes = core.channel.mentions.buildMentionRegexes(
|
|
866
|
+
config as OpenClawConfig,
|
|
867
|
+
route.agentId,
|
|
868
|
+
);
|
|
869
|
+
const nativeAtWxids = Array.from(
|
|
870
|
+
new Set(
|
|
871
|
+
entries
|
|
872
|
+
.flatMap((entry) => entry.message.atWxids ?? [])
|
|
873
|
+
.map((wxid) => wxid.trim())
|
|
874
|
+
.filter(Boolean),
|
|
875
|
+
),
|
|
876
|
+
);
|
|
877
|
+
const nativeAtAll = entries.some((entry) => entry.message.atAll === true);
|
|
878
|
+
const nativeAtTriggered = nativeAtWxids.includes(lastMessage.botWxid.trim());
|
|
879
|
+
const regexAtTriggered = mentionRegexes.length
|
|
839
880
|
? core.channel.mentions.matchesMentionPatterns(rawBodyCandidate, mentionRegexes)
|
|
840
881
|
: false;
|
|
882
|
+
const wasAtTriggered = nativeAtTriggered || regexAtTriggered;
|
|
841
883
|
const latestQuote = entries.at(-1)?.quoteDetails;
|
|
842
884
|
const wasQuoteTriggered = Boolean(
|
|
843
885
|
latestQuote &&
|
|
@@ -862,23 +904,32 @@ export async function handleGeweInboundBatch(params: {
|
|
|
862
904
|
commandAuthorized,
|
|
863
905
|
});
|
|
864
906
|
if (triggerGate.shouldSkip) {
|
|
907
|
+
const detail =
|
|
908
|
+
triggerMode === "at"
|
|
909
|
+
? [
|
|
910
|
+
`agent=${route.agentId ?? "default"}`,
|
|
911
|
+
`wasAtTriggered=${String(wasAtTriggered)}`,
|
|
912
|
+
`nativeAtTriggered=${String(nativeAtTriggered)}`,
|
|
913
|
+
`nativeAtAll=${String(nativeAtAll)}`,
|
|
914
|
+
`regexAtTriggered=${String(regexAtTriggered)}`,
|
|
915
|
+
`wasQuoteTriggered=${String(wasQuoteTriggered)}`,
|
|
916
|
+
`mentionRegexes=${JSON.stringify(mentionRegexes.map((regex) => regex.source))}`,
|
|
917
|
+
`nativeAtWxids=${JSON.stringify(nativeAtWxids)}`,
|
|
918
|
+
summarizeTextPreview(rawBodyCandidate)
|
|
919
|
+
? `rawBody=${summarizeTextPreview(rawBodyCandidate)}`
|
|
920
|
+
: undefined,
|
|
921
|
+
]
|
|
922
|
+
.filter(Boolean)
|
|
923
|
+
.join(" ")
|
|
924
|
+
: undefined;
|
|
865
925
|
runtime.log?.(
|
|
866
926
|
isGroup
|
|
867
|
-
? `gewe: drop group ${groupId} (trigger=${triggerMode})`
|
|
868
|
-
: `gewe: drop DM sender ${senderId} (trigger=${triggerMode})`,
|
|
927
|
+
? `gewe: drop group ${groupId} (trigger=${triggerMode})${detail ? ` ${detail}` : ""}`
|
|
928
|
+
: `gewe: drop DM sender ${senderId} (trigger=${triggerMode})${detail ? ` ${detail}` : ""}`,
|
|
869
929
|
);
|
|
870
930
|
return;
|
|
871
931
|
}
|
|
872
932
|
|
|
873
|
-
const route = core.channel.routing.resolveAgentRoute({
|
|
874
|
-
cfg: config as OpenClawConfig,
|
|
875
|
-
channel: CHANNEL_ID,
|
|
876
|
-
accountId: account.accountId,
|
|
877
|
-
peer: {
|
|
878
|
-
kind: isGroup ? "group" : "direct",
|
|
879
|
-
id: isGroup ? groupId ?? "" : senderId,
|
|
880
|
-
},
|
|
881
|
-
});
|
|
882
933
|
const storePath = core.channel.session.resolveStorePath(config.session?.store, {
|
|
883
934
|
agentId: route.agentId,
|
|
884
935
|
});
|
package/src/monitor.ts
CHANGED
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
GeweWebhookServerOptions,
|
|
24
24
|
ResolvedGeweAccount,
|
|
25
25
|
} from "./types.js";
|
|
26
|
+
import { extractAtUserList } from "./xml.js";
|
|
26
27
|
|
|
27
28
|
const DEFAULT_WEBHOOK_PORT = 4399;
|
|
28
29
|
const DEFAULT_WEBHOOK_HOST = "0.0.0.0";
|
|
@@ -155,6 +156,8 @@ function payloadToInboundMessage(payload: GeweCallbackPayload): GeweInboundMessa
|
|
|
155
156
|
const groupParsed = isGroupChat ? splitGroupContent(content) : { body: content };
|
|
156
157
|
const senderId = (isGroupChat ? groupParsed.senderId : fromId) ?? fromId;
|
|
157
158
|
const text = groupParsed.body?.trim() ?? "";
|
|
159
|
+
const atWxids = extractAtUserList(data.MsgSource);
|
|
160
|
+
const atAll = atWxids.includes("notify@all");
|
|
158
161
|
|
|
159
162
|
return {
|
|
160
163
|
messageId: String(msgId),
|
|
@@ -166,6 +169,8 @@ function payloadToInboundMessage(payload: GeweCallbackPayload): GeweInboundMessa
|
|
|
166
169
|
senderId,
|
|
167
170
|
senderName: resolveSenderName(data.PushContent),
|
|
168
171
|
text,
|
|
172
|
+
atWxids: atWxids.length ? atWxids : undefined,
|
|
173
|
+
atAll,
|
|
169
174
|
msgType,
|
|
170
175
|
xml: text,
|
|
171
176
|
timestamp,
|
|
@@ -178,7 +183,7 @@ export function createGeweWebhookServer(opts: GeweWebhookServerOptions): {
|
|
|
178
183
|
start: () => Promise<void>;
|
|
179
184
|
stop: () => void;
|
|
180
185
|
} {
|
|
181
|
-
const { port, host, path, mediaPath, secret, onMessage, onError, abortSignal } = opts;
|
|
186
|
+
const { port, host, path, mediaPath, secret, onRawPayload, onMessage, onError, abortSignal } = opts;
|
|
182
187
|
|
|
183
188
|
const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
|
|
184
189
|
if (req.url === HEALTH_PATH) {
|
|
@@ -230,6 +235,7 @@ export function createGeweWebhookServer(opts: GeweWebhookServerOptions): {
|
|
|
230
235
|
return;
|
|
231
236
|
}
|
|
232
237
|
|
|
238
|
+
onRawPayload?.(bodyResult.raw);
|
|
233
239
|
const payload = parseWebhookPayload(bodyResult.raw);
|
|
234
240
|
if (!payload) {
|
|
235
241
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
@@ -357,6 +363,7 @@ export async function monitorGeweProvider(
|
|
|
357
363
|
path,
|
|
358
364
|
mediaPath: shouldStartMedia ? mediaPath : undefined,
|
|
359
365
|
secret,
|
|
366
|
+
onRawPayload: (raw) => runtime.log?.(`[${account.accountId}] GeWe webhook raw: ${raw}`),
|
|
360
367
|
onMessage: async (message) => {
|
|
361
368
|
const isSelf = message.fromId === message.botWxid || message.senderId === message.botWxid;
|
|
362
369
|
if (isSelf) return;
|
package/src/types.ts
CHANGED
|
@@ -175,6 +175,7 @@ export type GeweCallbackPayload = {
|
|
|
175
175
|
ToUserName?: { string?: string };
|
|
176
176
|
MsgType?: number;
|
|
177
177
|
Content?: { string?: string };
|
|
178
|
+
MsgSource?: string;
|
|
178
179
|
CreateTime?: number;
|
|
179
180
|
PushContent?: string;
|
|
180
181
|
};
|
|
@@ -190,6 +191,8 @@ export type GeweInboundMessage = {
|
|
|
190
191
|
senderId: string;
|
|
191
192
|
senderName?: string;
|
|
192
193
|
text: string;
|
|
194
|
+
atWxids?: string[];
|
|
195
|
+
atAll?: boolean;
|
|
193
196
|
msgType: number;
|
|
194
197
|
xml?: string;
|
|
195
198
|
timestamp: number;
|
|
@@ -202,6 +205,7 @@ export type GeweWebhookServerOptions = {
|
|
|
202
205
|
path: string;
|
|
203
206
|
mediaPath?: string;
|
|
204
207
|
secret?: string;
|
|
208
|
+
onRawPayload?: (raw: string) => void;
|
|
205
209
|
onMessage: (message: GeweInboundMessage) => void | Promise<void>;
|
|
206
210
|
onError?: (error: Error) => void;
|
|
207
211
|
abortSignal?: AbortSignal;
|
package/src/xml.ts
CHANGED
|
@@ -120,6 +120,24 @@ export function extractXmlTag(xml: string, tag: string): string | undefined {
|
|
|
120
120
|
return decodeEntities(raw);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
export function extractAtUserList(xml?: string): string[] {
|
|
124
|
+
const atUserList = xml?.trim() ? extractXmlTag(xml, "atuserlist") : undefined;
|
|
125
|
+
if (!atUserList) return [];
|
|
126
|
+
|
|
127
|
+
const seen = new Set<string>();
|
|
128
|
+
const values = atUserList
|
|
129
|
+
.split(/[,\uFF0C;\s]+/)
|
|
130
|
+
.map((value) => value.trim())
|
|
131
|
+
.filter(Boolean)
|
|
132
|
+
.filter((value) => {
|
|
133
|
+
if (seen.has(value)) return false;
|
|
134
|
+
seen.add(value);
|
|
135
|
+
return true;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return values;
|
|
139
|
+
}
|
|
140
|
+
|
|
123
141
|
export function extractAppMsgType(xml: string): number | undefined {
|
|
124
142
|
const match = /<appmsg[\s\S]*?<type>(\d+)<\/type>/i.exec(xml);
|
|
125
143
|
if (!match?.[1]) return undefined;
|