@aigne/afs-wecom 1.11.0-beta.12

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/LICENSE.md ADDED
@@ -0,0 +1,26 @@
1
+ # Proprietary License
2
+
3
+ Copyright (c) 2024-2025 ArcBlock, Inc. All Rights Reserved.
4
+
5
+ This software and associated documentation files (the "Software") are proprietary
6
+ and confidential. Unauthorized copying, modification, distribution, or use of
7
+ this Software, via any medium, is strictly prohibited.
8
+
9
+ The Software is provided for internal use only within ArcBlock, Inc. and its
10
+ authorized affiliates.
11
+
12
+ ## No License Granted
13
+
14
+ No license, express or implied, is granted to any party for any purpose.
15
+ All rights are reserved by ArcBlock, Inc.
16
+
17
+ ## Public Artifact Distribution
18
+
19
+ Portions of this Software may be released publicly under separate open-source
20
+ licenses (such as MIT License) through designated public repositories. Such
21
+ public releases are governed by their respective licenses and do not affect
22
+ the proprietary nature of this repository.
23
+
24
+ ## Contact
25
+
26
+ For licensing inquiries, contact: legal@arcblock.io
@@ -0,0 +1,11 @@
1
+
2
+ //#region \0@oxc-project+runtime@0.108.0/helpers/decorate.js
3
+ function __decorate(decorators, target, key, desc) {
4
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
5
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
7
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
8
+ }
9
+
10
+ //#endregion
11
+ exports.__decorate = __decorate;
@@ -0,0 +1,10 @@
1
+ //#region \0@oxc-project+runtime@0.108.0/helpers/decorate.js
2
+ function __decorate(decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ }
8
+
9
+ //#endregion
10
+ export { __decorate };
@@ -0,0 +1,130 @@
1
+
2
+ //#region src/client.ts
3
+ /**
4
+ * WeComClient — pure WeCom (企业微信) REST API wrapper.
5
+ *
6
+ * Two modes:
7
+ * - Webhook: outbound-only, POST to Group Robot Webhook URL
8
+ * - Bot: bidirectional, uses corpId + corpSecret → access_token → message/send
9
+ *
10
+ * Zero external dependencies (uses native fetch).
11
+ * No AFS or application-specific concepts.
12
+ */
13
+ const DEFAULT_API_BASE = "https://qyapi.weixin.qq.com/cgi-bin";
14
+ const DEFAULT_TIMEOUT = 3e4;
15
+ /** Refresh token 5 minutes before expiry */
16
+ const TOKEN_REFRESH_MARGIN = 300;
17
+ var WeComClient = class WeComClient {
18
+ mode;
19
+ _webhookKey;
20
+ _corpId;
21
+ _corpSecret;
22
+ _agentId;
23
+ _apiBase;
24
+ _timeout;
25
+ _accessToken = null;
26
+ _tokenExpiresAt = 0;
27
+ constructor(opts) {
28
+ this.mode = opts.mode;
29
+ this._webhookKey = opts.webhookKey ?? null;
30
+ this._corpId = opts.corpId ?? null;
31
+ this._corpSecret = opts.corpSecret ?? null;
32
+ this._agentId = opts.agentId ?? null;
33
+ this._apiBase = opts.apiBase ?? DEFAULT_API_BASE;
34
+ this._timeout = opts.timeout ?? DEFAULT_TIMEOUT;
35
+ }
36
+ static webhook(key, options) {
37
+ if (!key) throw new Error("WeComClient.webhook requires a key");
38
+ return new WeComClient({
39
+ mode: "webhook",
40
+ webhookKey: key,
41
+ apiBase: options?.apiBase,
42
+ timeout: options?.timeout
43
+ });
44
+ }
45
+ static bot(options) {
46
+ if (!options.corpId) throw new Error("WeComClient.bot requires corpId");
47
+ if (!options.corpSecret) throw new Error("WeComClient.bot requires corpSecret");
48
+ if (!options.agentId) throw new Error("WeComClient.bot requires agentId");
49
+ return new WeComClient({
50
+ mode: "bot",
51
+ corpId: options.corpId,
52
+ corpSecret: options.corpSecret,
53
+ agentId: options.agentId,
54
+ apiBase: options.apiBase,
55
+ timeout: options.timeout
56
+ });
57
+ }
58
+ async sendWebhook(text) {
59
+ if (!this._webhookKey) throw new Error("sendWebhook requires webhook mode");
60
+ const url = `${this._apiBase}/webhook/send?key=${this._webhookKey}`;
61
+ const data = await (await fetch(url, {
62
+ method: "POST",
63
+ headers: { "Content-Type": "application/json" },
64
+ body: JSON.stringify({
65
+ msgtype: "text",
66
+ text: { content: text }
67
+ }),
68
+ signal: AbortSignal.timeout(this._timeout)
69
+ })).json();
70
+ if (data.errcode !== 0) throw new Error(`WeCom webhook error: ${data.errmsg} (code: ${data.errcode})`);
71
+ }
72
+ async sendMessage(target, text, options) {
73
+ await this._ensureToken();
74
+ const body = {
75
+ msgtype: "text",
76
+ agentid: this._agentId,
77
+ text: { content: text }
78
+ };
79
+ if (options?.chatId) body.chatid = options.chatId;
80
+ else if (target === "@all") body.touser = "@all";
81
+ else body.touser = target;
82
+ const url = `${this._apiBase}/message/send?access_token=${this._accessToken}`;
83
+ const data = await (await fetch(url, {
84
+ method: "POST",
85
+ headers: { "Content-Type": "application/json" },
86
+ body: JSON.stringify(body),
87
+ signal: AbortSignal.timeout(this._timeout)
88
+ })).json();
89
+ if (data.errcode !== 0) {
90
+ const desc = (data.errmsg || "Unknown error").replace(this._corpSecret ?? "", "***").replace(this._accessToken ?? "", "***");
91
+ throw new Error(`WeCom API error: ${desc} (code: ${data.errcode})`);
92
+ }
93
+ return { msgId: String(data.msgid ?? Date.now()) };
94
+ }
95
+ async _ensureToken() {
96
+ const now = Math.floor(Date.now() / 1e3);
97
+ if (this._accessToken && now < this._tokenExpiresAt - TOKEN_REFRESH_MARGIN) return;
98
+ if (!this._corpId || !this._corpSecret) throw new Error("Token refresh requires corpId and corpSecret");
99
+ const url = `${this._apiBase}/gettoken?corpid=${this._corpId}&corpsecret=${this._corpSecret}`;
100
+ const data = await (await fetch(url, {
101
+ method: "GET",
102
+ signal: AbortSignal.timeout(this._timeout)
103
+ })).json();
104
+ if (data.errcode !== 0 || !data.access_token) {
105
+ const desc = (data.errmsg || "Unknown error").replace(this._corpSecret, "***");
106
+ throw new Error(`WeCom token error: ${desc} (code: ${data.errcode})`);
107
+ }
108
+ this._accessToken = data.access_token;
109
+ this._tokenExpiresAt = now + (data.expires_in ?? 7200);
110
+ }
111
+ static parseEvent(payload) {
112
+ if (!payload) return null;
113
+ if ((payload.MsgType ?? payload.msgtype) !== "text") return null;
114
+ const text = String(payload.Content ?? payload.content ?? "").trim();
115
+ if (!text) return null;
116
+ return {
117
+ type: "message",
118
+ text,
119
+ msgId: String(payload.MsgId ?? payload.msgid ?? Date.now()),
120
+ fromUser: String(payload.FromUserName ?? payload.from ?? ""),
121
+ createTime: Number(payload.CreateTime ?? payload.createtime ?? Math.floor(Date.now() / 1e3))
122
+ };
123
+ }
124
+ static escapeContent(text) {
125
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
126
+ }
127
+ };
128
+
129
+ //#endregion
130
+ exports.WeComClient = WeComClient;
@@ -0,0 +1,66 @@
1
+ //#region src/client.d.ts
2
+ /**
3
+ * WeComClient — pure WeCom (企业微信) REST API wrapper.
4
+ *
5
+ * Two modes:
6
+ * - Webhook: outbound-only, POST to Group Robot Webhook URL
7
+ * - Bot: bidirectional, uses corpId + corpSecret → access_token → message/send
8
+ *
9
+ * Zero external dependencies (uses native fetch).
10
+ * No AFS or application-specific concepts.
11
+ */
12
+ interface WeComBotOptions {
13
+ corpId: string;
14
+ corpSecret: string;
15
+ agentId: number;
16
+ apiBase?: string;
17
+ timeout?: number;
18
+ }
19
+ interface WeComSendOptions {
20
+ /** Send to a specific chat instead of users */
21
+ chatId?: string;
22
+ }
23
+ /** WeCom callback event (JSON mode, subset). */
24
+ interface WeComEvent {
25
+ MsgType: string;
26
+ Content?: string;
27
+ MsgId?: string;
28
+ FromUserName?: string;
29
+ CreateTime?: number;
30
+ AgentID?: number;
31
+ }
32
+ /** Parsed WeCom event result. */
33
+ type WeComParsedEvent = {
34
+ type: "message";
35
+ text: string;
36
+ msgId: string;
37
+ fromUser: string;
38
+ createTime: number;
39
+ } | null;
40
+ declare class WeComClient {
41
+ readonly mode: "webhook" | "bot";
42
+ private readonly _webhookKey;
43
+ private readonly _corpId;
44
+ private readonly _corpSecret;
45
+ private readonly _agentId;
46
+ private readonly _apiBase;
47
+ private readonly _timeout;
48
+ private _accessToken;
49
+ private _tokenExpiresAt;
50
+ private constructor();
51
+ static webhook(key: string, options?: {
52
+ apiBase?: string;
53
+ timeout?: number;
54
+ }): WeComClient;
55
+ static bot(options: WeComBotOptions): WeComClient;
56
+ sendWebhook(text: string): Promise<void>;
57
+ sendMessage(target: string, text: string, options?: WeComSendOptions): Promise<{
58
+ msgId: string;
59
+ }>;
60
+ _ensureToken(): Promise<void>;
61
+ static parseEvent(payload: any): WeComParsedEvent;
62
+ static escapeContent(text: string): string;
63
+ }
64
+ //#endregion
65
+ export { WeComClient, WeComEvent, WeComParsedEvent };
66
+ //# sourceMappingURL=client.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.cts","names":[],"sources":["../src/client.ts"],"mappings":";;AAgBA;;;;;;;;;UAAiB,eAAA;EACf,MAAA;EACA,UAAA;EACA,OAAA;EACA,OAAA;EACA,OAAA;AAAA;AAAA,UAGe,gBAAA;EAMU;EAJzB,MAAA;AAAA;;UAIe,UAAA;EACf,OAAA;EACA,OAAA;EACA,KAAA;EACA,YAAA;EACA,UAAA;EACA,OAAA;AAAA;;KAIU,gBAAA;EACV,IAAA;EACA,IAAA;EACA,KAAA;EACA,QAAA;EACA,UAAA;AAAA;AAAA,cAGW,WAAA;EAAA,SACF,IAAA;EAAA,iBACQ,WAAA;EAAA,iBACA,OAAA;EAAA,iBACA,WAAA;EAAA,iBACA,QAAA;EAAA,iBACA,QAAA;EAAA,iBACA,QAAA;EAAA,QAET,YAAA;EAAA,QACA,eAAA;EAAA,QAED,WAAA,CAAA;EAAA,OAoBA,OAAA,CAAQ,GAAA,UAAa,OAAA;IAAY,OAAA;IAAkB,OAAA;EAAA,IAAqB,WAAA;EAAA,OAUxE,GAAA,CAAI,OAAA,EAAS,eAAA,GAAkB,WAAA;EAgBhC,WAAA,CAAY,IAAA,WAAe,OAAA;EAmB3B,WAAA,CACJ,MAAA,UACA,IAAA,UACA,OAAA,GAAU,gBAAA,GACT,OAAA;IAAU,KAAA;EAAA;EAsCP,YAAA,CAAA,GAAgB,OAAA;EAAA,OAkCf,UAAA,CAAW,OAAA,QAAe,gBAAA;EAAA,OAoB1B,aAAA,CAAc,IAAA;AAAA"}
@@ -0,0 +1,66 @@
1
+ //#region src/client.d.ts
2
+ /**
3
+ * WeComClient — pure WeCom (企业微信) REST API wrapper.
4
+ *
5
+ * Two modes:
6
+ * - Webhook: outbound-only, POST to Group Robot Webhook URL
7
+ * - Bot: bidirectional, uses corpId + corpSecret → access_token → message/send
8
+ *
9
+ * Zero external dependencies (uses native fetch).
10
+ * No AFS or application-specific concepts.
11
+ */
12
+ interface WeComBotOptions {
13
+ corpId: string;
14
+ corpSecret: string;
15
+ agentId: number;
16
+ apiBase?: string;
17
+ timeout?: number;
18
+ }
19
+ interface WeComSendOptions {
20
+ /** Send to a specific chat instead of users */
21
+ chatId?: string;
22
+ }
23
+ /** WeCom callback event (JSON mode, subset). */
24
+ interface WeComEvent {
25
+ MsgType: string;
26
+ Content?: string;
27
+ MsgId?: string;
28
+ FromUserName?: string;
29
+ CreateTime?: number;
30
+ AgentID?: number;
31
+ }
32
+ /** Parsed WeCom event result. */
33
+ type WeComParsedEvent = {
34
+ type: "message";
35
+ text: string;
36
+ msgId: string;
37
+ fromUser: string;
38
+ createTime: number;
39
+ } | null;
40
+ declare class WeComClient {
41
+ readonly mode: "webhook" | "bot";
42
+ private readonly _webhookKey;
43
+ private readonly _corpId;
44
+ private readonly _corpSecret;
45
+ private readonly _agentId;
46
+ private readonly _apiBase;
47
+ private readonly _timeout;
48
+ private _accessToken;
49
+ private _tokenExpiresAt;
50
+ private constructor();
51
+ static webhook(key: string, options?: {
52
+ apiBase?: string;
53
+ timeout?: number;
54
+ }): WeComClient;
55
+ static bot(options: WeComBotOptions): WeComClient;
56
+ sendWebhook(text: string): Promise<void>;
57
+ sendMessage(target: string, text: string, options?: WeComSendOptions): Promise<{
58
+ msgId: string;
59
+ }>;
60
+ _ensureToken(): Promise<void>;
61
+ static parseEvent(payload: any): WeComParsedEvent;
62
+ static escapeContent(text: string): string;
63
+ }
64
+ //#endregion
65
+ export { WeComClient, WeComEvent, WeComParsedEvent };
66
+ //# sourceMappingURL=client.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;AAgBA;;;;;;;;;UAAiB,eAAA;EACf,MAAA;EACA,UAAA;EACA,OAAA;EACA,OAAA;EACA,OAAA;AAAA;AAAA,UAGe,gBAAA;EAMU;EAJzB,MAAA;AAAA;;UAIe,UAAA;EACf,OAAA;EACA,OAAA;EACA,KAAA;EACA,YAAA;EACA,UAAA;EACA,OAAA;AAAA;;KAIU,gBAAA;EACV,IAAA;EACA,IAAA;EACA,KAAA;EACA,QAAA;EACA,UAAA;AAAA;AAAA,cAGW,WAAA;EAAA,SACF,IAAA;EAAA,iBACQ,WAAA;EAAA,iBACA,OAAA;EAAA,iBACA,WAAA;EAAA,iBACA,QAAA;EAAA,iBACA,QAAA;EAAA,iBACA,QAAA;EAAA,QAET,YAAA;EAAA,QACA,eAAA;EAAA,QAED,WAAA,CAAA;EAAA,OAoBA,OAAA,CAAQ,GAAA,UAAa,OAAA;IAAY,OAAA;IAAkB,OAAA;EAAA,IAAqB,WAAA;EAAA,OAUxE,GAAA,CAAI,OAAA,EAAS,eAAA,GAAkB,WAAA;EAgBhC,WAAA,CAAY,IAAA,WAAe,OAAA;EAmB3B,WAAA,CACJ,MAAA,UACA,IAAA,UACA,OAAA,GAAU,gBAAA,GACT,OAAA;IAAU,KAAA;EAAA;EAsCP,YAAA,CAAA,GAAgB,OAAA;EAAA,OAkCf,UAAA,CAAW,OAAA,QAAe,gBAAA;EAAA,OAoB1B,aAAA,CAAc,IAAA;AAAA"}
@@ -0,0 +1,130 @@
1
+ //#region src/client.ts
2
+ /**
3
+ * WeComClient — pure WeCom (企业微信) REST API wrapper.
4
+ *
5
+ * Two modes:
6
+ * - Webhook: outbound-only, POST to Group Robot Webhook URL
7
+ * - Bot: bidirectional, uses corpId + corpSecret → access_token → message/send
8
+ *
9
+ * Zero external dependencies (uses native fetch).
10
+ * No AFS or application-specific concepts.
11
+ */
12
+ const DEFAULT_API_BASE = "https://qyapi.weixin.qq.com/cgi-bin";
13
+ const DEFAULT_TIMEOUT = 3e4;
14
+ /** Refresh token 5 minutes before expiry */
15
+ const TOKEN_REFRESH_MARGIN = 300;
16
+ var WeComClient = class WeComClient {
17
+ mode;
18
+ _webhookKey;
19
+ _corpId;
20
+ _corpSecret;
21
+ _agentId;
22
+ _apiBase;
23
+ _timeout;
24
+ _accessToken = null;
25
+ _tokenExpiresAt = 0;
26
+ constructor(opts) {
27
+ this.mode = opts.mode;
28
+ this._webhookKey = opts.webhookKey ?? null;
29
+ this._corpId = opts.corpId ?? null;
30
+ this._corpSecret = opts.corpSecret ?? null;
31
+ this._agentId = opts.agentId ?? null;
32
+ this._apiBase = opts.apiBase ?? DEFAULT_API_BASE;
33
+ this._timeout = opts.timeout ?? DEFAULT_TIMEOUT;
34
+ }
35
+ static webhook(key, options) {
36
+ if (!key) throw new Error("WeComClient.webhook requires a key");
37
+ return new WeComClient({
38
+ mode: "webhook",
39
+ webhookKey: key,
40
+ apiBase: options?.apiBase,
41
+ timeout: options?.timeout
42
+ });
43
+ }
44
+ static bot(options) {
45
+ if (!options.corpId) throw new Error("WeComClient.bot requires corpId");
46
+ if (!options.corpSecret) throw new Error("WeComClient.bot requires corpSecret");
47
+ if (!options.agentId) throw new Error("WeComClient.bot requires agentId");
48
+ return new WeComClient({
49
+ mode: "bot",
50
+ corpId: options.corpId,
51
+ corpSecret: options.corpSecret,
52
+ agentId: options.agentId,
53
+ apiBase: options.apiBase,
54
+ timeout: options.timeout
55
+ });
56
+ }
57
+ async sendWebhook(text) {
58
+ if (!this._webhookKey) throw new Error("sendWebhook requires webhook mode");
59
+ const url = `${this._apiBase}/webhook/send?key=${this._webhookKey}`;
60
+ const data = await (await fetch(url, {
61
+ method: "POST",
62
+ headers: { "Content-Type": "application/json" },
63
+ body: JSON.stringify({
64
+ msgtype: "text",
65
+ text: { content: text }
66
+ }),
67
+ signal: AbortSignal.timeout(this._timeout)
68
+ })).json();
69
+ if (data.errcode !== 0) throw new Error(`WeCom webhook error: ${data.errmsg} (code: ${data.errcode})`);
70
+ }
71
+ async sendMessage(target, text, options) {
72
+ await this._ensureToken();
73
+ const body = {
74
+ msgtype: "text",
75
+ agentid: this._agentId,
76
+ text: { content: text }
77
+ };
78
+ if (options?.chatId) body.chatid = options.chatId;
79
+ else if (target === "@all") body.touser = "@all";
80
+ else body.touser = target;
81
+ const url = `${this._apiBase}/message/send?access_token=${this._accessToken}`;
82
+ const data = await (await fetch(url, {
83
+ method: "POST",
84
+ headers: { "Content-Type": "application/json" },
85
+ body: JSON.stringify(body),
86
+ signal: AbortSignal.timeout(this._timeout)
87
+ })).json();
88
+ if (data.errcode !== 0) {
89
+ const desc = (data.errmsg || "Unknown error").replace(this._corpSecret ?? "", "***").replace(this._accessToken ?? "", "***");
90
+ throw new Error(`WeCom API error: ${desc} (code: ${data.errcode})`);
91
+ }
92
+ return { msgId: String(data.msgid ?? Date.now()) };
93
+ }
94
+ async _ensureToken() {
95
+ const now = Math.floor(Date.now() / 1e3);
96
+ if (this._accessToken && now < this._tokenExpiresAt - TOKEN_REFRESH_MARGIN) return;
97
+ if (!this._corpId || !this._corpSecret) throw new Error("Token refresh requires corpId and corpSecret");
98
+ const url = `${this._apiBase}/gettoken?corpid=${this._corpId}&corpsecret=${this._corpSecret}`;
99
+ const data = await (await fetch(url, {
100
+ method: "GET",
101
+ signal: AbortSignal.timeout(this._timeout)
102
+ })).json();
103
+ if (data.errcode !== 0 || !data.access_token) {
104
+ const desc = (data.errmsg || "Unknown error").replace(this._corpSecret, "***");
105
+ throw new Error(`WeCom token error: ${desc} (code: ${data.errcode})`);
106
+ }
107
+ this._accessToken = data.access_token;
108
+ this._tokenExpiresAt = now + (data.expires_in ?? 7200);
109
+ }
110
+ static parseEvent(payload) {
111
+ if (!payload) return null;
112
+ if ((payload.MsgType ?? payload.msgtype) !== "text") return null;
113
+ const text = String(payload.Content ?? payload.content ?? "").trim();
114
+ if (!text) return null;
115
+ return {
116
+ type: "message",
117
+ text,
118
+ msgId: String(payload.MsgId ?? payload.msgid ?? Date.now()),
119
+ fromUser: String(payload.FromUserName ?? payload.from ?? ""),
120
+ createTime: Number(payload.CreateTime ?? payload.createtime ?? Math.floor(Date.now() / 1e3))
121
+ };
122
+ }
123
+ static escapeContent(text) {
124
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
125
+ }
126
+ };
127
+
128
+ //#endregion
129
+ export { WeComClient };
130
+ //# sourceMappingURL=client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["/**\n * WeComClient — pure WeCom (企业微信) REST API wrapper.\n *\n * Two modes:\n * - Webhook: outbound-only, POST to Group Robot Webhook URL\n * - Bot: bidirectional, uses corpId + corpSecret → access_token → message/send\n *\n * Zero external dependencies (uses native fetch).\n * No AFS or application-specific concepts.\n */\n\nconst DEFAULT_API_BASE = \"https://qyapi.weixin.qq.com/cgi-bin\";\nconst DEFAULT_TIMEOUT = 30_000;\n/** Refresh token 5 minutes before expiry */\nconst TOKEN_REFRESH_MARGIN = 5 * 60;\n\nexport interface WeComBotOptions {\n corpId: string;\n corpSecret: string;\n agentId: number;\n apiBase?: string;\n timeout?: number;\n}\n\nexport interface WeComSendOptions {\n /** Send to a specific chat instead of users */\n chatId?: string;\n}\n\n/** WeCom callback event (JSON mode, subset). */\nexport interface WeComEvent {\n MsgType: string;\n Content?: string;\n MsgId?: string;\n FromUserName?: string;\n CreateTime?: number;\n AgentID?: number;\n}\n\n/** Parsed WeCom event result. */\nexport type WeComParsedEvent = {\n type: \"message\";\n text: string;\n msgId: string;\n fromUser: string;\n createTime: number;\n} | null;\n\nexport class WeComClient {\n readonly mode: \"webhook\" | \"bot\";\n private readonly _webhookKey: string | null;\n private readonly _corpId: string | null;\n private readonly _corpSecret: string | null;\n private readonly _agentId: number | null;\n private readonly _apiBase: string;\n private readonly _timeout: number;\n\n private _accessToken: string | null = null;\n private _tokenExpiresAt = 0;\n\n private constructor(opts: {\n mode: \"webhook\" | \"bot\";\n webhookKey?: string;\n corpId?: string;\n corpSecret?: string;\n agentId?: number;\n apiBase?: string;\n timeout?: number;\n }) {\n this.mode = opts.mode;\n this._webhookKey = opts.webhookKey ?? null;\n this._corpId = opts.corpId ?? null;\n this._corpSecret = opts.corpSecret ?? null;\n this._agentId = opts.agentId ?? null;\n this._apiBase = opts.apiBase ?? DEFAULT_API_BASE;\n this._timeout = opts.timeout ?? DEFAULT_TIMEOUT;\n }\n\n // ─── Factory Methods ──────────────────────────────────────\n\n static webhook(key: string, options?: { apiBase?: string; timeout?: number }): WeComClient {\n if (!key) throw new Error(\"WeComClient.webhook requires a key\");\n return new WeComClient({\n mode: \"webhook\",\n webhookKey: key,\n apiBase: options?.apiBase,\n timeout: options?.timeout,\n });\n }\n\n static bot(options: WeComBotOptions): WeComClient {\n if (!options.corpId) throw new Error(\"WeComClient.bot requires corpId\");\n if (!options.corpSecret) throw new Error(\"WeComClient.bot requires corpSecret\");\n if (!options.agentId) throw new Error(\"WeComClient.bot requires agentId\");\n return new WeComClient({\n mode: \"bot\",\n corpId: options.corpId,\n corpSecret: options.corpSecret,\n agentId: options.agentId,\n apiBase: options.apiBase,\n timeout: options.timeout,\n });\n }\n\n // ─── Webhook Mode ─────────────────────────────────────────\n\n async sendWebhook(text: string): Promise<void> {\n if (!this._webhookKey) throw new Error(\"sendWebhook requires webhook mode\");\n\n const url = `${this._apiBase}/webhook/send?key=${this._webhookKey}`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ msgtype: \"text\", text: { content: text } }),\n signal: AbortSignal.timeout(this._timeout),\n });\n\n const data = (await response.json()) as { errcode: number; errmsg: string };\n if (data.errcode !== 0) {\n throw new Error(`WeCom webhook error: ${data.errmsg} (code: ${data.errcode})`);\n }\n }\n\n // ─── Bot Mode ─────────────────────────────────────────────\n\n async sendMessage(\n target: string,\n text: string,\n options?: WeComSendOptions,\n ): Promise<{ msgId: string }> {\n await this._ensureToken();\n\n const body: Record<string, unknown> = {\n msgtype: \"text\",\n agentid: this._agentId,\n text: { content: text },\n };\n\n if (options?.chatId) {\n body.chatid = options.chatId;\n } else if (target === \"@all\") {\n body.touser = \"@all\";\n } else {\n body.touser = target;\n }\n\n const url = `${this._apiBase}/message/send?access_token=${this._accessToken}`;\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(this._timeout),\n });\n\n const data = (await response.json()) as { errcode: number; errmsg: string; msgid?: string };\n if (data.errcode !== 0) {\n const desc = (data.errmsg || \"Unknown error\")\n .replace(this._corpSecret ?? \"\", \"***\")\n .replace(this._accessToken ?? \"\", \"***\");\n throw new Error(`WeCom API error: ${desc} (code: ${data.errcode})`);\n }\n\n return { msgId: String(data.msgid ?? Date.now()) };\n }\n\n // ─── Token Management ─────────────────────────────────────\n\n async _ensureToken(): Promise<void> {\n const now = Math.floor(Date.now() / 1000);\n if (this._accessToken && now < this._tokenExpiresAt - TOKEN_REFRESH_MARGIN) {\n return;\n }\n\n if (!this._corpId || !this._corpSecret) {\n throw new Error(\"Token refresh requires corpId and corpSecret\");\n }\n\n const url = `${this._apiBase}/gettoken?corpid=${this._corpId}&corpsecret=${this._corpSecret}`;\n const response = await fetch(url, {\n method: \"GET\",\n signal: AbortSignal.timeout(this._timeout),\n });\n\n const data = (await response.json()) as {\n errcode: number;\n errmsg: string;\n access_token?: string;\n expires_in?: number;\n };\n\n if (data.errcode !== 0 || !data.access_token) {\n const desc = (data.errmsg || \"Unknown error\").replace(this._corpSecret, \"***\");\n throw new Error(`WeCom token error: ${desc} (code: ${data.errcode})`);\n }\n\n this._accessToken = data.access_token;\n this._tokenExpiresAt = now + (data.expires_in ?? 7200);\n }\n\n // ─── Events Parsing ───────────────────────────────────────\n\n static parseEvent(payload: any): WeComParsedEvent {\n if (!payload) return null;\n\n const msgType = payload.MsgType ?? payload.msgtype;\n if (msgType !== \"text\") return null;\n\n const text = String(payload.Content ?? payload.content ?? \"\").trim();\n if (!text) return null;\n\n const msgId = String(payload.MsgId ?? payload.msgid ?? Date.now());\n const fromUser = String(payload.FromUserName ?? payload.from ?? \"\");\n const createTime = Number(\n payload.CreateTime ?? payload.createtime ?? Math.floor(Date.now() / 1000),\n );\n\n return { type: \"message\", text, msgId, fromUser, createTime };\n }\n\n // ─── Helpers ───────────────────────────────────────────────\n\n static escapeContent(text: string): string {\n return text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n }\n}\n"],"mappings":"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"}