weixin-mcp 1.7.3 → 1.7.5
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.en.md +51 -0
- package/README.md +51 -0
- package/dist/api.d.ts +10 -0
- package/dist/api.js +12 -0
- package/dist/cli.js +4 -1
- package/dist/messaging.js +83 -14
- package/package.json +1 -1
- package/src/api.ts +25 -0
- package/src/cli.ts +4 -1
- package/src/messaging.ts +94 -13
package/README.en.md
CHANGED
|
@@ -91,6 +91,22 @@ npx weixin-mcp send abc12 "hello"
|
|
|
91
91
|
# ✓ Resolved "abc12" → abc123xyz456@im.wechat
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
### Send Images/Files/Videos
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Send image
|
|
98
|
+
npx weixin-mcp send abc12 --image /path/to/photo.jpg
|
|
99
|
+
|
|
100
|
+
# Send file
|
|
101
|
+
npx weixin-mcp send abc12 --file /path/to/document.pdf
|
|
102
|
+
|
|
103
|
+
# Send video
|
|
104
|
+
npx weixin-mcp send abc12 --video /path/to/video.mp4
|
|
105
|
+
|
|
106
|
+
# With caption
|
|
107
|
+
npx weixin-mcp send abc12 --image /path/to/photo.jpg --caption "Check this out"
|
|
108
|
+
```
|
|
109
|
+
|
|
94
110
|
---
|
|
95
111
|
|
|
96
112
|
## 🔧 MCP Tools
|
|
@@ -143,6 +159,41 @@ Priority: `$WEIXIN_MCP_DIR` > `~/.openclaw/openclaw-weixin/` > `~/.weixin-mcp/`
|
|
|
143
159
|
|
|
144
160
|
---
|
|
145
161
|
|
|
162
|
+
## 🔀 Multi-Instance Mode
|
|
163
|
+
|
|
164
|
+
One bot = one WeChat account. For multiple accounts, run instances in different directories:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Instance A (port 3001)
|
|
168
|
+
WEIXIN_MCP_DIR=~/.weixin-mcp-a npx weixin-mcp login
|
|
169
|
+
WEIXIN_MCP_DIR=~/.weixin-mcp-a npx weixin-mcp start --port 3001
|
|
170
|
+
|
|
171
|
+
# Instance B (port 3002)
|
|
172
|
+
WEIXIN_MCP_DIR=~/.weixin-mcp-b npx weixin-mcp login
|
|
173
|
+
WEIXIN_MCP_DIR=~/.weixin-mcp-b npx weixin-mcp start --port 3002
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Claude Desktop multi-account config:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"mcpServers": {
|
|
181
|
+
"weixin-personal": {
|
|
182
|
+
"command": "npx",
|
|
183
|
+
"args": ["weixin-mcp", "start", "--port", "3001"],
|
|
184
|
+
"env": { "WEIXIN_MCP_DIR": "/Users/you/.weixin-mcp-personal" }
|
|
185
|
+
},
|
|
186
|
+
"weixin-work": {
|
|
187
|
+
"command": "npx",
|
|
188
|
+
"args": ["weixin-mcp", "start", "--port", "3002"],
|
|
189
|
+
"env": { "WEIXIN_MCP_DIR": "/Users/you/.weixin-mcp-work" }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
146
197
|
## 🔗 Related Projects
|
|
147
198
|
|
|
148
199
|
- [OpenClaw](https://github.com/anthropics/openclaw) — AI Agent Infrastructure
|
package/README.md
CHANGED
|
@@ -91,6 +91,22 @@ npx weixin-mcp send abc12 "hello"
|
|
|
91
91
|
# ✓ Resolved "abc12" → abc123xyz456@im.wechat
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
### 发送图片/文件/视频
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# 发送图片
|
|
98
|
+
npx weixin-mcp send abc12 --image /path/to/photo.jpg
|
|
99
|
+
|
|
100
|
+
# 发送文件
|
|
101
|
+
npx weixin-mcp send abc12 --file /path/to/document.pdf
|
|
102
|
+
|
|
103
|
+
# 发送视频
|
|
104
|
+
npx weixin-mcp send abc12 --video /path/to/video.mp4
|
|
105
|
+
|
|
106
|
+
# 带文字说明
|
|
107
|
+
npx weixin-mcp send abc12 --image /path/to/photo.jpg --caption "看看这张图"
|
|
108
|
+
```
|
|
109
|
+
|
|
94
110
|
---
|
|
95
111
|
|
|
96
112
|
## 🔧 MCP 工具
|
|
@@ -143,6 +159,41 @@ npx weixin-mcp start --webhook http://your-server/weixin-hook
|
|
|
143
159
|
|
|
144
160
|
---
|
|
145
161
|
|
|
162
|
+
## 🔀 多实例模式
|
|
163
|
+
|
|
164
|
+
一个 bot = 一个微信账号。需要多账号时,用不同目录运行多个实例:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# 实例 A(端口 3001)
|
|
168
|
+
WEIXIN_MCP_DIR=~/.weixin-mcp-a npx weixin-mcp login
|
|
169
|
+
WEIXIN_MCP_DIR=~/.weixin-mcp-a npx weixin-mcp start --port 3001
|
|
170
|
+
|
|
171
|
+
# 实例 B(端口 3002)
|
|
172
|
+
WEIXIN_MCP_DIR=~/.weixin-mcp-b npx weixin-mcp login
|
|
173
|
+
WEIXIN_MCP_DIR=~/.weixin-mcp-b npx weixin-mcp start --port 3002
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Claude Desktop 配置多账号:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"mcpServers": {
|
|
181
|
+
"weixin-personal": {
|
|
182
|
+
"command": "npx",
|
|
183
|
+
"args": ["weixin-mcp", "start", "--port", "3001"],
|
|
184
|
+
"env": { "WEIXIN_MCP_DIR": "/Users/you/.weixin-mcp-personal" }
|
|
185
|
+
},
|
|
186
|
+
"weixin-work": {
|
|
187
|
+
"command": "npx",
|
|
188
|
+
"args": ["weixin-mcp", "start", "--port", "3002"],
|
|
189
|
+
"env": { "WEIXIN_MCP_DIR": "/Users/you/.weixin-mcp-work" }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
146
197
|
## 🔗 相关项目
|
|
147
198
|
|
|
148
199
|
- [OpenClaw](https://github.com/anthropics/openclaw) — AI Agent 基础设施
|
package/dist/api.d.ts
CHANGED
|
@@ -53,3 +53,13 @@ export declare function sendFileMessage(to: string, uploaded: UploadedMedia, tok
|
|
|
53
53
|
* Send a video message using a previously uploaded file.
|
|
54
54
|
*/
|
|
55
55
|
export declare function sendVideoMessage(to: string, uploaded: UploadedMedia, token: string, baseUrl: string, contextToken?: string, caption?: string): Promise<void>;
|
|
56
|
+
export type MediaSendType = "image" | "file" | "video";
|
|
57
|
+
export declare function sendMediaMessage(opts: {
|
|
58
|
+
to: string;
|
|
59
|
+
mediaType: MediaSendType;
|
|
60
|
+
uploaded: UploadedMedia;
|
|
61
|
+
caption?: string;
|
|
62
|
+
token: string;
|
|
63
|
+
baseUrl: string;
|
|
64
|
+
contextToken?: string;
|
|
65
|
+
}): Promise<void>;
|
package/dist/api.js
CHANGED
|
@@ -249,3 +249,15 @@ export async function sendVideoMessage(to, uploaded, token, baseUrl, contextToke
|
|
|
249
249
|
}, token, baseUrl);
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
|
+
export async function sendMediaMessage(opts) {
|
|
253
|
+
switch (opts.mediaType) {
|
|
254
|
+
case "image":
|
|
255
|
+
return sendImageMessage(opts.to, opts.uploaded, opts.token, opts.baseUrl, opts.contextToken, opts.caption);
|
|
256
|
+
case "file":
|
|
257
|
+
return sendFileMessage(opts.to, opts.uploaded, opts.token, opts.baseUrl, opts.contextToken, opts.caption);
|
|
258
|
+
case "video":
|
|
259
|
+
return sendVideoMessage(opts.to, opts.uploaded, opts.token, opts.baseUrl, opts.contextToken, opts.caption);
|
|
260
|
+
default:
|
|
261
|
+
throw new Error(`Unknown media type: ${opts.mediaType}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -122,7 +122,10 @@ Commands:
|
|
|
122
122
|
contacts Show contact book (users who messaged the bot)
|
|
123
123
|
update Check and install latest version
|
|
124
124
|
--version / -v Print version
|
|
125
|
-
send <userId> <text> Send a message
|
|
125
|
+
send <userId> <text> Send a text message
|
|
126
|
+
send <userId> --image <path> [--caption <text>] Send an image
|
|
127
|
+
send <userId> --file <path> [--caption <text>] Send a file
|
|
128
|
+
send <userId> --video <path> [--caption <text>] Send a video
|
|
126
129
|
poll [--watch|-w] [--reset] Poll messages once, or watch continuously
|
|
127
130
|
accounts [list] List all accounts
|
|
128
131
|
accounts remove <id> Remove an account
|
package/dist/messaging.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { ACCOUNTS_DIR } from "./paths.js";
|
|
9
|
-
import { DEFAULT_BASE_URL, sendTextMessage, getUpdates, loadCursor, saveCursor, } from "./api.js";
|
|
9
|
+
import { DEFAULT_BASE_URL, sendTextMessage, sendMediaMessage, getUpdates, loadCursor, saveCursor, } from "./api.js";
|
|
10
|
+
import { uploadMedia } from "./cdn.js";
|
|
10
11
|
import { updateContactsFromMsgs, loadContacts } from "./contacts.js";
|
|
11
12
|
/** Resolve a short/partial userId to a full one from contacts. */
|
|
12
13
|
function resolveUserId(input) {
|
|
@@ -52,26 +53,94 @@ function formatMsg(msg) {
|
|
|
52
53
|
const prefix = msgType === 1 ? "← " : "→ "; // incoming vs outgoing
|
|
53
54
|
return `${prefix}${from.slice(0, 20)}: ${parts.join(" ") || "(empty)"}`;
|
|
54
55
|
}
|
|
56
|
+
function parseCliSendArgs(args) {
|
|
57
|
+
const opts = { to: "" };
|
|
58
|
+
let i = 0;
|
|
59
|
+
// First arg is always <to>
|
|
60
|
+
if (args[i] && !args[i].startsWith("--")) {
|
|
61
|
+
opts.to = args[i++];
|
|
62
|
+
}
|
|
63
|
+
while (i < args.length) {
|
|
64
|
+
const arg = args[i];
|
|
65
|
+
if (arg === "--image" && args[i + 1]) {
|
|
66
|
+
opts.image = args[++i];
|
|
67
|
+
}
|
|
68
|
+
else if (arg === "--file" && args[i + 1]) {
|
|
69
|
+
opts.file = args[++i];
|
|
70
|
+
}
|
|
71
|
+
else if (arg === "--video" && args[i + 1]) {
|
|
72
|
+
opts.video = args[++i];
|
|
73
|
+
}
|
|
74
|
+
else if (arg === "--caption" && args[i + 1]) {
|
|
75
|
+
opts.caption = args[++i];
|
|
76
|
+
}
|
|
77
|
+
else if (!arg.startsWith("--")) {
|
|
78
|
+
// Collect remaining as text
|
|
79
|
+
opts.text = args.slice(i).join(" ");
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
i++;
|
|
83
|
+
}
|
|
84
|
+
return opts;
|
|
85
|
+
}
|
|
55
86
|
export async function cliSend(args) {
|
|
56
|
-
const
|
|
57
|
-
if (!to
|
|
58
|
-
console.error(
|
|
87
|
+
const opts = parseCliSendArgs(args);
|
|
88
|
+
if (!opts.to) {
|
|
89
|
+
console.error(`Usage: npx weixin-mcp send <userId> <text>
|
|
90
|
+
npx weixin-mcp send <userId> --image <path> [--caption <text>]
|
|
91
|
+
npx weixin-mcp send <userId> --file <path> [--caption <text>]
|
|
92
|
+
npx weixin-mcp send <userId> --video <path> [--caption <text>]`);
|
|
59
93
|
process.exit(1);
|
|
60
94
|
}
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
console.log(`Resolved "${to}" → ${resolvedTo}`);
|
|
95
|
+
const resolvedTo = resolveUserId(opts.to);
|
|
96
|
+
if (resolvedTo !== opts.to)
|
|
97
|
+
console.log(`Resolved "${opts.to}" → ${resolvedTo}`);
|
|
65
98
|
const { token, baseUrl = DEFAULT_BASE_URL } = loadAccount();
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
99
|
+
// Determine what to send
|
|
100
|
+
const mediaPath = opts.image || opts.file || opts.video;
|
|
101
|
+
const mediaType = opts.image ? "image" : opts.file ? "file" : opts.video ? "video" : null;
|
|
102
|
+
if (mediaPath && mediaType) {
|
|
103
|
+
// Check file exists
|
|
104
|
+
if (!fs.existsSync(mediaPath)) {
|
|
105
|
+
console.error(`File not found: ${mediaPath}`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
process.stdout.write(`Uploading ${mediaType}... `);
|
|
109
|
+
const uploaded = await uploadMedia({
|
|
110
|
+
source: mediaPath,
|
|
111
|
+
mediaType,
|
|
112
|
+
toUserId: resolvedTo,
|
|
113
|
+
token: token,
|
|
114
|
+
baseUrl,
|
|
115
|
+
});
|
|
116
|
+
console.log("✅");
|
|
117
|
+
process.stdout.write(`Sending ${mediaType} to ${resolvedTo}... `);
|
|
118
|
+
await sendMediaMessage({
|
|
119
|
+
to: resolvedTo,
|
|
120
|
+
mediaType,
|
|
121
|
+
uploaded,
|
|
122
|
+
caption: opts.caption,
|
|
123
|
+
token: token,
|
|
124
|
+
baseUrl,
|
|
125
|
+
});
|
|
70
126
|
console.log("✅ Sent");
|
|
71
127
|
}
|
|
128
|
+
else if (opts.text) {
|
|
129
|
+
// Text message
|
|
130
|
+
process.stdout.write(`Sending to ${resolvedTo}... `);
|
|
131
|
+
const result = await sendTextMessage(resolvedTo, opts.text, token, baseUrl);
|
|
132
|
+
const ret = result?.ret ?? result?.errcode;
|
|
133
|
+
if (ret === 0 || ret === undefined) {
|
|
134
|
+
console.log("✅ Sent");
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.log(`❌ Failed (ret=${ret})`);
|
|
138
|
+
console.log(JSON.stringify(result, null, 2));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
72
141
|
else {
|
|
73
|
-
console.
|
|
74
|
-
|
|
142
|
+
console.error("Nothing to send. Provide text or --image/--file/--video");
|
|
143
|
+
process.exit(1);
|
|
75
144
|
}
|
|
76
145
|
}
|
|
77
146
|
export async function cliPoll(args) {
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -355,3 +355,28 @@ export async function sendVideoMessage(
|
|
|
355
355
|
);
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
|
+
|
|
359
|
+
// ── Unified media sender for CLI ───────────────────────────────────────────
|
|
360
|
+
|
|
361
|
+
export type MediaSendType = "image" | "file" | "video";
|
|
362
|
+
|
|
363
|
+
export async function sendMediaMessage(opts: {
|
|
364
|
+
to: string;
|
|
365
|
+
mediaType: MediaSendType;
|
|
366
|
+
uploaded: UploadedMedia;
|
|
367
|
+
caption?: string;
|
|
368
|
+
token: string;
|
|
369
|
+
baseUrl: string;
|
|
370
|
+
contextToken?: string;
|
|
371
|
+
}) {
|
|
372
|
+
switch (opts.mediaType) {
|
|
373
|
+
case "image":
|
|
374
|
+
return sendImageMessage(opts.to, opts.uploaded, opts.token, opts.baseUrl, opts.contextToken, opts.caption);
|
|
375
|
+
case "file":
|
|
376
|
+
return sendFileMessage(opts.to, opts.uploaded, opts.token, opts.baseUrl, opts.contextToken, opts.caption);
|
|
377
|
+
case "video":
|
|
378
|
+
return sendVideoMessage(opts.to, opts.uploaded, opts.token, opts.baseUrl, opts.contextToken, opts.caption);
|
|
379
|
+
default:
|
|
380
|
+
throw new Error(`Unknown media type: ${opts.mediaType}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -124,7 +124,10 @@ Commands:
|
|
|
124
124
|
contacts Show contact book (users who messaged the bot)
|
|
125
125
|
update Check and install latest version
|
|
126
126
|
--version / -v Print version
|
|
127
|
-
send <userId> <text> Send a message
|
|
127
|
+
send <userId> <text> Send a text message
|
|
128
|
+
send <userId> --image <path> [--caption <text>] Send an image
|
|
129
|
+
send <userId> --file <path> [--caption <text>] Send a file
|
|
130
|
+
send <userId> --video <path> [--caption <text>] Send a video
|
|
128
131
|
poll [--watch|-w] [--reset] Poll messages once, or watch continuously
|
|
129
132
|
accounts [list] List all accounts
|
|
130
133
|
accounts remove <id> Remove an account
|
package/src/messaging.ts
CHANGED
|
@@ -10,10 +10,12 @@ import { ACCOUNTS_DIR } from "./paths.js";
|
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_BASE_URL,
|
|
12
12
|
sendTextMessage,
|
|
13
|
+
sendMediaMessage,
|
|
13
14
|
getUpdates,
|
|
14
15
|
loadCursor,
|
|
15
16
|
saveCursor,
|
|
16
17
|
} from "./api.js";
|
|
18
|
+
import { uploadMedia, type MediaType } from "./cdn.js";
|
|
17
19
|
import { updateContactsFromMsgs, loadContacts } from "./contacts.js";
|
|
18
20
|
|
|
19
21
|
/** Resolve a short/partial userId to a full one from contacts. */
|
|
@@ -59,25 +61,104 @@ function formatMsg(msg: Record<string, unknown>): string {
|
|
|
59
61
|
return `${prefix}${from.slice(0, 20)}: ${parts.join(" ") || "(empty)"}`;
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
interface SendOptions {
|
|
65
|
+
to: string;
|
|
66
|
+
text?: string;
|
|
67
|
+
image?: string;
|
|
68
|
+
file?: string;
|
|
69
|
+
video?: string;
|
|
70
|
+
caption?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function parseCliSendArgs(args: string[]): SendOptions {
|
|
74
|
+
const opts: SendOptions = { to: "" };
|
|
75
|
+
let i = 0;
|
|
76
|
+
|
|
77
|
+
// First arg is always <to>
|
|
78
|
+
if (args[i] && !args[i].startsWith("--")) {
|
|
79
|
+
opts.to = args[i++];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
while (i < args.length) {
|
|
83
|
+
const arg = args[i];
|
|
84
|
+
if (arg === "--image" && args[i + 1]) {
|
|
85
|
+
opts.image = args[++i];
|
|
86
|
+
} else if (arg === "--file" && args[i + 1]) {
|
|
87
|
+
opts.file = args[++i];
|
|
88
|
+
} else if (arg === "--video" && args[i + 1]) {
|
|
89
|
+
opts.video = args[++i];
|
|
90
|
+
} else if (arg === "--caption" && args[i + 1]) {
|
|
91
|
+
opts.caption = args[++i];
|
|
92
|
+
} else if (!arg.startsWith("--")) {
|
|
93
|
+
// Collect remaining as text
|
|
94
|
+
opts.text = args.slice(i).join(" ");
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
i++;
|
|
98
|
+
}
|
|
99
|
+
return opts;
|
|
100
|
+
}
|
|
101
|
+
|
|
62
102
|
export async function cliSend(args: string[]) {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
103
|
+
const opts = parseCliSendArgs(args);
|
|
104
|
+
|
|
105
|
+
if (!opts.to) {
|
|
106
|
+
console.error(`Usage: npx weixin-mcp send <userId> <text>
|
|
107
|
+
npx weixin-mcp send <userId> --image <path> [--caption <text>]
|
|
108
|
+
npx weixin-mcp send <userId> --file <path> [--caption <text>]
|
|
109
|
+
npx weixin-mcp send <userId> --video <path> [--caption <text>]`);
|
|
66
110
|
process.exit(1);
|
|
67
111
|
}
|
|
68
|
-
|
|
69
|
-
const resolvedTo = resolveUserId(to);
|
|
70
|
-
if (resolvedTo !== to) console.log(`Resolved "${to}" → ${resolvedTo}`);
|
|
112
|
+
|
|
113
|
+
const resolvedTo = resolveUserId(opts.to);
|
|
114
|
+
if (resolvedTo !== opts.to) console.log(`Resolved "${opts.to}" → ${resolvedTo}`);
|
|
71
115
|
const { token, baseUrl = DEFAULT_BASE_URL } = loadAccount();
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
116
|
+
|
|
117
|
+
// Determine what to send
|
|
118
|
+
const mediaPath = opts.image || opts.file || opts.video;
|
|
119
|
+
const mediaType: MediaType | null = opts.image ? "image" : opts.file ? "file" : opts.video ? "video" : null;
|
|
120
|
+
|
|
121
|
+
if (mediaPath && mediaType) {
|
|
122
|
+
// Check file exists
|
|
123
|
+
if (!fs.existsSync(mediaPath)) {
|
|
124
|
+
console.error(`File not found: ${mediaPath}`);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
process.stdout.write(`Uploading ${mediaType}... `);
|
|
129
|
+
const uploaded = await uploadMedia({
|
|
130
|
+
source: mediaPath,
|
|
131
|
+
mediaType,
|
|
132
|
+
toUserId: resolvedTo,
|
|
133
|
+
token: token!,
|
|
134
|
+
baseUrl,
|
|
135
|
+
});
|
|
136
|
+
console.log("✅");
|
|
137
|
+
|
|
138
|
+
process.stdout.write(`Sending ${mediaType} to ${resolvedTo}... `);
|
|
139
|
+
await sendMediaMessage({
|
|
140
|
+
to: resolvedTo,
|
|
141
|
+
mediaType,
|
|
142
|
+
uploaded,
|
|
143
|
+
caption: opts.caption,
|
|
144
|
+
token: token!,
|
|
145
|
+
baseUrl,
|
|
146
|
+
});
|
|
77
147
|
console.log("✅ Sent");
|
|
148
|
+
} else if (opts.text) {
|
|
149
|
+
// Text message
|
|
150
|
+
process.stdout.write(`Sending to ${resolvedTo}... `);
|
|
151
|
+
const result = await sendTextMessage(resolvedTo, opts.text, token!, baseUrl) as Record<string, unknown>;
|
|
152
|
+
const ret = result?.ret ?? result?.errcode;
|
|
153
|
+
if (ret === 0 || ret === undefined) {
|
|
154
|
+
console.log("✅ Sent");
|
|
155
|
+
} else {
|
|
156
|
+
console.log(`❌ Failed (ret=${ret})`);
|
|
157
|
+
console.log(JSON.stringify(result, null, 2));
|
|
158
|
+
}
|
|
78
159
|
} else {
|
|
79
|
-
console.
|
|
80
|
-
|
|
160
|
+
console.error("Nothing to send. Provide text or --image/--file/--video");
|
|
161
|
+
process.exit(1);
|
|
81
162
|
}
|
|
82
163
|
}
|
|
83
164
|
|