@ecat/weixin-bot-cli 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/LICENSE +27 -0
- package/README.md +77 -0
- package/dist/api/api.d.ts +47 -0
- package/dist/api/api.js +233 -0
- package/dist/api/api.js.map +1 -0
- package/dist/api/config-cache.d.ts +18 -0
- package/dist/api/config-cache.js +64 -0
- package/dist/api/config-cache.js.map +1 -0
- package/dist/api/session-guard.d.ts +15 -0
- package/dist/api/session-guard.js +49 -0
- package/dist/api/session-guard.js.map +1 -0
- package/dist/api/types.d.ts +201 -0
- package/dist/api/types.js +35 -0
- package/dist/api/types.js.map +1 -0
- package/dist/auth/accounts.d.ts +30 -0
- package/dist/auth/accounts.js +158 -0
- package/dist/auth/accounts.js.map +1 -0
- package/dist/auth/login-qr.d.ts +31 -0
- package/dist/auth/login-qr.js +235 -0
- package/dist/auth/login-qr.js.map +1 -0
- package/dist/cdn/aes-ecb.d.ts +6 -0
- package/dist/cdn/aes-ecb.js +19 -0
- package/dist/cdn/aes-ecb.js.map +1 -0
- package/dist/cdn/cdn-upload.d.ts +17 -0
- package/dist/cdn/cdn-upload.js +73 -0
- package/dist/cdn/cdn-upload.js.map +1 -0
- package/dist/cdn/cdn-url.d.ts +13 -0
- package/dist/cdn/cdn-url.js +14 -0
- package/dist/cdn/cdn-url.js.map +1 -0
- package/dist/cdn/pic-decrypt.d.ts +9 -0
- package/dist/cdn/pic-decrypt.js +89 -0
- package/dist/cdn/pic-decrypt.js.map +1 -0
- package/dist/cdn/upload.d.ts +42 -0
- package/dist/cdn/upload.js +106 -0
- package/dist/cdn/upload.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +127 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/config-schema.d.ts +16 -0
- package/dist/config/config-schema.js +17 -0
- package/dist/config/config-schema.js.map +1 -0
- package/dist/media/media-download.d.ts +18 -0
- package/dist/media/media-download.js +95 -0
- package/dist/media/media-download.js.map +1 -0
- package/dist/media/mime.d.ts +6 -0
- package/dist/media/mime.js +73 -0
- package/dist/media/mime.js.map +1 -0
- package/dist/media/silk-transcode.d.ts +8 -0
- package/dist/media/silk-transcode.js +64 -0
- package/dist/media/silk-transcode.js.map +1 -0
- package/dist/messaging/debug-mode.d.ts +9 -0
- package/dist/messaging/debug-mode.js +63 -0
- package/dist/messaging/debug-mode.js.map +1 -0
- package/dist/messaging/inbound.d.ts +69 -0
- package/dist/messaging/inbound.js +201 -0
- package/dist/messaging/inbound.js.map +1 -0
- package/dist/messaging/send-media.d.ts +21 -0
- package/dist/messaging/send-media.js +54 -0
- package/dist/messaging/send-media.js.map +1 -0
- package/dist/messaging/send.d.ts +70 -0
- package/dist/messaging/send.js +203 -0
- package/dist/messaging/send.js.map +1 -0
- package/dist/monitor/monitor.d.ts +12 -0
- package/dist/monitor/monitor.js +145 -0
- package/dist/monitor/monitor.js.map +1 -0
- package/dist/storage/state-dir.d.ts +2 -0
- package/dist/storage/state-dir.js +8 -0
- package/dist/storage/state-dir.js.map +1 -0
- package/dist/storage/sync-buf.d.ts +20 -0
- package/dist/storage/sync-buf.js +64 -0
- package/dist/storage/sync-buf.js.map +1 -0
- package/dist/util/logger.d.ts +14 -0
- package/dist/util/logger.js +119 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/random.d.ts +10 -0
- package/dist/util/random.js +16 -0
- package/dist/util/random.js.map +1 -0
- package/dist/util/redact.d.ts +20 -0
- package/dist/util/redact.js +54 -0
- package/dist/util/redact.js.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weixin protocol types (mirrors proto: GetUpdatesReq/Resp, WeixinMessage, SendMessageReq).
|
|
3
|
+
* API uses JSON over HTTP; bytes fields are base64 strings in JSON.
|
|
4
|
+
*/
|
|
5
|
+
/** Common request metadata attached to every CGI request. */
|
|
6
|
+
export interface BaseInfo {
|
|
7
|
+
channel_version?: string;
|
|
8
|
+
}
|
|
9
|
+
/** proto: UploadMediaType */
|
|
10
|
+
export declare const UploadMediaType: {
|
|
11
|
+
readonly IMAGE: 1;
|
|
12
|
+
readonly VIDEO: 2;
|
|
13
|
+
readonly FILE: 3;
|
|
14
|
+
readonly VOICE: 4;
|
|
15
|
+
};
|
|
16
|
+
export interface GetUploadUrlReq {
|
|
17
|
+
filekey?: string;
|
|
18
|
+
/** proto field 2: media_type, see UploadMediaType */
|
|
19
|
+
media_type?: number;
|
|
20
|
+
to_user_id?: string;
|
|
21
|
+
/** 原文件明文大小 */
|
|
22
|
+
rawsize?: number;
|
|
23
|
+
/** 原文件明文 MD5 */
|
|
24
|
+
rawfilemd5?: string;
|
|
25
|
+
/** 原文件密文大小(AES-128-ECB 加密后) */
|
|
26
|
+
filesize?: number;
|
|
27
|
+
/** 缩略图明文大小(IMAGE/VIDEO 时必填) */
|
|
28
|
+
thumb_rawsize?: number;
|
|
29
|
+
/** 缩略图明文 MD5(IMAGE/VIDEO 时必填) */
|
|
30
|
+
thumb_rawfilemd5?: string;
|
|
31
|
+
/** 缩略图密文大小(IMAGE/VIDEO 时必填) */
|
|
32
|
+
thumb_filesize?: number;
|
|
33
|
+
/** 不需要缩略图上传 URL,默认 false */
|
|
34
|
+
no_need_thumb?: boolean;
|
|
35
|
+
/** 加密 key */
|
|
36
|
+
aeskey?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface GetUploadUrlResp {
|
|
39
|
+
/** 原图上传加密参数 */
|
|
40
|
+
upload_param?: string;
|
|
41
|
+
/** 缩略图上传加密参数,无缩略图时为空 */
|
|
42
|
+
thumb_upload_param?: string;
|
|
43
|
+
/** 完整上传 URL(服务端直接返回,无需客户端拼接) */
|
|
44
|
+
upload_full_url?: string;
|
|
45
|
+
}
|
|
46
|
+
export declare const MessageType: {
|
|
47
|
+
readonly NONE: 0;
|
|
48
|
+
readonly USER: 1;
|
|
49
|
+
readonly BOT: 2;
|
|
50
|
+
};
|
|
51
|
+
export declare const MessageItemType: {
|
|
52
|
+
readonly NONE: 0;
|
|
53
|
+
readonly TEXT: 1;
|
|
54
|
+
readonly IMAGE: 2;
|
|
55
|
+
readonly VOICE: 3;
|
|
56
|
+
readonly FILE: 4;
|
|
57
|
+
readonly VIDEO: 5;
|
|
58
|
+
};
|
|
59
|
+
export declare const MessageState: {
|
|
60
|
+
readonly NEW: 0;
|
|
61
|
+
readonly GENERATING: 1;
|
|
62
|
+
readonly FINISH: 2;
|
|
63
|
+
};
|
|
64
|
+
export interface TextItem {
|
|
65
|
+
text?: string;
|
|
66
|
+
}
|
|
67
|
+
/** CDN media reference; aes_key is base64-encoded bytes in JSON. */
|
|
68
|
+
export interface CDNMedia {
|
|
69
|
+
encrypt_query_param?: string;
|
|
70
|
+
aes_key?: string;
|
|
71
|
+
/** 加密类型: 0=只加密fileid, 1=打包缩略图/中图等信息 */
|
|
72
|
+
encrypt_type?: number;
|
|
73
|
+
/** 完整下载 URL(服务端直接返回,无需客户端拼接) */
|
|
74
|
+
full_url?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface ImageItem {
|
|
77
|
+
/** 原图 CDN 引用 */
|
|
78
|
+
media?: CDNMedia;
|
|
79
|
+
/** 缩略图 CDN 引用 */
|
|
80
|
+
thumb_media?: CDNMedia;
|
|
81
|
+
/** Raw AES-128 key as hex string (16 bytes); preferred over media.aes_key for inbound decryption. */
|
|
82
|
+
aeskey?: string;
|
|
83
|
+
url?: string;
|
|
84
|
+
mid_size?: number;
|
|
85
|
+
thumb_size?: number;
|
|
86
|
+
thumb_height?: number;
|
|
87
|
+
thumb_width?: number;
|
|
88
|
+
hd_size?: number;
|
|
89
|
+
}
|
|
90
|
+
export interface VoiceItem {
|
|
91
|
+
media?: CDNMedia;
|
|
92
|
+
/** 语音编码类型:1=pcm 2=adpcm 3=feature 4=speex 5=amr 6=silk 7=mp3 8=ogg-speex */
|
|
93
|
+
encode_type?: number;
|
|
94
|
+
bits_per_sample?: number;
|
|
95
|
+
/** 采样率 (Hz) */
|
|
96
|
+
sample_rate?: number;
|
|
97
|
+
/** 语音长度 (毫秒) */
|
|
98
|
+
playtime?: number;
|
|
99
|
+
/** 语音转文字内容 */
|
|
100
|
+
text?: string;
|
|
101
|
+
}
|
|
102
|
+
export interface FileItem {
|
|
103
|
+
media?: CDNMedia;
|
|
104
|
+
file_name?: string;
|
|
105
|
+
md5?: string;
|
|
106
|
+
len?: string;
|
|
107
|
+
}
|
|
108
|
+
export interface VideoItem {
|
|
109
|
+
media?: CDNMedia;
|
|
110
|
+
video_size?: number;
|
|
111
|
+
play_length?: number;
|
|
112
|
+
video_md5?: string;
|
|
113
|
+
thumb_media?: CDNMedia;
|
|
114
|
+
thumb_size?: number;
|
|
115
|
+
thumb_height?: number;
|
|
116
|
+
thumb_width?: number;
|
|
117
|
+
}
|
|
118
|
+
export interface RefMessage {
|
|
119
|
+
message_item?: MessageItem;
|
|
120
|
+
title?: string;
|
|
121
|
+
}
|
|
122
|
+
export interface MessageItem {
|
|
123
|
+
type?: number;
|
|
124
|
+
create_time_ms?: number;
|
|
125
|
+
update_time_ms?: number;
|
|
126
|
+
is_completed?: boolean;
|
|
127
|
+
msg_id?: string;
|
|
128
|
+
ref_msg?: RefMessage;
|
|
129
|
+
text_item?: TextItem;
|
|
130
|
+
image_item?: ImageItem;
|
|
131
|
+
voice_item?: VoiceItem;
|
|
132
|
+
file_item?: FileItem;
|
|
133
|
+
video_item?: VideoItem;
|
|
134
|
+
}
|
|
135
|
+
/** Unified message (proto: WeixinMessage). Replaces the old split Message + MessageContent + FullMessage. */
|
|
136
|
+
export interface WeixinMessage {
|
|
137
|
+
seq?: number;
|
|
138
|
+
message_id?: number;
|
|
139
|
+
from_user_id?: string;
|
|
140
|
+
to_user_id?: string;
|
|
141
|
+
client_id?: string;
|
|
142
|
+
create_time_ms?: number;
|
|
143
|
+
update_time_ms?: number;
|
|
144
|
+
delete_time_ms?: number;
|
|
145
|
+
session_id?: string;
|
|
146
|
+
group_id?: string;
|
|
147
|
+
message_type?: number;
|
|
148
|
+
message_state?: number;
|
|
149
|
+
item_list?: MessageItem[];
|
|
150
|
+
context_token?: string;
|
|
151
|
+
}
|
|
152
|
+
/** GetUpdates request: bytes fields are base64 strings in JSON. */
|
|
153
|
+
export interface GetUpdatesReq {
|
|
154
|
+
/** @deprecated compat only, will be removed */
|
|
155
|
+
sync_buf?: string;
|
|
156
|
+
/** Full context buf cached locally; send "" when none (first request or after reset). */
|
|
157
|
+
get_updates_buf?: string;
|
|
158
|
+
}
|
|
159
|
+
/** GetUpdates response: bytes fields are base64 strings in JSON. */
|
|
160
|
+
export interface GetUpdatesResp {
|
|
161
|
+
ret?: number;
|
|
162
|
+
/** Error code returned by the server (e.g. -14 = session timeout). Present when request fails. */
|
|
163
|
+
errcode?: number;
|
|
164
|
+
errmsg?: string;
|
|
165
|
+
msgs?: WeixinMessage[];
|
|
166
|
+
/** @deprecated compat only */
|
|
167
|
+
sync_buf?: string;
|
|
168
|
+
/** Full context buf to cache locally and send on next request. */
|
|
169
|
+
get_updates_buf?: string;
|
|
170
|
+
/** Server-suggested timeout (ms) for the next getUpdates long-poll. */
|
|
171
|
+
longpolling_timeout_ms?: number;
|
|
172
|
+
}
|
|
173
|
+
/** SendMessage request: wraps a single WeixinMessage. */
|
|
174
|
+
export interface SendMessageReq {
|
|
175
|
+
msg?: WeixinMessage;
|
|
176
|
+
}
|
|
177
|
+
export interface SendMessageResp {
|
|
178
|
+
}
|
|
179
|
+
/** Typing status: 1 = typing (default), 2 = cancel typing. */
|
|
180
|
+
export declare const TypingStatus: {
|
|
181
|
+
readonly TYPING: 1;
|
|
182
|
+
readonly CANCEL: 2;
|
|
183
|
+
};
|
|
184
|
+
/** SendTyping request: send a typing indicator to a user. */
|
|
185
|
+
export interface SendTypingReq {
|
|
186
|
+
ilink_user_id?: string;
|
|
187
|
+
typing_ticket?: string;
|
|
188
|
+
/** 1=typing (default), 2=cancel typing */
|
|
189
|
+
status?: number;
|
|
190
|
+
}
|
|
191
|
+
export interface SendTypingResp {
|
|
192
|
+
ret?: number;
|
|
193
|
+
errmsg?: string;
|
|
194
|
+
}
|
|
195
|
+
/** GetConfig response: bot config including typing_ticket. */
|
|
196
|
+
export interface GetConfigResp {
|
|
197
|
+
ret?: number;
|
|
198
|
+
errmsg?: string;
|
|
199
|
+
/** Base64-encoded typing ticket for sendTyping. */
|
|
200
|
+
typing_ticket?: string;
|
|
201
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weixin protocol types (mirrors proto: GetUpdatesReq/Resp, WeixinMessage, SendMessageReq).
|
|
3
|
+
* API uses JSON over HTTP; bytes fields are base64 strings in JSON.
|
|
4
|
+
*/
|
|
5
|
+
/** proto: UploadMediaType */
|
|
6
|
+
export const UploadMediaType = {
|
|
7
|
+
IMAGE: 1,
|
|
8
|
+
VIDEO: 2,
|
|
9
|
+
FILE: 3,
|
|
10
|
+
VOICE: 4,
|
|
11
|
+
};
|
|
12
|
+
export const MessageType = {
|
|
13
|
+
NONE: 0,
|
|
14
|
+
USER: 1,
|
|
15
|
+
BOT: 2,
|
|
16
|
+
};
|
|
17
|
+
export const MessageItemType = {
|
|
18
|
+
NONE: 0,
|
|
19
|
+
TEXT: 1,
|
|
20
|
+
IMAGE: 2,
|
|
21
|
+
VOICE: 3,
|
|
22
|
+
FILE: 4,
|
|
23
|
+
VIDEO: 5,
|
|
24
|
+
};
|
|
25
|
+
export const MessageState = {
|
|
26
|
+
NEW: 0,
|
|
27
|
+
GENERATING: 1,
|
|
28
|
+
FINISH: 2,
|
|
29
|
+
};
|
|
30
|
+
/** Typing status: 1 = typing (default), 2 = cancel typing. */
|
|
31
|
+
export const TypingStatus = {
|
|
32
|
+
TYPING: 1,
|
|
33
|
+
CANCEL: 2,
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,6BAA6B;AAC7B,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,KAAK,EAAE,CAAC;IACR,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACA,CAAC;AAkCX,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,GAAG,EAAE,CAAC;CACE,CAAC;AAEX,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACA,CAAC;AAEX,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,GAAG,EAAE,CAAC;IACN,UAAU,EAAE,CAAC;IACb,MAAM,EAAE,CAAC;CACD,CAAC;AAmIX,8DAA8D;AAC9D,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;CACD,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com";
|
|
2
|
+
export declare const CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c";
|
|
3
|
+
export declare function normalizeAccountId(id: string): string;
|
|
4
|
+
export declare function deriveRawAccountId(normalizedId: string): string | undefined;
|
|
5
|
+
export declare function listIndexedWeixinAccountIds(): string[];
|
|
6
|
+
export declare function registerWeixinAccountId(accountId: string): void;
|
|
7
|
+
export declare function unregisterWeixinAccountId(accountId: string): void;
|
|
8
|
+
export declare function clearStaleAccountsForUserId(currentAccountId: string, userId: string, onClearContextTokens?: (accountId: string) => void): void;
|
|
9
|
+
export type WeixinAccountData = {
|
|
10
|
+
token?: string;
|
|
11
|
+
savedAt?: string;
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
userId?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function loadWeixinAccount(accountId: string): WeixinAccountData | null;
|
|
16
|
+
export declare function saveWeixinAccount(accountId: string, update: {
|
|
17
|
+
token?: string;
|
|
18
|
+
baseUrl?: string;
|
|
19
|
+
userId?: string;
|
|
20
|
+
}): void;
|
|
21
|
+
export declare function clearWeixinAccount(accountId: string): void;
|
|
22
|
+
export type ResolvedWeixinAccount = {
|
|
23
|
+
accountId: string;
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
cdnBaseUrl: string;
|
|
26
|
+
token?: string;
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
configured: boolean;
|
|
29
|
+
};
|
|
30
|
+
export declare function resolveWeixinAccount(accountId?: string | null): ResolvedWeixinAccount;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { resolveStateDir } from "../storage/state-dir.js";
|
|
4
|
+
import { logger } from "../util/logger.js";
|
|
5
|
+
export const DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com";
|
|
6
|
+
export const CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c";
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Account ID compatibility (legacy raw ID → normalized ID)
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
export function normalizeAccountId(id) {
|
|
11
|
+
// Convert special characters to hyphen, ensuring filesystem-safe names
|
|
12
|
+
return id.replace(/[^a-zA-Z0-9_-]/g, '-');
|
|
13
|
+
}
|
|
14
|
+
export function deriveRawAccountId(normalizedId) {
|
|
15
|
+
if (normalizedId.endsWith("-im-bot")) {
|
|
16
|
+
return `${normalizedId.slice(0, -7)}@im.bot`;
|
|
17
|
+
}
|
|
18
|
+
if (normalizedId.endsWith("-im-wechat")) {
|
|
19
|
+
return `${normalizedId.slice(0, -10)}@im.wechat`;
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Account index (persistent list of registered account IDs)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
function resolveAccountIndexPath() {
|
|
27
|
+
return path.join(resolveStateDir(), "accounts.json");
|
|
28
|
+
}
|
|
29
|
+
export function listIndexedWeixinAccountIds() {
|
|
30
|
+
const filePath = resolveAccountIndexPath();
|
|
31
|
+
try {
|
|
32
|
+
if (!fs.existsSync(filePath))
|
|
33
|
+
return [];
|
|
34
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
35
|
+
const parsed = JSON.parse(raw);
|
|
36
|
+
if (!Array.isArray(parsed))
|
|
37
|
+
return [];
|
|
38
|
+
return parsed.filter((id) => typeof id === "string" && id.trim() !== "");
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function registerWeixinAccountId(accountId) {
|
|
45
|
+
const dir = resolveStateDir();
|
|
46
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
47
|
+
const existing = listIndexedWeixinAccountIds();
|
|
48
|
+
if (existing.includes(accountId))
|
|
49
|
+
return;
|
|
50
|
+
const updated = [...existing, accountId];
|
|
51
|
+
fs.writeFileSync(resolveAccountIndexPath(), JSON.stringify(updated, null, 2), "utf-8");
|
|
52
|
+
}
|
|
53
|
+
export function unregisterWeixinAccountId(accountId) {
|
|
54
|
+
const existing = listIndexedWeixinAccountIds();
|
|
55
|
+
const updated = existing.filter((id) => id !== accountId);
|
|
56
|
+
if (updated.length !== existing.length) {
|
|
57
|
+
fs.writeFileSync(resolveAccountIndexPath(), JSON.stringify(updated, null, 2), "utf-8");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export function clearStaleAccountsForUserId(currentAccountId, userId, onClearContextTokens) {
|
|
61
|
+
if (!userId)
|
|
62
|
+
return;
|
|
63
|
+
const allIds = listIndexedWeixinAccountIds();
|
|
64
|
+
for (const id of allIds) {
|
|
65
|
+
if (id === currentAccountId)
|
|
66
|
+
continue;
|
|
67
|
+
const data = loadWeixinAccount(id);
|
|
68
|
+
if (data?.userId?.trim() === userId) {
|
|
69
|
+
logger.info(`clearStaleAccountsForUserId: removing stale account=${id} (same userId=${userId})`);
|
|
70
|
+
onClearContextTokens?.(id);
|
|
71
|
+
clearWeixinAccount(id);
|
|
72
|
+
unregisterWeixinAccountId(id);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function resolveAccountsDir() {
|
|
77
|
+
return path.join(resolveStateDir(), "accounts");
|
|
78
|
+
}
|
|
79
|
+
function resolveAccountPath(accountId) {
|
|
80
|
+
return path.join(resolveAccountsDir(), `${accountId}.json`);
|
|
81
|
+
}
|
|
82
|
+
function readAccountFile(filePath) {
|
|
83
|
+
try {
|
|
84
|
+
if (fs.existsSync(filePath)) {
|
|
85
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// ignore
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
export function loadWeixinAccount(accountId) {
|
|
94
|
+
const primary = readAccountFile(resolveAccountPath(accountId));
|
|
95
|
+
if (primary)
|
|
96
|
+
return primary;
|
|
97
|
+
const rawId = deriveRawAccountId(accountId);
|
|
98
|
+
if (rawId) {
|
|
99
|
+
const compat = readAccountFile(resolveAccountPath(rawId));
|
|
100
|
+
if (compat)
|
|
101
|
+
return compat;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
export function saveWeixinAccount(accountId, update) {
|
|
106
|
+
const dir = resolveAccountsDir();
|
|
107
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
108
|
+
const existing = loadWeixinAccount(accountId) ?? {};
|
|
109
|
+
const token = update.token?.trim() || existing.token;
|
|
110
|
+
const baseUrl = update.baseUrl?.trim() || existing.baseUrl;
|
|
111
|
+
const userId = update.userId !== undefined
|
|
112
|
+
? update.userId.trim() || undefined
|
|
113
|
+
: existing.userId?.trim() || undefined;
|
|
114
|
+
const data = {
|
|
115
|
+
...(token ? { token, savedAt: new Date().toISOString() } : {}),
|
|
116
|
+
...(baseUrl ? { baseUrl } : {}),
|
|
117
|
+
...(userId ? { userId } : {}),
|
|
118
|
+
};
|
|
119
|
+
const filePath = resolveAccountPath(accountId);
|
|
120
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
121
|
+
try {
|
|
122
|
+
fs.chmodSync(filePath, 0o600);
|
|
123
|
+
}
|
|
124
|
+
catch { }
|
|
125
|
+
}
|
|
126
|
+
export function clearWeixinAccount(accountId) {
|
|
127
|
+
const dir = resolveAccountsDir();
|
|
128
|
+
const accountFiles = [
|
|
129
|
+
`${accountId}.json`,
|
|
130
|
+
`${accountId}.sync.json`,
|
|
131
|
+
`${accountId}.context-tokens.json`,
|
|
132
|
+
];
|
|
133
|
+
for (const file of accountFiles) {
|
|
134
|
+
try {
|
|
135
|
+
fs.unlinkSync(path.join(dir, file));
|
|
136
|
+
}
|
|
137
|
+
catch { }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export function resolveWeixinAccount(accountId) {
|
|
141
|
+
const raw = accountId?.trim();
|
|
142
|
+
if (!raw) {
|
|
143
|
+
throw new Error("weixin: accountId is required (no default account)");
|
|
144
|
+
}
|
|
145
|
+
const id = normalizeAccountId(raw);
|
|
146
|
+
const accountData = loadWeixinAccount(id);
|
|
147
|
+
const token = accountData?.token?.trim() || undefined;
|
|
148
|
+
const stateBaseUrl = accountData?.baseUrl?.trim() || "";
|
|
149
|
+
return {
|
|
150
|
+
accountId: id,
|
|
151
|
+
baseUrl: stateBaseUrl || DEFAULT_BASE_URL,
|
|
152
|
+
cdnBaseUrl: CDN_BASE_URL,
|
|
153
|
+
token,
|
|
154
|
+
enabled: true,
|
|
155
|
+
configured: Boolean(token),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=accounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/auth/accounts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Default `bot_type` for ilink get_bot_qrcode / get_qrcode_status (this channel build). */
|
|
2
|
+
export declare const DEFAULT_ILINK_BOT_TYPE = "3";
|
|
3
|
+
export type WeixinQrStartResult = {
|
|
4
|
+
qrcodeUrl?: string;
|
|
5
|
+
message: string;
|
|
6
|
+
sessionKey: string;
|
|
7
|
+
};
|
|
8
|
+
export type WeixinQrWaitResult = {
|
|
9
|
+
connected: boolean;
|
|
10
|
+
botToken?: string;
|
|
11
|
+
accountId?: string;
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
/** The user ID of the person who scanned the QR code; add to allowFrom. */
|
|
14
|
+
userId?: string;
|
|
15
|
+
message: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function startWeixinLoginWithQr(opts: {
|
|
18
|
+
verbose?: boolean;
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
force?: boolean;
|
|
21
|
+
accountId?: string;
|
|
22
|
+
apiBaseUrl: string;
|
|
23
|
+
botType?: string;
|
|
24
|
+
}): Promise<WeixinQrStartResult>;
|
|
25
|
+
export declare function waitForWeixinLogin(opts: {
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
verbose?: boolean;
|
|
28
|
+
sessionKey: string;
|
|
29
|
+
apiBaseUrl: string;
|
|
30
|
+
botType?: string;
|
|
31
|
+
}): Promise<WeixinQrWaitResult>;
|