@gloablehive/ipad-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.
- package/index.ts +39 -0
- package/openclaw.plugin.json +91 -0
- package/package.json +30 -0
- package/src/channel.ts +250 -0
- package/src/client.ts +272 -0
- package/tsconfig.json +16 -0
package/index.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw Channel Plugin Entry Point
|
|
3
|
+
*
|
|
4
|
+
* iPad WeChat Plugin - enables OpenClaw to send/receive WeChat messages
|
|
5
|
+
* through the iPad protocol (different from WorkPhone/phone protocol)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
|
9
|
+
import { ipadWeChatPlugin } from "./src/channel.js";
|
|
10
|
+
|
|
11
|
+
export default defineChannelPluginEntry({
|
|
12
|
+
id: "ipad-wechat",
|
|
13
|
+
name: "iPad WeChat",
|
|
14
|
+
description: "Connect OpenClaw to iPad WeChat protocol for sending and receiving WeChat messages",
|
|
15
|
+
plugin: ipadWeChatPlugin,
|
|
16
|
+
|
|
17
|
+
registerFull(api) {
|
|
18
|
+
// Register webhook endpoint for receiving messages
|
|
19
|
+
api.registerHttpRoute({
|
|
20
|
+
path: "/ipad-wechat/webhook",
|
|
21
|
+
auth: "plugin",
|
|
22
|
+
handler: async (req, res) => {
|
|
23
|
+
try {
|
|
24
|
+
const payload = req.body;
|
|
25
|
+
// Handle inbound message
|
|
26
|
+
console.log("[iPad WeChat] Received webhook:", payload);
|
|
27
|
+
res.statusCode = 200;
|
|
28
|
+
res.end("ok");
|
|
29
|
+
return true;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("[iPad WeChat] Webhook error:", error);
|
|
32
|
+
res.statusCode = 500;
|
|
33
|
+
res.end("Internal error");
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://openclaw.ai/schema/plugin.json",
|
|
3
|
+
"id": "ipad-wechat",
|
|
4
|
+
"name": "iPad WeChat",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"description": "Connect OpenClaw to iPad WeChat protocol 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/ipad-wechat-plugin"
|
|
14
|
+
},
|
|
15
|
+
"keywords": ["openclaw", "channel", "wechat", "ipad", "messaging"],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"type": "channel",
|
|
18
|
+
"capabilities": {
|
|
19
|
+
"inbound": {
|
|
20
|
+
"message": true,
|
|
21
|
+
"friend_request": true,
|
|
22
|
+
"group_invite": true
|
|
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
|
+
"configSchema": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"properties": {
|
|
42
|
+
"apiKey": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "iPad WeChat API key for authentication",
|
|
45
|
+
"secret": true,
|
|
46
|
+
"required": true
|
|
47
|
+
},
|
|
48
|
+
"baseUrl": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"description": "iPad WeChat API base URL"
|
|
51
|
+
},
|
|
52
|
+
"accountId": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"description": "Internal account ID for routing"
|
|
55
|
+
},
|
|
56
|
+
"wechatAccountId": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"description": "WeChat account ID in the system"
|
|
59
|
+
},
|
|
60
|
+
"wechatId": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"description": "WeChat ID (wxid_xxx)"
|
|
63
|
+
},
|
|
64
|
+
"nickName": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"description": "Display name for this WeChat account"
|
|
67
|
+
},
|
|
68
|
+
"allowFrom": {
|
|
69
|
+
"type": "array",
|
|
70
|
+
"items": { "type": "string" },
|
|
71
|
+
"description": "List of WeChat IDs that can DM the agent",
|
|
72
|
+
"default": []
|
|
73
|
+
},
|
|
74
|
+
"dmSecurity": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"enum": ["allowlist", "blocklist", "allowall"],
|
|
77
|
+
"description": "DM security policy",
|
|
78
|
+
"default": "allowlist"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"required": ["apiKey", "baseUrl"]
|
|
82
|
+
},
|
|
83
|
+
"permissions": {
|
|
84
|
+
"channels": ["ipad-wechat"],
|
|
85
|
+
"http": {
|
|
86
|
+
"outbound": ["*"],
|
|
87
|
+
"inbound": ["/ipad-wechat/webhook"]
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"apiVersion": "1.0.0"
|
|
91
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gloablehive/ipad-wechat-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "OpenClaw channel plugin for iPad WeChat protocol - enables sending/receiving WeChat messages through iPad protocol",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"test": "npx tsx test-ipad.ts",
|
|
10
|
+
"dev": "tsx watch index.ts"
|
|
11
|
+
},
|
|
12
|
+
"openclaw": {
|
|
13
|
+
"extensions": [
|
|
14
|
+
"./index.ts"
|
|
15
|
+
],
|
|
16
|
+
"channel": {
|
|
17
|
+
"id": "ipad-wechat",
|
|
18
|
+
"label": "iPad WeChat",
|
|
19
|
+
"blurb": "Connect OpenClaw to iPad WeChat protocol for sending and receiving WeChat messages"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@gloablehive/wechat-cache": "^1.0.0",
|
|
24
|
+
"openclaw": ">=1.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20.0.0",
|
|
28
|
+
"typescript": "^5.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/channel.ts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* iPad WeChat Channel Plugin for OpenClaw
|
|
3
|
+
*
|
|
4
|
+
* Uses iPad protocol (different from phone/WorkPhone protocol)
|
|
5
|
+
* API documentation: wechat/api.md
|
|
6
|
+
*
|
|
7
|
+
* Includes Local Cache (shared with celphone-wechat-plugin):
|
|
8
|
+
* - Per-account, per-user/conversation MD files
|
|
9
|
+
* - YAML frontmatter (aligned with Claude Code)
|
|
10
|
+
* - MEMORY.md indexing
|
|
11
|
+
* - 4-layer compression
|
|
12
|
+
* - AI summary extraction
|
|
13
|
+
* - SAAS connectivity + offline fallback
|
|
14
|
+
* - Cloud sync
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
createChatChannelPlugin,
|
|
19
|
+
createChannelPluginBase,
|
|
20
|
+
} from "openclaw/plugin-sdk/core";
|
|
21
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
22
|
+
import { createIPadClient, type WebhookPayload } from "./client.js";
|
|
23
|
+
|
|
24
|
+
// Import cache modules from shared package
|
|
25
|
+
import {
|
|
26
|
+
createCacheManager,
|
|
27
|
+
CacheManager,
|
|
28
|
+
WeChatAccount,
|
|
29
|
+
WeChatMessage,
|
|
30
|
+
} from "@gloablehive/wechat-cache";
|
|
31
|
+
|
|
32
|
+
// Cache manager instance (lazy initialized)
|
|
33
|
+
let cacheManager: CacheManager | null = null;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get or create cache manager
|
|
37
|
+
*/
|
|
38
|
+
function getCacheManager(cfg: OpenClawConfig): CacheManager {
|
|
39
|
+
if (cacheManager) return cacheManager;
|
|
40
|
+
|
|
41
|
+
const section = (cfg.channels as Record<string, any>)?.["ipad-wechat"];
|
|
42
|
+
const accounts = (section?.accounts || []) as WeChatAccount[];
|
|
43
|
+
|
|
44
|
+
// If no accounts configured, create default from main config
|
|
45
|
+
if (accounts.length === 0 && section?.wechatAccountId) {
|
|
46
|
+
accounts.push({
|
|
47
|
+
accountId: section.accountId || "default",
|
|
48
|
+
wechatAccountId: section.wechatAccountId,
|
|
49
|
+
wechatId: section.wechatId || "",
|
|
50
|
+
nickName: section.nickName || "WeChat User",
|
|
51
|
+
enabled: true,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const basePath = section?.cachePath || "./cache/wechat-ipad";
|
|
56
|
+
|
|
57
|
+
cacheManager = createCacheManager({
|
|
58
|
+
basePath,
|
|
59
|
+
accounts,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return cacheManager;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface IPadWeChatResolvedAccount {
|
|
66
|
+
accountId: string | null;
|
|
67
|
+
apiKey: string;
|
|
68
|
+
baseUrl: string;
|
|
69
|
+
wechatAccountId: string;
|
|
70
|
+
wechatId: string;
|
|
71
|
+
nickName: string;
|
|
72
|
+
allowFrom: string[];
|
|
73
|
+
dmPolicy: string | undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function resolveAccount(
|
|
77
|
+
cfg: OpenClawConfig,
|
|
78
|
+
accountId?: string | null
|
|
79
|
+
): IPadWeChatResolvedAccount {
|
|
80
|
+
const section = (cfg.channels as Record<string, any>)?.["ipad-wechat"];
|
|
81
|
+
const apiKey = section?.apiKey;
|
|
82
|
+
const baseUrl = section?.baseUrl || "https://api.example.com";
|
|
83
|
+
|
|
84
|
+
if (!apiKey) {
|
|
85
|
+
throw new Error("ipad-wechat: apiKey is required");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
accountId: accountId ?? null,
|
|
90
|
+
apiKey,
|
|
91
|
+
baseUrl,
|
|
92
|
+
wechatAccountId: section?.wechatAccountId || "",
|
|
93
|
+
wechatId: section?.wechatId || "",
|
|
94
|
+
nickName: section?.nickName || "WeChat User",
|
|
95
|
+
allowFrom: section?.allowFrom ?? [],
|
|
96
|
+
dmPolicy: section?.dmSecurity,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const ipadWeChatPlugin = createChatChannelPlugin<IPadWeChatResolvedAccount>({
|
|
101
|
+
base: createChannelPluginBase({
|
|
102
|
+
id: "ipad-wechat",
|
|
103
|
+
setup: {
|
|
104
|
+
resolveAccount,
|
|
105
|
+
inspectAccount(cfg, accountId) {
|
|
106
|
+
const section = (cfg.channels as Record<string, any>)?.["ipad-wechat"];
|
|
107
|
+
const hasApiKey = Boolean(section?.apiKey);
|
|
108
|
+
return {
|
|
109
|
+
enabled: hasApiKey,
|
|
110
|
+
configured: hasApiKey,
|
|
111
|
+
tokenStatus: hasApiKey ? "available" : "missing",
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
|
|
117
|
+
security: {
|
|
118
|
+
dm: {
|
|
119
|
+
channelKey: "ipad-wechat",
|
|
120
|
+
resolvePolicy: (account) => account.dmPolicy,
|
|
121
|
+
resolveAllowFrom: (account) => account.allowFrom,
|
|
122
|
+
defaultPolicy: "allowlist",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
threading: {
|
|
127
|
+
topLevelReplyToMode: "reply",
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
outbound: {
|
|
131
|
+
attachedResults: {
|
|
132
|
+
sendText: async (params) => {
|
|
133
|
+
const client = createIPadClient({
|
|
134
|
+
baseUrl: params.account.baseUrl,
|
|
135
|
+
apiKey: params.account.apiKey,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Check if DM or group
|
|
139
|
+
const isChatroom = params.to?.includes("@chatroom");
|
|
140
|
+
|
|
141
|
+
if (isChatroom) {
|
|
142
|
+
const result = await client.sendRoomMessage({
|
|
143
|
+
roomId: params.to,
|
|
144
|
+
content: params.text,
|
|
145
|
+
});
|
|
146
|
+
return { messageId: result.messageId };
|
|
147
|
+
} else {
|
|
148
|
+
const result = await client.sendFriendMessage({
|
|
149
|
+
friendWechatId: params.to,
|
|
150
|
+
content: params.text,
|
|
151
|
+
});
|
|
152
|
+
return { messageId: result.messageId };
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
sendMedia: async (params) => {
|
|
157
|
+
const client = createIPadClient({
|
|
158
|
+
baseUrl: params.account.baseUrl,
|
|
159
|
+
apiKey: params.account.apiKey,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const isChatroom = params.to?.includes("@chatroom");
|
|
163
|
+
|
|
164
|
+
if (isChatroom) {
|
|
165
|
+
const result = await client.sendRoomMedia({
|
|
166
|
+
roomId: params.to,
|
|
167
|
+
filePath: params.filePath || "",
|
|
168
|
+
});
|
|
169
|
+
return { messageId: result.messageId };
|
|
170
|
+
} else {
|
|
171
|
+
const result = await client.sendFriendMedia({
|
|
172
|
+
friendWechatId: params.to,
|
|
173
|
+
filePath: params.filePath || "",
|
|
174
|
+
});
|
|
175
|
+
return { messageId: result.messageId };
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
capabilities: {
|
|
182
|
+
supportedMessageTypes: ["text", "image", "video", "file", "link", "location", "contact"],
|
|
183
|
+
maxAttachmentSize: 25 * 1024 * 1024,
|
|
184
|
+
supportsMarkdown: false,
|
|
185
|
+
supportsHtml: false,
|
|
186
|
+
supportsEmoji: true,
|
|
187
|
+
supportsReactions: false,
|
|
188
|
+
supportsThreads: true,
|
|
189
|
+
supportsEditing: false,
|
|
190
|
+
supportsDeleting: false,
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
export async function handleInboundMessage(
|
|
195
|
+
api: any,
|
|
196
|
+
payload: WebhookPayload,
|
|
197
|
+
cfg?: OpenClawConfig
|
|
198
|
+
): Promise<void> {
|
|
199
|
+
const { event, message } = payload;
|
|
200
|
+
|
|
201
|
+
if (event === "message" && message) {
|
|
202
|
+
const isChatroom = !!(message as any).roomId;
|
|
203
|
+
const conversationId = isChatroom
|
|
204
|
+
? (message as any).roomId
|
|
205
|
+
: message.fromUser || message.toUser || "";
|
|
206
|
+
|
|
207
|
+
const openclawMessage = {
|
|
208
|
+
id: message.messageId,
|
|
209
|
+
conversation: {
|
|
210
|
+
type: isChatroom ? "group" as const : "dm" as const,
|
|
211
|
+
id: conversationId,
|
|
212
|
+
},
|
|
213
|
+
sender: {
|
|
214
|
+
id: message.fromUser || "",
|
|
215
|
+
},
|
|
216
|
+
content: {
|
|
217
|
+
type: "text",
|
|
218
|
+
text: message.content,
|
|
219
|
+
},
|
|
220
|
+
timestamp: new Date(message.timestamp || Date.now()),
|
|
221
|
+
isSelf: message.isSelf || false,
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Write to cache
|
|
225
|
+
if (cfg) {
|
|
226
|
+
try {
|
|
227
|
+
const cache = getCacheManager(cfg);
|
|
228
|
+
const wechatMessage: WeChatMessage = {
|
|
229
|
+
messageId: message.messageId,
|
|
230
|
+
accountId: (cfg.channels as Record<string, any>)?.["ipad-wechat"]?.accountId || "default",
|
|
231
|
+
conversationType: isChatroom ? "chatroom" : "friend",
|
|
232
|
+
conversationId,
|
|
233
|
+
senderId: message.fromUser || "",
|
|
234
|
+
content: message.content,
|
|
235
|
+
messageType: message.type || 1,
|
|
236
|
+
timestamp: message.timestamp || Date.now(),
|
|
237
|
+
isSelf: message.isSelf || false,
|
|
238
|
+
direction: "inbound",
|
|
239
|
+
};
|
|
240
|
+
await cache.onMessage(wechatMessage);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
console.error("[iPad WeChat] Cache error:", error);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
await api.inbound.dispatchMessage(openclawMessage);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export default ipadWeChatPlugin;
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* iPad WeChat API Client
|
|
3
|
+
*
|
|
4
|
+
* Based on wechat/api.md documentation
|
|
5
|
+
* Different from WorkPhone - uses iPad protocol
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface IPadClientConfig {
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
apiKey: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface WebhookPayload {
|
|
14
|
+
event: string;
|
|
15
|
+
message?: {
|
|
16
|
+
messageId: string;
|
|
17
|
+
fromUser: string;
|
|
18
|
+
toUser: string;
|
|
19
|
+
content: string;
|
|
20
|
+
type: number;
|
|
21
|
+
timestamp: number;
|
|
22
|
+
isSelf: boolean;
|
|
23
|
+
roomId?: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SendTextResponse {
|
|
28
|
+
messageId: string;
|
|
29
|
+
msgSvrId: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SendMediaResponse {
|
|
33
|
+
messageId: string;
|
|
34
|
+
msgSvrId: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface Contact {
|
|
38
|
+
wechatId: string;
|
|
39
|
+
nickName: string;
|
|
40
|
+
remark: string;
|
|
41
|
+
avatar?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface RoomInfo {
|
|
45
|
+
roomId: string;
|
|
46
|
+
name: string;
|
|
47
|
+
ownerId: string;
|
|
48
|
+
memberCount: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getHeaders(apiKey: string) {
|
|
52
|
+
return {
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createIPadClient(config: IPadClientConfig) {
|
|
59
|
+
const { baseUrl, apiKey } = config;
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
// ========== 消息发送 ==========
|
|
63
|
+
|
|
64
|
+
async sendFriendMessage(params: {
|
|
65
|
+
friendWechatId: string;
|
|
66
|
+
content: string;
|
|
67
|
+
}): Promise<SendTextResponse> {
|
|
68
|
+
// API: 发送文本消息 - api-316658257
|
|
69
|
+
const response = await fetch(`${baseUrl}/api-316658257`, {
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers: getHeaders(apiKey),
|
|
72
|
+
body: JSON.stringify({
|
|
73
|
+
toUser: params.friendWechatId,
|
|
74
|
+
content: params.content,
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
const data = await response.json();
|
|
78
|
+
return {
|
|
79
|
+
messageId: data.data?.messageId || `msg_${Date.now()}`,
|
|
80
|
+
msgSvrId: data.data?.msgSvrId || "",
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
async sendRoomMessage(params: {
|
|
85
|
+
roomId: string;
|
|
86
|
+
content: string;
|
|
87
|
+
}): Promise<SendTextResponse> {
|
|
88
|
+
// API: 发送群@消息 - api-316658258
|
|
89
|
+
const response = await fetch(`${baseUrl}/api-316658258`, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: getHeaders(apiKey),
|
|
92
|
+
body: JSON.stringify({
|
|
93
|
+
roomId: params.roomId,
|
|
94
|
+
content: params.content,
|
|
95
|
+
}),
|
|
96
|
+
});
|
|
97
|
+
const data = await response.json();
|
|
98
|
+
return {
|
|
99
|
+
messageId: data.data?.messageId || `msg_${Date.now()}`,
|
|
100
|
+
msgSvrId: data.data?.msgSvrId || "",
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
async sendFriendMedia(params: {
|
|
105
|
+
friendWechatId: string;
|
|
106
|
+
filePath: string;
|
|
107
|
+
}): Promise<SendMediaResponse> {
|
|
108
|
+
// API: 发送图片 - api-316658262
|
|
109
|
+
const response = await fetch(`${baseUrl}/api-316658262`, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: getHeaders(apiKey),
|
|
112
|
+
body: JSON.stringify({
|
|
113
|
+
toUser: params.friendWechatId,
|
|
114
|
+
filePath: params.filePath,
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
const data = await response.json();
|
|
118
|
+
return {
|
|
119
|
+
messageId: data.data?.messageId || `msg_${Date.now()}`,
|
|
120
|
+
msgSvrId: data.data?.msgSvrId || "",
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
async sendRoomMedia(params: {
|
|
125
|
+
roomId: string;
|
|
126
|
+
filePath: string;
|
|
127
|
+
}): Promise<SendMediaResponse> {
|
|
128
|
+
// API: 发送图片到群 - 复用图片接口
|
|
129
|
+
const response = await fetch(`${baseUrl}/api-316658262`, {
|
|
130
|
+
method: "POST",
|
|
131
|
+
headers: getHeaders(apiKey),
|
|
132
|
+
body: JSON.stringify({
|
|
133
|
+
roomId: params.roomId,
|
|
134
|
+
filePath: params.filePath,
|
|
135
|
+
}),
|
|
136
|
+
});
|
|
137
|
+
const data = await response.json();
|
|
138
|
+
return {
|
|
139
|
+
messageId: data.data?.messageId || `msg_${Date.now()}`,
|
|
140
|
+
msgSvrId: data.data?.msgSvrId || "",
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// ========== 联系人管理 ==========
|
|
145
|
+
|
|
146
|
+
async syncContacts(): Promise<Contact[]> {
|
|
147
|
+
// API: 同步联系人 - api-316658212
|
|
148
|
+
const response = await fetch(`${baseUrl}/api-316658212`, {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: getHeaders(apiKey),
|
|
151
|
+
body: JSON.stringify({}),
|
|
152
|
+
});
|
|
153
|
+
const data = await response.json();
|
|
154
|
+
return data.data?.contacts || [];
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
async getContactDetail(wechatId: string): Promise<Contact> {
|
|
158
|
+
// API: 获取联系人详细 - api-316658219
|
|
159
|
+
const response = await fetch(`${baseUrl}/api-316658219`, {
|
|
160
|
+
method: "POST",
|
|
161
|
+
headers: getHeaders(apiKey),
|
|
162
|
+
body: JSON.stringify({ userId: wechatId }),
|
|
163
|
+
});
|
|
164
|
+
const data = await response.json();
|
|
165
|
+
return data.data || {};
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
async updateFriendRemark(wechatId: string, remark: string): Promise<void> {
|
|
169
|
+
// API: 修改好友备注 - api-316658224
|
|
170
|
+
await fetch(`${baseUrl}/api-316658224`, {
|
|
171
|
+
method: "POST",
|
|
172
|
+
headers: getHeaders(apiKey),
|
|
173
|
+
body: JSON.stringify({
|
|
174
|
+
userId: wechatId,
|
|
175
|
+
remark,
|
|
176
|
+
}),
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
// ========== 群管理 ==========
|
|
181
|
+
|
|
182
|
+
async getRoomInfo(roomId: string): Promise<RoomInfo> {
|
|
183
|
+
// API: 获取群信息 - api-316658229
|
|
184
|
+
const response = await fetch(`${baseUrl}/api-316658229`, {
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: getHeaders(apiKey),
|
|
187
|
+
body: JSON.stringify({ roomId }),
|
|
188
|
+
});
|
|
189
|
+
const data = await response.json();
|
|
190
|
+
return data.data || {};
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
async getRoomMembers(roomId: string): Promise<string[]> {
|
|
194
|
+
// API: 获取群成员详细 - api-316658230
|
|
195
|
+
const response = await fetch(`${baseUrl}/api-316658230`, {
|
|
196
|
+
method: "POST",
|
|
197
|
+
headers: getHeaders(apiKey),
|
|
198
|
+
body: JSON.stringify({ roomId }),
|
|
199
|
+
});
|
|
200
|
+
const data = await response.json();
|
|
201
|
+
return data.data?.members || [];
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
async createRoom(memberIds: string[]): Promise<{ roomId: string }> {
|
|
205
|
+
// API: 创建群组 - api-316658231
|
|
206
|
+
const response = await fetch(`${baseUrl}/api-316658231`, {
|
|
207
|
+
method: "POST",
|
|
208
|
+
headers: getHeaders(apiKey),
|
|
209
|
+
body: JSON.stringify({ memberIds }),
|
|
210
|
+
});
|
|
211
|
+
const data = await response.json();
|
|
212
|
+
return { roomId: data.data?.roomId || "" };
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
async addRoomMember(roomId: string, memberId: string): Promise<void> {
|
|
216
|
+
// API: 添加群成员 - api-316658232
|
|
217
|
+
await fetch(`${baseUrl}/api-316658232`, {
|
|
218
|
+
method: "POST",
|
|
219
|
+
headers: getHeaders(apiKey),
|
|
220
|
+
body: JSON.stringify({ roomId, memberId }),
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
async removeRoomMember(roomId: string, memberId: string): Promise<void> {
|
|
225
|
+
// API: 移除群成员 - api-316658234
|
|
226
|
+
await fetch(`${baseUrl}/api-316658234`, {
|
|
227
|
+
method: "POST",
|
|
228
|
+
headers: getHeaders(apiKey),
|
|
229
|
+
body: JSON.stringify({ roomId, memberId }),
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
// ========== 账号 ==========
|
|
234
|
+
|
|
235
|
+
async getLoginAccountInfo(): Promise<{
|
|
236
|
+
wechatId: string;
|
|
237
|
+
nickName: string;
|
|
238
|
+
avatar: string;
|
|
239
|
+
}> {
|
|
240
|
+
// API: 获取登录账号信息 - api-316658209
|
|
241
|
+
const response = await fetch(`${baseUrl}/api-316658209`, {
|
|
242
|
+
method: "POST",
|
|
243
|
+
headers: getHeaders(apiKey),
|
|
244
|
+
body: JSON.stringify({}),
|
|
245
|
+
});
|
|
246
|
+
const data = await response.json();
|
|
247
|
+
return data.data || {};
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
// ========== 朋友圈 ==========
|
|
251
|
+
|
|
252
|
+
async getFriendMoments(wechatId: string, limit: number = 20): Promise<any[]> {
|
|
253
|
+
// API: 获取好友朋友圈动态 - api-316658314
|
|
254
|
+
const response = await fetch(`${baseUrl}/api-316658314`, {
|
|
255
|
+
method: "POST",
|
|
256
|
+
headers: getHeaders(apiKey),
|
|
257
|
+
body: JSON.stringify({ userId: wechatId, limit }),
|
|
258
|
+
});
|
|
259
|
+
const data = await response.json();
|
|
260
|
+
return data.data?.moments || [];
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
async publishMoment(content: string, images: string[]): Promise<void> {
|
|
264
|
+
// API: 发布朋友圈 - api-316658315
|
|
265
|
+
await fetch(`${baseUrl}/api-316658315`, {
|
|
266
|
+
method: "POST",
|
|
267
|
+
headers: getHeaders(apiKey),
|
|
268
|
+
body: JSON.stringify({ content, images }),
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"strict": false,
|
|
9
|
+
"noImplicitAny": false,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["*.ts", "src/**/*.ts"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|