zalo-agent-cli 1.0.25 → 1.0.27
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/README.md +26 -1
- package/package.json +1 -1
- package/src/commands/msg.js +119 -3
package/README.md
CHANGED
|
@@ -160,12 +160,13 @@ zalo-agent whoami
|
|
|
160
160
|
|
|
161
161
|
| Command | Description |
|
|
162
162
|
|---------|-------------|
|
|
163
|
-
| `msg send <threadId> <text> [-t 0\|1]` | Send text message |
|
|
163
|
+
| `msg send <threadId> <text> [-t 0\|1] [--md] [--style specs...]` | Send text message (with formatting) |
|
|
164
164
|
| `msg send-image <threadId> <paths...> [-t 0\|1] [-m caption]` | Send images |
|
|
165
165
|
| `msg send-file <threadId> <paths...> [-t 0\|1] [-m caption]` | Send files |
|
|
166
166
|
| `msg send-card <threadId> <userId> [-t 0\|1] [--phone NUM]` | Send contact card |
|
|
167
167
|
| `msg send-bank <threadId> <accountNum> -b BANK [-n name] [-t 0\|1]` | Send bank card |
|
|
168
168
|
| `msg send-qr-transfer <threadId> <accountNum> -b BANK [-a amount] [-m content] [--template tpl]` | Send VietQR transfer image |
|
|
169
|
+
| `msg send-link <threadId> <url> [-m caption] [-t 0\|1]` | Send link with auto-preview |
|
|
169
170
|
| `msg send-video <threadId> <videoUrl> --thumb <thumbUrl> [-m caption] [-d ms] [-W px] [-H px]` | Send video from URL |
|
|
170
171
|
| `msg sticker <threadId> <keyword> [-t 0\|1]` | Search and send sticker |
|
|
171
172
|
| `msg react <msgId> <threadId> <emoji> [-t 0\|1]` | React to a message |
|
|
@@ -174,6 +175,30 @@ zalo-agent whoami
|
|
|
174
175
|
|
|
175
176
|
> `-t 0` = User (default), `-t 1` = Group
|
|
176
177
|
|
|
178
|
+
**Text formatting with `--md` (markdown mode):**
|
|
179
|
+
```bash
|
|
180
|
+
zalo-agent msg send <threadId> "**Bold** *Italic* __Underline__ ~~Strike~~ {red:Red} {big:BIG}" --md
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
| Syntax | Style |
|
|
184
|
+
|--------|-------|
|
|
185
|
+
| `**text**` | Bold |
|
|
186
|
+
| `*text*` | Italic |
|
|
187
|
+
| `__text__` | Underline |
|
|
188
|
+
| `~~text~~` | Strikethrough |
|
|
189
|
+
| `{red:text}` | Red text |
|
|
190
|
+
| `{orange:text}` | Orange text |
|
|
191
|
+
| `{yellow:text}` | Yellow text |
|
|
192
|
+
| `{green:text}` | Green text |
|
|
193
|
+
| `{big:text}` | Large font |
|
|
194
|
+
| `{small:text}` | Small font |
|
|
195
|
+
|
|
196
|
+
**Manual style with `--style` (for agents/automation):**
|
|
197
|
+
```bash
|
|
198
|
+
# Format: start:len:style — style names: bold, italic, underline, strikethrough, red, orange, yellow, green, big, small
|
|
199
|
+
zalo-agent msg send <threadId> "Hello World" --style 0:5:bold 6:5:italic
|
|
200
|
+
```
|
|
201
|
+
|
|
177
202
|
#### Friends (`friend`)
|
|
178
203
|
|
|
179
204
|
| Command | Description |
|
package/package.json
CHANGED
package/src/commands/msg.js
CHANGED
|
@@ -7,16 +7,99 @@ import { resolve } from "path";
|
|
|
7
7
|
import { getApi } from "../core/zalo-client.js";
|
|
8
8
|
import { success, error, info, output } from "../utils/output.js";
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* TextStyle codes matching zca-js TextStyle enum.
|
|
12
|
+
* Used for --style option and markdown parsing.
|
|
13
|
+
*/
|
|
14
|
+
const TEXT_STYLES = {
|
|
15
|
+
bold: "b",
|
|
16
|
+
b: "b",
|
|
17
|
+
italic: "i",
|
|
18
|
+
i: "i",
|
|
19
|
+
underline: "u",
|
|
20
|
+
u: "u",
|
|
21
|
+
strikethrough: "s",
|
|
22
|
+
s: "s",
|
|
23
|
+
red: "c_db342e",
|
|
24
|
+
orange: "c_f27806",
|
|
25
|
+
yellow: "c_f7b503",
|
|
26
|
+
green: "c_15a85f",
|
|
27
|
+
small: "f_13",
|
|
28
|
+
big: "f_18",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parse markdown-like syntax from message text into plain text + styles array.
|
|
33
|
+
* Supports: **bold**, *italic*, __underline__, ~~strikethrough~~,
|
|
34
|
+
* {red:text}, {orange:text}, {green:text}, {yellow:text},
|
|
35
|
+
* {big:text}, {small:text}
|
|
36
|
+
*/
|
|
37
|
+
function parseMarkdownStyles(input) {
|
|
38
|
+
const styles = [];
|
|
39
|
+
let plain = input;
|
|
40
|
+
|
|
41
|
+
// Process markdown patterns (order matters: ** before *)
|
|
42
|
+
const patterns = [
|
|
43
|
+
{ regex: /\*\*(.+?)\*\*/g, st: "b" },
|
|
44
|
+
{ regex: /\*(.+?)\*/g, st: "i" },
|
|
45
|
+
{ regex: /__(.+?)__/g, st: "u" },
|
|
46
|
+
{ regex: /~~(.+?)~~/g, st: "s" },
|
|
47
|
+
{ regex: /\{(red|orange|yellow|green|big|small):(.+?)\}/g, st: null },
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
for (const p of patterns) {
|
|
51
|
+
let match;
|
|
52
|
+
// Re-run from scratch each time since offsets shift
|
|
53
|
+
while ((match = p.regex.exec(plain)) !== null) {
|
|
54
|
+
const fullMatch = match[0];
|
|
55
|
+
const start = match.index;
|
|
56
|
+
let content, st;
|
|
57
|
+
if (p.st === null) {
|
|
58
|
+
// Color/size pattern: {color:text}
|
|
59
|
+
st = TEXT_STYLES[match[1]];
|
|
60
|
+
content = match[2];
|
|
61
|
+
} else {
|
|
62
|
+
st = p.st;
|
|
63
|
+
content = match[1];
|
|
64
|
+
}
|
|
65
|
+
// Replace the markdown syntax with plain content
|
|
66
|
+
plain = plain.slice(0, start) + content + plain.slice(start + fullMatch.length);
|
|
67
|
+
styles.push({ start, len: content.length, st });
|
|
68
|
+
// Reset regex since string changed
|
|
69
|
+
p.regex.lastIndex = start + content.length;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { plain, styles };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Parse manual style specs: "start:len:style" → { start, len, st }
|
|
78
|
+
* Style names: bold, italic, underline, strikethrough, red, orange, yellow, green, big, small
|
|
79
|
+
*/
|
|
80
|
+
function parseStyleSpecs(specs) {
|
|
81
|
+
return specs
|
|
82
|
+
.map((spec) => {
|
|
83
|
+
const [start, len, style] = spec.split(":");
|
|
84
|
+
const st = TEXT_STYLES[style];
|
|
85
|
+
if (!st) return null;
|
|
86
|
+
return { start: Number(start), len: Number(len), st };
|
|
87
|
+
})
|
|
88
|
+
.filter(Boolean);
|
|
89
|
+
}
|
|
90
|
+
|
|
10
91
|
export function registerMsgCommands(program) {
|
|
11
92
|
const msg = program.command("msg").description("Send and manage messages");
|
|
12
93
|
|
|
13
94
|
msg.command("send <threadId> <message>")
|
|
14
|
-
.description("Send a text message")
|
|
95
|
+
.description("Send a text message with optional formatting")
|
|
15
96
|
.option("-t, --type <n>", "Thread type: 0=User, 1=Group", "0")
|
|
16
97
|
.option(
|
|
17
98
|
"--mention <specs...>",
|
|
18
99
|
"Mention users in group message. Format: pos:userId:len (e.g. 0:USER_ID:5). Use userId=-1 for @All.",
|
|
19
100
|
)
|
|
101
|
+
.option("--style <specs...>", "Text styles. Format: start:len:style (e.g. 0:5:bold 6:5:italic)")
|
|
102
|
+
.option("--md", "Parse markdown-like formatting: **bold** *italic* __underline__ ~~strike~~ {red:text}")
|
|
20
103
|
.option(
|
|
21
104
|
"--react <icon>",
|
|
22
105
|
"Auto-react to sent message. Codes: :> (haha), /-heart (heart), /-strong (like), :o (wow), :-(( (cry), :-h (angry)",
|
|
@@ -29,8 +112,27 @@ export function registerMsgCommands(program) {
|
|
|
29
112
|
return { pos: Number(pos), uid, len: Number(len) };
|
|
30
113
|
});
|
|
31
114
|
|
|
32
|
-
//
|
|
33
|
-
|
|
115
|
+
// Parse text styles
|
|
116
|
+
let styles = [];
|
|
117
|
+
let finalMsg = message;
|
|
118
|
+
|
|
119
|
+
if (opts.md) {
|
|
120
|
+
// Markdown-like parsing: **bold** *italic* __underline__ ~~strike~~
|
|
121
|
+
const parsed = parseMarkdownStyles(message);
|
|
122
|
+
finalMsg = parsed.plain;
|
|
123
|
+
styles = parsed.styles;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (opts.style) {
|
|
127
|
+
// Manual style specs: start:len:style
|
|
128
|
+
styles = styles.concat(parseStyleSpecs(opts.style));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Build message content
|
|
132
|
+
const hasExtras = mentions.length > 0 || styles.length > 0;
|
|
133
|
+
const msgContent = hasExtras
|
|
134
|
+
? { msg: finalMsg, ...(mentions.length > 0 && { mentions }), ...(styles.length > 0 && { styles }) }
|
|
135
|
+
: finalMsg;
|
|
34
136
|
|
|
35
137
|
const cliMsgId = String(Date.now());
|
|
36
138
|
const result = await getApi().sendMessage(msgContent, threadId, Number(opts.type));
|
|
@@ -217,6 +319,20 @@ export function registerMsgCommands(program) {
|
|
|
217
319
|
}
|
|
218
320
|
});
|
|
219
321
|
|
|
322
|
+
msg.command("send-link <threadId> <url>")
|
|
323
|
+
.description("Send a link with auto-preview (title, description, thumbnail)")
|
|
324
|
+
.option("-t, --type <n>", "Thread type: 0=User, 1=Group", "0")
|
|
325
|
+
.option("-m, --caption <text>", "Caption text")
|
|
326
|
+
.action(async (threadId, url, opts) => {
|
|
327
|
+
try {
|
|
328
|
+
info(`Sending link: ${url}`);
|
|
329
|
+
const result = await getApi().sendLink({ link: url, msg: opts.caption }, threadId, Number(opts.type));
|
|
330
|
+
output(result, program.opts().json, () => success(`Link sent to ${threadId}`));
|
|
331
|
+
} catch (e) {
|
|
332
|
+
error(`Send link failed: ${e.message}`);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
220
336
|
msg.command("send-video <threadId> <videoUrl>")
|
|
221
337
|
.description("Send a video from URL")
|
|
222
338
|
.requiredOption("--thumb <url>", "Thumbnail image URL")
|