@ozaiya/openclaw-channel 0.10.4 → 0.10.6
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/src/channel.js +2 -0
- package/dist/src/channel.js.map +1 -1
- package/dist/src/desktopContainer.d.ts +117 -0
- package/dist/src/desktopContainer.js +382 -0
- package/dist/src/desktopContainer.js.map +1 -0
- package/dist/src/desktopTool.d.ts +8 -0
- package/dist/src/desktopTool.js +112 -0
- package/dist/src/desktopTool.js.map +1 -0
- package/dist/src/gateway.js +470 -77
- package/dist/src/gateway.js.map +1 -1
- package/dist/src/wechatMonitor.d.ts +124 -0
- package/dist/src/wechatMonitor.js +617 -0
- package/dist/src/wechatMonitor.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WeChat message monitor for Docker containers.
|
|
3
|
+
*
|
|
4
|
+
* Detects new WeChat messages by polling the SQLCipher WAL file mtime,
|
|
5
|
+
* then takes a screenshot and uses Claude Vision to identify unread chats.
|
|
6
|
+
* Optionally auto-replies by clicking into the chat, reading the last message,
|
|
7
|
+
* generating a response, and pasting it via clipboard.
|
|
8
|
+
*
|
|
9
|
+
* Flow: inotify/stat poll → screenshot → Claude Vision → (optional) auto-reply
|
|
10
|
+
*/
|
|
11
|
+
import { dockerExec, atspiScreenshot, resolveAgentPort, humanType, xdotoolKey, atspiClick } from "./desktopContainer.js";
|
|
12
|
+
// ── Constants ────────────────────────────────────────────────────────────────
|
|
13
|
+
const DEFAULT_POLL_INTERVAL = 2000;
|
|
14
|
+
const DEFAULT_SCREENSHOT_DELAY = 500;
|
|
15
|
+
const DEFAULT_DEBOUNCE = 5000;
|
|
16
|
+
const DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
17
|
+
const MAX_HISTORY_PER_SENDER = 20;
|
|
18
|
+
const HISTORY_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
19
|
+
const CHAT_LIST_VISION_PROMPT = `You are analyzing a screenshot of the WeChat Linux desktop app.
|
|
20
|
+
Identify ALL chats that have unread messages (indicated by red badge numbers or red dots).
|
|
21
|
+
|
|
22
|
+
Return a JSON object with this exact structure:
|
|
23
|
+
{
|
|
24
|
+
"unreadChats": [
|
|
25
|
+
{
|
|
26
|
+
"sender": "name of the sender or group",
|
|
27
|
+
"preview": "preview text of the last message if visible",
|
|
28
|
+
"unreadCount": 1,
|
|
29
|
+
"position": {"x": 150, "y": 200}
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Rules:
|
|
35
|
+
- position.x and position.y are the approximate pixel coordinates of the chat item's CENTER
|
|
36
|
+
- unreadCount should be the number shown in the red badge, or 1 if it's just a red dot
|
|
37
|
+
- If no unread messages are found, return {"unreadChats": []}
|
|
38
|
+
- ALSO check the currently open chat area (right side): if there are new unread voice bubbles (with red dots) or new messages at the bottom from the other person, include the currently open chat as an unread item too. Use the chat name shown in the header of the chat area.
|
|
39
|
+
- Return ONLY the JSON, no markdown fences or explanation`;
|
|
40
|
+
const READ_MESSAGE_VISION_PROMPT = `You are analyzing a screenshot of an open WeChat chat.
|
|
41
|
+
Extract the recent messages visible on screen, focusing on the conversation flow.
|
|
42
|
+
|
|
43
|
+
Return a JSON object:
|
|
44
|
+
{
|
|
45
|
+
"sender": "name of the other person (not me/bot)",
|
|
46
|
+
"recentMessages": [
|
|
47
|
+
{"from": "other", "text": "their message text"},
|
|
48
|
+
{"from": "me", "text": "my reply text"},
|
|
49
|
+
{"from": "other", "text": "their latest message"}
|
|
50
|
+
],
|
|
51
|
+
"lastMessage": "the full text of the last message from the other person (empty if not text)",
|
|
52
|
+
"messageType": "text",
|
|
53
|
+
"isFromOther": true,
|
|
54
|
+
"bubblePosition": {"x": 300, "y": 500}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Rules:
|
|
58
|
+
- recentMessages: list the last few visible messages in chronological order (oldest first)
|
|
59
|
+
- "from" is "other" for the other person's messages, "me" for my/bot messages
|
|
60
|
+
- Include only the most recent 5-8 messages visible
|
|
61
|
+
- For non-text messages, describe briefly: "[image: photo of a cat]", "[voice: 5s]", "[sticker]"
|
|
62
|
+
- messageType of the LAST message from the other person: "text", "voice", "image", "sticker", "video", "file", "unknown"
|
|
63
|
+
- "voice" = a voice message bubble (usually shows a speaker icon and duration like "3\"")
|
|
64
|
+
- bubblePosition is the CENTER pixel coordinates of the last message bubble from the other person
|
|
65
|
+
- For text messages, include the full text in lastMessage
|
|
66
|
+
- For non-text messages, set lastMessage to empty string ""
|
|
67
|
+
|
|
68
|
+
If you cannot determine messages from the other person, return:
|
|
69
|
+
{"sender": "", "recentMessages": [], "lastMessage": "", "messageType": "unknown", "isFromOther": false, "bubblePosition": null}
|
|
70
|
+
|
|
71
|
+
Return ONLY the JSON, no markdown fences or explanation`;
|
|
72
|
+
// ── Active monitors ──────────────────────────────────────────────────────────
|
|
73
|
+
const activeMonitors = new Map();
|
|
74
|
+
export function getMonitor(containerId) {
|
|
75
|
+
return activeMonitors.get(containerId);
|
|
76
|
+
}
|
|
77
|
+
export function getAllMonitors() {
|
|
78
|
+
return activeMonitors;
|
|
79
|
+
}
|
|
80
|
+
// ── Session class ────────────────────────────────────────────────────────────
|
|
81
|
+
export class WeChatMonitorSession {
|
|
82
|
+
containerId;
|
|
83
|
+
config;
|
|
84
|
+
walPath = null;
|
|
85
|
+
lastMtime = null;
|
|
86
|
+
lastCheckAt = null;
|
|
87
|
+
totalChecks = 0;
|
|
88
|
+
totalDetections = 0;
|
|
89
|
+
pollTimer = null;
|
|
90
|
+
debounceTimer = null;
|
|
91
|
+
processing = false;
|
|
92
|
+
running = false;
|
|
93
|
+
conversationHistory = new Map();
|
|
94
|
+
log;
|
|
95
|
+
constructor(containerId, config, log) {
|
|
96
|
+
this.containerId = containerId;
|
|
97
|
+
this.config = {
|
|
98
|
+
pollIntervalMs: config.pollIntervalMs ?? DEFAULT_POLL_INTERVAL,
|
|
99
|
+
screenshotDelayMs: config.screenshotDelayMs ?? DEFAULT_SCREENSHOT_DELAY,
|
|
100
|
+
debounceMs: config.debounceMs ?? DEFAULT_DEBOUNCE,
|
|
101
|
+
anthropicApiKey: config.anthropicApiKey,
|
|
102
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
103
|
+
model: config.model ?? DEFAULT_MODEL,
|
|
104
|
+
systemPrompt: config.systemPrompt,
|
|
105
|
+
autoReply: config.autoReply ?? false,
|
|
106
|
+
autoReplyTargets: config.autoReplyTargets,
|
|
107
|
+
onNewMessage: config.onNewMessage,
|
|
108
|
+
onError: config.onError,
|
|
109
|
+
};
|
|
110
|
+
this.log = log ?? { info: console.log, warn: console.warn };
|
|
111
|
+
}
|
|
112
|
+
async start() {
|
|
113
|
+
if (this.running)
|
|
114
|
+
return;
|
|
115
|
+
// Discover WAL file path
|
|
116
|
+
this.walPath = this.findWalPath();
|
|
117
|
+
if (!this.walPath) {
|
|
118
|
+
throw new Error("WeChat message database WAL file not found in container");
|
|
119
|
+
}
|
|
120
|
+
this.log.info(`[wechat-monitor] WAL path: ${this.walPath}`);
|
|
121
|
+
// Get initial mtime so we don't trigger on startup
|
|
122
|
+
this.lastMtime = this.getWalMtime();
|
|
123
|
+
this.running = true;
|
|
124
|
+
activeMonitors.set(this.containerId, this);
|
|
125
|
+
this.scheduleNext();
|
|
126
|
+
this.log.info(`[wechat-monitor] started for container ${this.containerId}`);
|
|
127
|
+
}
|
|
128
|
+
stop() {
|
|
129
|
+
this.running = false;
|
|
130
|
+
if (this.pollTimer) {
|
|
131
|
+
clearTimeout(this.pollTimer);
|
|
132
|
+
this.pollTimer = null;
|
|
133
|
+
}
|
|
134
|
+
if (this.debounceTimer) {
|
|
135
|
+
clearTimeout(this.debounceTimer);
|
|
136
|
+
this.debounceTimer = null;
|
|
137
|
+
}
|
|
138
|
+
this.conversationHistory.clear();
|
|
139
|
+
activeMonitors.delete(this.containerId);
|
|
140
|
+
this.log.info(`[wechat-monitor] stopped for container ${this.containerId}`);
|
|
141
|
+
}
|
|
142
|
+
getStatus() {
|
|
143
|
+
return {
|
|
144
|
+
running: this.running,
|
|
145
|
+
containerId: this.containerId,
|
|
146
|
+
walPath: this.walPath,
|
|
147
|
+
lastMtime: this.lastMtime,
|
|
148
|
+
lastCheckAt: this.lastCheckAt,
|
|
149
|
+
totalChecks: this.totalChecks,
|
|
150
|
+
totalDetections: this.totalDetections,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// ── Internal ───────────────────────────────────────────────────────────
|
|
154
|
+
scheduleNext() {
|
|
155
|
+
if (!this.running)
|
|
156
|
+
return;
|
|
157
|
+
this.pollTimer = setTimeout(async () => {
|
|
158
|
+
await this.pollOnce();
|
|
159
|
+
this.scheduleNext();
|
|
160
|
+
}, this.config.pollIntervalMs);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Poll checks WAL mtime. On change, resets debounce timer.
|
|
164
|
+
* Actual processing only fires after debounceMs of silence.
|
|
165
|
+
*/
|
|
166
|
+
async pollOnce() {
|
|
167
|
+
if (!this.running || !this.walPath)
|
|
168
|
+
return;
|
|
169
|
+
this.totalChecks++;
|
|
170
|
+
this.lastCheckAt = Date.now();
|
|
171
|
+
try {
|
|
172
|
+
const currentMtime = this.getWalMtime();
|
|
173
|
+
if (currentMtime === null || currentMtime === this.lastMtime)
|
|
174
|
+
return;
|
|
175
|
+
this.lastMtime = currentMtime;
|
|
176
|
+
// Reset debounce timer — wait for silence before processing
|
|
177
|
+
if (this.debounceTimer)
|
|
178
|
+
clearTimeout(this.debounceTimer);
|
|
179
|
+
this.log.info(`[wechat-monitor] WAL changed, waiting for message burst to settle...`);
|
|
180
|
+
this.debounceTimer = setTimeout(() => {
|
|
181
|
+
this.processUnread().catch(err => {
|
|
182
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
183
|
+
this.log.warn(`[wechat-monitor] process error: ${error.message}`);
|
|
184
|
+
this.config.onError?.(error);
|
|
185
|
+
});
|
|
186
|
+
}, this.config.debounceMs);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
190
|
+
this.log.warn(`[wechat-monitor] poll error: ${error.message}`);
|
|
191
|
+
this.config.onError?.(error);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Process unread messages after debounce settles.
|
|
196
|
+
*/
|
|
197
|
+
async processUnread() {
|
|
198
|
+
if (!this.running)
|
|
199
|
+
return;
|
|
200
|
+
if (this.processing) {
|
|
201
|
+
this.log.info(`[wechat-monitor] processUnread: already processing, skipping`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
this.processing = true;
|
|
205
|
+
try {
|
|
206
|
+
// Wait for UI to update after DB write
|
|
207
|
+
await sleep(this.config.screenshotDelayMs);
|
|
208
|
+
// Take screenshot
|
|
209
|
+
const agentPort = resolveAgentPort(this.containerId);
|
|
210
|
+
const screenshotBase64 = atspiScreenshot(agentPort);
|
|
211
|
+
// Analyze with Vision
|
|
212
|
+
const apiKey = this.config.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
213
|
+
if (!apiKey) {
|
|
214
|
+
this.log.warn(`[wechat-monitor] no Anthropic API key, skipping analysis`);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const unreadChats = await this.analyzeScreenshot(screenshotBase64, apiKey);
|
|
218
|
+
if (unreadChats.length === 0)
|
|
219
|
+
return;
|
|
220
|
+
this.totalDetections++;
|
|
221
|
+
this.log.info(`[wechat-monitor] detected ${unreadChats.length} unread chat(s)`);
|
|
222
|
+
const event = {
|
|
223
|
+
type: "unread-detected",
|
|
224
|
+
containerId: this.containerId,
|
|
225
|
+
chats: unreadChats,
|
|
226
|
+
screenshotBase64,
|
|
227
|
+
timestamp: Date.now(),
|
|
228
|
+
};
|
|
229
|
+
this.config.onNewMessage?.(event);
|
|
230
|
+
// Auto-reply if enabled (optionally filtered by target names)
|
|
231
|
+
if (this.config.autoReply) {
|
|
232
|
+
const targets = this.config.autoReplyTargets;
|
|
233
|
+
const filtered = targets?.length
|
|
234
|
+
? unreadChats.filter(c => targets.some(t => c.sender.toLowerCase() === t.toLowerCase()))
|
|
235
|
+
: unreadChats;
|
|
236
|
+
for (const chat of filtered) {
|
|
237
|
+
try {
|
|
238
|
+
await this.handleAutoReply(chat, apiKey, agentPort);
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
this.log.warn(`[wechat-monitor] auto-reply failed for ${chat.sender}: ${err}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
this.processing = false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Find the WAL file for WeChat's message database inside the container.
|
|
252
|
+
*/
|
|
253
|
+
findWalPath() {
|
|
254
|
+
try {
|
|
255
|
+
const output = dockerExec(this.containerId, `bash -c "find /root /home -name 'message_0.db-wal' -type f 2>/dev/null | head -1"`, 10_000);
|
|
256
|
+
return output || null;
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get the mtime of the WAL file (Unix timestamp in seconds).
|
|
264
|
+
*/
|
|
265
|
+
getWalMtime() {
|
|
266
|
+
if (!this.walPath)
|
|
267
|
+
return null;
|
|
268
|
+
try {
|
|
269
|
+
const output = dockerExec(this.containerId, `stat -c '%Y' '${this.walPath}'`, 5_000);
|
|
270
|
+
return parseInt(output, 10) || null;
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Call LLM API (supports both Anthropic and OpenAI-compatible formats).
|
|
278
|
+
* When apiBaseUrl is set, uses OpenAI chat/completions format (for Clider etc).
|
|
279
|
+
* Otherwise uses Anthropic Messages API.
|
|
280
|
+
*/
|
|
281
|
+
async callLlm(opts) {
|
|
282
|
+
const model = this.config.model ?? DEFAULT_MODEL;
|
|
283
|
+
const baseUrl = this.config.apiBaseUrl;
|
|
284
|
+
if (baseUrl) {
|
|
285
|
+
// OpenAI-compatible format (Clider, etc)
|
|
286
|
+
const userContent = [];
|
|
287
|
+
if (opts.imageBase64) {
|
|
288
|
+
userContent.push({
|
|
289
|
+
type: "image_url",
|
|
290
|
+
image_url: { url: `data:image/jpeg;base64,${opts.imageBase64}` },
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
userContent.push({ type: "text", text: opts.userText });
|
|
294
|
+
const messages = [];
|
|
295
|
+
if (opts.systemText) {
|
|
296
|
+
messages.push({ role: "system", content: opts.systemText });
|
|
297
|
+
}
|
|
298
|
+
messages.push({ role: "user", content: userContent });
|
|
299
|
+
const res = await fetch(`${baseUrl}/chat/completions`, {
|
|
300
|
+
method: "POST",
|
|
301
|
+
headers: {
|
|
302
|
+
"Content-Type": "application/json",
|
|
303
|
+
"Authorization": `Bearer ${opts.apiKey}`,
|
|
304
|
+
},
|
|
305
|
+
body: JSON.stringify({ model, max_tokens: opts.maxTokens ?? 1024, messages }),
|
|
306
|
+
signal: AbortSignal.timeout(60_000),
|
|
307
|
+
});
|
|
308
|
+
if (!res.ok) {
|
|
309
|
+
const text = await res.text().catch(() => "");
|
|
310
|
+
throw new Error(`LLM API error ${res.status}: ${text}`);
|
|
311
|
+
}
|
|
312
|
+
const data = await res.json();
|
|
313
|
+
return data.choices?.[0]?.message?.content?.trim() || null;
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
// Anthropic Messages API
|
|
317
|
+
const userContent = [];
|
|
318
|
+
if (opts.imageBase64) {
|
|
319
|
+
userContent.push({
|
|
320
|
+
type: "image",
|
|
321
|
+
source: { type: "base64", media_type: "image/jpeg", data: opts.imageBase64 },
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
userContent.push({ type: "text", text: opts.userText });
|
|
325
|
+
const body = {
|
|
326
|
+
model,
|
|
327
|
+
max_tokens: opts.maxTokens ?? 1024,
|
|
328
|
+
messages: [{ role: "user", content: userContent }],
|
|
329
|
+
};
|
|
330
|
+
if (opts.systemText)
|
|
331
|
+
body.system = opts.systemText;
|
|
332
|
+
const res = await fetch("https://api.anthropic.com/v1/messages", {
|
|
333
|
+
method: "POST",
|
|
334
|
+
headers: {
|
|
335
|
+
"Content-Type": "application/json",
|
|
336
|
+
"x-api-key": opts.apiKey,
|
|
337
|
+
"anthropic-version": "2023-06-01",
|
|
338
|
+
},
|
|
339
|
+
body: JSON.stringify(body),
|
|
340
|
+
signal: AbortSignal.timeout(60_000),
|
|
341
|
+
});
|
|
342
|
+
if (!res.ok) {
|
|
343
|
+
const text = await res.text().catch(() => "");
|
|
344
|
+
throw new Error(`LLM API error ${res.status}: ${text}`);
|
|
345
|
+
}
|
|
346
|
+
const data = await res.json();
|
|
347
|
+
return data.content?.find((b) => b.type === "text")?.text?.trim() || null;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Analyze screenshot to find unread chats.
|
|
352
|
+
*/
|
|
353
|
+
async analyzeScreenshot(base64, apiKey) {
|
|
354
|
+
const text = await this.callLlm({
|
|
355
|
+
apiKey,
|
|
356
|
+
imageBase64: base64,
|
|
357
|
+
userText: CHAT_LIST_VISION_PROMPT,
|
|
358
|
+
});
|
|
359
|
+
if (!text)
|
|
360
|
+
return [];
|
|
361
|
+
try {
|
|
362
|
+
const parsed = JSON.parse(text);
|
|
363
|
+
return Array.isArray(parsed.unreadChats) ? parsed.unreadChats : [];
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
this.log.warn(`[wechat-monitor] failed to parse Vision response: ${text.slice(0, 200)}`);
|
|
367
|
+
return [];
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Click into an unread chat, read the last message, generate a reply,
|
|
372
|
+
* and send it via clipboard paste + Enter.
|
|
373
|
+
*/
|
|
374
|
+
async handleAutoReply(chat, apiKey, agentPort) {
|
|
375
|
+
atspiClick(agentPort, chat.position.x, chat.position.y);
|
|
376
|
+
await sleep(800);
|
|
377
|
+
const chatScreenshot = atspiScreenshot(agentPort);
|
|
378
|
+
let context = await this.extractLastMessage(chatScreenshot, apiKey);
|
|
379
|
+
this.log.info(`[wechat-monitor] extractLastMessage result: type=${context.messageType}, sender=${context.sender}, bubble=(${context.bubblePosition?.x},${context.bubblePosition?.y}), lastMsg="${context.lastMessage?.slice(0, 50)}"`);
|
|
380
|
+
// Voice message: right-click → "Audio to Text" → re-read
|
|
381
|
+
if (context.messageType === "voice" && context.bubblePosition) {
|
|
382
|
+
this.log.info(`[wechat-monitor] voice message from ${chat.sender}, converting to text...`);
|
|
383
|
+
const converted = await this.convertVoiceToText(context.bubblePosition, apiKey, agentPort);
|
|
384
|
+
if (converted) {
|
|
385
|
+
context = { ...context, lastMessage: converted, messageType: "text" };
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
this.log.warn(`[wechat-monitor] voice-to-text failed for ${chat.sender}`);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// For text/voice we need lastMessage; for visual types (image/sticker/video) we use the screenshot
|
|
393
|
+
const isVisual = ["image", "sticker", "video"].includes(context.messageType);
|
|
394
|
+
if (!context.lastMessage && !isVisual) {
|
|
395
|
+
this.log.warn(`[wechat-monitor] could not extract last message from ${chat.sender} (type: ${context.messageType})`);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
// Build conversation history for this sender
|
|
399
|
+
const senderKey = context.sender.toLowerCase();
|
|
400
|
+
const history = this.getHistory(senderKey);
|
|
401
|
+
const reply = await this.generateReply(context, history, apiKey);
|
|
402
|
+
if (!reply)
|
|
403
|
+
return;
|
|
404
|
+
// Save to conversation history
|
|
405
|
+
const incomingText = context.lastMessage || `[${context.messageType}]`;
|
|
406
|
+
this.appendHistory(senderKey, { role: "user", text: incomingText, timestamp: Date.now() });
|
|
407
|
+
this.appendHistory(senderKey, { role: "assistant", text: reply, timestamp: Date.now() });
|
|
408
|
+
this.log.info(`[wechat-monitor] replying to ${chat.sender}: ${reply.slice(0, 50)}...`);
|
|
409
|
+
// Click the message input box to ensure focus (bottom center of chat area)
|
|
410
|
+
atspiClick(agentPort, 750, 700);
|
|
411
|
+
await sleep(300);
|
|
412
|
+
// Split reply into paragraphs and send each as a separate message
|
|
413
|
+
const segments = reply.split(/\n{2,}/).map(s => s.trim()).filter(Boolean);
|
|
414
|
+
for (let i = 0; i < segments.length; i++) {
|
|
415
|
+
humanType(this.containerId, segments[i]);
|
|
416
|
+
await sleep(300);
|
|
417
|
+
xdotoolKey(this.containerId, "Return");
|
|
418
|
+
if (i < segments.length - 1)
|
|
419
|
+
await sleep(800);
|
|
420
|
+
}
|
|
421
|
+
this.log.info(`[wechat-monitor] reply sent to ${chat.sender}`);
|
|
422
|
+
}
|
|
423
|
+
// ── Conversation history helpers ──────────────────────────────────────
|
|
424
|
+
getHistory(senderKey) {
|
|
425
|
+
const entries = this.conversationHistory.get(senderKey) || [];
|
|
426
|
+
// Prune stale entries
|
|
427
|
+
const cutoff = Date.now() - HISTORY_TTL_MS;
|
|
428
|
+
const fresh = entries.filter(e => e.timestamp > cutoff);
|
|
429
|
+
if (fresh.length !== entries.length) {
|
|
430
|
+
this.conversationHistory.set(senderKey, fresh);
|
|
431
|
+
}
|
|
432
|
+
return fresh;
|
|
433
|
+
}
|
|
434
|
+
appendHistory(senderKey, entry) {
|
|
435
|
+
const entries = this.conversationHistory.get(senderKey) || [];
|
|
436
|
+
entries.push(entry);
|
|
437
|
+
// Keep only the last N entries
|
|
438
|
+
if (entries.length > MAX_HISTORY_PER_SENDER) {
|
|
439
|
+
entries.splice(0, entries.length - MAX_HISTORY_PER_SENDER);
|
|
440
|
+
}
|
|
441
|
+
this.conversationHistory.set(senderKey, entries);
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Right-click a voice bubble → click "Audio to Text" → wait → re-screenshot and extract text.
|
|
445
|
+
*/
|
|
446
|
+
async convertVoiceToText(bubblePos, apiKey, agentPort) {
|
|
447
|
+
this.log.info(`[wechat-monitor] convertVoiceToText: right-clicking at (${bubblePos.x}, ${bubblePos.y})`);
|
|
448
|
+
// Right-click the voice bubble to open context menu
|
|
449
|
+
atspiClick(agentPort, bubblePos.x, bubblePos.y, 3);
|
|
450
|
+
await sleep(800);
|
|
451
|
+
// Screenshot the context menu and find "Audio to Text" / "转文字"
|
|
452
|
+
const menuScreenshot = atspiScreenshot(agentPort);
|
|
453
|
+
this.log.info(`[wechat-monitor] convertVoiceToText: screenshot taken, calling findMenuItem...`);
|
|
454
|
+
const menuItem = await this.findMenuItem(menuScreenshot, apiKey);
|
|
455
|
+
if (!menuItem) {
|
|
456
|
+
this.log.warn(`[wechat-monitor] convertVoiceToText: findMenuItem returned null, dismissing menu`);
|
|
457
|
+
// Dismiss menu by clicking elsewhere
|
|
458
|
+
atspiClick(agentPort, 10, 10);
|
|
459
|
+
await sleep(200);
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
this.log.info(`[wechat-monitor] convertVoiceToText: found menu item at (${menuItem.x}, ${menuItem.y}), clicking...`);
|
|
463
|
+
// Click the voice-to-text menu item
|
|
464
|
+
atspiClick(agentPort, menuItem.x, menuItem.y);
|
|
465
|
+
await sleep(3000); // Wait for STT conversion
|
|
466
|
+
// Re-screenshot and extract the converted text
|
|
467
|
+
const convertedScreenshot = atspiScreenshot(agentPort);
|
|
468
|
+
this.log.info(`[wechat-monitor] convertVoiceToText: extracting converted text...`);
|
|
469
|
+
const text = await this.callLlm({
|
|
470
|
+
apiKey,
|
|
471
|
+
imageBase64: convertedScreenshot,
|
|
472
|
+
userText: `You are analyzing a WeChat chat screenshot after a voice message was converted to text.
|
|
473
|
+
Find the converted text that appears below or near the voice message bubble (usually shown in a lighter/gray text block under the voice bubble).
|
|
474
|
+
|
|
475
|
+
Return a JSON object:
|
|
476
|
+
{"convertedText": "the speech-to-text result"}
|
|
477
|
+
|
|
478
|
+
If you cannot find any converted text, return:
|
|
479
|
+
{"convertedText": ""}
|
|
480
|
+
|
|
481
|
+
Return ONLY the JSON, no markdown fences or explanation`,
|
|
482
|
+
maxTokens: 512,
|
|
483
|
+
});
|
|
484
|
+
this.log.info(`[wechat-monitor] convertVoiceToText: LLM response: ${text?.slice(0, 200) ?? "null"}`);
|
|
485
|
+
if (!text)
|
|
486
|
+
return null;
|
|
487
|
+
try {
|
|
488
|
+
const parsed = JSON.parse(text);
|
|
489
|
+
return parsed.convertedText || null;
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
this.log.warn(`[wechat-monitor] convertVoiceToText: failed to parse LLM response`);
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Find the voice-to-text menu item position from a context menu screenshot.
|
|
498
|
+
* WeChat uses different labels depending on language: "转文字", "Audio to Text", "Covert to Text", "Convert to Text".
|
|
499
|
+
*/
|
|
500
|
+
async findMenuItem(base64, apiKey) {
|
|
501
|
+
const text = await this.callLlm({
|
|
502
|
+
apiKey,
|
|
503
|
+
imageBase64: base64,
|
|
504
|
+
userText: `You are analyzing a screenshot of a WeChat right-click context menu on a voice/audio message.
|
|
505
|
+
Find the menu item for converting audio/voice to text. It may be labeled as any of:
|
|
506
|
+
- "Audio to Text"
|
|
507
|
+
- "转文字"
|
|
508
|
+
- "Covert to Text"
|
|
509
|
+
- "Convert to Text"
|
|
510
|
+
- "Speech to Text"
|
|
511
|
+
|
|
512
|
+
Return a JSON object:
|
|
513
|
+
{"found": true, "position": {"x": 150, "y": 300}}
|
|
514
|
+
|
|
515
|
+
position.x and position.y are the approximate pixel coordinates of the CENTER of that menu item.
|
|
516
|
+
|
|
517
|
+
If no such menu item is found, return:
|
|
518
|
+
{"found": false, "position": null}
|
|
519
|
+
|
|
520
|
+
Return ONLY the JSON, no markdown fences or explanation`,
|
|
521
|
+
maxTokens: 256,
|
|
522
|
+
});
|
|
523
|
+
this.log.info(`[wechat-monitor] findMenuItem: LLM response: ${text?.slice(0, 200) ?? "null"}`);
|
|
524
|
+
if (!text)
|
|
525
|
+
return null;
|
|
526
|
+
try {
|
|
527
|
+
const parsed = JSON.parse(text);
|
|
528
|
+
return parsed.found ? parsed.position : null;
|
|
529
|
+
}
|
|
530
|
+
catch {
|
|
531
|
+
this.log.warn(`[wechat-monitor] findMenuItem: failed to parse LLM response`);
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Extract the last message from a chat screenshot.
|
|
537
|
+
*/
|
|
538
|
+
async extractLastMessage(base64, apiKey) {
|
|
539
|
+
const empty = {
|
|
540
|
+
sender: "", lastMessage: "", recentMessages: [], messageType: "unknown", screenshotBase64: base64,
|
|
541
|
+
};
|
|
542
|
+
const text = await this.callLlm({
|
|
543
|
+
apiKey,
|
|
544
|
+
imageBase64: base64,
|
|
545
|
+
userText: READ_MESSAGE_VISION_PROMPT,
|
|
546
|
+
maxTokens: 1024,
|
|
547
|
+
});
|
|
548
|
+
if (!text)
|
|
549
|
+
return empty;
|
|
550
|
+
try {
|
|
551
|
+
const parsed = JSON.parse(text);
|
|
552
|
+
if (!parsed.isFromOther)
|
|
553
|
+
return empty;
|
|
554
|
+
const recentMessages = [];
|
|
555
|
+
if (Array.isArray(parsed.recentMessages)) {
|
|
556
|
+
for (const m of parsed.recentMessages) {
|
|
557
|
+
recentMessages.push({
|
|
558
|
+
sender: m.from === "me" ? "me" : (parsed.sender || "other"),
|
|
559
|
+
text: m.text || "",
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return {
|
|
564
|
+
sender: parsed.sender || "",
|
|
565
|
+
lastMessage: parsed.lastMessage || "",
|
|
566
|
+
recentMessages,
|
|
567
|
+
messageType: parsed.messageType || "text",
|
|
568
|
+
screenshotBase64: base64,
|
|
569
|
+
bubblePosition: parsed.bubblePosition || undefined,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
catch {
|
|
573
|
+
return empty;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Generate a reply message.
|
|
578
|
+
*/
|
|
579
|
+
async generateReply(context, history, apiKey) {
|
|
580
|
+
const systemText = this.config.systemPrompt
|
|
581
|
+
?? "You are a helpful assistant replying on WeChat. Keep responses concise and natural. Reply in the same language as the incoming message.";
|
|
582
|
+
const isVisual = ["image", "sticker", "video"].includes(context.messageType);
|
|
583
|
+
// Build context string from conversation history + on-screen messages
|
|
584
|
+
let historyBlock = "";
|
|
585
|
+
if (history.length > 0) {
|
|
586
|
+
const lines = history.map(e => e.role === "user" ? `${context.sender}: ${e.text}` : `Me: ${e.text}`);
|
|
587
|
+
historyBlock = `Previous conversation:\n${lines.join("\n")}\n\n`;
|
|
588
|
+
}
|
|
589
|
+
// On-screen recent messages (from Vision) supplement the history
|
|
590
|
+
let screenBlock = "";
|
|
591
|
+
if (context.recentMessages.length > 0) {
|
|
592
|
+
const lines = context.recentMessages.map(m => m.sender === "me" ? `Me: ${m.text}` : `${m.sender}: ${m.text}`);
|
|
593
|
+
screenBlock = `Recent messages on screen:\n${lines.join("\n")}\n\n`;
|
|
594
|
+
}
|
|
595
|
+
const contextBlock = historyBlock + screenBlock;
|
|
596
|
+
if (isVisual) {
|
|
597
|
+
return this.callLlm({
|
|
598
|
+
apiKey,
|
|
599
|
+
systemText,
|
|
600
|
+
imageBase64: context.screenshotBase64,
|
|
601
|
+
userText: `${contextBlock}${context.sender} sent a ${context.messageType} message in this WeChat chat (see the screenshot above).\n\nGenerate a brief, natural reply to what you see. Return ONLY the reply text, nothing else.`,
|
|
602
|
+
maxTokens: 512,
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
return this.callLlm({
|
|
606
|
+
apiKey,
|
|
607
|
+
systemText,
|
|
608
|
+
userText: `${contextBlock}${context.sender} says: "${context.lastMessage}"\n\nGenerate a brief, natural reply considering the conversation context. Return ONLY the reply text, nothing else.`,
|
|
609
|
+
maxTokens: 512,
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
614
|
+
function sleep(ms) {
|
|
615
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
616
|
+
}
|
|
617
|
+
//# sourceMappingURL=wechatMonitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wechatMonitor.js","sourceRoot":"","sources":["../../src/wechatMonitor.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ozaiya/openclaw-channel",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.6",
|
|
4
4
|
"description": "OpenClaw channel plugin for Ozaiya Chat",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"@deepgram/sdk": "^4.11.0",
|
|
66
66
|
"@livekit/rtc-node": "^0.13.0",
|
|
67
67
|
"libsodium-wrappers": "^0.7.15",
|
|
68
|
+
"node-pty": "^1.0.0",
|
|
68
69
|
"socket.io-client": "^4.8.1",
|
|
69
70
|
"ws": "^8.18.0"
|
|
70
71
|
},
|