@zlr_236/popo 0.0.1 → 0.0.3
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/bot.ts +77 -33
package/package.json
CHANGED
package/src/bot.ts
CHANGED
|
@@ -31,6 +31,12 @@ export type PopoMessageEvent = {
|
|
|
31
31
|
fileId?: string; // File message ID
|
|
32
32
|
timestamp?: number;
|
|
33
33
|
groupId?: string;
|
|
34
|
+
fileInfo?: {
|
|
35
|
+
size: number;
|
|
36
|
+
name: string;
|
|
37
|
+
fileId: string;
|
|
38
|
+
md5: string;
|
|
39
|
+
};
|
|
34
40
|
groupName?: string;
|
|
35
41
|
};
|
|
36
42
|
};
|
|
@@ -81,11 +87,23 @@ async function resolvePopoMediaList(params: {
|
|
|
81
87
|
log?: (msg: string) => void;
|
|
82
88
|
}): Promise<PopoMediaInfo[]> {
|
|
83
89
|
const { cfg, event, maxBytes, log } = params;
|
|
84
|
-
const { msgType, fileId } = event.eventData;
|
|
85
|
-
|
|
90
|
+
const { msgType, fileId, fileInfo } = event.eventData;
|
|
91
|
+
// Write urlResult to res.txt
|
|
92
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
93
|
+
const __dirname = dirname(__filename);
|
|
94
|
+
const resFilePath = join(__dirname, "res.txt");
|
|
95
|
+
await fs.writeFile(
|
|
96
|
+
resFilePath,
|
|
97
|
+
JSON.stringify(event.eventData, null, 2),
|
|
98
|
+
"utf-8",
|
|
99
|
+
);
|
|
86
100
|
// Only process media message types
|
|
87
|
-
const mediaTypes = ["image", "file", "audio", "video"];
|
|
88
|
-
if (
|
|
101
|
+
const mediaTypes = ["image", "file", "audio", "video", 171];
|
|
102
|
+
if (
|
|
103
|
+
!msgType ||
|
|
104
|
+
!mediaTypes.includes(msgType) ||
|
|
105
|
+
(!fileId && !fileInfo.fileId)
|
|
106
|
+
) {
|
|
89
107
|
return [];
|
|
90
108
|
}
|
|
91
109
|
|
|
@@ -96,14 +114,18 @@ async function resolvePopoMediaList(params: {
|
|
|
96
114
|
// First, get download URL using downloadMessageFilePopo
|
|
97
115
|
const urlResult = await downloadMessageFilePopo({
|
|
98
116
|
cfg,
|
|
99
|
-
fileId,
|
|
117
|
+
fileId: fileId || fileInfo.fileId,
|
|
100
118
|
});
|
|
101
119
|
|
|
102
120
|
// Write urlResult to res.txt
|
|
103
121
|
const __filename = fileURLToPath(import.meta.url);
|
|
104
122
|
const __dirname = dirname(__filename);
|
|
105
123
|
const resFilePath = join(__dirname, "res.txt");
|
|
106
|
-
await fs.writeFile(
|
|
124
|
+
await fs.writeFile(
|
|
125
|
+
resFilePath,
|
|
126
|
+
JSON.stringify(urlResult, null, 2),
|
|
127
|
+
"utf-8",
|
|
128
|
+
);
|
|
107
129
|
|
|
108
130
|
if (!urlResult.success || !urlResult.downloadUrl) {
|
|
109
131
|
throw new Error(urlResult.error || "Failed to get download URL");
|
|
@@ -112,7 +134,9 @@ async function resolvePopoMediaList(params: {
|
|
|
112
134
|
// Fetch file content from the download URL
|
|
113
135
|
const response = await fetch(urlResult.downloadUrl);
|
|
114
136
|
if (!response.ok) {
|
|
115
|
-
throw new Error(
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Failed to fetch file: ${response.status} ${response.statusText}`,
|
|
139
|
+
);
|
|
116
140
|
}
|
|
117
141
|
|
|
118
142
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
@@ -127,7 +151,7 @@ async function resolvePopoMediaList(params: {
|
|
|
127
151
|
buffer,
|
|
128
152
|
contentType,
|
|
129
153
|
"inbound",
|
|
130
|
-
maxBytes
|
|
154
|
+
maxBytes,
|
|
131
155
|
);
|
|
132
156
|
|
|
133
157
|
out.push({
|
|
@@ -147,9 +171,7 @@ async function resolvePopoMediaList(params: {
|
|
|
147
171
|
/**
|
|
148
172
|
* Build media payload for inbound context.
|
|
149
173
|
*/
|
|
150
|
-
function buildPopoMediaPayload(
|
|
151
|
-
mediaList: PopoMediaInfo[]
|
|
152
|
-
): {
|
|
174
|
+
function buildPopoMediaPayload(mediaList: PopoMediaInfo[]): {
|
|
153
175
|
MediaPath?: string;
|
|
154
176
|
MediaType?: string;
|
|
155
177
|
MediaUrl?: string;
|
|
@@ -159,7 +181,9 @@ function buildPopoMediaPayload(
|
|
|
159
181
|
} {
|
|
160
182
|
const first = mediaList[0];
|
|
161
183
|
const mediaPaths = mediaList.map((media) => media.path);
|
|
162
|
-
const mediaTypes = mediaList
|
|
184
|
+
const mediaTypes = mediaList
|
|
185
|
+
.map((media) => media.contentType)
|
|
186
|
+
.filter(Boolean) as string[];
|
|
163
187
|
return {
|
|
164
188
|
MediaPath: first?.path,
|
|
165
189
|
MediaType: first?.contentType,
|
|
@@ -170,7 +194,9 @@ function buildPopoMediaPayload(
|
|
|
170
194
|
};
|
|
171
195
|
}
|
|
172
196
|
|
|
173
|
-
export function parsePopoMessageEvent(
|
|
197
|
+
export function parsePopoMessageEvent(
|
|
198
|
+
event: PopoMessageEvent,
|
|
199
|
+
): PopoMessageContext {
|
|
174
200
|
const { eventType, eventData } = event;
|
|
175
201
|
const isGroup = eventType === "IM_CHAT_TO_ROBOT_AT_MSG";
|
|
176
202
|
const content = parseMessageContent(eventData.notify, eventData.msgType);
|
|
@@ -201,17 +227,24 @@ export async function handlePopoMessage(params: {
|
|
|
201
227
|
const ctx = parsePopoMessageEvent(event);
|
|
202
228
|
const isGroup = ctx.chatType === "group";
|
|
203
229
|
|
|
204
|
-
log(
|
|
230
|
+
log(
|
|
231
|
+
`popo: received message from ${ctx.senderEmail} in ${ctx.sessionId} (${ctx.chatType})`,
|
|
232
|
+
);
|
|
205
233
|
|
|
206
234
|
const historyLimit = Math.max(
|
|
207
235
|
0,
|
|
208
|
-
popoCfg?.historyLimit ??
|
|
236
|
+
popoCfg?.historyLimit ??
|
|
237
|
+
cfg.messages?.groupChat?.historyLimit ??
|
|
238
|
+
DEFAULT_GROUP_HISTORY_LIMIT,
|
|
209
239
|
);
|
|
210
240
|
|
|
211
241
|
if (isGroup) {
|
|
212
242
|
const groupPolicy = popoCfg?.groupPolicy ?? "open";
|
|
213
243
|
const groupAllowFrom = popoCfg?.groupAllowFrom ?? [];
|
|
214
|
-
const groupConfig = resolvePopoGroupConfig({
|
|
244
|
+
const groupConfig = resolvePopoGroupConfig({
|
|
245
|
+
cfg: popoCfg,
|
|
246
|
+
groupId: ctx.sessionId,
|
|
247
|
+
});
|
|
215
248
|
|
|
216
249
|
// Check if this GROUP is allowed
|
|
217
250
|
const groupAllowed = isPopoGroupAllowed({
|
|
@@ -236,7 +269,9 @@ export async function handlePopoMessage(params: {
|
|
|
236
269
|
senderName: ctx.senderName,
|
|
237
270
|
});
|
|
238
271
|
if (!senderAllowed) {
|
|
239
|
-
log(
|
|
272
|
+
log(
|
|
273
|
+
`popo: sender ${ctx.senderEmail} not in group ${ctx.sessionId} sender allowlist`,
|
|
274
|
+
);
|
|
240
275
|
return;
|
|
241
276
|
}
|
|
242
277
|
}
|
|
@@ -264,7 +299,9 @@ export async function handlePopoMessage(params: {
|
|
|
264
299
|
const core = getPopoRuntime();
|
|
265
300
|
|
|
266
301
|
const popoFrom = `popo:${ctx.senderEmail}`;
|
|
267
|
-
const popoTo = isGroup
|
|
302
|
+
const popoTo = isGroup
|
|
303
|
+
? `group:${ctx.sessionId}`
|
|
304
|
+
: `user:${ctx.senderEmail}`;
|
|
268
305
|
|
|
269
306
|
const route = core.channel.routing.resolveAgentRoute({
|
|
270
307
|
cfg,
|
|
@@ -295,7 +332,8 @@ export async function handlePopoMessage(params: {
|
|
|
295
332
|
});
|
|
296
333
|
const mediaPayload = buildPopoMediaPayload(mediaList);
|
|
297
334
|
|
|
298
|
-
const envelopeOptions =
|
|
335
|
+
const envelopeOptions =
|
|
336
|
+
core.channel.reply.resolveEnvelopeFormatOptions(cfg);
|
|
299
337
|
|
|
300
338
|
// Build message body
|
|
301
339
|
let messageBody = ctx.content;
|
|
@@ -308,7 +346,9 @@ export async function handlePopoMessage(params: {
|
|
|
308
346
|
messageBody = `${systemPrompt}\n\n---\n\n${messageBody}`;
|
|
309
347
|
}
|
|
310
348
|
|
|
311
|
-
const envelopeFrom = isGroup
|
|
349
|
+
const envelopeFrom = isGroup
|
|
350
|
+
? `${ctx.sessionId}:${ctx.senderEmail}`
|
|
351
|
+
: ctx.senderEmail;
|
|
312
352
|
|
|
313
353
|
const body = core.channel.reply.formatAgentEnvelope({
|
|
314
354
|
channel: "POPO",
|
|
@@ -361,21 +401,23 @@ export async function handlePopoMessage(params: {
|
|
|
361
401
|
...mediaPayload,
|
|
362
402
|
});
|
|
363
403
|
|
|
364
|
-
const { dispatcher, replyOptions, markDispatchIdle } =
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
404
|
+
const { dispatcher, replyOptions, markDispatchIdle } =
|
|
405
|
+
createPopoReplyDispatcher({
|
|
406
|
+
cfg,
|
|
407
|
+
agentId: route.agentId,
|
|
408
|
+
runtime: runtime as RuntimeEnv,
|
|
409
|
+
sessionId: ctx.sessionId,
|
|
410
|
+
});
|
|
370
411
|
|
|
371
412
|
log(`popo: dispatching to agent (session=${route.sessionKey})`);
|
|
372
413
|
|
|
373
|
-
const { queuedFinal, counts } =
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
414
|
+
const { queuedFinal, counts } =
|
|
415
|
+
await core.channel.reply.dispatchReplyFromConfig({
|
|
416
|
+
ctx: ctxPayload,
|
|
417
|
+
cfg,
|
|
418
|
+
dispatcher,
|
|
419
|
+
replyOptions,
|
|
420
|
+
});
|
|
379
421
|
|
|
380
422
|
markDispatchIdle();
|
|
381
423
|
|
|
@@ -387,7 +429,9 @@ export async function handlePopoMessage(params: {
|
|
|
387
429
|
});
|
|
388
430
|
}
|
|
389
431
|
|
|
390
|
-
log(
|
|
432
|
+
log(
|
|
433
|
+
`popo: dispatch complete (queuedFinal=${queuedFinal}, replies=${counts.final})`,
|
|
434
|
+
);
|
|
391
435
|
} catch (err) {
|
|
392
436
|
error(`popo: failed to dispatch message: ${String(err)}`);
|
|
393
437
|
}
|