Merge pull request #349 from hicccc77/dev

Dev
This commit is contained in:
xuncha
2026-03-02 16:45:32 +08:00
committed by Forrest
10 changed files with 55 additions and 16 deletions

View File

@@ -1533,10 +1533,10 @@ function registerIpcHandlers() {
}) })
}) })
ipcMain.handle('key:autoGetImageKey', async (event, manualDir?: string) => { ipcMain.handle('key:autoGetImageKey', async (event, manualDir?: string, wxid?: string) => {
return keyService.autoGetImageKey(manualDir, (message) => { return keyService.autoGetImageKey(manualDir, (message) => {
event.sender.send('key:imageKeyStatus', { message }) event.sender.send('key:imageKeyStatus', { message })
}) }, wxid)
}) })
// HTTP API 服务 // HTTP API 服务

View File

@@ -113,7 +113,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
// 密钥获取 // 密钥获取
key: { key: {
autoGetDbKey: () => ipcRenderer.invoke('key:autoGetDbKey'), autoGetDbKey: () => ipcRenderer.invoke('key:autoGetDbKey'),
autoGetImageKey: (manualDir?: string) => ipcRenderer.invoke('key:autoGetImageKey', manualDir), autoGetImageKey: (manualDir?: string, wxid?: string) => ipcRenderer.invoke('key:autoGetImageKey', manualDir, wxid),
onDbKeyStatus: (callback: (payload: { message: string; level: number }) => void) => { onDbKeyStatus: (callback: (payload: { message: string; level: number }) => void) => {
ipcRenderer.on('key:dbKeyStatus', (_, payload) => callback(payload)) ipcRenderer.on('key:dbKeyStatus', (_, payload) => callback(payload))
return () => ipcRenderer.removeAllListeners('key:dbKeyStatus') return () => ipcRenderer.removeAllListeners('key:dbKeyStatus')

View File

@@ -15,8 +15,16 @@ function getStaticFfmpegPath(): string | null {
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const ffmpegStatic = require('ffmpeg-static') const ffmpegStatic = require('ffmpeg-static')
if (typeof ffmpegStatic === 'string' && existsSync(ffmpegStatic)) { if (typeof ffmpegStatic === 'string') {
return ffmpegStatic // 修复:如果路径包含 app.asar打包后自动替换为 app.asar.unpacked
let fixedPath = ffmpegStatic
if (fixedPath.includes('app.asar') && !fixedPath.includes('app.asar.unpacked')) {
fixedPath = fixedPath.replace('app.asar', 'app.asar.unpacked')
}
if (existsSync(fixedPath)) {
return fixedPath
}
} }
// 方法2: 手动构建路径(开发环境) // 方法2: 手动构建路径(开发环境)

View File

@@ -651,7 +651,8 @@ export class KeyService {
async autoGetImageKey( async autoGetImageKey(
manualDir?: string, manualDir?: string,
onProgress?: (message: string) => void onProgress?: (message: string) => void,
wxidParam?: string
): Promise<ImageKeyResult> { ): Promise<ImageKeyResult> {
if (!this.ensureWin32()) return { success: false, error: '仅支持 Windows' } if (!this.ensureWin32()) return { success: false, error: '仅支持 Windows' }
if (!this.ensureLoaded()) return { success: false, error: 'wx_key.dll 未加载' } if (!this.ensureLoaded()) return { success: false, error: 'wx_key.dll 未加载' }
@@ -683,20 +684,29 @@ export class KeyService {
const codes: number[] = accounts[0].keys.map((k: any) => k.code) const codes: number[] = accounts[0].keys.map((k: any) => k.code)
console.log('[ImageKey] codes:', codes, 'DLL wxids:', accounts.map((a: any) => a.wxid)) console.log('[ImageKey] codes:', codes, 'DLL wxids:', accounts.map((a: any) => a.wxid))
// 从 manualDir 提取前端已配置好的正确 wxid // 优先级: 1. 直接传入的wxidParam 2. 从manualDir提取 3. DLL返回的wxid可能是unknown
// 格式: "D:\weixin\xwechat_files\wxid_xxx_1234" → "wxid_xxx_1234"
let targetWxid = '' let targetWxid = ''
if (manualDir) {
// 方案1: 直接使用传入的wxidParam最优先
if (wxidParam && wxidParam.startsWith('wxid_')) {
targetWxid = wxidParam
console.log('[ImageKey] 使用直接传入的 wxid:', targetWxid)
}
// 方案2: 从 manualDir 提取前端已配置好的正确 wxid
// 格式: "D:\weixin\xwechat_files\wxid_xxx_1234" → "wxid_xxx_1234"
if (!targetWxid && manualDir) {
const dirName = manualDir.replace(/[\\/]+$/, '').split(/[\\/]/).pop() ?? '' const dirName = manualDir.replace(/[\\/]+$/, '').split(/[\\/]/).pop() ?? ''
if (dirName.startsWith('wxid_')) { if (dirName.startsWith('wxid_')) {
targetWxid = dirName targetWxid = dirName
console.log('[ImageKey] 从 manualDir 提取 wxid:', targetWxid)
} }
} }
// 方案3: 回退到 DLL 发现的第一个(可能是 unknown
if (!targetWxid) { if (!targetWxid) {
// 无法从 manualDir 提取 wxid回退到 DLL 发现的第一个
targetWxid = accounts[0].wxid targetWxid = accounts[0].wxid
console.log('[ImageKey] 无法从 manualDir 提取 wxid使用 DLL 发现的:', targetWxid) console.log('[ImageKey] 无法取 wxid使用 DLL 发现的:', targetWxid)
} }
// CleanWxid: 截断到第二个下划线,与 xkey 算法一致 // CleanWxid: 截断到第二个下划线,与 xkey 算法一致

View File

@@ -16,7 +16,8 @@
"build": "tsc && vite build && electron-builder", "build": "tsc && vite build && electron-builder",
"preview": "vite preview", "preview": "vite preview",
"electron:dev": "vite --mode electron", "electron:dev": "vite --mode electron",
"electron:build": "npm run build" "electron:build": "npm run build",
"preinstall": "node preinstall.js"
}, },
"dependencies": { "dependencies": {
"better-sqlite3": "^12.5.0", "better-sqlite3": "^12.5.0",
@@ -140,4 +141,4 @@
} }
] ]
} }
} }

20
preinstall.js Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -779,7 +779,7 @@ function SettingsPage() {
try { try {
const accountPath = wxid ? `${dbPath}/${wxid}` : dbPath; const accountPath = wxid ? `${dbPath}/${wxid}` : dbPath;
const result = await window.electronAPI.key.autoGetImageKey(accountPath) const result = await window.electronAPI.key.autoGetImageKey(accountPath, wxid)
if (result.success && result.aesKey) { if (result.success && result.aesKey) {
if (typeof result.xorKey === 'number') { if (typeof result.xorKey === 'number') {
setImageXorKey(`0x${result.xorKey.toString(16).toUpperCase().padStart(2, '0')}`) setImageXorKey(`0x${result.xorKey.toString(16).toUpperCase().padStart(2, '0')}`)

View File

@@ -320,7 +320,7 @@ function WelcomePage({ standalone = false }: WelcomePageProps) {
try { try {
// 拼接完整的账号目录,确保 KeyService 能准确找到模板文件 // 拼接完整的账号目录,确保 KeyService 能准确找到模板文件
const accountPath = wxid ? `${dbPath}/${wxid}` : dbPath const accountPath = wxid ? `${dbPath}/${wxid}` : dbPath
const result = await window.electronAPI.key.autoGetImageKey(accountPath) const result = await window.electronAPI.key.autoGetImageKey(accountPath, wxid)
if (result.success && result.aesKey) { if (result.success && result.aesKey) {
if (typeof result.xorKey === 'number') { if (typeof result.xorKey === 'number') {
setImageXorKey(`0x${result.xorKey.toString(16).toUpperCase().padStart(2, '0')}`) setImageXorKey(`0x${result.xorKey.toString(16).toUpperCase().padStart(2, '0')}`)

View File

@@ -66,7 +66,7 @@ export interface ElectronAPI {
} }
key: { key: {
autoGetDbKey: () => Promise<{ success: boolean; key?: string; error?: string; logs?: string[] }> autoGetDbKey: () => Promise<{ success: boolean; key?: string; error?: string; logs?: string[] }>
autoGetImageKey: (manualDir?: string) => Promise<{ success: boolean; xorKey?: number; aesKey?: string; error?: string }> autoGetImageKey: (manualDir?: string, wxid?: string) => Promise<{ success: boolean; xorKey?: number; aesKey?: string; error?: string }>
onDbKeyStatus: (callback: (payload: { message: string; level: number }) => void) => () => void onDbKeyStatus: (callback: (payload: { message: string; level: number }) => void) => () => void
onImageKeyStatus: (callback: (payload: { message: string }) => void) => () => void onImageKeyStatus: (callback: (payload: { message: string }) => void) => () => void
} }