mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-17 19:47:08 +08:00
feat: 本地 HTTP API + 设置面板 + 账号自助入口
让 WechatExplorer 既能用图形界面浏览聊天记录,也能作为本机 MCP 数据源被
Claude / Codex 等客户端通过 127.0.0.1:6131 直接拉取。
本机 HTTP API
- 新增 http-server: 8 个端点,覆盖 health / current_time / contact /
chatroom / recent_chat / chatlog / group_snapshot / resolve / report
- 时间参数支持 YYYY-MM-DD / YYYY-MM-DD/HH:mm / Unix 秒级;日期单独使用
时自动补到 00:00:00~23:59:59,避免漏消息
- apiServer 单例支持动态启停,启动失败返回 friendlyMessage(把
EADDRINUSE 翻译成"端口已被占用"的中文错误并附 4 次重试)
数据库根目录与自服务入口
- Wcdb4Client 接受 accountRoot 时会自动解析:父目录下找最新含
db_storage 的 wxid 子目录;设置面板"测试连接"成功后回写解析后的
精确路径
- 抽 chat-service.ts:IPC 和 HTTP 共享 listContacts / listMessages /
getGroupSnapshot / searchMessages / getSelfAccountInfo / testConnection
/ reopenWithRoot
- WechatDb 接受可选 accountRoot;设置面板新增"应用并重新初始化"
按钮,改完 dbRoot 立即生效
- 新增 settings-store.ts,dbRoot / apiEnabled / apiHost / apiPort 落到
userData/settings.json
主进程健壮性
- 新增 safe-log.ts 包一层 console.log/warn/error,electron-vite 关闭
子进程 stderr 后写 EPIPE 不再炸 IPC handler(原 main build 启动时
即 installSafeConsole)
This commit is contained in:
@@ -145,7 +145,9 @@ export class Wcdb4Client {
|
||||
|
||||
constructor(key: string, accountRoot?: string) {
|
||||
this.key = key.replace(/^0x/i, '').trim()
|
||||
this.accountRoot = accountRoot || Wcdb4Client.findLatestAccountRoot()
|
||||
this.accountRoot = accountRoot
|
||||
? Wcdb4Client.resolveAccountRoot(accountRoot)
|
||||
: Wcdb4Client.findLatestAccountRoot()
|
||||
this.wxid = Wcdb4Client.cleanAccountDirName(path.basename(this.accountRoot))
|
||||
this.dbStoragePath = path.join(this.accountRoot, 'db_storage')
|
||||
this.sessionDbPath = this.findSessionDb()
|
||||
@@ -155,6 +157,37 @@ export class Wcdb4Client {
|
||||
}
|
||||
}
|
||||
|
||||
static resolveAccountRoot(accountRoot: string): string {
|
||||
const target = (accountRoot || '').trim().replace(/\/+$/, '')
|
||||
if (!target) {
|
||||
throw new Error('微信 4.0 账号目录不能为空')
|
||||
}
|
||||
if (fs.existsSync(path.join(target, 'db_storage'))) {
|
||||
return target
|
||||
}
|
||||
if (!fs.existsSync(target)) {
|
||||
throw new Error(`未找到微信 4.0 数据目录: ${target}`)
|
||||
}
|
||||
const candidates = fs
|
||||
.readdirSync(target)
|
||||
.map((name) => path.join(target, name))
|
||||
.filter((candidate) => {
|
||||
try {
|
||||
return (
|
||||
fs.statSync(candidate).isDirectory() &&
|
||||
fs.existsSync(path.join(candidate, 'db_storage'))
|
||||
)
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)
|
||||
if (!candidates[0]) {
|
||||
throw new Error(`未找到包含 db_storage 的微信 4.0 账号目录: ${target}`)
|
||||
}
|
||||
return candidates[0]
|
||||
}
|
||||
|
||||
static findLatestAccountRoot(): string {
|
||||
const root = Wcdb4Client.defaultRoot
|
||||
if (!fs.existsSync(root)) {
|
||||
@@ -714,6 +747,10 @@ export class Wcdb4Client {
|
||||
return this.accountRoot
|
||||
}
|
||||
|
||||
getKey(): string {
|
||||
return this.key
|
||||
}
|
||||
|
||||
resolveImageHardlink(md5: string): Wcdb4ImageHardlink | null {
|
||||
if (!this.wcdbResolveImageHardlink) return null
|
||||
const normalizedMd5 = String(md5 || '')
|
||||
@@ -1390,7 +1427,7 @@ export class Wcdb4Client {
|
||||
return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)))
|
||||
}
|
||||
|
||||
private getMyUsernameCandidates(): string[] {
|
||||
getMyUsernameCandidates(): string[] {
|
||||
const rawAccountName = path.basename(this.accountRoot)
|
||||
return this.uniq([this.wxid, rawAccountName, Wcdb4Client.cleanAccountDirName(rawAccountName)])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user