You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

415 lines
13 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
Surge conf builder
作者https://t.me/miniers
修改lhie1
*/
const _ = require('lodash');
$app.autoKeyboardEnabled = true;
$app.keyboardToolbarEnabled = true;
let baseUrl = "https://raw.githubusercontent.com/lhie1/Rules/master/",
SurgeList = [
'Apple',
'DIRECT',
'HOST',
'Hostname',
'PROXY',
'REJECT',
'URL Rewrite',
'URL REJECT',
'Header Rewrite',
{
name: 'TestFlight',
disabled: true,
path: 'Surge/TestFlight'
}
];
let remoteIsDone = 0;
let remoteRule = {};
const fields = [{
name: 'filename',
text: '生成的配置文件名(必填)'
},
{
name: 'General',
text: '(必填)'
},
{
name: 'Proxy',
text: '(必填)'
},
{
name: 'AutoGroup',
text: '自定义Auto组自动切换的节点只需填入节点名称且务必与Proxy中保持一致留空默认全选用英文逗号分隔若出错则说明输入有误'
},
{
name: 'Custom',
},
{
name: "ipRule"
},
{
name: 'Delete',
text: '删除所有包含该字符的规则,按行分隔'
},
{
name: 'Rewrite',
},
'Host',
'SSID',
{
name: 'extProxy',
text: '扩展代理,不会增加到自动生成的代理组'
},
{
name: 'hostname',
text: 'MITM中的hostname'
},
{
name: 'extProxyGroup',
text: '自定义代理组'
},
'MITM',
];
let config;
if ($drive.exists("Surge.json")) {
try {
let file = $drive.read("Surge.json");
config = JSON.parse($drive.read("Surge.json").string)
} catch (err) {
initConfig()
}
} else {
initConfig()
}
//初始化配置
function initConfig() {
config = {
filename: 'Surge.conf',
remoteSwitch: {},
'General': `// Auto
loglevel = notify
dns-server = system,1.2.4.8,80.80.80.80,80.80.81.81,1.1.1.1,1.0.0.1
skip-proxy = 127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,100.64.0.0/10,17.0.0.0/8,localhost,*.local,*.crashlytics.com,::ffff:0.0.0.0/1,::ffff:128.0.0.0/1
// iOS
external-controller-access = lhie1@0.0.0.0:6170
allow-wifi-access = true
// macOS
interface = 0.0.0.0
socks-interface = 0.0.0.0
port = 8888
socks-port = 8889
enhanced-mode-by-rule = false
// Auto
exclude-simple-hostnames = true
ipv6 = true
replica = false
[Replica]
hide-apple-request = true
hide-crashlytics-request = true
use-keyword-filter = false`,
'Proxy': `🇺🇸 US = custom,us.com,1234,rc4-md5,password,http://omgib13x8.bkt.clouddn.com/SSEncrypt.module
🇭🇰 HK = custom,hk.com,1234,rc4-md5,password,http://omgib13x8.bkt.clouddn.com/SSEncrypt.module
🇯🇵 JP = custom,jp.com,1234,rc4-md5,password,http://omgib13x8.bkt.clouddn.com/SSEncrypt.module
🇸🇬 SG = custom,sg.com,1234,rc4-md5,password,http://omgib13x8.bkt.clouddn.com/SSEncrypt.module
`,
'AutoGroup': `🇺🇸 US,🇭🇰 HK,🇯🇵 JP,🇸🇬 SG`,
'Custom': "",
'Rewrite': "",
'Delete': "",
'Host': "",
'SSID': "",
'extProxy': "",
'hostname': "",
'extProxyGroup': "",
'MITM': `skip-server-cert-verify = true
tcp-connection = false
enable = true
ca-passphrase = 4B676386
ca-p12 = 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`,
}
}
function saveConfig() {
var success = $drive.write({
data: $data({
string: JSON.stringify(config)
}),
path: "Surge.json"
})
}
function buildFile() {
var ProxyList = getProxyName(config.Proxy).join(',');
var AutoList = (`${config.AutoGroup}` != "") ? `${config.AutoGroup}`:`${ProxyList}`;
var cushostname = config.hostname.split('\n').join(',')
var result = `
[General]
${config.General}
[Proxy]
🚀 Direct = direct
# my Proxy
${config.Proxy}
# my extProxy
${config.extProxy}
[Proxy Group]
🍃 Proxy = select, 🏃 Auto, 🚀 Direct,${ProxyList}
🍂 Domestic = select, 🚀 Direct, 🍃 Proxy
🍎 Only = select, 🚀 Direct,${ProxyList}
☁️ Others = select,🚀 Direct,🍃 Proxy
🏃 Auto = url-test,${AutoList},url = http://www.gstatic.com/generate_204, interval = 1200
# my extProxyGroup
${config.extProxyGroup}
[Rule]
# my custom
${config.Custom}
# remote Apple
${remoteRule.Apple}
# remote REJECT
${remoteRule.REJECT}
# remote PROXY
${remoteRule.PROXY}
# remote DIRECT
${remoteRule.DIRECT}
# my ipRule
${config.ipRule}
// Detect local network
GEOIP,CN,🍂 Domestic
// Use Proxy for all others
FINAL,☁️ Others,dns-failed
[Host]
# my host
${config.Host}
# remote HOST
${remoteRule['HOST']}
[URL Rewrite]
# my Rewrite
${config.Rewrite}
# remote URL Rewrite
${remoteRule['URL Rewrite']}
# remote URL REJECT
${remoteRule['URL REJECT']}
[Header Rewrite]
# remote Header Rewrite
${remoteRule['Header Rewrite']}
${remoteRule['TestFlight']}
[SSID Setting]
# my SSID
${config.SSID}
# MITM
${config.MITM?'[MITM]':''}
${config.MITM?`hostname = ${cushostname?cushostname+",":""}${remoteRule.Hostname.split('\n').join(',')}`:''}
${config.MITM}`;
$share.sheet([config.filename || "Surge.conf", $data({
string: result
})]);
$ui.loading(false)
}
function getProxyName(proxys) {
return proxys.match(/.+(?=\s=\s(custom|http|https|socks5|socks5-tls))/g)
}
function build() {
_.forEach(SurgeList, function(name) {
let path, regex;
let disabled;
if (_.isObject(name)) {
regex = name.regex;
path = name.path;
name = name.name;
} else {
path = `Auto/${name}`;
}
if (config.remoteSwitch[name] === false) {
remoteIsDone++;
remoteRule[name] = '';
return;
}
$console.info(name);
$console.info(encodeURI([baseUrl, path, '.conf'].join('')));
$http.get({
url: encodeURI([baseUrl, path, '.conf'].join('')),
handler: function({ data }) {
if (!data) {
$console.warn(`${name}拉取数据失败`)
}
remoteRule[name] = removeDeleteRule(data, name);
if (regex && remoteRule[name]) {
remoteRule[name] = _.get(remoteRule[name].match(new RegExp(regex, 's')), 1, remoteRule[name])
}
remoteIsDone++;
if (remoteIsDone >= SurgeList.length) {
saveConfig();
buildFile();
}
}
})
})
}
const removeDeleteRule = function(rule, name) {
const deleteds = config.Delete.split('\n');
const orgRules = rule.split('\n');
let result = [],
deleted = [];
orgRules.forEach(function(line) {
let isDeleted = false;
deleteds.forEach(function(del) {
if (del && line.match(del)) {
isDeleted = true;
}
});
!isDeleted && result.push(line);
});
// $ui.alert(deleted.join('\n'))
return result.join('\n')
};
const changeRemoteSwitch = function(key) {
let switchBtn = $(`switch-${key}`);
config.remoteSwitch[key] = !switchBtn.on;
switchBtn.on = config.remoteSwitch[key];
console.log(`${key} = ${switchBtn.on}`)
}
$ui.render({
layout: $layout.fill,
views: [{
type: "button",
props: {
title: "生成配置"
},
layout: function(make) {
make.left.right.top.inset(10)
make.height.equalTo(32)
},
events: {
tapped: function(sender) {
$ui.loading(true)
fields.forEach(function(cus) {
let cfg = cus;
if (typeof cus === 'string') {
cfg = {
name: cus
}
}
config[cfg.name] = $(cfg.name).text
})
build()
// //selectItem()
}
}
}, {
type: "list",
layout: function(make) {
make.left.bottom.right.equalTo(0)
make.top.equalTo($("button").bottom).offset(0)
},
events: {
rowHeight: function(sender, indexPath) {
return fields.length === indexPath.section ? 50 : 150;
}
},
props: {
data: fields.map(function(cus) {
let cfg = cus;
if (typeof cus === 'string') {
cfg = {
name: cus
}
}
return {
title: `${cfg.name} ${cfg.text?`(${cfg.text})`:''}`,
rows: [{
type: "text",
props: {
id: cfg.name,
text: config[cfg.name] || '',
},
layout: function(make) {
make.edges.inset(5);
}
}]
}
}).concat([{
title: `远程规则开关`,
rows: SurgeList.map(function(key) {
if (_.isObject(key)) {
const disabled = key.disabled;
key = key.name;
if (disabled && _.isUndefined(config.remoteSwitch[key])) {
config.remoteSwitch[key] = false;
}
}
return {
type: 'view',
props: {},
layout: $layout.fill,
events: {
tapped: function(sender) {
changeRemoteSwitch(key);
}
},
views: [{
type: `switch`,
props: {
id: `switch-${key}`,
on: config.remoteSwitch[key] !== false
},
layout: function(make, view) {
make.left.top.equalTo(10)
},
events: {
changed: function(sender) {
changeRemoteSwitch(key);
}
}
},
{
type: 'label',
props: {
text: key,
align: $align.center
},
layout: function(make) {
make.top.equalTo(15);
make.left.equalTo(90)
}
}
]
}
})
}])
}
}]
})