@gloablehive/celphone-wechat-plugin 1.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.
Files changed (45) hide show
  1. package/INSTALL.md +231 -0
  2. package/README.md +259 -0
  3. package/dist/index-simple.js +9 -0
  4. package/dist/index.d.ts +16 -0
  5. package/dist/index.js +77 -0
  6. package/dist/mock-server.d.ts +6 -0
  7. package/dist/mock-server.js +203 -0
  8. package/dist/openclaw.plugin.json +96 -0
  9. package/dist/setup-entry.d.ts +9 -0
  10. package/dist/setup-entry.js +8 -0
  11. package/dist/src/cache/compactor.d.ts +36 -0
  12. package/dist/src/cache/compactor.js +154 -0
  13. package/dist/src/cache/extractor.d.ts +48 -0
  14. package/dist/src/cache/extractor.js +120 -0
  15. package/dist/src/cache/index.d.ts +15 -0
  16. package/dist/src/cache/index.js +16 -0
  17. package/dist/src/cache/indexer.d.ts +41 -0
  18. package/dist/src/cache/indexer.js +262 -0
  19. package/dist/src/cache/manager.d.ts +113 -0
  20. package/dist/src/cache/manager.js +271 -0
  21. package/dist/src/cache/message-queue.d.ts +59 -0
  22. package/dist/src/cache/message-queue.js +147 -0
  23. package/dist/src/cache/saas-connector.d.ts +94 -0
  24. package/dist/src/cache/saas-connector.js +289 -0
  25. package/dist/src/cache/syncer.d.ts +60 -0
  26. package/dist/src/cache/syncer.js +177 -0
  27. package/dist/src/cache/types.d.ts +198 -0
  28. package/dist/src/cache/types.js +43 -0
  29. package/dist/src/cache/writer.d.ts +81 -0
  30. package/dist/src/cache/writer.js +461 -0
  31. package/dist/src/channel.d.ts +65 -0
  32. package/dist/src/channel.js +334 -0
  33. package/dist/src/client.d.ts +280 -0
  34. package/dist/src/client.js +248 -0
  35. package/index-simple.ts +11 -0
  36. package/index.ts +89 -0
  37. package/mock-server.ts +237 -0
  38. package/openclaw.plugin.json +98 -0
  39. package/package.json +37 -0
  40. package/setup-entry.ts +10 -0
  41. package/src/channel.ts +398 -0
  42. package/src/client.ts +412 -0
  43. package/test-cache.ts +260 -0
  44. package/test-integration.ts +319 -0
  45. package/tsconfig.json +22 -0
@@ -0,0 +1,248 @@
1
+ /**
2
+ * WorkPhone WeChat API Client
3
+ *
4
+ * Interfaces with the WorkPhone API for sending/receiving WeChat messages
5
+ * Based on the Apifox API manual: data/apifox-workphone-3/
6
+ */
7
+ export class WorkPhoneWeChatClient {
8
+ config;
9
+ constructor(config) {
10
+ this.config = config;
11
+ }
12
+ async request(method, path, body, query) {
13
+ const url = new URL(`${this.config.baseUrl}${path}`);
14
+ if (query) {
15
+ Object.entries(query).forEach(([k, v]) => url.searchParams.append(k, v));
16
+ }
17
+ const headers = {
18
+ 'Authorization': `Bearer ${this.config.apiKey}`,
19
+ 'Content-Type': 'application/json',
20
+ };
21
+ if (this.config.accountId) {
22
+ headers['X-Account-Id'] = this.config.accountId;
23
+ }
24
+ const response = await fetch(url.toString(), {
25
+ method,
26
+ headers,
27
+ body: body ? JSON.stringify(body) : undefined,
28
+ });
29
+ if (!response.ok) {
30
+ const error = await response.text();
31
+ throw new Error(`WorkPhone API error: ${response.status} - ${error}`);
32
+ }
33
+ return response.json();
34
+ }
35
+ // ========== WeChat Account Operations ==========
36
+ /**
37
+ * Get all WeChat accounts for an account
38
+ * GET /api/v1/accounts/{accountId}/wechatAccounts
39
+ */
40
+ async getWeChatAccounts(accountId) {
41
+ return this.request('GET', `/api/v1/accounts/${accountId}/wechatAccounts`);
42
+ }
43
+ /**
44
+ * Get WeChat account info by wechatId
45
+ * GET /wp/api/v1/wechatAccounts/{wechatId}/byWechatId
46
+ */
47
+ async getWeChatAccountById(wechatId) {
48
+ return this.request('GET', `/wp/api/v1/wechatAccounts/${wechatId}/byWechatId`);
49
+ }
50
+ // ========== Friend Operations ==========
51
+ /**
52
+ * Get all friends for a WeChat account
53
+ * GET /wp/api/v1/wechatAccounts/{wechatAccountId}/friends
54
+ */
55
+ async getFriends(wechatAccountId, options) {
56
+ return this.request('GET', `/wp/api/v1/wechatAccounts/${wechatAccountId}/friends`, undefined, options);
57
+ }
58
+ /**
59
+ * Search friends
60
+ * GET /wp/api/v1/wechatFriends
61
+ */
62
+ async searchFriends(params) {
63
+ return this.request('GET', '/wp/api/v1/wechatFriends', undefined, params);
64
+ }
65
+ /**
66
+ * Get friend by ID
67
+ * GET /wp/api/v1/wechatFriends/{wechatFriendId}
68
+ */
69
+ async getFriend(wechatFriendId) {
70
+ return this.request('GET', `/wp/api/v1/wechatFriends/${wechatFriendId}`);
71
+ }
72
+ /**
73
+ * Add friend (send friend request)
74
+ * POST /wp/api/v1/wechatFriends/addFriend
75
+ */
76
+ async addFriend(params) {
77
+ return this.request('POST', '/wp/api/v1/wechatFriends/addFriend', params);
78
+ }
79
+ /**
80
+ * Accept friend request
81
+ * POST /wp/api/v1/wechatFriends/{wechatFriendId}/acceptFriend
82
+ */
83
+ async acceptFriend(wechatFriendId) {
84
+ return this.request('POST', `/wp/api/v1/wechatFriends/${wechatFriendId}/acceptFriend`);
85
+ }
86
+ /**
87
+ * Update friend remark
88
+ * PUT /wp/api/v1/wechatFriends/{wechatFriendId}/remark
89
+ */
90
+ async updateFriendRemark(wechatFriendId, remark) {
91
+ return this.request('PUT', `/wp/api/v1/wechatFriends/${wechatFriendId}/remark`, { remark });
92
+ }
93
+ /**
94
+ * Update friend labels
95
+ * PUT /wp/api/v1/wechatFriends/{wechatFriendId}/label
96
+ */
97
+ async updateFriendLabels(wechatFriendId, labelIds) {
98
+ return this.request('PUT', `/wp/api/v1/wechatFriends/${wechatFriendId}/label`, { labelIds });
99
+ }
100
+ // ========== Chatroom (Group) Operations ==========
101
+ /**
102
+ * Get all chatrooms for a WeChat account
103
+ * GET /wp/api/v1/wechatAccounts/{wechatAccountId}/chatrooms
104
+ */
105
+ async getChatrooms(wechatAccountId) {
106
+ return this.request('GET', `/wp/api/v1/wechatAccounts/${wechatAccountId}/chatrooms`);
107
+ }
108
+ /**
109
+ * Get chatroom info by chatroomId
110
+ * GET /wp/api/v1/wechatChatrooms/{wechatChatroomId}
111
+ */
112
+ async getChatroom(wechatChatroomId) {
113
+ return this.request('GET', `/wp/api/v1/wechatChatrooms/${wechatChatroomId}`);
114
+ }
115
+ /**
116
+ * Update chatroom name (announcement)
117
+ * PUT /wp/api/v1/wechatChatrooms/{wechatChatroomId}/chatroomName
118
+ */
119
+ async updateChatroomName(wechatChatroomId, name) {
120
+ return this.request('PUT', `/wp/api/v1/wechatChatrooms/${wechatChatroomId}/chatroomName`, { name });
121
+ }
122
+ /**
123
+ * Publish chatroom announcement
124
+ * PUT /api/v1/wechat/chatroom/announcement
125
+ */
126
+ async publishChatroomAnnouncement(wechatChatroomId, content) {
127
+ return this.request('PUT', '/api/v1/wechat/chatroom/announcement', {
128
+ wechatChatroomId,
129
+ content,
130
+ });
131
+ }
132
+ // ========== Message Operations ==========
133
+ /**
134
+ * Get friend messages (1-on-1 chat)
135
+ * GET /wp/api/v1/wechat/friendMessages
136
+ */
137
+ async getFriendMessages(params) {
138
+ return this.request('GET', '/wp/api/v1/wechat/friendMessages', undefined, params);
139
+ }
140
+ /**
141
+ * Get chatroom messages (group chat)
142
+ * GET /wp/api/v1/wechat/chatroomMessages
143
+ */
144
+ async getChatroomMessages(params) {
145
+ return this.request('GET', '/wp/api/v1/wechat/chatroomMessages', undefined, params);
146
+ }
147
+ /**
148
+ * Send friend message (1-on-1)
149
+ * POST /wp/api/v1/wechat/friendMessages
150
+ */
151
+ async sendFriendMessage(params) {
152
+ return this.request('POST', '/wp/api/v1/wechat/friendMessages', params);
153
+ }
154
+ /**
155
+ * Send chatroom message (group)
156
+ * POST /wp/api/v1/wechat/chatroomMessages
157
+ */
158
+ async sendChatroomMessage(params) {
159
+ return this.request('POST', '/wp/api/v1/wechat/chatroomMessages', params);
160
+ }
161
+ // ========== Moments (朋友圈) Operations ==========
162
+ /**
163
+ * Get moments
164
+ * GET /wp/api/v1/moments
165
+ */
166
+ async getMoments(params) {
167
+ return this.request('GET', '/wp/api/v1/moments', undefined, params);
168
+ }
169
+ /**
170
+ * Publish moment (post to Moments)
171
+ * POST /wp/api/v1/moments
172
+ */
173
+ async publishMoment(params) {
174
+ return this.request('POST', '/wp/api/v1/moments', params);
175
+ }
176
+ /**
177
+ * Comment on a moment
178
+ * POST /wp/api/v1/moments/{momentId}/comment
179
+ */
180
+ async commentMoment(momentId, content, replyCommentId) {
181
+ return this.request('POST', `/wp/api/v1/moments/${momentId}/comment`, {
182
+ content,
183
+ replyCommentId,
184
+ });
185
+ }
186
+ /**
187
+ * Like a moment
188
+ * POST /wp/api/v1/moments/{momentId}/like
189
+ */
190
+ async likeMoment(momentId) {
191
+ return this.request('POST', `/wp/api/v1/moments/${momentId}/like`);
192
+ }
193
+ // ========== Media Download ==========
194
+ /**
195
+ * Download friend message file
196
+ */
197
+ async downloadFriendFile(messageId, wechatTime) {
198
+ const url = `${this.config.baseUrl}/wp/api/v1/wechat/friendMessage/downloadFile/${messageId}/${wechatTime}`;
199
+ const response = await fetch(url, {
200
+ headers: { 'Authorization': `Bearer ${this.config.apiKey}` },
201
+ });
202
+ if (!response.ok) {
203
+ throw new Error(`Download failed: ${response.status}`);
204
+ }
205
+ return response.blob();
206
+ }
207
+ /**
208
+ * Download chatroom message file
209
+ */
210
+ async downloadChatroomFile(messageId, wechatTime) {
211
+ const url = `${this.config.baseUrl}/wp/api/v1/wechat/chatroomMessage/downloadFile/${messageId}/${wechatTime}`;
212
+ const response = await fetch(url, {
213
+ headers: { 'Authorization': `Bearer ${this.config.apiKey}` },
214
+ });
215
+ if (!response.ok) {
216
+ throw new Error(`Download failed: ${response.status}`);
217
+ }
218
+ return response.blob();
219
+ }
220
+ // ========== Webhook Parsing ==========
221
+ /**
222
+ * Parse webhook payload from WorkPhone callback
223
+ * This should be called by your webhook endpoint handler
224
+ */
225
+ parseWebhookPayload(payload) {
226
+ // WorkPhone webhook format - adjust based on actual webhook docs
227
+ const eventMap = {
228
+ 'msg': 'message',
229
+ 'friend_request': 'friend_request',
230
+ 'group_invite': 'group_invite',
231
+ 'system': 'system',
232
+ };
233
+ return {
234
+ event: eventMap[payload.event] || 'message',
235
+ accountId: payload.accountId || '',
236
+ wechatAccountId: payload.wechatAccountId || '',
237
+ message: payload.message,
238
+ friendRequest: payload.friendRequest,
239
+ timestamp: payload.timestamp || Date.now(),
240
+ };
241
+ }
242
+ }
243
+ /**
244
+ * Factory function to create a WorkPhone client from OpenClaw config
245
+ */
246
+ export function createWorkPhoneClient(config) {
247
+ return new WorkPhoneWeChatClient(config);
248
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * OpenClaw Channel Plugin Entry Point - Simplified Version
3
+ *
4
+ * WorkPhone WeChat Plugin - 最小可运行版本
5
+ */
6
+
7
+ import { celPhoneWeChatPlugin } from "./src/channel.js";
8
+
9
+ // 直接导出插件,不使用复杂包装
10
+ export default celPhoneWeChatPlugin;
11
+ export { celPhoneWeChatPlugin };
package/index.ts ADDED
@@ -0,0 +1,89 @@
1
+ /**
2
+ * OpenClaw Channel Plugin Entry Point
3
+ *
4
+ * WorkPhone WeChat Plugin - enables OpenClaw to send/receive WeChat messages
5
+ * through the WorkPhone API platform.
6
+ */
7
+
8
+ import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
9
+ import { celPhoneWeChatPlugin, handleInboundMessage } from "./src/channel.js";
10
+
11
+ export default defineChannelPluginEntry({
12
+ id: "celphone-wechat",
13
+ name: "WorkPhone WeChat",
14
+ description: "Connect OpenClaw to WorkPhone WeChat API for sending and receiving WeChat messages",
15
+ plugin: celPhoneWeChatPlugin,
16
+
17
+ registerCliMetadata(api) {
18
+ api.registerCli(
19
+ ({ program }) => {
20
+ program
21
+ .command("celphone-wechat")
22
+ .description("WorkPhone WeChat channel management")
23
+ .option("-a, --account <id>", "WeChat account ID")
24
+ .option("-l, --list", "List configured WeChat accounts");
25
+ },
26
+ {
27
+ descriptors: [
28
+ {
29
+ name: "celphone-wechat",
30
+ description: "WorkPhone WeChat channel",
31
+ hasSubcommands: true,
32
+ },
33
+ ],
34
+ }
35
+ );
36
+ },
37
+
38
+ registerFull(api) {
39
+ // Register webhook endpoint for receiving messages from WorkPhone
40
+ api.registerHttpRoute({
41
+ path: "/celphone-wechat/webhook",
42
+ auth: "plugin", // Plugin-managed auth - verify signatures yourself
43
+ handler: async (req, res) => {
44
+ try {
45
+ // Parse the webhook payload
46
+ // The exact format depends on WorkPhone's webhook configuration
47
+ const payload = req.body;
48
+
49
+ // Verify the request is from WorkPhone
50
+ // Add signature verification here if needed
51
+ const signature = req.headers["x-workphone-signature"];
52
+ if (!signature) {
53
+ res.statusCode = 401;
54
+ res.end("Missing signature");
55
+ return true;
56
+ }
57
+
58
+ // Handle the inbound message
59
+ await handleInboundMessage(api, payload);
60
+
61
+ res.statusCode = 200;
62
+ res.end("ok");
63
+ return true;
64
+ } catch (error) {
65
+ console.error("Webhook error:", error);
66
+ res.statusCode = 500;
67
+ res.end("Internal error");
68
+ return true;
69
+ }
70
+ },
71
+ });
72
+
73
+ // Register gateway method for outbound media handling
74
+ api.registerGatewayMethod({
75
+ method: "POST",
76
+ path: "/celphone-wechat/media",
77
+ handler: async (req, res) => {
78
+ const { messageId, mediaUrl, mediaType } = req.body;
79
+
80
+ // Handle media upload/send through WorkPhone
81
+ // This is called when the bot needs to send media files
82
+
83
+ res.statusCode = 200;
84
+ res.json({ success: true, messageId });
85
+ return true;
86
+ },
87
+ });
88
+ },
89
+ });
package/mock-server.ts ADDED
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Mock WorkPhone API Server for Testing
3
+ *
4
+ * 模拟 WorkPhone 的 API 端点,用于本地测试
5
+ */
6
+
7
+ import express from 'express';
8
+ import { createServer } from 'http';
9
+
10
+ const app = express();
11
+ app.use(express.json());
12
+
13
+ // 配置
14
+ const MOCK_CONFIG = {
15
+ apiKey: 'test-api-key-12345',
16
+ wechatAccountId: 'mock-wechat-001',
17
+ wechatId: 'wxid_mock001',
18
+ baseUrl: 'http://localhost:18999',
19
+ };
20
+
21
+ // 存储消息
22
+ const messages: any[] = [];
23
+ const friends: any[] = [
24
+ { wechatId: 'wxid_customer001', remark: '客户A', nickname: '张三' },
25
+ { wechatId: 'wxid_customer002', remark: '客户B', nickname: '李四' },
26
+ { wechatId: 'wxid_customer003', remark: '潜在客户', nickname: '王五' },
27
+ ];
28
+
29
+ // ============ API 端点 ============
30
+
31
+ // 健康检查
32
+ app.get('/health', (req, res) => {
33
+ res.json({ status: 'ok' });
34
+ });
35
+
36
+ // 获取账号列表
37
+ app.get('/api/v1/wechat/accounts', (req, res) => {
38
+ const auth = req.headers.authorization;
39
+ if (auth !== `Bearer ${MOCK_CONFIG.apiKey}`) {
40
+ return res.status(401).json({ error: 'unauthorized' });
41
+ }
42
+
43
+ res.json({
44
+ data: [
45
+ {
46
+ accountId: MOCK_CONFIG.wechatAccountId,
47
+ wechatId: MOCK_CONFIG.wechatId,
48
+ nickname: '测试客服',
49
+ status: 'online',
50
+ }
51
+ ]
52
+ });
53
+ });
54
+
55
+ // 获取好友列表
56
+ app.get(`/api/v1/wechat/accounts/${MOCK_CONFIG.wechatAccountId}/friends`, (req, res) => {
57
+ const auth = req.headers.authorization;
58
+ if (auth !== `Bearer ${MOCK_CONFIG.apiKey}`) {
59
+ return res.status(401).json({ error: 'unauthorized' });
60
+ }
61
+
62
+ res.json({ data: friends });
63
+ });
64
+
65
+ // 发送好友消息
66
+ app.post(`/api/v1/wechat/accounts/${MOCK_CONFIG.wechatAccountId}/messages/friend`, (req, res) => {
67
+ const auth = req.headers.authorization;
68
+ if (auth !== `Bearer ${MOCK_CONFIG.apiKey}`) {
69
+ return res.status(401).json({ error: 'unauthorized' });
70
+ }
71
+
72
+ const { friendWechatId, content, type } = req.body;
73
+
74
+ console.log(`[Mock] 发送消息给 ${friendWechatId}: ${content}`);
75
+
76
+ const messageId = `msg_${Date.now()}`;
77
+ messages.push({
78
+ messageId,
79
+ friendWechatId,
80
+ content,
81
+ type,
82
+ timestamp: Date.now(),
83
+ direction: 'outbound',
84
+ });
85
+
86
+ res.json({
87
+ code: 0,
88
+ message: 'success',
89
+ data: {
90
+ messageId,
91
+ msgSvrId: `svr_${Date.now()}`,
92
+ }
93
+ });
94
+ });
95
+
96
+ // 发送群消息
97
+ app.post(`/api/v1/wechat/accounts/${MOCK_CONFIG.wechatAccountId}/messages/chatroom`, (req, res) => {
98
+ const auth = req.headers.authorization;
99
+ if (auth !== `Bearer ${MOCK_CONFIG.apiKey}`) {
100
+ return res.status(401).json({ error: 'unauthorized' });
101
+ }
102
+
103
+ const { chatroomId, content, type, atUsers } = req.body;
104
+
105
+ console.log(`[Mock] 发送消息到群 ${chatroomId}: ${content}`);
106
+
107
+ const messageId = `msg_${Date.now()}`;
108
+ messages.push({
109
+ messageId,
110
+ chatroomId,
111
+ content,
112
+ type,
113
+ atUsers,
114
+ timestamp: Date.now(),
115
+ direction: 'outbound',
116
+ });
117
+
118
+ res.json({
119
+ code: 0,
120
+ message: 'success',
121
+ data: {
122
+ messageId,
123
+ msgSvrId: `svr_${Date.now()}`,
124
+ }
125
+ });
126
+ });
127
+
128
+ // Webhook 配置(返回当前 webhook 地址)
129
+ app.get(`/api/v1/wechat/accounts/${MOCK_CONFIG.wechatAccountId}/webhook`, (req, res) => {
130
+ res.json({
131
+ data: {
132
+ url: 'http://localhost:18999/webhook',
133
+ enabled: true,
134
+ }
135
+ });
136
+ });
137
+
138
+ // 设置 webhook
139
+ app.put(`/api/v1/wechat/accounts/${MOCK_CONFIG.wechatAccountId}/webhook`, (req, res) => {
140
+ const { url } = req.body;
141
+ console.log(`[Mock] Webhook 设置为: ${url}`);
142
+ res.json({ code: 0, message: 'success' });
143
+ });
144
+
145
+ // ============ Webhook 模拟触发 ============
146
+
147
+ // 模拟收到好友消息
148
+ app.post('/mock/trigger/friend-message', (req, res) => {
149
+ const { wechatId, content } = req.body;
150
+
151
+ const payload = {
152
+ event: 'message',
153
+ accountId: MOCK_CONFIG.wechatAccountId,
154
+ wechatAccountId: MOCK_CONFIG.wechatAccountId,
155
+ message: {
156
+ messageId: `in_msg_${Date.now()}`,
157
+ msgSvrId: `svr_${Date.now()}`,
158
+ fromUser: wechatId || 'wxid_customer001',
159
+ toUser: MOCK_CONFIG.wechatId,
160
+ content: content || '你好,这是测试消息',
161
+ type: 1,
162
+ timestamp: Date.now(),
163
+ isSelf: false,
164
+ }
165
+ };
166
+
167
+ // 回调(如果配置了 webhook)
168
+ // 实际使用时会让用户配置真实的 webhook URL
169
+
170
+ res.json({
171
+ code: 0,
172
+ message: 'triggered',
173
+ payload
174
+ });
175
+ });
176
+
177
+ // 模拟收到群消息
178
+ app.post('/mock/trigger/chatroom-message', (req, res) => {
179
+ const { chatroomId, content } = req.body;
180
+
181
+ const payload = {
182
+ event: 'message',
183
+ accountId: MOCK_CONFIG.wechatAccountId,
184
+ wechatAccountId: MOCK_CONFIG.wechatAccountId,
185
+ message: {
186
+ messageId: `in_msg_${Date.now()}`,
187
+ msgSvrId: `svr_${Date.now()}`,
188
+ fromUser: 'wxid_customer001',
189
+ chatroomId: chatroomId || '123456789@chatroom',
190
+ content: content || '大家好',
191
+ type: 1,
192
+ timestamp: Date.now(),
193
+ isSelf: false,
194
+ }
195
+ };
196
+
197
+ res.json({
198
+ code: 0,
199
+ message: 'triggered',
200
+ payload
201
+ });
202
+ });
203
+
204
+ // 获取消息历史
205
+ app.get('/mock/messages', (req, res) => {
206
+ res.json({ data: messages });
207
+ });
208
+
209
+ // 启动服务器
210
+ const PORT = 18999;
211
+ const server = createServer(app);
212
+
213
+ server.listen(PORT, () => {
214
+ console.log(`
215
+ ╔═══════════════════════════════════════════════════════════╗
216
+ ║ Mock WorkPhone API Server ║
217
+ ╠═══════════════════════════════════════════════════════════╣
218
+ ║ Base URL: http://localhost:${PORT} ║
219
+ ║ API Key: ${MOCK_CONFIG.apiKey} ║
220
+ ║ WeChat Account: ${MOCK_CONFIG.wechatAccountId} ║
221
+ ╠═══════════════════════════════════════════════════════════╣
222
+ ║ 测试端点: ║
223
+ ║ - POST /mock/trigger/friend-message 模拟好友消息 ║
224
+ ║ - POST /mock/trigger/chatroom-message 模拟群消息 ║
225
+ ║ - GET /mock/messages 查看消息历史 ║
226
+ ╚═══════════════════════════════════════════════════════════╝
227
+ `);
228
+ });
229
+
230
+ // 优雅关闭
231
+ process.on('SIGINT', () => {
232
+ console.log('\n[Mock] 关闭服务器...');
233
+ server.close(() => {
234
+ console.log('[Mock] 服务器已关闭');
235
+ process.exit(0);
236
+ });
237
+ });
@@ -0,0 +1,98 @@
1
+ {
2
+ "$schema": "https://openclaw.ai/schema/plugin.json",
3
+ "id": "celphone-wechat",
4
+ "name": "WorkPhone WeChat",
5
+ "version": "1.0.0",
6
+ "description": "Connect OpenClaw to WorkPhone WeChat API for sending and receiving WeChat messages",
7
+ "author": {
8
+ "name": "gloablehive",
9
+ "url": "https://github.com/gloablehive"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/gloablehive/channels/celphone-wechat-plugin"
14
+ },
15
+ "keywords": ["openclaw", "channel", "wechat", "workphone", "messaging"],
16
+ "license": "MIT",
17
+ "type": "channel",
18
+ "capabilities": {
19
+ "inbound": {
20
+ "message": true,
21
+ "friend_request": true,
22
+ "group_invite": false
23
+ },
24
+ "outbound": {
25
+ "text": true,
26
+ "media": true,
27
+ "link": true,
28
+ "location": true,
29
+ "contact": true
30
+ },
31
+ "features": {
32
+ "pairing": false,
33
+ "reactions": false,
34
+ "editing": false,
35
+ "deleting": false,
36
+ "threads": true
37
+ }
38
+ },
39
+ "config": {
40
+ "type": "object",
41
+ "properties": {
42
+ "apiKey": {
43
+ "type": "string",
44
+ "description": "WorkPhone API key for authentication",
45
+ "secret": true,
46
+ "required": true
47
+ },
48
+ "baseUrl": {
49
+ "type": "string",
50
+ "description": "WorkPhone API base URL",
51
+ "default": "https://api.workphone.example.com"
52
+ },
53
+ "accountId": {
54
+ "type": "string",
55
+ "description": "WorkPhone account ID"
56
+ },
57
+ "wechatAccountId": {
58
+ "type": "string",
59
+ "description": "WeChat account ID to use (from WorkPhone) - REQUIRED",
60
+ "required": true
61
+ },
62
+ "wechatId": {
63
+ "type": "string",
64
+ "description": "The actual WeChat ID (wxid_xxx) for this account"
65
+ },
66
+ "nickName": {
67
+ "type": "string",
68
+ "description": "Display name for this WeChat account"
69
+ },
70
+ "allowFrom": {
71
+ "type": "array",
72
+ "items": { "type": "string" },
73
+ "description": "List of WeChat IDs that can DM the agent",
74
+ "default": []
75
+ },
76
+ "dmSecurity": {
77
+ "type": "string",
78
+ "enum": ["allowlist", "blocklist", "allowall"],
79
+ "description": "DM security policy",
80
+ "default": "allowlist"
81
+ },
82
+ "webhookSecret": {
83
+ "type": "string",
84
+ "description": "Secret for verifying webhook requests",
85
+ "secret": true
86
+ }
87
+ },
88
+ "required": ["apiKey", "wechatAccountId"]
89
+ },
90
+ "permissions": {
91
+ "channels": ["celphone-wechat"],
92
+ "http": {
93
+ "outbound": ["*"],
94
+ "inbound": ["/celphone-wechat/webhook"]
95
+ }
96
+ },
97
+ "apiVersion": "1.0.0"
98
+ }