weflow-cli 1.5.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 +21 -0
- package/README.md +149 -0
- package/bin/weflow-cli-electron.cjs +59 -0
- package/bin/weflow-cli.ts +2311 -0
- package/cli.cjs +3 -0
- package/dist/bin/weflow-cli.d.ts +3 -0
- package/dist/bin/weflow-cli.d.ts.map +1 -0
- package/dist/bin/weflow-cli.js +2270 -0
- package/dist/bin/weflow-cli.js.map +1 -0
- package/dist/mcp-server/index.d.ts +3 -0
- package/dist/mcp-server/index.d.ts.map +1 -0
- package/dist/mcp-server/index.js +690 -0
- package/dist/mcp-server/index.js.map +1 -0
- package/dist/src/core/dbPathService.d.ts +40 -0
- package/dist/src/core/dbPathService.d.ts.map +1 -0
- package/dist/src/core/dbPathService.js +324 -0
- package/dist/src/core/dbPathService.js.map +1 -0
- package/dist/src/core/keyService.d.ts +59 -0
- package/dist/src/core/keyService.d.ts.map +1 -0
- package/dist/src/core/keyService.js +368 -0
- package/dist/src/core/keyService.js.map +1 -0
- package/dist/src/core/ntCore.d.ts +62 -0
- package/dist/src/core/ntCore.d.ts.map +1 -0
- package/dist/src/core/ntCore.js +191 -0
- package/dist/src/core/ntCore.js.map +1 -0
- package/dist/src/core/sqlcipherCore.d.ts +119 -0
- package/dist/src/core/sqlcipherCore.d.ts.map +1 -0
- package/dist/src/core/sqlcipherCore.js +693 -0
- package/dist/src/core/sqlcipherCore.js.map +1 -0
- package/dist/src/core/wcdbCore.d.ts +738 -0
- package/dist/src/core/wcdbCore.d.ts.map +1 -0
- package/dist/src/core/wcdbCore.js +4545 -0
- package/dist/src/core/wcdbCore.js.map +1 -0
- package/dist/src/core/wechatClient.d.ts +23 -0
- package/dist/src/core/wechatClient.d.ts.map +1 -0
- package/dist/src/core/wechatClient.js +166 -0
- package/dist/src/core/wechatClient.js.map +1 -0
- package/dist/src/services/chatService.d.ts +25 -0
- package/dist/src/services/chatService.d.ts.map +1 -0
- package/dist/src/services/chatService.js +291 -0
- package/dist/src/services/chatService.js.map +1 -0
- package/dist/src/services/configService.d.ts +43 -0
- package/dist/src/services/configService.d.ts.map +1 -0
- package/dist/src/services/configService.js +156 -0
- package/dist/src/services/configService.js.map +1 -0
- package/dist/src/services/exportService.d.ts +30 -0
- package/dist/src/services/exportService.d.ts.map +1 -0
- package/dist/src/services/exportService.js +273 -0
- package/dist/src/services/exportService.js.map +1 -0
- package/dist/src/services/wechat-formatter.d.ts +69 -0
- package/dist/src/services/wechat-formatter.d.ts.map +1 -0
- package/dist/src/services/wechat-formatter.js +248 -0
- package/dist/src/services/wechat-formatter.js.map +1 -0
- package/dist/src/services/wechatMessageService.d.ts +28 -0
- package/dist/src/services/wechatMessageService.d.ts.map +1 -0
- package/dist/src/services/wechatMessageService.js +341 -0
- package/dist/src/services/wechatMessageService.js.map +1 -0
- package/dist/src/services/wereadService.d.ts +206 -0
- package/dist/src/services/wereadService.d.ts.map +1 -0
- package/dist/src/services/wereadService.js +145 -0
- package/dist/src/services/wereadService.js.map +1 -0
- package/dist/src/services/whitelistService.d.ts +20 -0
- package/dist/src/services/whitelistService.d.ts.map +1 -0
- package/dist/src/services/whitelistService.js +60 -0
- package/dist/src/services/whitelistService.js.map +1 -0
- package/dist/src/types.d.ts +140 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/errors.d.ts +25 -0
- package/dist/src/utils/errors.d.ts.map +1 -0
- package/dist/src/utils/errors.js +43 -0
- package/dist/src/utils/errors.js.map +1 -0
- package/dist/src/utils/pathUtils.d.ts +5 -0
- package/dist/src/utils/pathUtils.d.ts.map +1 -0
- package/dist/src/utils/pathUtils.js +16 -0
- package/dist/src/utils/pathUtils.js.map +1 -0
- package/dist/src/utils/pythonRunner.d.ts +27 -0
- package/dist/src/utils/pythonRunner.d.ts.map +1 -0
- package/dist/src/utils/pythonRunner.js +62 -0
- package/dist/src/utils/pythonRunner.js.map +1 -0
- package/dist/src/utils/talkerUtils.d.ts +14 -0
- package/dist/src/utils/talkerUtils.d.ts.map +1 -0
- package/dist/src/utils/talkerUtils.js +73 -0
- package/dist/src/utils/talkerUtils.js.map +1 -0
- package/mcp-server/index.ts +757 -0
- package/package.json +72 -0
- package/resources/key/win32/x64/wx_key.dll +0 -0
- package/resources/wcdb/win32/x64/SDL2.dll +0 -0
- package/resources/wcdb/win32/x64/WCDB.dll +0 -0
- package/resources/wcdb/win32/x64/msvcp140.dll +0 -0
- package/resources/wcdb/win32/x64/msvcp140_1.dll +0 -0
- package/resources/wcdb/win32/x64/vcruntime140.dll +0 -0
- package/resources/wcdb/win32/x64/vcruntime140_1.dll +0 -0
- package/resources/wcdb/win32/x64/wcdb_api.dll +0 -0
- package/scripts/_batch_link_sources.py +73 -0
- package/scripts/_utils.py +531 -0
- package/scripts/annual_report.py +796 -0
- package/scripts/auto_tag.py +128 -0
- package/scripts/biz_daily.py +896 -0
- package/scripts/chat_report.py +397 -0
- package/scripts/chat_stats.py +702 -0
- package/scripts/classify_daily.py +411 -0
- package/scripts/compile_wiki.py +258 -0
- package/scripts/create_reading_notes.py +370 -0
- package/scripts/enrich_backlinks.py +163 -0
- package/scripts/export_chat_html.py +766 -0
- package/scripts/extract_3x_key.py +86 -0
- package/scripts/extract_todos.py +404 -0
- package/scripts/fav_server.py +392 -0
- package/scripts/fix_topics.py +111 -0
- package/scripts/generate_ai_report.py +367 -0
- package/scripts/generate_html.py +1372 -0
- package/scripts/generate_review.py +170 -0
- package/scripts/mcp_bridge.py +398 -0
- package/scripts/nt_decrypt.py +562 -0
- package/scripts/pipeline.py +145 -0
- package/scripts/promote_all.py +310 -0
- package/scripts/promote_ideas.py +297 -0
- package/scripts/rag_chat.py +191 -0
- package/scripts/scan_decrypt_4x.py +329 -0
- package/scripts/semantic_search.py +431 -0
- package/scripts/sync_fav.py +146 -0
- package/scripts/sync_weread.py +192 -0
- package/scripts/vault_rag.py +139 -0
- package/scripts/vault_search.py +141 -0
- package/src/core/dbPathService.ts +348 -0
- package/src/core/keyService.ts +409 -0
- package/src/core/ntCore.ts +224 -0
- package/src/core/sqlcipherCore.ts +764 -0
- package/src/core/wcdbCore.ts +4568 -0
- package/src/core/wechatClient.ts +204 -0
- package/src/lz4.d.ts +10 -0
- package/src/services/chatService.ts +296 -0
- package/src/services/configService.ts +197 -0
- package/src/services/exportService.ts +305 -0
- package/src/services/wechat-formatter.ts +348 -0
- package/src/services/wechatMessageService.ts +383 -0
- package/src/services/wereadService.ts +308 -0
- package/src/services/whitelistService.ts +72 -0
- package/src/types.ts +141 -0
- package/src/utils/errors.ts +42 -0
- package/src/utils/pathUtils.ts +16 -0
- package/src/utils/pythonRunner.ts +81 -0
- package/src/utils/talkerUtils.ts +84 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { join, basename } from 'path'
|
|
2
|
+
import { existsSync, readdirSync, statSync, readFileSync } from 'fs'
|
|
3
|
+
import { homedir } from 'os'
|
|
4
|
+
import { createDecipheriv } from 'crypto'
|
|
5
|
+
import { expandHomePath } from '../utils/pathUtils.js'
|
|
6
|
+
|
|
7
|
+
export interface WxidInfo {
|
|
8
|
+
wxid: string
|
|
9
|
+
modifiedTime: number
|
|
10
|
+
nickname?: string
|
|
11
|
+
avatarUrl?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class DbPathService {
|
|
15
|
+
private readVarint(buf: Buffer, offset: number): { value: number, length: number } {
|
|
16
|
+
let value = 0;
|
|
17
|
+
let length = 0;
|
|
18
|
+
let shift = 0;
|
|
19
|
+
while (offset < buf.length && shift < 32) {
|
|
20
|
+
const b = buf[offset++];
|
|
21
|
+
value |= (b & 0x7f) << shift;
|
|
22
|
+
length++;
|
|
23
|
+
if ((b & 0x80) === 0) break;
|
|
24
|
+
shift += 7;
|
|
25
|
+
}
|
|
26
|
+
return { value, length };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private extractMmkvString(buf: Buffer, keyName: string): string {
|
|
30
|
+
const keyBuf = Buffer.from(keyName, 'utf8');
|
|
31
|
+
const idx = buf.indexOf(keyBuf);
|
|
32
|
+
if (idx === -1) return '';
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
let offset = idx + keyBuf.length;
|
|
36
|
+
const v1 = this.readVarint(buf, offset);
|
|
37
|
+
offset += v1.length;
|
|
38
|
+
const v2 = this.readVarint(buf, offset);
|
|
39
|
+
offset += v2.length;
|
|
40
|
+
|
|
41
|
+
// 合理性检查
|
|
42
|
+
if (v2.value > 0 && v2.value <= 10000 && offset + v2.value <= buf.length) {
|
|
43
|
+
return buf.toString('utf8', offset, offset + v2.value);
|
|
44
|
+
}
|
|
45
|
+
} catch { }
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private parseGlobalConfig(rootPath: string): { wxid: string, nickname: string, avatarUrl: string } | null {
|
|
50
|
+
try {
|
|
51
|
+
const configPath = join(rootPath, 'all_users', 'config', 'global_config');
|
|
52
|
+
if (!existsSync(configPath)) return null;
|
|
53
|
+
|
|
54
|
+
const fullData = readFileSync(configPath);
|
|
55
|
+
if (fullData.length <= 4) return null;
|
|
56
|
+
const encryptedData = fullData.subarray(4);
|
|
57
|
+
|
|
58
|
+
const key = Buffer.alloc(16, 0);
|
|
59
|
+
Buffer.from('xwechat_crypt_key').copy(key); // 直接硬编码,iv更是不重要
|
|
60
|
+
const iv = Buffer.alloc(16, 0);
|
|
61
|
+
|
|
62
|
+
const decipher = createDecipheriv('aes-128-cfb', key, iv);
|
|
63
|
+
decipher.setAutoPadding(false);
|
|
64
|
+
const decrypted = Buffer.concat([decipher.update(encryptedData), decipher.final()]);
|
|
65
|
+
|
|
66
|
+
const wxid = this.extractMmkvString(decrypted, 'mmkv_key_user_name');
|
|
67
|
+
const nickname = this.extractMmkvString(decrypted, 'mmkv_key_nick_name');
|
|
68
|
+
let avatarUrl = this.extractMmkvString(decrypted, 'mmkv_key_head_img_url');
|
|
69
|
+
|
|
70
|
+
if (!avatarUrl && decrypted.includes('http')) {
|
|
71
|
+
const httpIdx = decrypted.indexOf('http');
|
|
72
|
+
const nullIdx = decrypted.indexOf(0x00, httpIdx);
|
|
73
|
+
if (nullIdx !== -1) {
|
|
74
|
+
avatarUrl = decrypted.toString('utf8', httpIdx, nullIdx);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (wxid || nickname) {
|
|
79
|
+
return { wxid, nickname, avatarUrl };
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.error('解析 global_config 失败:', e);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 自动检测微信数据库根目录
|
|
91
|
+
*/
|
|
92
|
+
async autoDetect(): Promise<{ success: boolean; path?: string; error?: string }> {
|
|
93
|
+
try {
|
|
94
|
+
const possiblePaths: string[] = []
|
|
95
|
+
const home = homedir()
|
|
96
|
+
|
|
97
|
+
if (process.platform === 'darwin') {
|
|
98
|
+
// macOS 微信 4.0.5+ 新路径(优先检测)
|
|
99
|
+
const appSupportBase = join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Library', 'Application Support', 'com.tencent.xinWeChat')
|
|
100
|
+
if (existsSync(appSupportBase)) {
|
|
101
|
+
try {
|
|
102
|
+
const entries = readdirSync(appSupportBase)
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
// 匹配形如 2.0b4.0.9 的版本目录
|
|
105
|
+
if (/^\d+\.\d+b\d+\.\d+/.test(entry) || /^\d+\.\d+\.\d+/.test(entry)) {
|
|
106
|
+
possiblePaths.push(join(appSupportBase, entry))
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} catch { }
|
|
110
|
+
}
|
|
111
|
+
// macOS 旧路径兜底
|
|
112
|
+
possiblePaths.push(join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Documents', 'xwechat_files'))
|
|
113
|
+
} else {
|
|
114
|
+
// Windows: 优先检测 NT 格式 (xwechat_files)
|
|
115
|
+
possiblePaths.push(join(home, 'xwechat_files'))
|
|
116
|
+
// Windows 微信4.x 数据目录 (Documents)
|
|
117
|
+
possiblePaths.push(join(home, 'Documents', 'xwechat_files'))
|
|
118
|
+
// Windows 微信3.x 数据目录
|
|
119
|
+
possiblePaths.push(join(home, 'Documents', 'WeChat Files'))
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for (const path of possiblePaths) {
|
|
123
|
+
if (!existsSync(path)) continue
|
|
124
|
+
|
|
125
|
+
// 检查是否有有效的账号目录,或本身就是账号目录
|
|
126
|
+
const accounts = this.findAccountDirs(path)
|
|
127
|
+
if (accounts.length > 0) {
|
|
128
|
+
return { success: true, path }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 如果该目录本身就是账号目录(直接包含 db_storage 等)
|
|
132
|
+
if (this.isAccountDir(path)) {
|
|
133
|
+
return { success: true, path }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { success: false, error: '未能自动检测到微信数据库目录' }
|
|
138
|
+
} catch (e) {
|
|
139
|
+
return { success: false, error: String(e) }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 查找账号目录(包含 db_storage 或图片目录)
|
|
145
|
+
*/
|
|
146
|
+
findAccountDirs(rootPath: string): string[] {
|
|
147
|
+
const resolvedRootPath = expandHomePath(rootPath)
|
|
148
|
+
const accounts: string[] = []
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const entries = readdirSync(resolvedRootPath)
|
|
152
|
+
|
|
153
|
+
for (const entry of entries) {
|
|
154
|
+
const entryPath = join(resolvedRootPath, entry)
|
|
155
|
+
let stat: ReturnType<typeof statSync>
|
|
156
|
+
try {
|
|
157
|
+
stat = statSync(entryPath)
|
|
158
|
+
} catch {
|
|
159
|
+
continue
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (stat.isDirectory()) {
|
|
163
|
+
if (!this.isPotentialAccountName(entry)) continue
|
|
164
|
+
|
|
165
|
+
// 检查是否有有效账号目录结构
|
|
166
|
+
if (this.isAccountDir(entryPath)) {
|
|
167
|
+
accounts.push(entry)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} catch { }
|
|
172
|
+
|
|
173
|
+
return accounts
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private isAccountDir(entryPath: string): boolean {
|
|
177
|
+
return (
|
|
178
|
+
existsSync(join(entryPath, 'db_storage')) || // WeChat 4.x
|
|
179
|
+
existsSync(join(entryPath, 'Msg')) || // WeChat 3.x
|
|
180
|
+
existsSync(join(entryPath, 'FileStorage', 'Image')) ||
|
|
181
|
+
existsSync(join(entryPath, 'FileStorage', 'Image2'))
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private isPotentialAccountName(name: string): boolean {
|
|
186
|
+
const lower = name.toLowerCase()
|
|
187
|
+
if (lower.startsWith('all') || lower.startsWith('applet') || lower.startsWith('backup') || lower.startsWith('wmpf')) {
|
|
188
|
+
return false
|
|
189
|
+
}
|
|
190
|
+
return true
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private getAccountModifiedTime(entryPath: string): number {
|
|
194
|
+
try {
|
|
195
|
+
const accountStat = statSync(entryPath)
|
|
196
|
+
let latest = accountStat.mtimeMs
|
|
197
|
+
|
|
198
|
+
const dbPath = join(entryPath, 'db_storage')
|
|
199
|
+
if (existsSync(dbPath)) {
|
|
200
|
+
const dbStat = statSync(dbPath)
|
|
201
|
+
latest = Math.max(latest, dbStat.mtimeMs)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const imagePath = join(entryPath, 'FileStorage', 'Image')
|
|
205
|
+
if (existsSync(imagePath)) {
|
|
206
|
+
const imageStat = statSync(imagePath)
|
|
207
|
+
latest = Math.max(latest, imageStat.mtimeMs)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const image2Path = join(entryPath, 'FileStorage', 'Image2')
|
|
211
|
+
if (existsSync(image2Path)) {
|
|
212
|
+
const image2Stat = statSync(image2Path)
|
|
213
|
+
latest = Math.max(latest, image2Stat.mtimeMs)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return latest
|
|
217
|
+
} catch {
|
|
218
|
+
return 0
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 扫描目录名候选(仅包含下划线的文件夹,排除 all_users)
|
|
224
|
+
*/
|
|
225
|
+
scanWxidCandidates(rootPath: string): WxidInfo[] {
|
|
226
|
+
const resolvedRootPath = expandHomePath(rootPath)
|
|
227
|
+
const wxids: WxidInfo[] = []
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
if (existsSync(resolvedRootPath)) {
|
|
231
|
+
const entries = readdirSync(resolvedRootPath)
|
|
232
|
+
for (const entry of entries) {
|
|
233
|
+
const entryPath = join(resolvedRootPath, entry)
|
|
234
|
+
let stat: ReturnType<typeof statSync>
|
|
235
|
+
try { stat = statSync(entryPath) } catch { continue }
|
|
236
|
+
if (!stat.isDirectory()) continue
|
|
237
|
+
const lower = entry.toLowerCase()
|
|
238
|
+
if (lower === 'all_users') continue
|
|
239
|
+
if (!entry.includes('_')) continue
|
|
240
|
+
wxids.push({ wxid: entry, modifiedTime: stat.mtimeMs })
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
if (wxids.length === 0) {
|
|
246
|
+
const rootName = basename(resolvedRootPath)
|
|
247
|
+
if (rootName.includes('_') && rootName.toLowerCase() !== 'all_users') {
|
|
248
|
+
const rootStat = statSync(resolvedRootPath)
|
|
249
|
+
wxids.push({ wxid: rootName, modifiedTime: rootStat.mtimeMs })
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
} catch { }
|
|
253
|
+
|
|
254
|
+
const sorted = wxids.sort((a, b) => {
|
|
255
|
+
if (b.modifiedTime !== a.modifiedTime) return b.modifiedTime - a.modifiedTime
|
|
256
|
+
return a.wxid.localeCompare(b.wxid)
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const globalInfo = this.parseGlobalConfig(resolvedRootPath);
|
|
260
|
+
if (globalInfo) {
|
|
261
|
+
for (const w of sorted) {
|
|
262
|
+
if (w.wxid.startsWith(globalInfo.wxid) || sorted.length === 1) {
|
|
263
|
+
w.nickname = globalInfo.nickname;
|
|
264
|
+
w.avatarUrl = globalInfo.avatarUrl;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return sorted;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 扫描 wxid 列表
|
|
275
|
+
*/
|
|
276
|
+
scanWxids(rootPath: string): WxidInfo[] {
|
|
277
|
+
const resolvedRootPath = expandHomePath(rootPath)
|
|
278
|
+
const wxids: WxidInfo[] = []
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
if (this.isAccountDir(resolvedRootPath)) {
|
|
282
|
+
const wxid = basename(resolvedRootPath)
|
|
283
|
+
const modifiedTime = this.getAccountModifiedTime(resolvedRootPath)
|
|
284
|
+
return [{ wxid, modifiedTime }]
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const accounts = this.findAccountDirs(resolvedRootPath)
|
|
288
|
+
|
|
289
|
+
for (const account of accounts) {
|
|
290
|
+
const fullPath = join(resolvedRootPath, account)
|
|
291
|
+
const modifiedTime = this.getAccountModifiedTime(fullPath)
|
|
292
|
+
wxids.push({ wxid: account, modifiedTime })
|
|
293
|
+
}
|
|
294
|
+
} catch { }
|
|
295
|
+
|
|
296
|
+
const sorted = wxids.sort((a, b) => {
|
|
297
|
+
if (b.modifiedTime !== a.modifiedTime) return b.modifiedTime - a.modifiedTime
|
|
298
|
+
return a.wxid.localeCompare(b.wxid)
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const globalInfo = this.parseGlobalConfig(resolvedRootPath);
|
|
302
|
+
if (globalInfo) {
|
|
303
|
+
for (const w of sorted) {
|
|
304
|
+
if (w.wxid.startsWith(globalInfo.wxid) || sorted.length === 1) {
|
|
305
|
+
w.nickname = globalInfo.nickname;
|
|
306
|
+
w.avatarUrl = globalInfo.avatarUrl;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return sorted;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* 获取默认数据库路径
|
|
315
|
+
*/
|
|
316
|
+
getDefaultPath(): string {
|
|
317
|
+
const home = homedir()
|
|
318
|
+
if (process.platform === 'darwin') {
|
|
319
|
+
// 优先返回 4.0.5+ 新路径
|
|
320
|
+
const appSupportBase = join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Library', 'Application Support', 'com.tencent.xinWeChat')
|
|
321
|
+
if (existsSync(appSupportBase)) {
|
|
322
|
+
try {
|
|
323
|
+
const entries = readdirSync(appSupportBase)
|
|
324
|
+
for (const entry of entries) {
|
|
325
|
+
if (/^\d+\.\d+b\d+\.\d+/.test(entry) || /^\d+\.\d+\.\d+/.test(entry)) {
|
|
326
|
+
const candidate = join(appSupportBase, entry)
|
|
327
|
+
if (existsSync(candidate)) return candidate
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
} catch { }
|
|
331
|
+
}
|
|
332
|
+
// 旧版本路径兜底
|
|
333
|
+
return join(home, 'Library', 'Containers', 'com.tencent.xinWeChat', 'Data', 'Documents', 'xwechat_files')
|
|
334
|
+
}
|
|
335
|
+
// 优先返回 4.x 路径
|
|
336
|
+
const xwechatPath = join(home, 'Documents', 'xwechat_files')
|
|
337
|
+
if (existsSync(xwechatPath)) return xwechatPath
|
|
338
|
+
// 备用 4.x 路径(部分安装将数据放在用户目录下)
|
|
339
|
+
const xwechatHomePath = join(home, 'xwechat_files')
|
|
340
|
+
if (existsSync(xwechatHomePath)) return xwechatHomePath
|
|
341
|
+
// 兜底 3.x 路径
|
|
342
|
+
const wechatFilesPath = join(home, 'Documents', 'WeChat Files')
|
|
343
|
+
if (existsSync(wechatFilesPath)) return wechatFilesPath
|
|
344
|
+
return xwechatPath
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export const dbPathService = new DbPathService()
|