@aster110/cc2wechat 1.0.2 → 2.0.0
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/dist/auth.js +13 -4
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +8 -4
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +2 -0
- package/dist/daemon.js +295 -0
- package/dist/daemon.js.map +1 -0
- package/dist/reply-cli.d.ts +2 -0
- package/dist/reply-cli.js +42 -0
- package/dist/reply-cli.js.map +1 -0
- package/package.json +2 -1
- package/src/auth.ts +13 -4
- package/src/cli.ts +8 -4
- package/src/daemon.ts +336 -0
- package/src/reply-cli.ts +50 -0
package/dist/auth.js
CHANGED
|
@@ -100,11 +100,20 @@ function buildQRPage(qrUrl) {
|
|
|
100
100
|
el.textContent = 'Scanned! Confirm on your phone...';
|
|
101
101
|
el.style.color = '#f0ad4e';
|
|
102
102
|
} else if (data.status === 'success') {
|
|
103
|
-
el.textContent = 'Connected!';
|
|
104
|
-
el.style.color = '#5cb85c';
|
|
105
|
-
document.getElementById('qr-container').style.display = 'none';
|
|
106
103
|
clearInterval(poll);
|
|
107
|
-
|
|
104
|
+
const cmd = 'claude --dangerously-load-development-channels server:wechat-channel';
|
|
105
|
+
navigator.clipboard.writeText(cmd).catch(()=>{});
|
|
106
|
+
document.querySelector('.container').innerHTML = \`
|
|
107
|
+
<div style="text-align:center;max-width:420px">
|
|
108
|
+
<div style="font-size:64px;margin-bottom:16px">✓</div>
|
|
109
|
+
<h2 style="font-size:28px;font-weight:600;margin-bottom:8px;color:#fff">WeChat Connected</h2>
|
|
110
|
+
<p style="color:#888;margin-bottom:32px;font-size:15px">Command copied to clipboard</p>
|
|
111
|
+
<div style="background:#2a2a3e;border-radius:10px;padding:14px 18px;font-family:'SF Mono',Menlo,monospace;font-size:13px;color:#a8b4ff;text-align:left;cursor:pointer;transition:background .2s" onclick="navigator.clipboard.writeText('\${cmd}');this.style.background='#1e3a1e';setTimeout(()=>this.style.background='#2a2a3e',800)">
|
|
112
|
+
\${cmd}
|
|
113
|
+
</div>
|
|
114
|
+
<p style="color:#555;margin-top:24px;font-size:13px">Paste in a new terminal to start</p>
|
|
115
|
+
</div>\`;
|
|
116
|
+
|
|
108
117
|
} else if (data.status === 'expired') {
|
|
109
118
|
el.textContent = 'QR expired, refreshing...';
|
|
110
119
|
el.style.color = '#d9534f';
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -12,10 +12,11 @@ function printUsage() {
|
|
|
12
12
|
🦞 wechat-claude — WeChat channel for Claude Code
|
|
13
13
|
|
|
14
14
|
Usage:
|
|
15
|
-
npx
|
|
16
|
-
npx
|
|
17
|
-
npx
|
|
18
|
-
npx
|
|
15
|
+
npx cc2wechat start v2 Pipe Mode: listen & auto-reply via claude -p
|
|
16
|
+
npx cc2wechat install Setup: register MCP + scan QR login (v1 MCP mode)
|
|
17
|
+
npx cc2wechat login Re-login (scan QR code)
|
|
18
|
+
npx cc2wechat status Check connection status
|
|
19
|
+
npx cc2wechat help Show this help
|
|
19
20
|
`);
|
|
20
21
|
}
|
|
21
22
|
async function install() {
|
|
@@ -99,6 +100,9 @@ function status() {
|
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
switch (command) {
|
|
103
|
+
case 'start':
|
|
104
|
+
import('./daemon.js');
|
|
105
|
+
break;
|
|
102
106
|
case 'install':
|
|
103
107
|
case 'setup':
|
|
104
108
|
install().catch(console.error);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AAErD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEhC,SAAS,UAAU;IACjB,OAAO,CAAC,GAAG,CAAC
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.d.ts
ADDED
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const replyCli = path.join(__dirname, 'reply-cli.js');
|
|
9
|
+
import { loginWithQRWeb } from './auth.js';
|
|
10
|
+
import { getActiveAccount, saveAccount, loadSyncBuf, saveSyncBuf } from './store.js';
|
|
11
|
+
import { getUpdates, sendMessage, sendTyping, getConfig, uploadAndSendMedia } from './wechat-api.js';
|
|
12
|
+
import { MessageItemType } from './types.js';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Constants
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
const SESSION_EXPIRED_ERRCODE = -14;
|
|
17
|
+
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
18
|
+
const BACKOFF_DELAY_MS = 30_000;
|
|
19
|
+
const RETRY_DELAY_MS = 2_000;
|
|
20
|
+
const SESSION_PAUSE_MS = 5 * 60_000;
|
|
21
|
+
const MAX_CHUNK_LENGTH = 3900;
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Helpers
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
function userIdToSessionUUID(userId) {
|
|
26
|
+
const hash = createHash('md5').update(`cc2wechat:${userId}`).digest('hex');
|
|
27
|
+
return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`;
|
|
28
|
+
}
|
|
29
|
+
function extractText(msg) {
|
|
30
|
+
const parts = [];
|
|
31
|
+
for (const item of msg.item_list ?? []) {
|
|
32
|
+
if (item.type === MessageItemType.TEXT && item.text_item?.text) {
|
|
33
|
+
parts.push(item.text_item.text);
|
|
34
|
+
}
|
|
35
|
+
else if (item.type === MessageItemType.IMAGE) {
|
|
36
|
+
parts.push('[Image]');
|
|
37
|
+
}
|
|
38
|
+
else if (item.type === MessageItemType.VOICE && item.voice_item?.text) {
|
|
39
|
+
parts.push(`[Voice] ${item.voice_item.text}`);
|
|
40
|
+
}
|
|
41
|
+
else if (item.type === MessageItemType.FILE && item.file_item?.file_name) {
|
|
42
|
+
parts.push(`[File: ${item.file_item.file_name}]`);
|
|
43
|
+
}
|
|
44
|
+
else if (item.type === MessageItemType.VIDEO) {
|
|
45
|
+
parts.push('[Video]');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return parts.join('\n') || '[Empty message]';
|
|
49
|
+
}
|
|
50
|
+
function stripMarkdown(text) {
|
|
51
|
+
let result = text;
|
|
52
|
+
// Code blocks: strip fences, keep content
|
|
53
|
+
result = result.replace(/```[^\n]*\n?([\s\S]*?)```/g, (_, code) => code.trim());
|
|
54
|
+
// Images: remove
|
|
55
|
+
result = result.replace(/!\[[^\]]*\]\([^)]*\)/g, '');
|
|
56
|
+
// Links: keep display text
|
|
57
|
+
result = result.replace(/\[([^\]]+)\]\([^)]*\)/g, '$1');
|
|
58
|
+
// Bold/italic
|
|
59
|
+
result = result.replace(/\*\*(.+?)\*\*/g, '$1');
|
|
60
|
+
result = result.replace(/\*(.+?)\*/g, '$1');
|
|
61
|
+
result = result.replace(/__(.+?)__/g, '$1');
|
|
62
|
+
result = result.replace(/_(.+?)_/g, '$1');
|
|
63
|
+
// Headings
|
|
64
|
+
result = result.replace(/^#{1,6}\s+/gm, '');
|
|
65
|
+
// Horizontal rules
|
|
66
|
+
result = result.replace(/^[-*_]{3,}$/gm, '');
|
|
67
|
+
// Blockquotes
|
|
68
|
+
result = result.replace(/^>\s?/gm, '');
|
|
69
|
+
return result.trim();
|
|
70
|
+
}
|
|
71
|
+
function chunkText(text) {
|
|
72
|
+
if (text.length <= MAX_CHUNK_LENGTH)
|
|
73
|
+
return [text];
|
|
74
|
+
const chunks = [];
|
|
75
|
+
let remaining = text;
|
|
76
|
+
while (remaining.length > 0) {
|
|
77
|
+
if (remaining.length <= MAX_CHUNK_LENGTH) {
|
|
78
|
+
chunks.push(remaining);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
// Try to break at newline
|
|
82
|
+
let breakAt = remaining.lastIndexOf('\n', MAX_CHUNK_LENGTH);
|
|
83
|
+
if (breakAt < MAX_CHUNK_LENGTH * 0.5) {
|
|
84
|
+
// No good newline break, try space
|
|
85
|
+
breakAt = remaining.lastIndexOf(' ', MAX_CHUNK_LENGTH);
|
|
86
|
+
}
|
|
87
|
+
if (breakAt < MAX_CHUNK_LENGTH * 0.3) {
|
|
88
|
+
breakAt = MAX_CHUNK_LENGTH;
|
|
89
|
+
}
|
|
90
|
+
chunks.push(remaining.slice(0, breakAt));
|
|
91
|
+
remaining = remaining.slice(breakAt).trimStart();
|
|
92
|
+
}
|
|
93
|
+
return chunks;
|
|
94
|
+
}
|
|
95
|
+
function sleep(ms) {
|
|
96
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
97
|
+
}
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// Core message handler
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
async function handleMessage(msg, account) {
|
|
102
|
+
const text = extractText(msg);
|
|
103
|
+
const userId = msg.from_user_id ?? '';
|
|
104
|
+
const contextToken = msg.context_token ?? '';
|
|
105
|
+
const sessionId = userIdToSessionUUID(userId);
|
|
106
|
+
console.log(`[cc2wechat] <- ${userId.slice(0, 10)}...: ${text.slice(0, 50)}`);
|
|
107
|
+
const systemPrompt = `You are responding to a WeChat message. Keep replies concise (under 500 chars when possible).
|
|
108
|
+
|
|
109
|
+
You have these WeChat commands available via Bash:
|
|
110
|
+
- Send image/file to user: node ${replyCli} --image /absolute/path/to/file
|
|
111
|
+
- Send text message mid-process: node ${replyCli} --text "processing..."
|
|
112
|
+
|
|
113
|
+
When user asks for screenshots, files, or images:
|
|
114
|
+
1. Create/save the file (e.g. screencapture -x /tmp/screenshot.png)
|
|
115
|
+
2. Send it: node ${replyCli} --image /tmp/screenshot.png
|
|
116
|
+
3. Confirm in your response
|
|
117
|
+
|
|
118
|
+
Your final text response will also be sent to WeChat automatically.
|
|
119
|
+
|
|
120
|
+
IMPORTANT: You are running in non-interactive mode. Do NOT use Agent Teams (TeamCreate/TaskCreate). Handle all tasks yourself sequentially.`;
|
|
121
|
+
// Write context for cc2wechat-reply CLI
|
|
122
|
+
const contextPath = '/tmp/cc2wechat-context.json';
|
|
123
|
+
fs.writeFileSync(contextPath, JSON.stringify({
|
|
124
|
+
token: account.token,
|
|
125
|
+
baseUrl: account.baseUrl,
|
|
126
|
+
userId,
|
|
127
|
+
contextToken,
|
|
128
|
+
}));
|
|
129
|
+
// Send typing indicator
|
|
130
|
+
try {
|
|
131
|
+
const cfg = await getConfig(account.token, userId, contextToken, account.baseUrl);
|
|
132
|
+
if (cfg.typing_ticket) {
|
|
133
|
+
await sendTyping(account.token, userId, cfg.typing_ticket, 1, account.baseUrl).catch(() => { });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// non-critical
|
|
138
|
+
}
|
|
139
|
+
// Call claude -p (try resume first, fallback to new session)
|
|
140
|
+
let result;
|
|
141
|
+
const prompt = JSON.stringify(text);
|
|
142
|
+
try {
|
|
143
|
+
// Try resuming existing session
|
|
144
|
+
result = execSync(`claude -p ${prompt} --resume ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${JSON.stringify(systemPrompt)}`, { encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd: process.cwd() }).trim();
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
const execErr = err;
|
|
148
|
+
const stderr = execErr.stderr ?? execErr.message ?? '';
|
|
149
|
+
// If session not found, create new one
|
|
150
|
+
if (stderr.includes('session') || stderr.includes('resume') || !execErr.stdout?.trim()) {
|
|
151
|
+
try {
|
|
152
|
+
result = execSync(`claude -p ${prompt} --session-id ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${JSON.stringify(systemPrompt)}`, { encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd: process.cwd() }).trim();
|
|
153
|
+
}
|
|
154
|
+
catch (err2) {
|
|
155
|
+
const execErr2 = err2;
|
|
156
|
+
result = execErr2.stdout?.trim() || `Error: ${execErr2.message ?? 'unknown'}`;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
result = execErr.stdout?.trim() || `Error: ${stderr}`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
console.log(`[cc2wechat] -> ${result.slice(0, 100)}...`);
|
|
164
|
+
// Strip markdown + chunk + send text
|
|
165
|
+
const plainText = stripMarkdown(result);
|
|
166
|
+
const chunks = chunkText(plainText);
|
|
167
|
+
for (const chunk of chunks) {
|
|
168
|
+
await sendMessage(account.token, userId, chunk, contextToken, account.baseUrl);
|
|
169
|
+
}
|
|
170
|
+
// Detect file paths in output, auto-send
|
|
171
|
+
const fileMatch = result.match(/\/(tmp|Users|home)[\w/._-]+\.(png|jpg|jpeg|gif|mp4|pdf|zip)/gi);
|
|
172
|
+
if (fileMatch) {
|
|
173
|
+
for (const filePath of fileMatch) {
|
|
174
|
+
if (fs.existsSync(filePath)) {
|
|
175
|
+
console.log(`[cc2wechat] Sending file: ${filePath}`);
|
|
176
|
+
try {
|
|
177
|
+
await uploadAndSendMedia({
|
|
178
|
+
token: account.token,
|
|
179
|
+
toUser: userId,
|
|
180
|
+
contextToken,
|
|
181
|
+
filePath,
|
|
182
|
+
baseUrl: account.baseUrl,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
const sendErr = err;
|
|
187
|
+
console.error(`[cc2wechat] Failed to send file: ${sendErr.message ?? 'unknown'}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Cancel typing
|
|
193
|
+
try {
|
|
194
|
+
const cfg = await getConfig(account.token, userId, contextToken, account.baseUrl);
|
|
195
|
+
if (cfg.typing_ticket) {
|
|
196
|
+
await sendTyping(account.token, userId, cfg.typing_ticket, 2, account.baseUrl).catch(() => { });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// non-critical
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Long-polling loop
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
async function pollLoop(account) {
|
|
207
|
+
let buf = loadSyncBuf(account.accountId);
|
|
208
|
+
let consecutiveFailures = 0;
|
|
209
|
+
let nextTimeoutMs = 35_000;
|
|
210
|
+
console.log(`[cc2wechat] Polling started for account ${account.accountId}`);
|
|
211
|
+
// eslint-disable-next-line no-constant-condition
|
|
212
|
+
while (true) {
|
|
213
|
+
try {
|
|
214
|
+
const resp = await getUpdates(account.token, buf, account.baseUrl, nextTimeoutMs);
|
|
215
|
+
// Update timeout if server suggests one
|
|
216
|
+
if (resp.longpolling_timeout_ms != null && resp.longpolling_timeout_ms > 0) {
|
|
217
|
+
nextTimeoutMs = resp.longpolling_timeout_ms;
|
|
218
|
+
}
|
|
219
|
+
// Check for API errors
|
|
220
|
+
const isApiError = (resp.ret !== undefined && resp.ret !== 0) ||
|
|
221
|
+
(resp.errcode !== undefined && resp.errcode !== 0);
|
|
222
|
+
if (isApiError) {
|
|
223
|
+
const isSessionExpired = resp.errcode === SESSION_EXPIRED_ERRCODE || resp.ret === SESSION_EXPIRED_ERRCODE;
|
|
224
|
+
if (isSessionExpired) {
|
|
225
|
+
console.log(`[cc2wechat] Session expired (errcode ${SESSION_EXPIRED_ERRCODE}), pausing ${Math.ceil(SESSION_PAUSE_MS / 60_000)} min`);
|
|
226
|
+
consecutiveFailures = 0;
|
|
227
|
+
await sleep(SESSION_PAUSE_MS);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
consecutiveFailures++;
|
|
231
|
+
console.error(`[cc2wechat] getUpdates error: ret=${resp.ret} errcode=${resp.errcode} errmsg=${resp.errmsg ?? ''} (${consecutiveFailures}/${MAX_CONSECUTIVE_FAILURES})`);
|
|
232
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
233
|
+
consecutiveFailures = 0;
|
|
234
|
+
await sleep(BACKOFF_DELAY_MS);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
await sleep(RETRY_DELAY_MS);
|
|
238
|
+
}
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
consecutiveFailures = 0;
|
|
242
|
+
// Save sync buf
|
|
243
|
+
if (resp.get_updates_buf != null && resp.get_updates_buf !== '') {
|
|
244
|
+
saveSyncBuf(account.accountId, resp.get_updates_buf);
|
|
245
|
+
buf = resp.get_updates_buf;
|
|
246
|
+
}
|
|
247
|
+
// Process messages
|
|
248
|
+
const msgs = resp.msgs ?? [];
|
|
249
|
+
for (const msg of msgs) {
|
|
250
|
+
// Only process user messages (message_type === 1)
|
|
251
|
+
if (msg.message_type !== 1)
|
|
252
|
+
continue;
|
|
253
|
+
// Handle each message (sequential to avoid race conditions)
|
|
254
|
+
await handleMessage(msg, account);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
consecutiveFailures++;
|
|
259
|
+
console.error(`[cc2wechat] Poll error (${consecutiveFailures}/${MAX_CONSECUTIVE_FAILURES}): ${String(err)}`);
|
|
260
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
261
|
+
consecutiveFailures = 0;
|
|
262
|
+
await sleep(BACKOFF_DELAY_MS);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
await sleep(RETRY_DELAY_MS);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
// Main
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
async function main() {
|
|
274
|
+
console.log('\n cc2wechat v2 — Pipe Mode\n');
|
|
275
|
+
let account = getActiveAccount();
|
|
276
|
+
if (!account) {
|
|
277
|
+
console.log(' No saved credentials. Starting login...');
|
|
278
|
+
const result = await loginWithQRWeb();
|
|
279
|
+
saveAccount({
|
|
280
|
+
accountId: result.accountId.replace(/@/g, '-').replace(/\./g, '-'),
|
|
281
|
+
token: result.token,
|
|
282
|
+
baseUrl: result.baseUrl,
|
|
283
|
+
savedAt: new Date().toISOString(),
|
|
284
|
+
});
|
|
285
|
+
account = getActiveAccount();
|
|
286
|
+
}
|
|
287
|
+
console.log(` Account: ${account.accountId}`);
|
|
288
|
+
console.log(' Listening for WeChat messages...\n');
|
|
289
|
+
await pollLoop(account);
|
|
290
|
+
}
|
|
291
|
+
main().catch((err) => {
|
|
292
|
+
console.error(`Fatal: ${String(err)}`);
|
|
293
|
+
process.exit(1);
|
|
294
|
+
});
|
|
295
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// CC calls this from Bash: cc2wechat-reply --image /path/to/file
|
|
3
|
+
// Or: cc2wechat-reply --text "hello"
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import { sendMessage, uploadAndSendMedia } from './wechat-api.js';
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const contextPath = '/tmp/cc2wechat-context.json';
|
|
8
|
+
if (!fs.existsSync(contextPath)) {
|
|
9
|
+
console.error('No active WeChat context. cc2wechat must be running.');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const ctx = JSON.parse(fs.readFileSync(contextPath, 'utf-8'));
|
|
13
|
+
async function main() {
|
|
14
|
+
if (args[0] === '--image' || args[0] === '--file') {
|
|
15
|
+
const filePath = args[1];
|
|
16
|
+
if (!filePath || !fs.existsSync(filePath)) {
|
|
17
|
+
console.error(`File not found: ${filePath}`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
await uploadAndSendMedia({
|
|
21
|
+
token: ctx.token,
|
|
22
|
+
toUser: ctx.userId,
|
|
23
|
+
contextToken: ctx.contextToken,
|
|
24
|
+
filePath,
|
|
25
|
+
baseUrl: ctx.baseUrl,
|
|
26
|
+
});
|
|
27
|
+
console.log(`Sent: ${filePath}`);
|
|
28
|
+
}
|
|
29
|
+
else if (args[0] === '--text') {
|
|
30
|
+
const text = args.slice(1).join(' ');
|
|
31
|
+
await sendMessage(ctx.token, ctx.userId, text, ctx.contextToken, ctx.baseUrl);
|
|
32
|
+
console.log(`Sent: ${text.slice(0, 50)}...`);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.log('Usage: cc2wechat-reply --image <path> | --text <message>');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
main().catch((err) => {
|
|
39
|
+
console.error(String(err));
|
|
40
|
+
process.exit(1);
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=reply-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reply-cli.js","sourceRoot":"","sources":["../src/reply-cli.ts"],"names":[],"mappings":";AACA,iEAAiE;AACjE,qCAAqC;AAErC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAElE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,WAAW,GAAG,6BAA6B,CAAC;AAElD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;IACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAK3D,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1C,OAAO,CAAC,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;YAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,kBAAkB,CAAC;YACvB,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,QAAQ;YACR,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,SAAS,QAAQ,EAAE,CAAC,CAAC;IACnC,CAAC;SAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAC9E,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aster110/cc2wechat",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "WeChat channel for Claude Code — chat with Claude Code from WeChat via iLink Bot API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cc2wechat": "dist/cli.js",
|
|
8
|
+
"cc2wechat-reply": "dist/reply-cli.js",
|
|
8
9
|
"wechat-cc-channel": "dist/server.js"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
package/src/auth.ts
CHANGED
|
@@ -116,11 +116,20 @@ function buildQRPage(qrUrl: string): string {
|
|
|
116
116
|
el.textContent = 'Scanned! Confirm on your phone...';
|
|
117
117
|
el.style.color = '#f0ad4e';
|
|
118
118
|
} else if (data.status === 'success') {
|
|
119
|
-
el.textContent = 'Connected!';
|
|
120
|
-
el.style.color = '#5cb85c';
|
|
121
|
-
document.getElementById('qr-container').style.display = 'none';
|
|
122
119
|
clearInterval(poll);
|
|
123
|
-
|
|
120
|
+
const cmd = 'claude --dangerously-load-development-channels server:wechat-channel';
|
|
121
|
+
navigator.clipboard.writeText(cmd).catch(()=>{});
|
|
122
|
+
document.querySelector('.container').innerHTML = \`
|
|
123
|
+
<div style="text-align:center;max-width:420px">
|
|
124
|
+
<div style="font-size:64px;margin-bottom:16px">✓</div>
|
|
125
|
+
<h2 style="font-size:28px;font-weight:600;margin-bottom:8px;color:#fff">WeChat Connected</h2>
|
|
126
|
+
<p style="color:#888;margin-bottom:32px;font-size:15px">Command copied to clipboard</p>
|
|
127
|
+
<div style="background:#2a2a3e;border-radius:10px;padding:14px 18px;font-family:'SF Mono',Menlo,monospace;font-size:13px;color:#a8b4ff;text-align:left;cursor:pointer;transition:background .2s" onclick="navigator.clipboard.writeText('\${cmd}');this.style.background='#1e3a1e';setTimeout(()=>this.style.background='#2a2a3e',800)">
|
|
128
|
+
\${cmd}
|
|
129
|
+
</div>
|
|
130
|
+
<p style="color:#555;margin-top:24px;font-size:13px">Paste in a new terminal to start</p>
|
|
131
|
+
</div>\`;
|
|
132
|
+
|
|
124
133
|
} else if (data.status === 'expired') {
|
|
125
134
|
el.textContent = 'QR expired, refreshing...';
|
|
126
135
|
el.style.color = '#d9534f';
|
package/src/cli.ts
CHANGED
|
@@ -16,10 +16,11 @@ function printUsage(): void {
|
|
|
16
16
|
🦞 wechat-claude — WeChat channel for Claude Code
|
|
17
17
|
|
|
18
18
|
Usage:
|
|
19
|
-
npx
|
|
20
|
-
npx
|
|
21
|
-
npx
|
|
22
|
-
npx
|
|
19
|
+
npx cc2wechat start v2 Pipe Mode: listen & auto-reply via claude -p
|
|
20
|
+
npx cc2wechat install Setup: register MCP + scan QR login (v1 MCP mode)
|
|
21
|
+
npx cc2wechat login Re-login (scan QR code)
|
|
22
|
+
npx cc2wechat status Check connection status
|
|
23
|
+
npx cc2wechat help Show this help
|
|
23
24
|
`);
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -110,6 +111,9 @@ function status(): void {
|
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
switch (command) {
|
|
114
|
+
case 'start':
|
|
115
|
+
import('./daemon.js');
|
|
116
|
+
break;
|
|
113
117
|
case 'install':
|
|
114
118
|
case 'setup':
|
|
115
119
|
install().catch(console.error);
|
package/src/daemon.ts
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const replyCli = path.join(__dirname, 'reply-cli.js');
|
|
11
|
+
|
|
12
|
+
import { loginWithQRWeb } from './auth.js';
|
|
13
|
+
import { getActiveAccount, saveAccount, loadSyncBuf, saveSyncBuf } from './store.js';
|
|
14
|
+
import { getUpdates, sendMessage, sendTyping, getConfig, uploadAndSendMedia } from './wechat-api.js';
|
|
15
|
+
import type { WeixinMessage } from './types.js';
|
|
16
|
+
import { MessageItemType } from './types.js';
|
|
17
|
+
import type { AccountData } from './store.js';
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Constants
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
const SESSION_EXPIRED_ERRCODE = -14;
|
|
24
|
+
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
25
|
+
const BACKOFF_DELAY_MS = 30_000;
|
|
26
|
+
const RETRY_DELAY_MS = 2_000;
|
|
27
|
+
const SESSION_PAUSE_MS = 5 * 60_000;
|
|
28
|
+
const MAX_CHUNK_LENGTH = 3900;
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Helpers
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
function userIdToSessionUUID(userId: string): string {
|
|
35
|
+
const hash = createHash('md5').update(`cc2wechat:${userId}`).digest('hex');
|
|
36
|
+
return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function extractText(msg: WeixinMessage): string {
|
|
40
|
+
const parts: string[] = [];
|
|
41
|
+
for (const item of msg.item_list ?? []) {
|
|
42
|
+
if (item.type === MessageItemType.TEXT && item.text_item?.text) {
|
|
43
|
+
parts.push(item.text_item.text);
|
|
44
|
+
} else if (item.type === MessageItemType.IMAGE) {
|
|
45
|
+
parts.push('[Image]');
|
|
46
|
+
} else if (item.type === MessageItemType.VOICE && item.voice_item?.text) {
|
|
47
|
+
parts.push(`[Voice] ${item.voice_item.text}`);
|
|
48
|
+
} else if (item.type === MessageItemType.FILE && item.file_item?.file_name) {
|
|
49
|
+
parts.push(`[File: ${item.file_item.file_name}]`);
|
|
50
|
+
} else if (item.type === MessageItemType.VIDEO) {
|
|
51
|
+
parts.push('[Video]');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return parts.join('\n') || '[Empty message]';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function stripMarkdown(text: string): string {
|
|
58
|
+
let result = text;
|
|
59
|
+
// Code blocks: strip fences, keep content
|
|
60
|
+
result = result.replace(/```[^\n]*\n?([\s\S]*?)```/g, (_, code: string) => code.trim());
|
|
61
|
+
// Images: remove
|
|
62
|
+
result = result.replace(/!\[[^\]]*\]\([^)]*\)/g, '');
|
|
63
|
+
// Links: keep display text
|
|
64
|
+
result = result.replace(/\[([^\]]+)\]\([^)]*\)/g, '$1');
|
|
65
|
+
// Bold/italic
|
|
66
|
+
result = result.replace(/\*\*(.+?)\*\*/g, '$1');
|
|
67
|
+
result = result.replace(/\*(.+?)\*/g, '$1');
|
|
68
|
+
result = result.replace(/__(.+?)__/g, '$1');
|
|
69
|
+
result = result.replace(/_(.+?)_/g, '$1');
|
|
70
|
+
// Headings
|
|
71
|
+
result = result.replace(/^#{1,6}\s+/gm, '');
|
|
72
|
+
// Horizontal rules
|
|
73
|
+
result = result.replace(/^[-*_]{3,}$/gm, '');
|
|
74
|
+
// Blockquotes
|
|
75
|
+
result = result.replace(/^>\s?/gm, '');
|
|
76
|
+
return result.trim();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function chunkText(text: string): string[] {
|
|
80
|
+
if (text.length <= MAX_CHUNK_LENGTH) return [text];
|
|
81
|
+
const chunks: string[] = [];
|
|
82
|
+
let remaining = text;
|
|
83
|
+
while (remaining.length > 0) {
|
|
84
|
+
if (remaining.length <= MAX_CHUNK_LENGTH) {
|
|
85
|
+
chunks.push(remaining);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
// Try to break at newline
|
|
89
|
+
let breakAt = remaining.lastIndexOf('\n', MAX_CHUNK_LENGTH);
|
|
90
|
+
if (breakAt < MAX_CHUNK_LENGTH * 0.5) {
|
|
91
|
+
// No good newline break, try space
|
|
92
|
+
breakAt = remaining.lastIndexOf(' ', MAX_CHUNK_LENGTH);
|
|
93
|
+
}
|
|
94
|
+
if (breakAt < MAX_CHUNK_LENGTH * 0.3) {
|
|
95
|
+
breakAt = MAX_CHUNK_LENGTH;
|
|
96
|
+
}
|
|
97
|
+
chunks.push(remaining.slice(0, breakAt));
|
|
98
|
+
remaining = remaining.slice(breakAt).trimStart();
|
|
99
|
+
}
|
|
100
|
+
return chunks;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function sleep(ms: number): Promise<void> {
|
|
104
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Core message handler
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
async function handleMessage(msg: WeixinMessage, account: AccountData): Promise<void> {
|
|
112
|
+
const text = extractText(msg);
|
|
113
|
+
const userId = msg.from_user_id ?? '';
|
|
114
|
+
const contextToken = msg.context_token ?? '';
|
|
115
|
+
const sessionId = userIdToSessionUUID(userId);
|
|
116
|
+
|
|
117
|
+
console.log(`[cc2wechat] <- ${userId.slice(0, 10)}...: ${text.slice(0, 50)}`);
|
|
118
|
+
|
|
119
|
+
const systemPrompt = `You are responding to a WeChat message. Keep replies concise (under 500 chars when possible).
|
|
120
|
+
|
|
121
|
+
You have these WeChat commands available via Bash:
|
|
122
|
+
- Send image/file to user: node ${replyCli} --image /absolute/path/to/file
|
|
123
|
+
- Send text message mid-process: node ${replyCli} --text "processing..."
|
|
124
|
+
|
|
125
|
+
When user asks for screenshots, files, or images:
|
|
126
|
+
1. Create/save the file (e.g. screencapture -x /tmp/screenshot.png)
|
|
127
|
+
2. Send it: node ${replyCli} --image /tmp/screenshot.png
|
|
128
|
+
3. Confirm in your response
|
|
129
|
+
|
|
130
|
+
Your final text response will also be sent to WeChat automatically.
|
|
131
|
+
|
|
132
|
+
IMPORTANT: You are running in non-interactive mode. Do NOT use Agent Teams (TeamCreate/TaskCreate). Handle all tasks yourself sequentially.`;
|
|
133
|
+
|
|
134
|
+
// Write context for cc2wechat-reply CLI
|
|
135
|
+
const contextPath = '/tmp/cc2wechat-context.json';
|
|
136
|
+
fs.writeFileSync(contextPath, JSON.stringify({
|
|
137
|
+
token: account.token,
|
|
138
|
+
baseUrl: account.baseUrl,
|
|
139
|
+
userId,
|
|
140
|
+
contextToken,
|
|
141
|
+
}));
|
|
142
|
+
|
|
143
|
+
// Send typing indicator
|
|
144
|
+
try {
|
|
145
|
+
const cfg = await getConfig(account.token, userId, contextToken, account.baseUrl);
|
|
146
|
+
if (cfg.typing_ticket) {
|
|
147
|
+
await sendTyping(account.token, userId, cfg.typing_ticket, 1, account.baseUrl).catch(() => {});
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
// non-critical
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Call claude -p (try resume first, fallback to new session)
|
|
154
|
+
let result: string;
|
|
155
|
+
const prompt = JSON.stringify(text);
|
|
156
|
+
try {
|
|
157
|
+
// Try resuming existing session
|
|
158
|
+
result = execSync(
|
|
159
|
+
`claude -p ${prompt} --resume ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${JSON.stringify(systemPrompt)}`,
|
|
160
|
+
{ encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd: process.cwd() },
|
|
161
|
+
).trim();
|
|
162
|
+
} catch (err: unknown) {
|
|
163
|
+
const execErr = err as { stdout?: string; stderr?: string; message?: string };
|
|
164
|
+
const stderr = execErr.stderr ?? execErr.message ?? '';
|
|
165
|
+
// If session not found, create new one
|
|
166
|
+
if (stderr.includes('session') || stderr.includes('resume') || !execErr.stdout?.trim()) {
|
|
167
|
+
try {
|
|
168
|
+
result = execSync(
|
|
169
|
+
`claude -p ${prompt} --session-id ${sessionId} --output-format text --permission-mode bypassPermissions --system-prompt ${JSON.stringify(systemPrompt)}`,
|
|
170
|
+
{ encoding: 'utf-8', timeout: 120_000, maxBuffer: 10 * 1024 * 1024, cwd: process.cwd() },
|
|
171
|
+
).trim();
|
|
172
|
+
} catch (err2: unknown) {
|
|
173
|
+
const execErr2 = err2 as { stdout?: string; message?: string };
|
|
174
|
+
result = execErr2.stdout?.trim() || `Error: ${execErr2.message ?? 'unknown'}`;
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
result = execErr.stdout?.trim() || `Error: ${stderr}`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
console.log(`[cc2wechat] -> ${result.slice(0, 100)}...`);
|
|
182
|
+
|
|
183
|
+
// Strip markdown + chunk + send text
|
|
184
|
+
const plainText = stripMarkdown(result);
|
|
185
|
+
const chunks = chunkText(plainText);
|
|
186
|
+
for (const chunk of chunks) {
|
|
187
|
+
await sendMessage(account.token, userId, chunk, contextToken, account.baseUrl);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Detect file paths in output, auto-send
|
|
191
|
+
const fileMatch = result.match(/\/(tmp|Users|home)[\w/._-]+\.(png|jpg|jpeg|gif|mp4|pdf|zip)/gi);
|
|
192
|
+
if (fileMatch) {
|
|
193
|
+
for (const filePath of fileMatch) {
|
|
194
|
+
if (fs.existsSync(filePath)) {
|
|
195
|
+
console.log(`[cc2wechat] Sending file: ${filePath}`);
|
|
196
|
+
try {
|
|
197
|
+
await uploadAndSendMedia({
|
|
198
|
+
token: account.token,
|
|
199
|
+
toUser: userId,
|
|
200
|
+
contextToken,
|
|
201
|
+
filePath,
|
|
202
|
+
baseUrl: account.baseUrl,
|
|
203
|
+
});
|
|
204
|
+
} catch (err: unknown) {
|
|
205
|
+
const sendErr = err as { message?: string };
|
|
206
|
+
console.error(`[cc2wechat] Failed to send file: ${sendErr.message ?? 'unknown'}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Cancel typing
|
|
213
|
+
try {
|
|
214
|
+
const cfg = await getConfig(account.token, userId, contextToken, account.baseUrl);
|
|
215
|
+
if (cfg.typing_ticket) {
|
|
216
|
+
await sendTyping(account.token, userId, cfg.typing_ticket, 2, account.baseUrl).catch(() => {});
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
// non-critical
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
// Long-polling loop
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
|
|
227
|
+
async function pollLoop(account: AccountData): Promise<void> {
|
|
228
|
+
let buf = loadSyncBuf(account.accountId);
|
|
229
|
+
let consecutiveFailures = 0;
|
|
230
|
+
let nextTimeoutMs = 35_000;
|
|
231
|
+
|
|
232
|
+
console.log(`[cc2wechat] Polling started for account ${account.accountId}`);
|
|
233
|
+
|
|
234
|
+
// eslint-disable-next-line no-constant-condition
|
|
235
|
+
while (true) {
|
|
236
|
+
try {
|
|
237
|
+
const resp = await getUpdates(account.token, buf, account.baseUrl, nextTimeoutMs);
|
|
238
|
+
|
|
239
|
+
// Update timeout if server suggests one
|
|
240
|
+
if (resp.longpolling_timeout_ms != null && resp.longpolling_timeout_ms > 0) {
|
|
241
|
+
nextTimeoutMs = resp.longpolling_timeout_ms;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Check for API errors
|
|
245
|
+
const isApiError =
|
|
246
|
+
(resp.ret !== undefined && resp.ret !== 0) ||
|
|
247
|
+
(resp.errcode !== undefined && resp.errcode !== 0);
|
|
248
|
+
|
|
249
|
+
if (isApiError) {
|
|
250
|
+
const isSessionExpired =
|
|
251
|
+
resp.errcode === SESSION_EXPIRED_ERRCODE || resp.ret === SESSION_EXPIRED_ERRCODE;
|
|
252
|
+
|
|
253
|
+
if (isSessionExpired) {
|
|
254
|
+
console.log(
|
|
255
|
+
`[cc2wechat] Session expired (errcode ${SESSION_EXPIRED_ERRCODE}), pausing ${Math.ceil(SESSION_PAUSE_MS / 60_000)} min`,
|
|
256
|
+
);
|
|
257
|
+
consecutiveFailures = 0;
|
|
258
|
+
await sleep(SESSION_PAUSE_MS);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
consecutiveFailures++;
|
|
263
|
+
console.error(
|
|
264
|
+
`[cc2wechat] getUpdates error: ret=${resp.ret} errcode=${resp.errcode} errmsg=${resp.errmsg ?? ''} (${consecutiveFailures}/${MAX_CONSECUTIVE_FAILURES})`,
|
|
265
|
+
);
|
|
266
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
267
|
+
consecutiveFailures = 0;
|
|
268
|
+
await sleep(BACKOFF_DELAY_MS);
|
|
269
|
+
} else {
|
|
270
|
+
await sleep(RETRY_DELAY_MS);
|
|
271
|
+
}
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
consecutiveFailures = 0;
|
|
276
|
+
|
|
277
|
+
// Save sync buf
|
|
278
|
+
if (resp.get_updates_buf != null && resp.get_updates_buf !== '') {
|
|
279
|
+
saveSyncBuf(account.accountId, resp.get_updates_buf);
|
|
280
|
+
buf = resp.get_updates_buf;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Process messages
|
|
284
|
+
const msgs = resp.msgs ?? [];
|
|
285
|
+
for (const msg of msgs) {
|
|
286
|
+
// Only process user messages (message_type === 1)
|
|
287
|
+
if (msg.message_type !== 1) continue;
|
|
288
|
+
|
|
289
|
+
// Handle each message (sequential to avoid race conditions)
|
|
290
|
+
await handleMessage(msg, account);
|
|
291
|
+
}
|
|
292
|
+
} catch (err) {
|
|
293
|
+
consecutiveFailures++;
|
|
294
|
+
console.error(
|
|
295
|
+
`[cc2wechat] Poll error (${consecutiveFailures}/${MAX_CONSECUTIVE_FAILURES}): ${String(err)}`,
|
|
296
|
+
);
|
|
297
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
298
|
+
consecutiveFailures = 0;
|
|
299
|
+
await sleep(BACKOFF_DELAY_MS);
|
|
300
|
+
} else {
|
|
301
|
+
await sleep(RETRY_DELAY_MS);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
// Main
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
async function main(): Promise<void> {
|
|
312
|
+
console.log('\n cc2wechat v2 — Pipe Mode\n');
|
|
313
|
+
|
|
314
|
+
let account = getActiveAccount();
|
|
315
|
+
if (!account) {
|
|
316
|
+
console.log(' No saved credentials. Starting login...');
|
|
317
|
+
const result = await loginWithQRWeb();
|
|
318
|
+
saveAccount({
|
|
319
|
+
accountId: result.accountId.replace(/@/g, '-').replace(/\./g, '-'),
|
|
320
|
+
token: result.token,
|
|
321
|
+
baseUrl: result.baseUrl,
|
|
322
|
+
savedAt: new Date().toISOString(),
|
|
323
|
+
});
|
|
324
|
+
account = getActiveAccount()!;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
console.log(` Account: ${account.accountId}`);
|
|
328
|
+
console.log(' Listening for WeChat messages...\n');
|
|
329
|
+
|
|
330
|
+
await pollLoop(account);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
main().catch((err) => {
|
|
334
|
+
console.error(`Fatal: ${String(err)}`);
|
|
335
|
+
process.exit(1);
|
|
336
|
+
});
|
package/src/reply-cli.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// CC calls this from Bash: cc2wechat-reply --image /path/to/file
|
|
3
|
+
// Or: cc2wechat-reply --text "hello"
|
|
4
|
+
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import { sendMessage, uploadAndSendMedia } from './wechat-api.js';
|
|
7
|
+
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
const contextPath = '/tmp/cc2wechat-context.json';
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(contextPath)) {
|
|
12
|
+
console.error('No active WeChat context. cc2wechat must be running.');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const ctx = JSON.parse(fs.readFileSync(contextPath, 'utf-8')) as {
|
|
17
|
+
token: string;
|
|
18
|
+
baseUrl?: string;
|
|
19
|
+
userId: string;
|
|
20
|
+
contextToken: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
async function main(): Promise<void> {
|
|
24
|
+
if (args[0] === '--image' || args[0] === '--file') {
|
|
25
|
+
const filePath = args[1];
|
|
26
|
+
if (!filePath || !fs.existsSync(filePath)) {
|
|
27
|
+
console.error(`File not found: ${filePath}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
await uploadAndSendMedia({
|
|
31
|
+
token: ctx.token,
|
|
32
|
+
toUser: ctx.userId,
|
|
33
|
+
contextToken: ctx.contextToken,
|
|
34
|
+
filePath,
|
|
35
|
+
baseUrl: ctx.baseUrl,
|
|
36
|
+
});
|
|
37
|
+
console.log(`Sent: ${filePath}`);
|
|
38
|
+
} else if (args[0] === '--text') {
|
|
39
|
+
const text = args.slice(1).join(' ');
|
|
40
|
+
await sendMessage(ctx.token, ctx.userId, text, ctx.contextToken, ctx.baseUrl);
|
|
41
|
+
console.log(`Sent: ${text.slice(0, 50)}...`);
|
|
42
|
+
} else {
|
|
43
|
+
console.log('Usage: cc2wechat-reply --image <path> | --text <message>');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
main().catch((err) => {
|
|
48
|
+
console.error(String(err));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
});
|