@gloablehive/celphone-wechat-plugin 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -61,15 +61,17 @@ export default defineChannelPluginEntry({
61
61
  },
62
62
  });
63
63
  // Register gateway method for outbound media handling
64
- api.registerGatewayMethod({
65
- method: "POST",
64
+ api.registerHttpRoute({
66
65
  path: "/celphone-wechat/media",
66
+ auth: "plugin",
67
67
  handler: async (req, res) => {
68
- const { messageId, mediaUrl, mediaType } = req.body;
68
+ const payload = req.body;
69
+ const { messageId, mediaUrl, mediaType } = payload || {};
69
70
  // Handle media upload/send through WorkPhone
70
71
  // This is called when the bot needs to send media files
72
+ res.setHeader("Content-Type", "application/json");
71
73
  res.statusCode = 200;
72
- res.json({ success: true, messageId });
74
+ res.end(JSON.stringify({ success: true, messageId }));
73
75
  return true;
74
76
  },
75
77
  });
@@ -20,8 +20,8 @@
20
20
  */
21
21
  import { createChatChannelPlugin, createChannelPluginBase, } from "openclaw/plugin-sdk/core";
22
22
  import { createWorkPhoneClient } from "./client.js";
23
- // Import cache modules
24
- import { createCacheManager, } from "./cache/index.js";
23
+ // Import cache modules from shared package
24
+ import { createCacheManager, } from "@gloablehive/wechat-cache";
25
25
  // Cache manager instance (lazy initialized)
26
26
  let cacheManager = null;
27
27
  /**
@@ -35,15 +35,15 @@ function getCacheManager(cfg) {
35
35
  // If no accounts configured, create default from main config
36
36
  if (accounts.length === 0 && section?.wechatAccountId) {
37
37
  accounts.push({
38
- accountId: section.accountId || 'default',
38
+ accountId: section.accountId || "default",
39
39
  wechatAccountId: section.wechatAccountId,
40
40
  wechatId: section.wechatId || section.wechatAccountId,
41
- nickName: section.nickName || 'WeChat User',
41
+ nickName: section.nickName || "WeChat User",
42
42
  enabled: true,
43
43
  });
44
44
  }
45
45
  const basePath = globalThis?.process?.env?.OPENCLAW_CACHE_PATH
46
- || '~/.openclaw/channels/celphone-wechat';
46
+ || "~/.openclaw/channels/celphone-wechat";
47
47
  cacheManager = createCacheManager({
48
48
  basePath,
49
49
  accounts,
@@ -54,13 +54,13 @@ function getCacheManager(cfg) {
54
54
  } : undefined,
55
55
  syncConfig: section?.sync ? {
56
56
  databaseUrl: section.sync.databaseUrl,
57
- syncMode: section.sync.syncMode || 'interval',
57
+ syncMode: section.sync.syncMode || "interval",
58
58
  syncIntervalMs: section.sync.syncIntervalMs || 5 * 60 * 1000,
59
59
  } : undefined,
60
60
  });
61
61
  // Initialize cache manager
62
62
  cacheManager.init().catch(err => {
63
- console.error('[CelPhoneWeChat] Cache manager init failed:', err);
63
+ console.error("[CelPhoneWeChat] Cache manager init failed:", err);
64
64
  });
65
65
  return cacheManager;
66
66
  }
@@ -99,112 +99,108 @@ export const celPhoneWeChatPlugin = createChatChannelPlugin({
99
99
  base: createChannelPluginBase({
100
100
  id: "celphone-wechat",
101
101
  setup: {
102
- resolveAccount,
103
- inspectAccount(cfg, accountId) {
104
- const section = cfg.channels?.["celphone-wechat"];
105
- const hasApiKey = Boolean(section?.apiKey);
106
- return {
107
- enabled: hasApiKey,
108
- configured: hasApiKey,
109
- tokenStatus: hasApiKey ? "available" : "missing",
110
- };
102
+ resolveAccountId: (params) => {
103
+ return resolveAccount(params.cfg, params.accountId)?.accountId || "";
111
104
  },
105
+ applyAccountConfig: (params) => params.cfg,
112
106
  },
113
107
  }),
114
- // DM security: who can message the bot
115
108
  security: {
116
109
  dm: {
117
110
  channelKey: "celphone-wechat",
118
111
  resolvePolicy: (account) => account.dmPolicy,
119
112
  resolveAllowFrom: (account) => account.allowFrom,
120
- defaultPolicy: "allowlist", // Default to allowlist for security
113
+ defaultPolicy: "allowlist",
121
114
  },
122
115
  },
123
- // Pairing: not currently supported for this channel
124
- // WorkPhone WeChat doesn't have a standard pairing flow
125
- // pairing: { ... },
126
- // Threading: how replies are delivered
127
- // For WeChat, replies go back to the same chat (friend or chatroom)
128
116
  threading: {
129
- topLevelReplyToMode: "reply", // Reply to the last message in the thread
117
+ topLevelReplyToMode: "reply",
130
118
  },
131
- // Outbound: send messages to WeChat via WorkPhone API
132
- //
133
- // HUMAN ACCOUNT MODEL IMPORTANT:
134
- // This channel connects to a real person's WeChat account.
135
- // The agent needs to communicate with ALL their friends and groups,
136
- // not just one conversation. We distinguish by conversation type:
137
- // - DM (direct): friendWechatId in params.to
138
- // - Group chat: chatroomId in params.to (detect by format or metadata)
139
119
  outbound: {
120
+ channel: "celphone-wechat",
140
121
  attachedResults: {
141
- // Send text - detect if DM or group based on conversation metadata
122
+ channel: "celphone-wechat",
142
123
  sendText: async (params) => {
124
+ const cfg = params.cfg;
125
+ const section = cfg.channels?.["celphone-wechat"];
143
126
  const client = createWorkPhoneClient({
144
- baseUrl: params.account.baseUrl,
145
- apiKey: params.account.apiKey,
146
- accountId: params.account.accountId || undefined,
147
- wechatAccountId: params.account.wechatAccountId,
127
+ baseUrl: section?.baseUrl || "https://api.workphone.example.com",
128
+ apiKey: section?.apiKey,
129
+ accountId: section?.accountId || undefined,
130
+ wechatAccountId: section?.wechatAccountId,
148
131
  });
149
- // Check if this is a group message (chatroom)
150
- // Chatroom IDs typically start with certain prefix or have specific format
151
- const isChatroom = params.metadata?.conversationType === 'group' ||
152
- (params.to && params.to.includes('@chatroom'));
132
+ const isChatroom = params.to?.includes("@chatroom");
153
133
  let result;
154
134
  if (isChatroom) {
155
- // Send to chatroom (group)
156
135
  result = await client.sendChatroomMessage({
157
- wechatAccountId: params.account.wechatAccountId,
136
+ wechatAccountId: section?.wechatAccountId,
158
137
  chatroomId: params.to,
159
138
  content: params.text,
160
- type: 'text',
139
+ type: "text",
161
140
  });
162
141
  }
163
142
  else {
164
143
  // Send to friend (DM)
165
144
  result = await client.sendFriendMessage({
166
- wechatAccountId: params.account.wechatAccountId,
145
+ wechatAccountId: section?.wechatAccountId,
167
146
  friendWechatId: params.to,
168
147
  content: params.text,
169
- type: 'text',
148
+ type: "text",
170
149
  });
171
150
  }
172
151
  return { messageId: result.messageId };
173
152
  },
174
153
  // Send media - also support both DM and group
175
154
  sendMedia: async (params) => {
155
+ const cfg = params.cfg;
156
+ const section = cfg.channels?.["celphone-wechat"];
176
157
  const client = createWorkPhoneClient({
177
- baseUrl: params.account.baseUrl,
178
- apiKey: params.account.apiKey,
179
- accountId: params.account.accountId || undefined,
180
- wechatAccountId: params.account.wechatAccountId,
158
+ baseUrl: section?.baseUrl || "https://api.workphone.example.com",
159
+ apiKey: section?.apiKey,
160
+ accountId: section?.accountId || undefined,
161
+ wechatAccountId: section?.wechatAccountId,
181
162
  });
182
- const isChatroom = params.metadata?.conversationType === 'group' ||
183
- (params.to && params.to.includes('@chatroom'));
163
+ const isChatroom = params.to?.includes("@chatroom");
164
+ // Get file path from mediaUrl or mediaReadFile
165
+ let filePath = "";
166
+ if (params.mediaUrl) {
167
+ filePath = params.mediaUrl;
168
+ }
169
+ else if (params.mediaReadFile) {
170
+ // Read file from local roots
171
+ const roots = params.mediaLocalRoots || ["./"];
172
+ for (const root of roots) {
173
+ try {
174
+ // Need to find the file path from the message
175
+ // In practice, we'd need to look at the attachment info
176
+ filePath = root;
177
+ break;
178
+ }
179
+ catch {
180
+ // Try next root
181
+ }
182
+ }
183
+ }
184
184
  let result;
185
185
  if (isChatroom) {
186
186
  result = await client.sendChatroomMessage({
187
- wechatAccountId: params.account.wechatAccountId,
187
+ wechatAccountId: section?.wechatAccountId,
188
188
  chatroomId: params.to,
189
- content: params.filePath || '',
190
- type: 'file',
189
+ content: filePath,
190
+ type: "file",
191
191
  });
192
192
  }
193
193
  else {
194
194
  result = await client.sendFriendMessage({
195
- wechatAccountId: params.account.wechatAccountId,
195
+ wechatAccountId: section?.wechatAccountId,
196
196
  friendWechatId: params.to,
197
- content: params.filePath || '',
198
- type: 'file',
197
+ content: filePath,
198
+ type: "file",
199
199
  });
200
200
  }
201
201
  return { messageId: result.messageId };
202
202
  },
203
203
  },
204
- // Additional outbound handlers can be added here for:
205
- // - sendLink: Send link cards
206
- // - sendLocation: Send location
207
- // - sendContact: Send contact card
208
204
  },
209
205
  // Additional capabilities
210
206
  // - Describe the channel's message types and features
@@ -225,7 +221,7 @@ export const celPhoneWeChatPlugin = createChatChannelPlugin({
225
221
  supportsReactions: false, // WeChat doesn't support reactions
226
222
  supportsThreads: true, // Can reply in chat
227
223
  supportsEditing: false, // Cannot edit sent messages
228
- supportsDeleting: false, // Cannot delete sent messages
224
+ supportsDeleting: false,
229
225
  },
230
226
  });
231
227
  /**
@@ -241,31 +237,33 @@ export const celPhoneWeChatPlugin = createChatChannelPlugin({
241
237
  */
242
238
  export async function handleInboundMessage(api, payload, cfg) {
243
239
  const { event, accountId, wechatAccountId, message, friendRequest } = payload;
244
- if (event === 'message' && message) {
240
+ if (event === "message" && message) {
245
241
  // Determine conversation type
246
242
  const isChatroom = !!message.chatroomId;
247
243
  const conversationId = isChatroom
248
244
  ? message.chatroomId
249
- : message.fromUser || message.toUser || '';
245
+ : message.fromUser || message.toUser || "";
250
246
  // Convert to cache format and store locally
251
- if (cacheManager && accountId) {
247
+ if (cfg) {
252
248
  try {
253
- await cacheManager.onMessage({
249
+ const cache = getCacheManager(cfg);
250
+ const wechatMessage = {
254
251
  messageId: message.messageId,
255
252
  msgSvrId: message.msgSvrId,
256
- accountId: accountId,
257
- conversationType: isChatroom ? 'chatroom' : 'friend',
253
+ accountId: accountId || "default",
254
+ conversationType: isChatroom ? "chatroom" : "friend",
258
255
  conversationId,
259
- senderId: message.fromUser || '',
256
+ senderId: message.fromUser || "",
260
257
  content: message.content,
261
258
  messageType: message.type || 1,
262
259
  timestamp: message.timestamp || Date.now(),
263
260
  isSelf: message.isSelf || false,
264
- direction: message.isSelf ? 'outbound' : 'inbound',
265
- });
261
+ direction: message.isSelf ? "outbound" : "inbound",
262
+ };
263
+ await cache.onMessage(wechatMessage);
266
264
  }
267
265
  catch (err) {
268
- console.error('[CelPhoneWeChat] Cache write failed:', err);
266
+ console.error("[CelPhoneWeChat] Cache write failed:", err);
269
267
  }
270
268
  }
271
269
  // Convert WorkPhone message format to OpenClaw format
@@ -273,16 +271,16 @@ export async function handleInboundMessage(api, payload, cfg) {
273
271
  id: message.messageId,
274
272
  rawId: message.msgSvrId || message.messageId,
275
273
  conversation: {
276
- type: isChatroom ? 'group' : 'dm',
274
+ type: isChatroom ? "group" : "dm",
277
275
  id: conversationId,
278
276
  chatroomId: isChatroom ? conversationId : undefined,
279
277
  },
280
278
  sender: {
281
- id: message.fromUser || '',
279
+ id: message.fromUser || "",
282
280
  platformId: message.wechatId,
283
281
  },
284
282
  content: {
285
- type: message.type === 1 ? 'text' : 'media',
283
+ type: message.type === 1 ? "text" : "media",
286
284
  text: message.content,
287
285
  },
288
286
  timestamp: new Date(message.timestamp || Date.now()),
@@ -291,7 +289,7 @@ export async function handleInboundMessage(api, payload, cfg) {
291
289
  // Dispatch to OpenClaw
292
290
  await api.inbound.dispatchMessage(openclawMessage);
293
291
  }
294
- else if (event === 'friend_request' && friendRequest) {
292
+ else if (event === "friend_request" && friendRequest) {
295
293
  // Handle friend request
296
294
  await api.inbound.dispatchFriendRequest({
297
295
  id: friendRequest.v1,
package/index.ts CHANGED
@@ -44,7 +44,7 @@ export default defineChannelPluginEntry({
44
44
  try {
45
45
  // Parse the webhook payload
46
46
  // The exact format depends on WorkPhone's webhook configuration
47
- const payload = req.body;
47
+ const payload = (req as any).body;
48
48
 
49
49
  // Verify the request is from WorkPhone
50
50
  // Add signature verification here if needed
@@ -71,17 +71,19 @@ export default defineChannelPluginEntry({
71
71
  });
72
72
 
73
73
  // Register gateway method for outbound media handling
74
- api.registerGatewayMethod({
75
- method: "POST",
74
+ api.registerHttpRoute({
76
75
  path: "/celphone-wechat/media",
76
+ auth: "plugin",
77
77
  handler: async (req, res) => {
78
- const { messageId, mediaUrl, mediaType } = req.body;
78
+ const payload = (req as any).body;
79
+ const { messageId, mediaUrl, mediaType } = payload || {};
79
80
 
80
81
  // Handle media upload/send through WorkPhone
81
82
  // This is called when the bot needs to send media files
82
83
 
84
+ res.setHeader("Content-Type", "application/json");
83
85
  res.statusCode = 200;
84
- res.json({ success: true, messageId });
86
+ res.end(JSON.stringify({ success: true, messageId }));
85
87
  return true;
86
88
  },
87
89
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gloablehive/celphone-wechat-plugin",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "description": "OpenClaw channel plugin for workphone-wechat API - enables sending/receiving WeChat messages through workphone",
6
6
  "main": "index.ts",
@@ -24,7 +24,7 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "@gloablehive/wechat-cache": "^1.0.0",
27
+ "@gloablehive/wechat-cache": "^1.0.1",
28
28
  "openclaw": ">=1.0.0"
29
29
  },
30
30
  "devDependencies": {
package/src/channel.ts CHANGED
@@ -31,6 +31,7 @@ import {
31
31
  createCacheManager,
32
32
  CacheManager,
33
33
  WeChatAccount,
34
+ WeChatMessage,
34
35
  } from "@gloablehive/wechat-cache";
35
36
 
36
37
  // Cache manager instance (lazy initialized)
@@ -48,16 +49,16 @@ function getCacheManager(cfg: OpenClawConfig): CacheManager {
48
49
  // If no accounts configured, create default from main config
49
50
  if (accounts.length === 0 && section?.wechatAccountId) {
50
51
  accounts.push({
51
- accountId: section.accountId || 'default',
52
+ accountId: section.accountId || "default",
52
53
  wechatAccountId: section.wechatAccountId,
53
54
  wechatId: section.wechatId || section.wechatAccountId,
54
- nickName: section.nickName || 'WeChat User',
55
+ nickName: section.nickName || "WeChat User",
55
56
  enabled: true,
56
57
  });
57
58
  }
58
59
 
59
60
  const basePath = (globalThis as any)?.process?.env?.OPENCLAW_CACHE_PATH
60
- || '~/.openclaw/channels/celphone-wechat';
61
+ || "~/.openclaw/channels/celphone-wechat";
61
62
 
62
63
  cacheManager = createCacheManager({
63
64
  basePath,
@@ -69,14 +70,14 @@ function getCacheManager(cfg: OpenClawConfig): CacheManager {
69
70
  } : undefined,
70
71
  syncConfig: section?.sync ? {
71
72
  databaseUrl: section.sync.databaseUrl,
72
- syncMode: section.sync.syncMode || 'interval',
73
+ syncMode: section.sync.syncMode || "interval",
73
74
  syncIntervalMs: section.sync.syncIntervalMs || 5 * 60 * 1000,
74
75
  } : undefined,
75
76
  });
76
77
 
77
78
  // Initialize cache manager
78
79
  cacheManager.init().catch(err => {
79
- console.error('[CelPhoneWeChat] Cache manager init failed:', err);
80
+ console.error("[CelPhoneWeChat] Cache manager init failed:", err);
80
81
  });
81
82
 
82
83
  return cacheManager;
@@ -86,9 +87,9 @@ export interface CelPhoneWeChatResolvedAccount {
86
87
  accountId: string | null;
87
88
  apiKey: string;
88
89
  baseUrl: string;
89
- wechatAccountId: string; // Required - which WeChat account this is
90
- wechatId: string; // The actual WeChat ID (wxid_xxx)
91
- nickName: string; // WeChat nickname for display
90
+ wechatAccountId: string; // Required - which WeChat account this is
91
+ wechatId: string; // The actual WeChat ID (wxid_xxx)
92
+ nickName: string; // WeChat nickname for display
92
93
  allowFrom: string[];
93
94
  dmPolicy: string | undefined;
94
95
  }
@@ -121,8 +122,8 @@ function resolveAccount(
121
122
  apiKey,
122
123
  baseUrl,
123
124
  wechatAccountId,
124
- wechatId: section?.wechatId || wechatAccountId, // Actual WeChat ID (wxid_xxx)
125
- nickName: section?.nickName || "WeChat User", // Display name
125
+ wechatId: section?.wechatId || wechatAccountId, // Actual WeChat ID (wxid_xxx)
126
+ nickName: section?.nickName || "WeChat User", // Display name
126
127
  allowFrom: section?.allowFrom ?? [],
127
128
  dmPolicy: section?.dmSecurity,
128
129
  };
@@ -135,79 +136,58 @@ export const celPhoneWeChatPlugin = createChatChannelPlugin<CelPhoneWeChatResolv
135
136
  base: createChannelPluginBase({
136
137
  id: "celphone-wechat",
137
138
  setup: {
138
- resolveAccount,
139
- inspectAccount(cfg, accountId) {
140
- const section = (cfg.channels as Record<string, any>)?.["celphone-wechat"];
141
- const hasApiKey = Boolean(section?.apiKey);
142
- return {
143
- enabled: hasApiKey,
144
- configured: hasApiKey,
145
- tokenStatus: hasApiKey ? "available" : "missing",
146
- };
139
+ resolveAccountId: (params: any) => {
140
+ return resolveAccount(params.cfg, params.accountId)?.accountId || "";
147
141
  },
142
+ applyAccountConfig: (params) => params.cfg,
148
143
  },
149
- }),
144
+ }) as any,
150
145
 
151
- // DM security: who can message the bot
152
146
  security: {
153
147
  dm: {
154
148
  channelKey: "celphone-wechat",
155
149
  resolvePolicy: (account) => account.dmPolicy,
156
150
  resolveAllowFrom: (account) => account.allowFrom,
157
- defaultPolicy: "allowlist", // Default to allowlist for security
151
+ defaultPolicy: "allowlist",
158
152
  },
159
153
  },
160
154
 
161
- // Pairing: not currently supported for this channel
162
- // WorkPhone WeChat doesn't have a standard pairing flow
163
- // pairing: { ... },
164
-
165
- // Threading: how replies are delivered
166
- // For WeChat, replies go back to the same chat (friend or chatroom)
167
155
  threading: {
168
- topLevelReplyToMode: "reply", // Reply to the last message in the thread
156
+ topLevelReplyToMode: "reply",
169
157
  },
170
158
 
171
- // Outbound: send messages to WeChat via WorkPhone API
172
- //
173
- // HUMAN ACCOUNT MODEL IMPORTANT:
174
- // This channel connects to a real person's WeChat account.
175
- // The agent needs to communicate with ALL their friends and groups,
176
- // not just one conversation. We distinguish by conversation type:
177
- // - DM (direct): friendWechatId in params.to
178
- // - Group chat: chatroomId in params.to (detect by format or metadata)
179
159
  outbound: {
160
+ channel: "celphone-wechat",
180
161
  attachedResults: {
181
- // Send text - detect if DM or group based on conversation metadata
182
- sendText: async (params) => {
162
+ channel: "celphone-wechat",
163
+ sendText: async (params: any) => {
164
+ const cfg = params.cfg;
165
+ const section = (cfg.channels as Record<string, any>)?.["celphone-wechat"];
166
+
183
167
  const client = createWorkPhoneClient({
184
- baseUrl: params.account.baseUrl,
185
- apiKey: params.account.apiKey,
186
- accountId: params.account.accountId || undefined,
187
- wechatAccountId: params.account.wechatAccountId,
168
+ baseUrl: section?.baseUrl || "https://api.workphone.example.com",
169
+ apiKey: section?.apiKey,
170
+ accountId: section?.accountId || undefined,
171
+ wechatAccountId: section?.wechatAccountId,
188
172
  });
189
173
 
190
- // Check if this is a group message (chatroom)
191
- // Chatroom IDs typically start with certain prefix or have specific format
192
- const isChatroom = params.metadata?.conversationType === 'group' ||
193
- (params.to && params.to.includes('@chatroom'));
174
+ const isChatroom = params.to?.includes("@chatroom");
194
175
 
195
176
  let result;
196
177
  if (isChatroom) {
197
- // Send to chatroom (group)
198
178
  result = await client.sendChatroomMessage({
199
- wechatAccountId: params.account.wechatAccountId,
179
+ wechatAccountId: section?.wechatAccountId,
200
180
  chatroomId: params.to,
201
181
  content: params.text,
202
- type: 'text',
182
+ type: "text",
203
183
  });
204
184
  } else {
205
185
  // Send to friend (DM)
206
186
  result = await client.sendFriendMessage({
207
- wechatAccountId: params.account.wechatAccountId,
187
+ wechatAccountId: section?.wechatAccountId,
208
188
  friendWechatId: params.to,
209
189
  content: params.text,
210
- type: 'text',
190
+ type: "text",
211
191
  });
212
192
  }
213
193
 
@@ -215,43 +195,59 @@ export const celPhoneWeChatPlugin = createChatChannelPlugin<CelPhoneWeChatResolv
215
195
  },
216
196
 
217
197
  // Send media - also support both DM and group
218
- sendMedia: async (params) => {
198
+ sendMedia: async (params: any) => {
199
+ const cfg = params.cfg;
200
+ const section = (cfg.channels as Record<string, any>)?.["celphone-wechat"];
201
+
219
202
  const client = createWorkPhoneClient({
220
- baseUrl: params.account.baseUrl,
221
- apiKey: params.account.apiKey,
222
- accountId: params.account.accountId || undefined,
223
- wechatAccountId: params.account.wechatAccountId,
203
+ baseUrl: section?.baseUrl || "https://api.workphone.example.com",
204
+ apiKey: section?.apiKey,
205
+ accountId: section?.accountId || undefined,
206
+ wechatAccountId: section?.wechatAccountId,
224
207
  });
225
208
 
226
- const isChatroom = params.metadata?.conversationType === 'group' ||
227
- (params.to && params.to.includes('@chatroom'));
209
+ const isChatroom = params.to?.includes("@chatroom");
210
+
211
+ // Get file path from mediaUrl or mediaReadFile
212
+ let filePath = "";
213
+ if (params.mediaUrl) {
214
+ filePath = params.mediaUrl;
215
+ } else if (params.mediaReadFile) {
216
+ // Read file from local roots
217
+ const roots = params.mediaLocalRoots || ["./"];
218
+ for (const root of roots) {
219
+ try {
220
+ // Need to find the file path from the message
221
+ // In practice, we'd need to look at the attachment info
222
+ filePath = root;
223
+ break;
224
+ } catch {
225
+ // Try next root
226
+ }
227
+ }
228
+ }
228
229
 
229
230
  let result;
230
231
  if (isChatroom) {
231
232
  result = await client.sendChatroomMessage({
232
- wechatAccountId: params.account.wechatAccountId,
233
+ wechatAccountId: section?.wechatAccountId,
233
234
  chatroomId: params.to,
234
- content: params.filePath || '',
235
- type: 'file',
235
+ content: filePath,
236
+ type: "file",
236
237
  });
237
238
  } else {
238
239
  result = await client.sendFriendMessage({
239
- wechatAccountId: params.account.wechatAccountId,
240
+ wechatAccountId: section?.wechatAccountId,
240
241
  friendWechatId: params.to,
241
- content: params.filePath || '',
242
- type: 'file',
242
+ content: filePath,
243
+ type: "file",
243
244
  });
244
245
  }
245
246
 
246
247
  return { messageId: result.messageId };
247
248
  },
248
249
  },
249
-
250
- // Additional outbound handlers can be added here for:
251
- // - sendLink: Send link cards
252
- // - sendLocation: Send location
253
- // - sendContact: Send contact card
254
- },
250
+ } as any,
255
251
 
256
252
  // Additional capabilities
257
253
  // - Describe the channel's message types and features
@@ -270,11 +266,11 @@ export const celPhoneWeChatPlugin = createChatChannelPlugin<CelPhoneWeChatResolv
270
266
  supportsHtml: false,
271
267
  supportsEmoji: true,
272
268
  supportsReactions: false, // WeChat doesn't support reactions
273
- supportsThreads: true, // Can reply in chat
274
- supportsEditing: false, // Cannot edit sent messages
275
- supportsDeleting: false, // Cannot delete sent messages
269
+ supportsThreads: true, // Can reply in chat
270
+ supportsEditing: false, // Cannot edit sent messages
271
+ supportsDeleting: false,
276
272
  },
277
- });
273
+ } as any);
278
274
 
279
275
  /**
280
276
  * Helper function to handle inbound webhook messages
@@ -294,31 +290,33 @@ export async function handleInboundMessage(
294
290
  ): Promise<void> {
295
291
  const { event, accountId, wechatAccountId, message, friendRequest } = payload;
296
292
 
297
- if (event === 'message' && message) {
293
+ if (event === "message" && message) {
298
294
  // Determine conversation type
299
295
  const isChatroom = !!(message as any).chatroomId;
300
296
  const conversationId = isChatroom
301
297
  ? (message as any).chatroomId
302
- : message.fromUser || message.toUser || '';
298
+ : message.fromUser || message.toUser || "";
303
299
 
304
300
  // Convert to cache format and store locally
305
- if (cacheManager && accountId) {
301
+ if (cfg) {
306
302
  try {
307
- await cacheManager.onMessage({
303
+ const cache = getCacheManager(cfg);
304
+ const wechatMessage: WeChatMessage = {
308
305
  messageId: message.messageId,
309
306
  msgSvrId: message.msgSvrId,
310
- accountId: accountId,
311
- conversationType: isChatroom ? 'chatroom' : 'friend',
307
+ accountId: accountId || "default",
308
+ conversationType: isChatroom ? "chatroom" : "friend",
312
309
  conversationId,
313
- senderId: message.fromUser || '',
310
+ senderId: message.fromUser || "",
314
311
  content: message.content,
315
312
  messageType: message.type || 1,
316
313
  timestamp: message.timestamp || Date.now(),
317
314
  isSelf: message.isSelf || false,
318
- direction: message.isSelf ? 'outbound' : 'inbound',
319
- });
315
+ direction: message.isSelf ? "outbound" : "inbound",
316
+ };
317
+ await cache.onMessage(wechatMessage);
320
318
  } catch (err) {
321
- console.error('[CelPhoneWeChat] Cache write failed:', err);
319
+ console.error("[CelPhoneWeChat] Cache write failed:", err);
322
320
  }
323
321
  }
324
322
 
@@ -327,16 +325,16 @@ export async function handleInboundMessage(
327
325
  id: message.messageId,
328
326
  rawId: message.msgSvrId || message.messageId,
329
327
  conversation: {
330
- type: isChatroom ? 'group' as const : 'dm' as const,
328
+ type: isChatroom ? "group" as const : "dm" as const,
331
329
  id: conversationId,
332
330
  chatroomId: isChatroom ? conversationId : undefined,
333
331
  },
334
332
  sender: {
335
- id: message.fromUser || '',
333
+ id: message.fromUser || "",
336
334
  platformId: message.wechatId,
337
335
  },
338
336
  content: {
339
- type: message.type === 1 ? 'text' : 'media',
337
+ type: message.type === 1 ? "text" as const : "media" as const,
340
338
  text: message.content,
341
339
  },
342
340
  timestamp: new Date(message.timestamp || Date.now()),
@@ -345,7 +343,7 @@ export async function handleInboundMessage(
345
343
 
346
344
  // Dispatch to OpenClaw
347
345
  await api.inbound.dispatchMessage(openclawMessage);
348
- } else if (event === 'friend_request' && friendRequest) {
346
+ } else if (event === "friend_request" && friendRequest) {
349
347
  // Handle friend request
350
348
  await api.inbound.dispatchFriendRequest({
351
349
  id: friendRequest.v1,
package/src/client.ts CHANGED
@@ -107,7 +107,7 @@ export class WorkPhoneWeChatClient {
107
107
  throw new Error(`WorkPhone API error: ${response.status} - ${error}`);
108
108
  }
109
109
 
110
- return response.json();
110
+ return response.json() as Promise<T>;
111
111
  }
112
112
 
113
113
  // ========== WeChat Account Operations ==========
package/test-cache.ts CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  CacheManager,
11
11
  WeChatAccount,
12
12
  WeChatMessage,
13
- } from './src/cache/index.js';
13
+ } from "@gloablehive/wechat-cache";
14
14
 
15
15
  const TEST_CACHE_PATH = '/tmp/wechat-cache-test';
16
16
  const ENCODING = 'utf-8';