@ardennguyen/zalo-agent-cli 2.5.0 → 2.5.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ardennguyen/zalo-agent-cli",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "CLI tool for Zalo automation — multi-account, proxy, bank transfers, QR payments, Official Account API v3.0",
5
5
  "type": "module",
6
6
  "bin": {
@@ -460,8 +460,8 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
460
460
  {
461
461
  title: "Send Zalo Message",
462
462
  description:
463
- "Send a text message, link preview card (`link`), or contact card (`cardUserId`) to a Zalo thread " +
464
- "(DM or group). threadType: 0=DM(User), 1=Group. " +
463
+ "Send a text message, file/image attachment (`filePath` or `files`), link preview card (`link`), or " +
464
+ "contact card (`cardUserId`) to a Zalo thread (DM or group). threadType: 0=DM(User), 1=Group. " +
465
465
  "To @-mention someone in a group, put `@[uid]` in the text where the tag belongs — " +
466
466
  "`@[123456]` becomes `@Their Name` and notifies them, and `@[-1]` is @All. " +
467
467
  "Use the `senderId` from zalo_get_messages or zalo_get_history as the uid; the display name " +
@@ -481,8 +481,22 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
481
481
  .min(1)
482
482
  .optional()
483
483
  .describe(
484
- "Message text to send (required unless `link` or `cardUserId` is set; optional caption when `link` is set). " +
485
- "`@[uid]` tokens expand to @-mentions; `@[-1]` is @All.",
484
+ "Message text to send (required unless `filePath`, `files`, `link`, or `cardUserId` is set; " +
485
+ "optional caption when `filePath`, `files`, or `link` is set). `@[uid]` tokens expand to @-mentions; `@[-1]` is @All.",
486
+ ),
487
+ filePath: z
488
+ .string()
489
+ .min(1)
490
+ .optional()
491
+ .describe(
492
+ "Optional local file or image path to send as an attachment (uses `text` as optional caption)",
493
+ ),
494
+ files: z
495
+ .array(z.string().min(1))
496
+ .min(1)
497
+ .optional()
498
+ .describe(
499
+ "Optional list of local file or image paths to send as attachments in one call (uses `text` as optional caption)",
486
500
  ),
487
501
  link: z
488
502
  .string()
@@ -520,17 +534,27 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
520
534
  ),
521
535
  })
522
536
  .refine(
523
- (val) => Boolean(val.text || val.link || val.cardUserId),
524
- "Provide at least one of text, link, or cardUserId",
537
+ (val) =>
538
+ Boolean(
539
+ val.text ||
540
+ val.link ||
541
+ val.cardUserId ||
542
+ val.filePath ||
543
+ (Array.isArray(val.files) && val.files.length > 0),
544
+ ),
545
+ "Provide at least one of text, filePath, files, link, or cardUserId",
525
546
  )
526
- .refine(
527
- (val) => !(val.link && val.cardUserId),
528
- "Cannot send both link and cardUserId in a single call",
529
- ),
547
+ .refine((val) => {
548
+ const hasFile = Boolean(val.filePath || (Array.isArray(val.files) && val.files.length > 0));
549
+ const modes = [Boolean(val.link), Boolean(val.cardUserId), hasFile].filter(Boolean).length;
550
+ return modes <= 1;
551
+ }, "Cannot combine link, cardUserId, and file attachments in a single call"),
530
552
  },
531
553
  async ({
532
554
  threadId: threadIdIn,
533
555
  text,
556
+ filePath,
557
+ files,
534
558
  link,
535
559
  cardUserId,
536
560
  threadType: threadTypeIn,
@@ -577,8 +601,44 @@ export function registerTools(server, api, buffer, filter, config, nameCache, ac
577
601
  });
578
602
  }
579
603
 
604
+ const rawFiles = [...(filePath ? [filePath] : []), ...(Array.isArray(files) ? files : [])];
605
+ if (rawFiles.length > 0) {
606
+ const absPaths = rawFiles.map((p) => resolve(String(p).trim()));
607
+ const missing = absPaths.filter((p) => !existsSync(p));
608
+ if (missing.length > 0) {
609
+ return err(`File not found: ${missing.join(", ")}`);
610
+ }
611
+ const result = await api.sendMessage(
612
+ { msg: text || "", attachments: absPaths },
613
+ threadId,
614
+ Number(threadType),
615
+ );
616
+ const rawAtt = result?.attachment;
617
+ const attRows = rawAtt ? (Array.isArray(rawAtt) ? rawAtt : [rawAtt]) : [];
618
+ const sent = attRows
619
+ .filter((r) => (r?.msgId ?? null) !== null)
620
+ .map((r) => ({
621
+ msgId: String(r.msgId),
622
+ cliMsgId: (r.cliMsgId ?? null) === null ? null : String(r.cliMsgId),
623
+ }));
624
+ const messageId =
625
+ result?.message?.msgId ?? result?.msgId ?? (sent.length > 0 ? sent[0].msgId : null);
626
+ const cliMsgId =
627
+ result?.message?.cliMsgId ?? result?.cliMsgId ?? (sent.length > 0 ? sent[0].cliMsgId : null);
628
+ return ok({
629
+ success: true,
630
+ kind: "attachment",
631
+ files: absPaths,
632
+ sent,
633
+ messageId,
634
+ ...(cliMsgId !== null && { cliMsgId: String(cliMsgId) }),
635
+ threadType,
636
+ ...(thread.notice && { threadId, notice: thread.notice }),
637
+ });
638
+ }
639
+
580
640
  if (!text) {
581
- return err("Provide text, link, or cardUserId.");
641
+ return err("Provide text, filePath, files, link, or cardUserId.");
582
642
  }
583
643
 
584
644
  const warnings = [];