@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/bot.ts +77 -33
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zlr_236/popo",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "description": "OpenClaw POPO channel plugin",
6
6
  "license": "MIT",
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 (!msgType || !mediaTypes.includes(msgType) || !fileId) {
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(resFilePath, JSON.stringify(urlResult, null, 2), "utf-8");
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(`Failed to fetch file: ${response.status} ${response.statusText}`);
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.map((media) => media.contentType).filter(Boolean) as string[];
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(event: PopoMessageEvent): PopoMessageContext {
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(`popo: received message from ${ctx.senderEmail} in ${ctx.sessionId} (${ctx.chatType})`);
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 ?? cfg.messages?.groupChat?.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT
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({ cfg: popoCfg, groupId: ctx.sessionId });
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(`popo: sender ${ctx.senderEmail} not in group ${ctx.sessionId} sender allowlist`);
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 ? `group:${ctx.sessionId}` : `user:${ctx.senderEmail}`;
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 = core.channel.reply.resolveEnvelopeFormatOptions(cfg);
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 ? `${ctx.sessionId}:${ctx.senderEmail}` : ctx.senderEmail;
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 } = createPopoReplyDispatcher({
365
- cfg,
366
- agentId: route.agentId,
367
- runtime: runtime as RuntimeEnv,
368
- sessionId: ctx.sessionId,
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 } = await core.channel.reply.dispatchReplyFromConfig({
374
- ctx: ctxPayload,
375
- cfg,
376
- dispatcher,
377
- replyOptions,
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(`popo: dispatch complete (queuedFinal=${queuedFinal}, replies=${counts.final})`);
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
  }