support direct connection under specific WiFi SSID

This commit is contained in:
pompurin404 2024-09-22 16:05:35 +08:00
parent 43f2b2e701
commit 80f22e719a
No known key found for this signature in database
6 changed files with 138 additions and 4 deletions

View File

@ -1,3 +1,7 @@
### Features
- 支持在特定WiFi SSID下直连
### Bug Fixes
- 修复某些mac无法开启开机启动的问题

View File

@ -262,7 +262,7 @@ export function isEncryptionAvailable(): boolean {
return safeStorage.isEncryptionAvailable()
}
async function getDefaultService(password?: string): Promise<string> {
export async function getDefaultService(password?: string): Promise<string> {
const execPromise = promisify(exec)
let sudo = ''
if (password) sudo = `echo "${password}" | sudo -S `

79
src/main/sys/ssid.ts Normal file
View File

@ -0,0 +1,79 @@
import { exec } from 'child_process'
import { promisify } from 'util'
import { getAppConfig, patchControledMihomoConfig } from '../config'
import { patchMihomoConfig } from '../core/mihomoApi'
import { mainWindow } from '..'
import { ipcMain } from 'electron'
import { getDefaultService } from '../core/manager'
export async function getCurrentSSID(): Promise<string | undefined> {
const execPromise = promisify(exec)
try {
if (process.platform === 'win32') {
const { stdout } = await execPromise('netsh wlan show interfaces')
for (const line of stdout.split('\n')) {
if (line.trim().startsWith('SSID')) {
return line.split(': ')[1].trim()
}
}
}
if (process.platform === 'linux') {
const { stdout } = await execPromise(
`iwconfig 2>/dev/null | grep 'ESSID' | awk -F'"' '{print $2}'`
)
if (stdout.trim() !== '') {
return stdout.trim()
}
}
if (process.platform === 'darwin') {
try {
const { stdout } = await execPromise(
'/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I'
)
for (const line of stdout.split('\n')) {
if (line.trim().startsWith('SSID')) {
return line.split(': ')[1].trim()
}
}
} catch {
const service = await getDefaultService()
const { stdout } = await execPromise(`networksetup -getairportnetwork ${service}`)
if (stdout.split(': ').length > 1) {
return stdout.split(': ')[1].trim()
}
}
}
} catch {
// ignore
}
return undefined
}
let lastSSID: string | undefined
export async function checkSSID(): Promise<void> {
try {
const { pauseSSID = [] } = await getAppConfig()
if (pauseSSID.length === 0) return
const currentSSID = await getCurrentSSID()
if (currentSSID === lastSSID) return
lastSSID = currentSSID
if (currentSSID && pauseSSID.includes(currentSSID)) {
await patchControledMihomoConfig({ mode: 'direct' })
await patchMihomoConfig({ mode: 'direct' })
mainWindow?.webContents.send('controledMihomoConfigUpdated')
ipcMain.emit('updateTrayMenu')
} else {
await patchControledMihomoConfig({ mode: 'rule' })
await patchMihomoConfig({ mode: 'rule' })
mainWindow?.webContents.send('controledMihomoConfigUpdated')
ipcMain.emit('updateTrayMenu')
}
} catch {
// ignore
}
}
export async function startSSIDCheck(): Promise<void> {
await checkSSID()
setInterval(checkSSID, 30000)
}

View File

@ -34,6 +34,7 @@ import {
patchControledMihomoConfig
} from '../config'
import { app } from 'electron'
import { startSSIDCheck } from '../sys/ssid'
async function initDirs(): Promise<void> {
if (!existsSync(dataDir())) {
@ -212,6 +213,7 @@ export async function init(): Promise<void> {
await startSubStoreServer()
const { sysProxy } = await getAppConfig()
await triggerSysProxy(sysProxy.enable)
await startSSIDCheck()
initDeeplink()
}

View File

@ -1,10 +1,11 @@
import React, { useState } from 'react'
import SettingCard from '../base/base-setting-card'
import SettingItem from '../base/base-setting-item'
import { Input, Select, SelectItem, Switch } from '@nextui-org/react'
import { Button, Input, Select, SelectItem, Switch } from '@nextui-org/react'
import { useAppConfig } from '@renderer/hooks/use-app-config'
import debounce from '@renderer/utils/debounce'
import { patchControledMihomoConfig, restartCore } from '@renderer/utils/ipc'
import { MdDeleteForever } from 'react-icons/md'
const MihomoConfig: React.FC = () => {
const { appConfig, patchAppConfig } = useAppConfig()
@ -13,11 +14,13 @@ const MihomoConfig: React.FC = () => {
controlSniff = true,
delayTestTimeout,
autoCloseConnection = true,
pauseSSID = [],
delayTestUrl,
userAgent,
proxyCols = 'auto'
} = appConfig || {}
const [url, setUrl] = useState(delayTestUrl)
const [pauseSSIDInput, setPauseSSIDInput] = useState(pauseSSID)
const setUrlDebounce = debounce((v: string) => {
patchAppConfig({ delayTestUrl: v })
}, 500)
@ -109,7 +112,7 @@ const MihomoConfig: React.FC = () => {
}}
/>
</SettingItem>
<SettingItem title="自动断开连接">
<SettingItem title="自动断开连接" divider>
<Switch
size="sm"
isSelected={autoCloseConnection}
@ -118,6 +121,51 @@ const MihomoConfig: React.FC = () => {
}}
/>
</SettingItem>
<SettingItem title="在特定的 WiFi SSID 下直连">
{pauseSSIDInput.join('') !== pauseSSID.join('') && (
<Button
size="sm"
color="primary"
onPress={() => {
patchAppConfig({ pauseSSID: pauseSSIDInput })
}}
>
</Button>
)}
</SettingItem>
<div className="flex flex-col items-stretch mt-2">
{[...pauseSSIDInput, ''].map((ssid, index) => {
return (
<div key={index} className="flex mb-2">
<Input
size="sm"
fullWidth
placeholder="SSID"
value={ssid || ''}
onValueChange={(v) => {
if (index === pauseSSIDInput.length) {
setPauseSSIDInput([...pauseSSIDInput, v])
} else {
setPauseSSIDInput(pauseSSIDInput.map((a, i) => (i === index ? v : a)))
}
}}
/>
{index < pauseSSIDInput.length && (
<Button
className="ml-2"
size="sm"
variant="flat"
color="warning"
onClick={() => setPauseSSIDInput(pauseSSIDInput.filter((_, i) => i !== index))}
>
<MdDeleteForever className="text-lg" />
</Button>
)}
</div>
)
})}
</div>
</SettingCard>
)
}

View File

@ -213,6 +213,7 @@ interface IAppConfig {
connectionCardStatus?: CardStatus
dnsCardStatus?: CardStatus
logCardStatus?: CardStatus
pauseSSID?: string[]
mihomoCoreCardStatus?: CardStatus
overrideCardStatus?: CardStatus
profileCardStatus?: CardStatus
@ -237,7 +238,7 @@ interface IAppConfig {
proxyInTray: boolean
siderOrder: string[]
appTheme: AppTheme
customTheme: string
customTheme?: string
autoCheckUpdate: boolean
silentStart: boolean
autoCloseConnection: boolean