@max1874/openclaw-wecom 0.1.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/README.md +110 -0
- package/docs/CONFIG.md +362 -0
- package/docs/SETUP.md +162 -0
- package/index.ts +43 -0
- package/openclaw.plugin.json +17 -0
- package/package.json +73 -0
- package/src/accounts.ts +42 -0
- package/src/bot.ts +377 -0
- package/src/channel.ts +208 -0
- package/src/config-schema.ts +77 -0
- package/src/media.ts +149 -0
- package/src/monitor.ts +75 -0
- package/src/outbound.ts +75 -0
- package/src/policy.ts +111 -0
- package/src/probe.ts +32 -0
- package/src/reply-dispatcher.ts +91 -0
- package/src/runtime.ts +14 -0
- package/src/send.ts +250 -0
- package/src/targets.ts +66 -0
- package/src/types.ts +257 -0
- package/src/webhook.ts +171 -0
package/src/send.ts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import type { ClawdbotConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import type {
|
|
3
|
+
WecomConfig,
|
|
4
|
+
WecomSendResult,
|
|
5
|
+
WecomSendRequest,
|
|
6
|
+
WecomSendResponse,
|
|
7
|
+
SendTextPayload,
|
|
8
|
+
SendImagePayload,
|
|
9
|
+
SendFilePayload,
|
|
10
|
+
SendLinkPayload,
|
|
11
|
+
} from "./types.js";
|
|
12
|
+
import { normalizeWecomTarget } from "./targets.js";
|
|
13
|
+
|
|
14
|
+
const STRIDE_API_URL = "https://stride-bg.dpclouds.com/stream-api/message/send";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Send a message via Stride API.
|
|
18
|
+
*/
|
|
19
|
+
async function sendStrideMessage(request: WecomSendRequest): Promise<WecomSendResponse> {
|
|
20
|
+
const response = await fetch(STRIDE_API_URL, {
|
|
21
|
+
method: "POST",
|
|
22
|
+
headers: {
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
},
|
|
25
|
+
body: JSON.stringify(request),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
throw new Error(`Stride API error: ${response.status} ${response.statusText}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (await response.json()) as WecomSendResponse;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type SendWecomMessageParams = {
|
|
36
|
+
cfg: ClawdbotConfig;
|
|
37
|
+
to: string;
|
|
38
|
+
text: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Send a text message.
|
|
43
|
+
*/
|
|
44
|
+
export async function sendMessageWecom(params: SendWecomMessageParams): Promise<WecomSendResult> {
|
|
45
|
+
const { cfg, to, text } = params;
|
|
46
|
+
const wecomCfg = cfg.channels?.wecom as WecomConfig | undefined;
|
|
47
|
+
|
|
48
|
+
if (!wecomCfg?.token) {
|
|
49
|
+
throw new Error("WeChat channel not configured (token required)");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const chatId = normalizeWecomTarget(to) ?? wecomCfg.chatId;
|
|
53
|
+
if (!chatId) {
|
|
54
|
+
throw new Error(`Invalid WeChat target: ${to}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const payload: SendTextPayload = { text };
|
|
58
|
+
|
|
59
|
+
const request: WecomSendRequest = {
|
|
60
|
+
token: wecomCfg.token,
|
|
61
|
+
chatId,
|
|
62
|
+
messageType: 0,
|
|
63
|
+
payload,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const response = await sendStrideMessage(request);
|
|
68
|
+
if (response.code !== 0) {
|
|
69
|
+
return {
|
|
70
|
+
chatId,
|
|
71
|
+
success: false,
|
|
72
|
+
error: response.message ?? `Error code: ${response.code}`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { chatId, success: true };
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return {
|
|
78
|
+
chatId,
|
|
79
|
+
success: false,
|
|
80
|
+
error: String(err),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type SendWecomImageParams = {
|
|
86
|
+
cfg: ClawdbotConfig;
|
|
87
|
+
to: string;
|
|
88
|
+
imageUrl: string;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Send an image message.
|
|
93
|
+
*/
|
|
94
|
+
export async function sendImageWecom(params: SendWecomImageParams): Promise<WecomSendResult> {
|
|
95
|
+
const { cfg, to, imageUrl } = params;
|
|
96
|
+
const wecomCfg = cfg.channels?.wecom as WecomConfig | undefined;
|
|
97
|
+
|
|
98
|
+
if (!wecomCfg?.token) {
|
|
99
|
+
throw new Error("WeChat channel not configured (token required)");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const chatId = normalizeWecomTarget(to) ?? wecomCfg.chatId;
|
|
103
|
+
if (!chatId) {
|
|
104
|
+
throw new Error(`Invalid WeChat target: ${to}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const payload: SendImagePayload = {
|
|
108
|
+
imageUrl,
|
|
109
|
+
url: imageUrl,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const request: WecomSendRequest = {
|
|
113
|
+
token: wecomCfg.token,
|
|
114
|
+
chatId,
|
|
115
|
+
messageType: 1,
|
|
116
|
+
payload,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const response = await sendStrideMessage(request);
|
|
121
|
+
if (response.code !== 0) {
|
|
122
|
+
return {
|
|
123
|
+
chatId,
|
|
124
|
+
success: false,
|
|
125
|
+
error: response.message ?? `Error code: ${response.code}`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return { chatId, success: true };
|
|
129
|
+
} catch (err) {
|
|
130
|
+
return {
|
|
131
|
+
chatId,
|
|
132
|
+
success: false,
|
|
133
|
+
error: String(err),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type SendWecomFileParams = {
|
|
139
|
+
cfg: ClawdbotConfig;
|
|
140
|
+
to: string;
|
|
141
|
+
fileUrl: string;
|
|
142
|
+
fileName: string;
|
|
143
|
+
fileSize?: number;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Send a file message.
|
|
148
|
+
*/
|
|
149
|
+
export async function sendFileWecom(params: SendWecomFileParams): Promise<WecomSendResult> {
|
|
150
|
+
const { cfg, to, fileUrl, fileName, fileSize } = params;
|
|
151
|
+
const wecomCfg = cfg.channels?.wecom as WecomConfig | undefined;
|
|
152
|
+
|
|
153
|
+
if (!wecomCfg?.token) {
|
|
154
|
+
throw new Error("WeChat channel not configured (token required)");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const chatId = normalizeWecomTarget(to) ?? wecomCfg.chatId;
|
|
158
|
+
if (!chatId) {
|
|
159
|
+
throw new Error(`Invalid WeChat target: ${to}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const payload: SendFilePayload = {
|
|
163
|
+
name: fileName,
|
|
164
|
+
url: fileUrl,
|
|
165
|
+
size: fileSize,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const request: WecomSendRequest = {
|
|
169
|
+
token: wecomCfg.token,
|
|
170
|
+
chatId,
|
|
171
|
+
messageType: 3,
|
|
172
|
+
payload,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
const response = await sendStrideMessage(request);
|
|
177
|
+
if (response.code !== 0) {
|
|
178
|
+
return {
|
|
179
|
+
chatId,
|
|
180
|
+
success: false,
|
|
181
|
+
error: response.message ?? `Error code: ${response.code}`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return { chatId, success: true };
|
|
185
|
+
} catch (err) {
|
|
186
|
+
return {
|
|
187
|
+
chatId,
|
|
188
|
+
success: false,
|
|
189
|
+
error: String(err),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export type SendWecomLinkParams = {
|
|
195
|
+
cfg: ClawdbotConfig;
|
|
196
|
+
to: string;
|
|
197
|
+
title: string;
|
|
198
|
+
url: string;
|
|
199
|
+
summary?: string;
|
|
200
|
+
imageUrl?: string;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Send a link message.
|
|
205
|
+
*/
|
|
206
|
+
export async function sendLinkWecom(params: SendWecomLinkParams): Promise<WecomSendResult> {
|
|
207
|
+
const { cfg, to, title, url, summary, imageUrl } = params;
|
|
208
|
+
const wecomCfg = cfg.channels?.wecom as WecomConfig | undefined;
|
|
209
|
+
|
|
210
|
+
if (!wecomCfg?.token) {
|
|
211
|
+
throw new Error("WeChat channel not configured (token required)");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const chatId = normalizeWecomTarget(to) ?? wecomCfg.chatId;
|
|
215
|
+
if (!chatId) {
|
|
216
|
+
throw new Error(`Invalid WeChat target: ${to}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const payload: SendLinkPayload = {
|
|
220
|
+
title,
|
|
221
|
+
sourceUrl: url,
|
|
222
|
+
summary,
|
|
223
|
+
imageUrl,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const request: WecomSendRequest = {
|
|
227
|
+
token: wecomCfg.token,
|
|
228
|
+
chatId,
|
|
229
|
+
messageType: 2,
|
|
230
|
+
payload,
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
const response = await sendStrideMessage(request);
|
|
235
|
+
if (response.code !== 0) {
|
|
236
|
+
return {
|
|
237
|
+
chatId,
|
|
238
|
+
success: false,
|
|
239
|
+
error: response.message ?? `Error code: ${response.code}`,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return { chatId, success: true };
|
|
243
|
+
} catch (err) {
|
|
244
|
+
return {
|
|
245
|
+
chatId,
|
|
246
|
+
success: false,
|
|
247
|
+
error: String(err),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
package/src/targets.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a target string to a raw ID.
|
|
3
|
+
* Supported formats:
|
|
4
|
+
* - "chat:<chatId>" -> chatId
|
|
5
|
+
* - "user:<contactId>" -> contactId
|
|
6
|
+
* - "room:<roomId>" -> roomId
|
|
7
|
+
* - raw ID (no prefix)
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeWecomTarget(raw: string): string | null {
|
|
10
|
+
const trimmed = raw.trim();
|
|
11
|
+
if (!trimmed) return null;
|
|
12
|
+
|
|
13
|
+
const lowered = trimmed.toLowerCase();
|
|
14
|
+
if (lowered.startsWith("chat:")) {
|
|
15
|
+
return trimmed.slice("chat:".length).trim() || null;
|
|
16
|
+
}
|
|
17
|
+
if (lowered.startsWith("user:")) {
|
|
18
|
+
return trimmed.slice("user:".length).trim() || null;
|
|
19
|
+
}
|
|
20
|
+
if (lowered.startsWith("room:")) {
|
|
21
|
+
return trimmed.slice("room:".length).trim() || null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return trimmed;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Format a target ID with type prefix for display.
|
|
29
|
+
*/
|
|
30
|
+
export function formatWecomTarget(id: string, type?: "chat" | "user" | "room"): string {
|
|
31
|
+
const trimmed = id.trim();
|
|
32
|
+
if (type === "chat") return `chat:${trimmed}`;
|
|
33
|
+
if (type === "user") return `user:${trimmed}`;
|
|
34
|
+
if (type === "room") return `room:${trimmed}`;
|
|
35
|
+
return trimmed;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check if a string looks like a WeChat/Stride ID.
|
|
40
|
+
*/
|
|
41
|
+
export function looksLikeWecomId(raw: string): boolean {
|
|
42
|
+
const trimmed = raw.trim();
|
|
43
|
+
if (!trimmed) return false;
|
|
44
|
+
if (/^(chat|user|room):/i.test(trimmed)) return true;
|
|
45
|
+
// Stride chatId format: 24-char hex
|
|
46
|
+
if (/^[a-f0-9]{24}$/.test(trimmed)) return true;
|
|
47
|
+
// WeChat wxid format: numeric string
|
|
48
|
+
if (/^\d{10,20}$/.test(trimmed)) return true;
|
|
49
|
+
// Room ID format: R:xxxxx
|
|
50
|
+
if (/^R:\d+$/.test(trimmed)) return true;
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Detect the type of an ID based on its format.
|
|
56
|
+
*/
|
|
57
|
+
export function detectIdType(id: string): "chat" | "room" | "user" | null {
|
|
58
|
+
const trimmed = id.trim();
|
|
59
|
+
// Stride chatId: 24-char hex
|
|
60
|
+
if (/^[a-f0-9]{24}$/.test(trimmed)) return "chat";
|
|
61
|
+
// Room ID: R:xxxxx
|
|
62
|
+
if (/^R:\d+$/.test(trimmed)) return "room";
|
|
63
|
+
// WeChat wxid: numeric
|
|
64
|
+
if (/^\d{10,20}$/.test(trimmed)) return "user";
|
|
65
|
+
return null;
|
|
66
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import type { WecomConfigSchema, WecomGroupSchema, z } from "./config-schema.js";
|
|
2
|
+
|
|
3
|
+
export type WecomConfig = z.infer<typeof WecomConfigSchema>;
|
|
4
|
+
export type WecomGroupConfig = z.infer<typeof WecomGroupSchema>;
|
|
5
|
+
|
|
6
|
+
export type ResolvedWecomAccount = {
|
|
7
|
+
accountId: string;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
configured: boolean;
|
|
10
|
+
token?: string;
|
|
11
|
+
chatId?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// --- Stride Webhook Event Types ---
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Common sender/room information present in all webhook events
|
|
18
|
+
*/
|
|
19
|
+
export type WecomEventMeta = {
|
|
20
|
+
contactName: string;
|
|
21
|
+
contactId: string;
|
|
22
|
+
avatar?: string;
|
|
23
|
+
roomTopic?: string;
|
|
24
|
+
roomId?: string;
|
|
25
|
+
chatId: string;
|
|
26
|
+
mentionSelf: boolean;
|
|
27
|
+
isSelf: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Inbound message types (from webhook)
|
|
32
|
+
*/
|
|
33
|
+
export type InboundMessageType = 1 | 2 | 4 | 6 | 7 | 12 | 10000 | 10001;
|
|
34
|
+
|
|
35
|
+
// Type 1: File message
|
|
36
|
+
export type FilePayload = {
|
|
37
|
+
fileUrl: string;
|
|
38
|
+
name: string;
|
|
39
|
+
size?: number;
|
|
40
|
+
md5?: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Type 2: Voice message (with transcription)
|
|
44
|
+
export type VoicePayload = {
|
|
45
|
+
voiceUrl: string;
|
|
46
|
+
duration?: number;
|
|
47
|
+
text?: string; // Transcribed text from Stride
|
|
48
|
+
md5?: string;
|
|
49
|
+
wxVoiceUrl?: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Type 4: Chat history (merged forward)
|
|
53
|
+
export type ChatHistoryItem = {
|
|
54
|
+
type: number;
|
|
55
|
+
avatar?: string;
|
|
56
|
+
senderName?: string;
|
|
57
|
+
corpName?: string;
|
|
58
|
+
time?: number;
|
|
59
|
+
message: {
|
|
60
|
+
content?: string;
|
|
61
|
+
imageUrl?: string;
|
|
62
|
+
name?: string;
|
|
63
|
+
fileUrl?: string;
|
|
64
|
+
link?: {
|
|
65
|
+
title?: string;
|
|
66
|
+
des?: string;
|
|
67
|
+
thumbnailUrl?: string;
|
|
68
|
+
link?: string;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type ChatHistoryPayload = {
|
|
74
|
+
content: string;
|
|
75
|
+
chatHistoryList: ChatHistoryItem[];
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Type 6: Image message
|
|
79
|
+
export type ImagePayload = {
|
|
80
|
+
imageUrl: string;
|
|
81
|
+
size?: number;
|
|
82
|
+
width?: number;
|
|
83
|
+
height?: number;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Type 7: Text message
|
|
87
|
+
export type TextPayload = {
|
|
88
|
+
text: string;
|
|
89
|
+
mention?: string[];
|
|
90
|
+
pureText?: string;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Type 12: Link message
|
|
94
|
+
export type LinkPayload = {
|
|
95
|
+
title: string;
|
|
96
|
+
description?: string;
|
|
97
|
+
url: string;
|
|
98
|
+
thumbnailUrl?: string;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Type 10000: System message (join notification)
|
|
102
|
+
export type SystemJoinPayload = {
|
|
103
|
+
type: number;
|
|
104
|
+
subPayload: {
|
|
105
|
+
memberNames?: string[];
|
|
106
|
+
inviterName?: string;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Type 10001: System message (member list)
|
|
111
|
+
export type SystemMemberListPayload = {
|
|
112
|
+
subPayload: {
|
|
113
|
+
inviteeList?: Array<{
|
|
114
|
+
displayName: string;
|
|
115
|
+
wxid: string;
|
|
116
|
+
isSelf: boolean;
|
|
117
|
+
}>;
|
|
118
|
+
inviter?: {
|
|
119
|
+
displayName: string;
|
|
120
|
+
wxid: string;
|
|
121
|
+
isSelf: boolean;
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Union type for all inbound message payloads
|
|
128
|
+
*/
|
|
129
|
+
export type InboundPayload =
|
|
130
|
+
| FilePayload
|
|
131
|
+
| VoicePayload
|
|
132
|
+
| ChatHistoryPayload
|
|
133
|
+
| ImagePayload
|
|
134
|
+
| TextPayload
|
|
135
|
+
| LinkPayload
|
|
136
|
+
| SystemJoinPayload
|
|
137
|
+
| SystemMemberListPayload;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Inbound webhook event structure
|
|
141
|
+
*/
|
|
142
|
+
export type WecomWebhookEvent = WecomEventMeta & {
|
|
143
|
+
type: InboundMessageType;
|
|
144
|
+
payload: InboundPayload;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// --- Outbound Message Types (for sending) ---
|
|
148
|
+
|
|
149
|
+
export type OutboundMessageType = 0 | 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10;
|
|
150
|
+
|
|
151
|
+
// messageType 0: Text
|
|
152
|
+
export type SendTextPayload = {
|
|
153
|
+
text: string;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// messageType 1: Image
|
|
157
|
+
export type SendImagePayload = {
|
|
158
|
+
imageUrl: string;
|
|
159
|
+
url: string;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// messageType 2: Link
|
|
163
|
+
export type SendLinkPayload = {
|
|
164
|
+
title: string;
|
|
165
|
+
summary?: string;
|
|
166
|
+
sourceUrl: string;
|
|
167
|
+
imageUrl?: string;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// messageType 3: File
|
|
171
|
+
export type SendFilePayload = {
|
|
172
|
+
name: string;
|
|
173
|
+
url: string;
|
|
174
|
+
size?: number;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// messageType 5: Video
|
|
178
|
+
export type SendVideoPayload = {
|
|
179
|
+
url: string; // mp4 only
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// messageType 8: Voice
|
|
183
|
+
export type SendVoicePayload = {
|
|
184
|
+
voiceUrl: string; // silk only
|
|
185
|
+
duration?: number;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// messageType 9: Emoji
|
|
189
|
+
export type SendEmojiPayload = {
|
|
190
|
+
imageUrl: string;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Union type for all outbound message payloads
|
|
195
|
+
*/
|
|
196
|
+
export type OutboundPayload =
|
|
197
|
+
| SendTextPayload
|
|
198
|
+
| SendImagePayload
|
|
199
|
+
| SendLinkPayload
|
|
200
|
+
| SendFilePayload
|
|
201
|
+
| SendVideoPayload
|
|
202
|
+
| SendVoicePayload
|
|
203
|
+
| SendEmojiPayload;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Outbound message request structure
|
|
207
|
+
*/
|
|
208
|
+
export type WecomSendRequest = {
|
|
209
|
+
token: string;
|
|
210
|
+
chatId: string;
|
|
211
|
+
externalRequestId?: string;
|
|
212
|
+
messageType: OutboundMessageType;
|
|
213
|
+
payload: OutboundPayload;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Outbound API response
|
|
218
|
+
*/
|
|
219
|
+
export type WecomSendResponse = {
|
|
220
|
+
code: number;
|
|
221
|
+
message?: string;
|
|
222
|
+
data?: unknown;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// --- Internal Types ---
|
|
226
|
+
|
|
227
|
+
export type WecomMessageContext = {
|
|
228
|
+
chatId: string;
|
|
229
|
+
contactId: string;
|
|
230
|
+
contactName: string;
|
|
231
|
+
roomId?: string;
|
|
232
|
+
roomTopic?: string;
|
|
233
|
+
chatType: "dm" | "group";
|
|
234
|
+
mentionedBot: boolean;
|
|
235
|
+
content: string;
|
|
236
|
+
contentType: string;
|
|
237
|
+
mediaUrl?: string;
|
|
238
|
+
mediaType?: string;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export type WecomSendResult = {
|
|
242
|
+
chatId: string;
|
|
243
|
+
success: boolean;
|
|
244
|
+
error?: string;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
export type WecomProbeResult = {
|
|
248
|
+
ok: boolean;
|
|
249
|
+
error?: string;
|
|
250
|
+
chatId?: string;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export type WecomMediaInfo = {
|
|
254
|
+
url: string;
|
|
255
|
+
contentType?: string;
|
|
256
|
+
placeholder: string;
|
|
257
|
+
};
|