import { exec, execFile, execSync, spawn } from 'child_process' import { app, dialog, nativeTheme, shell } from 'electron' import { readFile } from 'fs/promises' import path from 'path' import { promisify } from 'util' import { dataDir, exePath, mihomoCorePath, overridePath, profilePath, resourcesDir, resourcesFilesDir, taskDir } from '../utils/dirs' import { copyFileSync, writeFileSync } from 'fs' export function getFilePath(ext: string[]): string[] | undefined { return dialog.showOpenDialogSync({ title: '选择订阅文件', filters: [{ name: `${ext} file`, extensions: ext }], properties: ['openFile'] }) } export async function readTextFile(filePath: string): Promise { return await readFile(filePath, 'utf8') } export function openFile(type: 'profile' | 'override', id: string, ext?: 'yaml' | 'js'): void { if (type === 'profile') { shell.openPath(profilePath(id)) } if (type === 'override') { shell.openPath(overridePath(id, ext || 'js')) } } export async function openUWPTool(): Promise { const execFilePromise = promisify(execFile) const uwpToolPath = path.join(resourcesDir(), 'files', 'enableLoopback.exe') await execFilePromise(uwpToolPath) } export async function setupFirewall(): Promise { const execPromise = promisify(exec) const removeCommand = ` Remove-NetFirewallRule -DisplayName "mihomo" -ErrorAction SilentlyContinue Remove-NetFirewallRule -DisplayName "mihomo-alpha" -ErrorAction SilentlyContinue Remove-NetFirewallRule -DisplayName "Mihomo Party" -ErrorAction SilentlyContinue ` const createCommand = ` New-NetFirewallRule -DisplayName "mihomo" -Direction Inbound -Action Allow -Program "${mihomoCorePath('mihomo')}" -Enabled True -Profile Any -ErrorAction SilentlyContinue New-NetFirewallRule -DisplayName "mihomo-alpha" -Direction Inbound -Action Allow -Program "${mihomoCorePath('mihomo-alpha')}" -Enabled True -Profile Any -ErrorAction SilentlyContinue New-NetFirewallRule -DisplayName "Mihomo Party" -Direction Inbound -Action Allow -Program "${exePath()}" -Enabled True -Profile Any -ErrorAction SilentlyContinue ` if (process.platform === 'win32') { await execPromise(removeCommand, { shell: 'powershell' }) await execPromise(createCommand, { shell: 'powershell' }) } } export function setNativeTheme(theme: 'system' | 'light' | 'dark'): void { nativeTheme.themeSource = theme } const elevateTaskXml = ` InteractiveToken HighestAvailable Parallel false false false false false false false true true false false false PT0S 3 "${path.join(taskDir(), `mihomo-party-run.exe`)}" "${exePath()}" ` export function createElevateTask(): void { const taskFilePath = path.join(taskDir(), `mihomo-party-run.xml`) writeFileSync(taskFilePath, Buffer.from(`\ufeff${elevateTaskXml}`, 'utf-16le')) copyFileSync( path.join(resourcesFilesDir(), 'mihomo-party-run.exe'), path.join(taskDir(), 'mihomo-party-run.exe') ) execSync( `%SystemRoot%\\System32\\schtasks.exe /create /tn "mihomo-party-run" /xml "${taskFilePath}" /f` ) } export function resetAppConfig(): void { if (process.platform === 'win32') { spawn( 'cmd', [ '/C', `"timeout /t 2 /nobreak >nul && rmdir /s /q "${dataDir()}" && start "" "${exePath()}""` ], { shell: true, detached: true } ).unref() } else { const script = `while kill -0 ${process.pid} 2>/dev/null; do sleep 0.1 done rm -rf '${dataDir()}' ${process.argv.join(' ')} & disown exit ` spawn('sh', ['-c', `"${script}"`], { shell: true, detached: true, stdio: 'ignore' }) } app.quit() }