import { getControledMihomoConfig, getProfileConfig, getProfile, getProfileItem, getOverride, getOverrideItem } from '../config' import { mihomoWorkConfigPath, overridePath } from '../utils/dirs' import yaml from 'yaml' import { readFile, writeFile } from 'fs/promises' import { deepMerge } from '../utils/merge' import vm from 'vm' import { writeFileSync } from 'fs' export async function generateProfile(): Promise { const { current } = await getProfileConfig() const currentProfile = await overrideProfile(current, await getProfile(current)) const controledMihomoConfig = await getControledMihomoConfig() const profile = deepMerge(currentProfile, controledMihomoConfig) await writeFile(mihomoWorkConfigPath(), yaml.stringify(profile)) } async function overrideProfile( current: string | undefined, profile: IMihomoConfig ): Promise { const { override = [] } = (await getProfileItem(current)) || {} for (const ov of override) { const item = await getOverrideItem(ov) const content = await getOverride(ov, item?.ext || 'js') switch (item?.ext) { case 'js': profile = runOverrideScript(profile, content, item) break case 'yaml': { const patch = yaml.parse(content) if (patch.rules) { patch.rules = [...patch.rules, ...(profile.rules || [])] } if (patch.proxies) { patch.proxies = [...patch.proxies, ...(profile.proxies || [])] } profile = deepMerge(profile, patch) break } } } return profile } function runOverrideScript( profile: IMihomoConfig, script: string, item: IOverrideItem ): IMihomoConfig { const log = (type: string, data: string, flag = 'a'): void => { writeFileSync(overridePath(item.id, 'log'), `[${type}] ${data}\n`, { encoding: 'utf-8', flag }) } try { const ctx = { console: Object.freeze({ log(data: never) { log('log', JSON.stringify(data)) }, info(data: never) { log('info', JSON.stringify(data)) }, error(data: never) { log('error', JSON.stringify(data)) }, debug(data: never) { log('debug', JSON.stringify(data)) } }) } vm.createContext(ctx) const code = `${script} main(${JSON.stringify(profile)})` log('info', '开始执行脚本', 'w') const newProfile = vm.runInContext(code, ctx) if (typeof newProfile !== 'object') { throw new Error('脚本返回值必须是对象') } log('info', '脚本执行成功') return newProfile } catch (e) { log('exception', `脚本执行失败: ${e}`) return profile } } export async function getRuntimeConfigStr(): Promise { return await readFile(mihomoWorkConfigPath(), 'utf8') } export async function getRuntimeConfig(): Promise { return yaml.parse(await getRuntimeConfigStr()) }