del: JSBox

pull/334/head
lhie1 5 years ago
parent 410e0a49fc
commit 5a2d84c1c2

Binary file not shown.

@ -1,30 +0,0 @@
## lhie1规则生成
### 简介
经过一周的迭代更新目前脚本已经可以完全替代Workflow甚至能做到Workflow不能做到的如
1. 由于没有运行动画,生成速度快
2. 能自由对节点进行排序和筛选等操作
3. 提供小组件支持,可以快速判断规则是否有更新并快速生成一次配置
4. 提供Safari拓展支持可以像Surge3一样在浏览网页时添加对应的规则
5. 能识别批量ss://或surge链接
6. 支持URL Scheme一键生成配置文件
更多介绍请参考:
[Rules-lhie1脚本简介](https://github.com/Fndroid/jsbox_script/blob/master/Rules-lhie1/docs.md)
[Rules-lhie1脚本更新日志](https://github.com/Fndroid/jsbox_script/blob/master/Rules-lhie1/updateLog.md)
[打赏名单](https://github.com/Fndroid/jsbox_script/blob/master/Rules-lhie1/donate.md)
### 界面截图
![](https://github.com/Fndroid/jsbox_script/blob/master/imgs/Rules-lhie1_pre6.png?raw=true)
### 下载脚本
[下载Rules-lhie1](https://xteko.com/redir?name=Rules-lhie1&url=https://raw.githubusercontent.com/Fndroid/jsbox_script/master/Rules-lhie1/.output/Rules-lhie1.box)
### 感谢支持
![](https://raw.githubusercontent.com/Fndroid/jsbox_script/master/imgs/thankyou.jpg)

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

@ -1,22 +0,0 @@
{
"info": {
"name": "Rules-lhie1",
"url": "https://raw.githubusercontent.com/Fndroid/jsbox_script/master/Rules-lhie1/.output/Rules-lhie1.box",
"version": "1.8.1",
"author": "Fndroid",
"website": "https://github.com/Fndroid/jsbox_script/tree/master/Rules-lhie1",
"types": 15
},
"settings": {
"minSDKVer": "1.0.0",
"minOSVer": "10.0.0",
"idleTimerDisabled": false,
"autoKeyboardEnabled": false,
"keyboardToolbarEnabled": false,
"rotateDisabled": false
},
"widget": {
"tintColor": "",
"iconColor": ""
}
}

File diff suppressed because one or more lines are too long

@ -1,30 +0,0 @@
[General]
// Auto
loglevel = notify
dns-server = system,1.2.4.8,223.5.5.5,223.6.6.6
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
// 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
show-error-page-for-reject = true
// Auto
exclude-simple-hostnames = true
ipv6 = true
replica = false
[Replica]
hide-apple-request = true
hide-crashlytics-request = true
hide-udp = false
use-keyword-filter = false

@ -1,3 +0,0 @@
enable = true
ca-passphrase = 4B676386
ca-p12 = MIIJtAIBAzCCCX4GCSqGSIb3DQEHAaCCCW8EgglrMIIJZzCCA9cGCSqGSIb3DQEHBqCCA8gwggPEAgEAMIIDvQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI6Y6Nt7P0s1QCAggAgIIDkE4px9tUmX4zyAE2qK9f761b7vkat/g7X4gjWSPRtrdovsbnP05XaNdYF8sRn+GktrbqJ6m4LwPe1GUCDht8vuno76ZPAKdT5LVxAeKKJIz8+kqvdKh5COwMSHUD8SqJpncfiH90xu/HmzPbIPCKIE89ZWTRDECmJc9bwH97kefu+U/FB6suMVyEKD7oKhYcjY7110DLNe0okD+MMOLZkMv2DcPb/B9RqKCNAT86bFyF2jtsvyQ15WxkILb03R8Pal1LqkDD9P+r0tTjSRNLKKzWXK0blQeL3teZcusClXPUWo3wZZwNe+8kfUoe23vm62TjSIdYF0gi7G2wpoIIlSlijiPffFFfvG6FS2Y976uLPZb1MonWRdjBYYwry180YQJOyWZQQOR+lWj01lp9o5GaYLKNRNGfrGdsbHx/xKcYEX7Fo/SycUQhzvDh0YbgYz09VNVsbKQDRj0lnxYLnJTLVX0DMmNlWWT6qMwXJ7HLYVT8sgA51h/meUfHmpzI1Qv9k8T/KZQtcVpHSWZ2LXdmwwLJ1A4VgQWxPS7a2GisrYs8DJbDLqaCpRrCyTqpOUclvZ/ONFqiqvJNbuzg33clgutbQNIxoyqJ5A9VDvbKcwgEq91KdSfsQ1shpS/lxGNCsfF+kFcgD95YS2ZfQ5QoFMszoSMCIkz/juc0aLbrGehpmrtd+LGOjomE/y7m8zJ2AxBLQpKSICRu6Dcz0nC2Jgf25/NJlUuX4kIZJyz0MxBBNreUzcevpFIIgsUpwlYAKZKP01/clVV+mVyax49RRVZttMKTaLymSeKO0lGqi9xzbnd0TCtmzN6wp4UpwtISxqLju3fTcgiWnCYRiEY7JZcaAO02J8C8dRsGU1lOBJOJ6hksPwbJ6B52maLmF3cu7WBG5RAmx/MtvJrvzNZYAyord6jjThcfQp8bMv1evmo8BDDpQ6FQb6TR8W9GvLSH21iLbuRFygDnzkKQ+s+LyiO3G0LNseNLxILEcxBgSx0hzoh7/k/MVaO+p0w5csf+VIlOLcew+7Oen5KJFRXhcUDKR3Km7cdcOPv8M8lqvHeScfga6X+W83B+u1+fYhkE8rwlFPj/bUk11A+fbThnM6K069DMh8388S9Tz8otf7zKzad24mUHWSx37GJx1jg0T3dVHegx2JJ3iBLQlGXxF+JiXY/DEeT0VxXJZXt2QbPY9LQ3McIKngeHKpYh4zCCBYgGCSqGSIb3DQEHAaCCBXkEggV1MIIFcTCCBW0GCyqGSIb3DQEMCgECoIIE7jCCBOowHAYKKoZIhvcNAQwBAzAOBAgB2aT5GqvE2AICCAAEggTIPV10t7HrCN6H+dB2i0z4MuGWtSblZVax8UGxygolskl9x7ATpi5+Wo7CpW1EQljzCUmIWygajuqSwvywT5clhuXplenLIXMJcknaf/IjoqF847TY0qSvnOJm+ywoLZ8MhOjTjSNk2N6c+szhr46eSu+1AnMD/fGdh+Z/Xp0i3BdqD4DO+9r8fmjoJlQ1I6ieI6M0baQc/yVPYXJfwZPGYfB3BukXdq1TXtByuCowA8lezoiHQ8EtAongtN2mekiUtHtwK4qHqLqEdr15arht8cFQPXxsX/OE09D/UjzOu04goqEctIBofbUhRMfBprarMfa6qZcFf5saUT7OWk7uC+LcRXd0ZF9XqCTe87u7030jbIh3zuRAX64xbvsowrs8cxM1OaOeqW0Fr0XydsmoPg6r+XlXXyItZvSJllumJHoB2QcpQO03hrRcBuhm7PT5pBG36S4DxwDRBPxID+kWLzbDKJUvsxyX6FyRfn2pySX03w2GcPxlqCexkVHRYInL+MPEUJ8zPOfBx7GvpZStkadnoIo2Zf0t6miHYoxuw81IL6jBQpMaWwL1TMWnioisvoE3zn3oHCTLXWoLwUEKXNX0tQ7IokuXdG7abeD3iBRwXyX6q5jbeQ4PfLll9utT17YSswz2WDoX8fwyiiv+DEZgfWUA5Fc56eVX1w5JIUN/SnqfB1DLcXI4KzSlhZBk+P2MYHCZFTYiFEBYS+A5TPIdt4nhK8+LSA7PA9YyTNSBn09nRjMNtrfSvp5tL4DHkNOPsn4Uxtpl1PW1xxkbfWS3WK3LRAM84jTIE06pqpixSube62F/GqTi5DwyDOdobehgkfXxmQmjTKe4HvIXYC12Mfl9r5VY/gnVLNV0z5PoEA3ycRNWBhXL4BkRyQijZTLm7oH/xP+wWKdOimbJnxAzl8jhiTkJKZGSgj63pioTC60eyDQo8Dh6BdASuSnIdLoqDjB3vFA1Gbp9rOI7YLEYp0bXBzwdbsamasuQKN5juqzvOhmTIKTskx+IKUBRzZ1+5Zse5606PCKMOPyXYgkk42XfdZiyXcmLo/LvHWsxHYvsLspegLRJBDl+HREOzSKoT1FYW//twJaYh34928E1Ek5BeMUzRYrVJklPwAhUoUTpZuZE+kGdCqCBlTq2fN6CEcMB4t88TjGWDS1AGby9zQTK23NQkdTTx0sBnWZDecLAWk1xTdHxV/dvfnQGgByQKilmDf9meeRFFZn89uMX9SK3hhQ3bAh1Z4lTpqLYNyi7j3QSrhhn9ByLL8awH8Hn71EIRznw7mGGOIcyVKQQsE6Z7a7xMMKHhTvdjLwVpTsSLp46nTmBgk7AluBATeJM9FQpOrP142ZtHRifAFUSuhWLhBXYE+NYRaQT1VJQJU5FLadgUQzRGQuvi3dBkM6zXJapEB94OOvq1QjP6bt0SJXVW26+tqBS4tpcqUUJ5fTrfDzAX1ZuVbSSAQw73wNwSsd6OikYfIsg5jL+WnBMPOXnwTKdR3cUYynoqmbLf8A39m+EyRg5Z4kEZksxLxQ9oQH2O+XfgvFbRq9C/POwJV01knSRwjQCjvE6kr7bJSAF/DEekJMhP96ayZ1ZgzUDv+aazlPP2fLIjf4wYmg5h5+Et6GGMWwwIwYJKoZIhvcNAQkVMRYEFAk2eIvfMVYoZsDby30OzlTkJrwGMEUGCSqGSIb3DQEJFDE4HjYAUwB1AHIAZwBlACAARwBlAG4AZQByAGEAdABlAGQAIABDAEEAIAA0AEIANgA3ADYAMwA4ADYwLTAhMAkGBSsOAwIaBQAEFBY2VuZtNCmmQeiV3UDh7JuSWFqPBAj+OgUq8sPPwA==

@ -1,10 +0,0 @@
[Proxy Group]
🍃 Proxy = select,🏃 Auto,🚀 Direct,Proxy Header
🍂 Domestic = select,🚀 Direct,🍃 Proxy
🍎 Only = select,🚀 Direct,Proxy Header
☁️ Others = select,🚀 Direct,🍃 Proxy
🏃 Auto = url-test,ProxyHeader,url = http://www.gstatic.com/generate_204,interval = 1200

@ -1,2 +0,0 @@
# 带流量信息的订阅或托管链接
# 每行一个格式为name=url

@ -1,60 +0,0 @@
### Rules-lhie1脚本使用简介
#### 主界面名词
- 去广告加载去广告规则现有总规则6000+
- 开启MITM不选中则不使用MITM证书选中后默认使用lhie1的证书CA 4B676386可以在进阶设置中进行自定义
- 节点后缀用于在节点后添加UDP、TFO和插件等功能
- 导出配置采用Action Sheet将.conf配置文件导出不打开则尝试直接导出配置文件到Surge 3
- 进阶设置可自行配置常规General、代理分组Proxy Group、代理规则Custom Rule、本地DNS映射Host、URL重定向URL Rewrite、Header修改Header Rewrite、SSID、主机名Hostname和配置根证书MITM也可一键还原全部进阶设置
- 生成配置:拉取规则生成.conf配置文件
#### 节点导入编辑
> 这是脚本的一个特点,可以对节点进行编辑筛选等操作。
- 导入、更新:可以通过剪贴板或二维码导入,支持``ss://链接``、``Surge链接``和``托管地址``,支持批量导入(每行一个)
- 倒序:将节点顺序颠倒,方便不同机场节点选择
- 占位符为策略组添加占位符默认Auto组的占位符是ProxyHeader
- 特殊代理节点左滑即可将节点设置为某些链接的特殊代理按钮可以设置Direct、查看设置和清除特殊代理设置
- 节点删除:在节点列表左滑选择删除
- 节点重命名:节点列表左滑选择重命名(方便部分不带节点名节点改名)
- 节点排序:节点列表长按拖动即可调整顺序
#### 新手指导
熟悉以上内容后只需根据自己的需求导入节点在列表中选择测速节点选中开关拉取规则生成即可享受最新的lhie1规则而且在托管没有发生变化的情况下每次进入脚本都可以直接拉取生成最新规则不需要重新配置。
#### 进阶设置
> 进阶设置配置完毕后即可自动保存
- 常规General常规配置内容可以设置DNS、Replica等内容
- 代理分组Proxy Group配置代理组内容每个组别都可以通过``Proxy Header有空格``代表所有节点名,``ProxyHeader无空格``代替Auto节点名
- 代理规则Custom Rule自定义规则配置将需要添加的规则添加到规则的最上面如果要删除lhie1的规则在规则前添加``-``即可,添加的优先级较高
- 本地DNS映射Host自定义本地DNS映射
- URL重定向URL Rewrite自定义URL重定向
- Header修改Header Rewrite自定义Header修改
- SSID自定义SSID
- 主机名Hostname自定义主机名
- MITM用于配置根证书默认为lhie1的证书CA 4B676386也可自行配置
注:
1.以上进阶设置请在“知道自己在做什么”的情况下修改;
2.如果信息填错导致Surge无法启动可以将对应部分清空保存即可自动恢复为默认也可一键还原全部进阶设置。
#### JSBox小组件
> 可在iOS的Widget页面中添加使用使用前需先在JSBox的小组件设置中选中该脚本。小组件视图高度可考虑设置为120。
- Surge3启动/停止运行红色Surge3图标利用Surge3提供的URL Scheme可做到Surge3一键启动/停止运行
- 生成配置绿色下载图标一键生成配置并根据个人脚本中的设置自动执行导出到Surge3或者打开导出配置的系统选单
- 打开脚本蓝色JSBox图标从小组件进到该JSBox脚本的主界面
> 当规则有更新时小组件会有角标提醒及更新内容提示
#### URL Scheme
脚本支持自动根据配置拉取最新规则URL Scheme如下
```
jsbox://run?name=Rules-lhie1&auto=1
```
#### 最后
目前该JSBox脚本只支持生成Surge3的配置生成配置的各项功能已经能替代大部分的Workflow的自定义功能。
如觉得这个脚本好用,愿意打赏,感激不尽。

@ -1 +0,0 @@
感谢所有可爱的支持者!

@ -1,55 +0,0 @@
console.clear()
const app = require('scripts/app')
const init = require('scripts/init')
const today = require('scripts/today')
const extension = require('scripts/extension')
const updateUtil = require('scripts/updateUtil')
const siri = require('scripts/siri')
$app.autoKeyboardEnabled = true
$app.rotateDisabled = true
$app.keyboardToolbarEnabled = true
let query = $context.query
$objc('notification').invoke('objectForKey')
if (query.auto == 1) {
app.autoGen()
return
}
if ($app.env === $env.today) {
today.renderTodayUI()
return
} else if ($app.env === $env.safari) {
extension.renderExtensionUI()
return
} else if ($app.env === $env.action) {
extension.collectRules()
return
} else if ($app.env === $env.siri) {
siri.renderSiriUI()
return
}
app.setUpWorkspace()
init.asyncInitialize()
app.renderUI()
updateUtil.getLatestVersion({
handler: version => {
if (updateUtil.needUpdate(version, updateUtil.getCurVersion())) {
$http.get({
url: 'https://raw.githubusercontent.com/Fndroid/jsbox_script/master/Rules-lhie1/updateLog.md' + '?t=' + new Date().getTime(),
handler: resp => {
updateUtil.updateScript(version)
}
})
}
}
})

File diff suppressed because it is too large Load Diff

@ -1,60 +0,0 @@
/*
提示自定义颜色请建立_custom/color.js文件以覆盖默认颜色
格式
module.exports = {
editorItemText: "tint",
editorItemIcon: "tint",
}
*/
const DEFAULT = {
statusBar: "#000000", // 状态栏文字颜色,#ffffff为白色clear隐藏其他为黑色
navBar: "#ffffff", // 导航栏颜色
navTitleText: "tint", // 标题栏文字颜色
navIconLoading: "tint", // 加载图标颜色
navIconLeft: "tint", // 标题栏红包按钮文字颜色
navIconRight: "tint", // 标题栏备份按钮文字颜色
importBtnText: "tint", // 导入按钮文字颜色
controlBtnText: "#000000", // 操作栏文字颜色
editorItemText: "tint", // 服务器列表文字颜色
editorItemIcon: "tint", // 服务器列表图标颜色
outputFormatText: "tint", // 输出格式文字颜色
usualBtnOnBg: "#ff6666", // 常规选项按钮开启背景色
usualBtnOnFg: "#ffffff", // 常规选项按钮开启前景色
usualBtnOffBg: "#ffffff", // 常规选项按钮关闭背景色
usualBtnOffFg: "#000000", // 常规选项按钮关闭前景色
advanceBtnBg: "#808080", // 进阶按钮背景色
advanceBtnFg: "#ffffff", // 进阶按钮前景色
aboutBtnBg: "#808080", // 关于按钮背景色
aboutBtnFg: "#ffffff", // 关于按钮前景色
genBtnBg: "tint", // 生成按钮背景色
genBtnFg: "#ffffff", // 生成按钮前景色
advanceGridOnBg: "#ffda40", // 进阶设置网格选中背景色
advanceGridOnFg: "#034769", // 进阶设置网格选中前景色
advanceGridOffBg: "#63add0", // 进阶设置网格默认背景色
advanceGridOffFg: "#ffffff", // 进阶设置网格默认前景色
}
const customColor = $file.exists("_custom/color.js") ? require('_custom/color.js') : {}
function isEqual(color1, color2) {
return color1.runtimeValue().invoke('isEqual', color2)
}
function getColor(name, hex = false) {
let resColor = "tint"
if (name in customColor) {
resColor = customColor[name]
} else if (name in DEFAULT) {
resColor = DEFAULT[name]
}
return hex ? resColor : $color(resColor)
}
module.exports = {
isEqual: isEqual,
getColor: getColor
}

@ -1,325 +0,0 @@
const app = require('scripts/app')
const FILE = 'data.js'
let screenHeight = $device.info.screen.height
const screenWidth = $device.info.screen.width
const iPhoneX = screenWidth == 375 && screenHeight == 812
if (iPhoneX) {
screenHeight -= 48
}
let renderExtensionUI = function () {
const host = $context.safari.items.location.hostname
$ui.render({
props: {
title: "增加规则"
},
views: [{
type: "scroll",
props: {
id: "mainScrollView",
bgcolor: $color("#f0f5f5")
},
layout: $layout.fill,
views: [{
type: "label",
props: {
text: "当前网页路径"
},
layout: (make, view) => {
make.width.equalTo(view.super)
make.left.equalTo(5)
make.height.equalTo(30)
make.top.equalTo(view.super.top).offset(10)
}
}, {
type: "text",
props: {
editable: false,
text: $context.safari.items.baseURI
},
layout: (make, view) => {
make.top.equalTo(view.prev.bottom).offset(5)
make.width.equalTo(view.super)
make.height.equalTo(60)
}
}, {
type: "label",
props: {
text: "规则匹配项"
},
layout: (make, view) => {
make.width.equalTo(view.super)
make.left.equalTo(5)
make.height.equalTo(30)
make.top.equalTo(view.prev.bottom).offset(10)
}
}, {
type: "list",
props: {
rowHeight: 60,
id: "styleList",
template: {
views: [{
type: "label",
props: {
id: "styleName",
font: $font("bold", 16)
},
layout: (make, view) => {
make.width.equalTo(view.super)
make.height.equalTo(view.super).dividedBy(2)
make.left.equalTo(view.super.left).offset(15)
make.top.equalTo(view.super.top).offset(5)
}
}, {
type: "label",
props: {
id: "styleValue",
},
layout: (make, view) => {
make.width.equalTo(view.super)
make.height.equalTo(view.super).dividedBy(2)
make.left.equalTo(view.super.left).offset(15)
make.top.equalTo(view.prev.bottom).offset(-10)
}
}, {
type: "image",
props: {
id: "styleSelected",
bgcolor: $color("#ffffff")
},
layout: (make, view) => {
make.centerY.equalTo(view.super)
make.height.width.equalTo(20)
make.right.equalTo(view.super.right).offset(-20)
}
}]
}
},
layout: (make, view) => {
make.top.equalTo(view.prev.bottom).offset(5)
make.width.equalTo(view.super)
make.height.equalTo(0)
},
events: {
didSelect: (sender, indexPath, data) => {
let uiData = sender.data.map((i, idx) => {
return {
styleName: i.styleName,
styleValue: i.styleValue,
styleSelected: { data: null }
}
})
uiData[indexPath.row].styleSelected = { data: $file.read("assets/selected_icon.png") }
sender.data = uiData
sender.info = indexPath.row
}
}
}, {
type: "label",
props: {
text: "选择代理"
},
layout: (make, view) => {
make.width.equalTo(view.super)
make.left.equalTo(5)
make.height.equalTo(30)
make.top.equalTo(view.prev.bottom).offset(10)
}
}, {
type: "list",
props: {
id: "proxyList",
rowHeight: 40,
template: {
views: [{
type: "label",
props: {
id: "proxyName"
},
layout: (make, view) => {
make.centerY.equalTo(view.super)
make.width.equalTo(view.super).offset(-80)
make.height.equalTo(view.super)
make.left.equalTo(view.super.left).offset(15)
}
}, {
type: "image",
props: {
id: "proxySelected",
bgcolor: $color("#ffffff")
},
layout: (make, view) => {
make.centerY.equalTo(view.super)
make.height.width.equalTo(20)
make.right.equalTo(view.super.right).offset(-20)
}
}]
}
},
layout: (make, view) => {
make.top.equalTo(view.prev.bottom).offset(5)
make.width.equalTo(view.super)
make.height.equalTo(0)
},
events: {
didSelect: (sender, indexPath, data) => {
let uiData = sender.data.map((i, idx) => {
return {
proxyName: i.proxyName,
proxySelected: { data: null }
}
})
uiData[indexPath.row].proxySelected = { data: $file.read("assets/selected_icon.png") }
sender.data = uiData
sender.info = indexPath.row
}
}
}, {
type: "label",
props: {
text: "添加规则",
align: $align.center,
bgcolor: $color("#fff")
},
layout: (make, view) => {
make.width.equalTo(view.super)
make.height.equalTo(40)
make.top.equalTo(view.prev.bottom).offset(20)
},
events: {
tapped: sender => {
if ($("styleList").info !== null && $("proxyList").info !== null) {
let styleName = $("styleList").data[$("styleList").info].styleName.text
let styleValue = $("styleList").data[$("styleList").info].styleValue.text
let proxy = $("proxyList").data[$("proxyList").info].proxyName.text
let rule = [styleName, styleValue, proxy].join(',')
saveRule(rule)
$context.close()
} else {
$ui.alert("请选择匹配项和代理")
}
}
}
}]
}]
})
let styles = genStyleDefaultData(host)
let styleListHeight = styles.length * 60
$("styleList").data = styles
$("styleList").updateLayout(make => {
make.height.equalTo(styleListHeight)
})
let savedData = JSON.parse($file.read(FILE).string)
let proxyGroup = savedData.proxyGroupSettings.split('\n').filter(i => /^(?!\/|#)[\s\S]+=/.test(i)).map(i => i.split(/[\s]*=/)[0])
let flatServerData = savedData.workspace.serverData.reduce((all, cur) => {
return {
rows: all.rows.concat(cur.rows)
}
}).rows
let proxies = ['🚀 Direct'].concat(proxyGroup, ['REJECT', 'REJECT-TINYGIF'], flatServerData.map(i => i.proxyName.text))
let proxiesData = genProxyDefaultData(proxies)
let proxiesListHeight = proxiesData.length * 40
$("proxyList").data = proxiesData
$("proxyList").updateLayout(make => {
make.height.equalTo(proxiesListHeight)
})
resizeScrollView(styleListHeight + proxiesListHeight)
}
let resizeScrollView = function (listHeight) {
$("mainScrollView").contentSize = $size(screenWidth, 350 + listHeight)
}
let genStyleDefaultData = function (domain) {
let dp = domain.split('.')
let res = []
res.push({
styleName: { text: 'DOMAIN' },
styleValue: { text: domain }
})
return res.concat(dp.map((item, idx, obj) => {
return {
styleName: { text: 'DOMAIN-SUFFIX' },
styleValue: { text: obj.slice(idx).join('.') }
}
}))
}
let saveRule = function (rule) {
let data = JSON.parse($file.read(FILE).string)
data.customSettings += `\n${rule}\n`
$file.write({
data: $data({ "string": JSON.stringify(data) }),
path: FILE
})
}
let genProxyDefaultData = function (proxies) {
return proxies.map(i => {
return {
proxyName: { text: i }
}
})
}
function collectRules() {
let confFile = $context.data.string
let surgeReg = /\[Rule\]\n([\s\S]+)\n# Custom/
let quanReg = /\[SERVER\]\n([\s\S]+)\n\[SOURCE\]/
if (surgeReg.test(confFile)) {
let matcher = confFile.match(surgeReg)
if (matcher.length === 2) {
let rules = matcher[1]
saveRule(rules)
}
$context.close()
} else if (quanReg.test(confFile)) {
let matcher = confFile.match(quanReg);
let serversRaw = matcher[1]
let servers = serversRaw.split(/[\r\n]+/).filter(i => /.*?=/.test(i))
let serverDataItem = {
title: "Quantumult导出节点",
url: "",
rows: servers.map(server => {
return {
proxyLink: server,
proxyName: {
bgcolor: false,
text: server.split(/=/)[0].trim()
}
}
})
}
let file = JSON.parse($file.read(FILE).string);
let workspace = file.workspace;
let serverData = workspace.serverData;
serverData.unshift(serverDataItem);
let success = $file.write({
data: $data({ "string": JSON.stringify(file) }),
path: FILE
})
$ui.alert({
title: "导入" + success ? '成功': '失败',
message: success ? `已经成功导入${serverDataItem.rows.length}个服务器至脚本,请重启脚本查看` : '文件格式有误',
actions: [{
title: 'OK',
handler: () => {
$context.close()
}
}]
});
} else {
$ui.alert("分享文件不合法")
}
}
module.exports = {
renderExtensionUI: renderExtensionUI,
collectRules: collectRules
}

@ -1,23 +0,0 @@
const knownURLs = [
{domain: 'api.rixcloud.io', name: 'rixCloud'},
{domain: 'dler.cloud', name: 'Dler Cloud'}
]
function getConfName(url) {
let matchConst = knownURLs.find(i => url.indexOf(i.domain) > -1)
if (matchConst) {
return matchConst.name
} else {
let path = url.split('?')[0].split('/')
let filename = path[path.length - 1]
if (filename.indexOf('.conf') == filename.length - 5) {
return filename.substring(0, filename.length - 5)
} else {
return filename
}
}
}
module.exports = {
getConfName: getConfName
}

@ -1,21 +0,0 @@
function asyncInitialize() {
updateSpecialReg()
}
function updateSpecialReg() {
$http.download({
showsProgress: false,
url: "https://raw.githubusercontent.com/Fndroid/specialReg/master/specialReg.js?t=" + new Date().getTime()
}).then(resp => {
if (resp.response.statusCode === 200) {
let success = $file.write({
data: resp.data,
path: "scripts/videoReg.js"
});
}
})
}
module.exports = {
asyncInitialize: asyncInitialize
}

@ -1,318 +0,0 @@
const filenameUtil = require('scripts/filenameUtil')
String.prototype.strictTrim = function () {
let trimed = this.trim()
if ((matcher = trimed.match(/([\s\S]+),$/)) !== null) {
return matcher[1]
}
return trimed
}
function urlsafeBase64Encode(url) {
return $text.base64Encode(url).replace(/\-/g, '+').replace(/\\/g, '_').replace(/=+$/, '')
}
function urlsafeBase64Decode(base64) {
// Add removed at end '='
base64 += Array(5 - base64.length % 4).join('=');
base64 = base64
.replace(/\-/g, '+') // Convert '-' to '+'
.replace(/\_/g, '/'); // Convert '_' to '/'
return $text.base64Decode(base64).replace(/\u0000/, '');
}
function promiseConf(url) {
return new Promise((resolve, reject) => {
$http.get({
url: url,
header: {
'User-Agent': 'Surge/1174 CFNetwork/962 Darwin/18.0.0'
},
handler: function (resp) {
let data = resp.data + ''
let filename = url
try {
let matcher = resp.response.runtimeValue().invoke('allHeaderFields').rawValue()["Content-Disposition"].match(/filename="?(.*?)(?:.conf|"|$)/)
filename = matcher[1]
} catch (e) {
filename = filenameUtil.getConfName(url)
}
// 兼容不规范ssr链接
let noPaddingData = data
let padding = noPaddingData.length % 4 == 0 ? 0 : 4 - noPaddingData.length % 4
for (let i = 0; i < padding; i++) {
noPaddingData += '='
}
let decodedData = $text.base64Decode(data) || $text.base64Decode(noPaddingData)
if (/\[Proxy\]([\s\S]*?)\[Proxy Group\]/.test(data)) {
// Surge托管
resolve({
servers: RegExp.$1,
filename: filename,
type: 0
})
} else if (/^(ssr|ss|vmess):\/\//.test(decodedData)) {
let rawLinks = decodedData.split(/[\n\r\|\s]+/g).filter(i => i !== '' && /^(ssr|ss|vmess):\/\//.test(i));
let output = rawLinks.map(i => {
if (/^ssr:\/\//.test(i)) {
let res = decodeSSR([i])
return res.servers
} else if (/^ss:\/\//.test(i)) {
let res = decodeScheme([i])
return res.servers
} else {
let res = decodeVmess([i])
return res.servers
}
})
resolve({
servers: output.reduce((p, c) => {
return p.concat(c)
}, []).join('\n'),
filename: getDomain(url),
type: 4
})
} else if (/^ssr:\/\//.test(decodedData)) {
// SSR订阅
let rawLinks = decodedData.split(/[\n\r\|\s]+/g).filter(i => i !== '' && /^ssr:\/\//.test(i));
let res = decodeSSR(rawLinks);
resolve({
servers: res.servers.join('\n'),
filename: res.sstag || filename,
type: 1
})
} else if (/^ss:\/\//.test(decodedData)) {
// SS订阅
let rawLinks = decodedData.split(/[\n\r\|\s]+/g).filter(i => i !== '' && /^ss:\/\//.test(i));
let serInfo = decodeScheme(rawLinks);
resolve({
servers: serInfo.servers.join('\n'),
filename: serInfo.sstag || filename,
type: 2
})
} else if (/^vmess:\/\//.test(decodedData)) {
let rawLinks = decodedData.split(/[\n\r\|\s]+/g).filter(i => i !== '' && /^vmess:\/\//.test(i));
console.log('rawLinks', typeof rawLinks);
let res = decodeVmess(rawLinks);
console.log('res', res);
resolve({
servers: res.servers.join('\n'),
filename: res.sstag || filename,
type: 3
})
} else {
resolve()
}
}
})
})
}
function getDomain(url) {
if (/https?:\/\/.*?\.(.*?)\.(.*?)(?=\/|$)/.test(url)) {
return `${RegExp.$1.trim()}.${RegExp.$2.trim()}`
}
return '批量导入'
}
function decodeSSR(links) {
let tag = ''
let first = ''
function getParam(key, content) {
let reg = new RegExp(`${key}=(.*?)(?:&|$)`);
let matcher = content.match(reg);
return matcher && matcher[1] ? matcher[1] : '';
}
let decodedLinks = links.map(i => {
let rawContentMatcher = i.match(/^ssr:\/\/(.*?)$/);
if (rawContentMatcher && rawContentMatcher[1]) {
let rawContent = urlsafeBase64Decode(rawContentMatcher[1]);
let rawContentParts = rawContent.split(/\/*\?/g)
let paramsMatcher = rawContentParts[0].match(/^(.*?):(.*?):(.*?):(.*?):(.*?):(.*?)$/);
if (paramsMatcher && paramsMatcher.length === 7) {
let host = paramsMatcher[1];
let port = paramsMatcher[2];
let protocol = paramsMatcher[3];
let method = paramsMatcher[4];
let obfs = paramsMatcher[5];
let pass = urlsafeBase64Decode(paramsMatcher[6]);
let obfsparam = '';
let protoparam = '';
let group = '';
let remarks = '';
if (rawContentParts.length > 1) {
let target = rawContentParts[1];
obfsparam = urlsafeBase64Decode(getParam('obfsparam', target));
protoparam = urlsafeBase64Decode(getParam('protoparam', target));
group = urlsafeBase64Decode(getParam('group', target));
remarks = urlsafeBase64Decode(getParam('remarks', target));
}
if (tag === '' && group !== '') {
tag = group;
}
let finalName = remarks === '' ? `${host}:${port}` : remarks
first = finalName
let res = `${finalName} = shadowsocksr, ${host}, ${port}, ${method}, "${pass}", protocol=${protocol}, obfs=${obfs}`;
res += protoparam ? `, protocol_param=${protoparam}` : '';
res += obfsparam ? `, obfs_param="${obfsparam}"` : '';
return res;
}
else {
return '';
}
}
else {
return '';
}
});
let sstag = first
if (decodedLinks.length > 1) {
sstag = `批量SSR节点${decodedLinks.length}`
}
if (tag !== '') {
sstag = tag
}
return { servers: decodedLinks, sstag: sstag }
}
function getServersFromConfFile(params) {
let promiseArray = params.urls.map(i => promiseConf(i))
Promise.all(promiseArray).then(confs => {
for (let idx in confs) {
let res = confs[idx]
let type = res ? res.type : -1
let filename = res ? res.filename : '';
let servers = res ? res.servers.split(/[\n\r]+/).filter(item => item !== '').map(i => i.strictTrim()) : [];
params.handler({ servers: servers, filename: filename, url: params.urls[idx], type: type })
}
}).catch(reason => {
console.error(reason.stack)
params.handler(null)
})
}
function isJson(str) {
try {
JSON.parse(str)
} catch (e) {
return false
}
return true
}
function decodeVmess(links) {
let result = []
let tag = ''
for (let idx in links) {
let link = links[idx]
if (/^vmess:\/\/(.*?)$/.test(link)) {
let content = urlsafeBase64Decode(RegExp.$1)
if (isJson(content)) {
// v2rayN style
let jsonConf = JSON.parse(content)
let group = ''
const ua = 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16A5366a'
let obfs = `,obfs=${jsonConf.net === 'ws' ? 'ws' : 'http'},obfs-path="${jsonConf.path || '/'}",obfs-header="Host:${jsonConf.host || jsonConf.add}[Rr][Nn]User-Agent:${ua}"`
let quanVmess = `${jsonConf.ps} = vmess,${jsonConf.add},${jsonConf.port},chacha20-ietf-poly1305,"${jsonConf.id}",group=${group},over-tls=${jsonConf.tls === 'tls' ? 'true' : 'false'},certificate=1${jsonConf.type === 'none' && jsonConf.net !== 'ws' ? '' : obfs}`
result.push(quanVmess)
} else {
// Quantumult style
if (/group=(.*?),/.test(content)) {
tag = RegExp.$1
}
result.push(content)
}
}
}
return { servers: result, sstag: tag || `批量V2Ray节点${result.length}` }
}
function decodeScheme(urls) {
// let urls = params.ssURL
let result = []
let tag
let group = ''
for (let idx in urls) {
let url = urls[idx]
let method, password, hostname, port, plugin
if (!url.includes('#')) {
let name = '无节点名称'
url += `#${name}`
}
tag = $text.URLDecode(url.match(/#(.*?)$/)[1])
if (url.includes('?')) {
// tag = $text.URLDecode(url.match(/#(.*?)$/)[1])
let mdps = url.match(/ss:\/\/(.*?)@/)[1]
let padding = 4 - mdps.length % 4
if (padding < 4) {
mdps += Array(padding + 1).join('=')
}
let userinfo = $text.base64Decode(mdps)
method = userinfo.split(':')[0]
password = userinfo.split(':')[1]
let htpr = url.match(/@(.*?)\?/)[1].replace('\/', '')
hostname = htpr.split(':')[0]
port = htpr.split(':')[1]
let ps = $text.URLDecode(url.match(/\?(.*?)#/)[1])
let obfsMatcher = ps.match(/obfs=(.*?)(;|$)/)
let obfsHostMatcher = ps.match(/obfs-host=(.*?)(&|;|$)/)
if (obfsMatcher) {
let obfs = obfsMatcher[1]
let obfsHost = obfsHostMatcher ? obfsHostMatcher[1] : 'cloudfront.net'
plugin = `obfs=${obfs}, obfs-host=${obfsHost}`
}
if (/group=(.*)(&|;|$)/.test(ps)) {
group = $text.base64Decode(RegExp.$1.trim())
}
} else {
if (/ss:\/\/([^#]*)/.test(url)) {
let mdps = RegExp.$1
if (/^(.*)@(.*?):(.*?)$/.test(mdps)) {
hostname = RegExp.$2
port = RegExp.$3
let methodAndPass = urlsafeBase64Decode(RegExp.$1)
console.log('methodAndPass', methodAndPass);
if (/^(.*?):(.*?)$/.test(methodAndPass)) {
method = RegExp.$1
password = RegExp.$2
}
} else {
let padding = 4 - mdps.length % 4
if (padding < 4) {
mdps += Array(padding + 1).join('=')
}
if (/^(.*?):(.*)@(.*?):(.*?)$/.test($text.base64Decode(mdps))) {
method = RegExp.$1
password = RegExp.$2
hostname = RegExp.$3
port = RegExp.$4
}
}
}
}
let proxy = `${tag} = custom, ${hostname}, ${port}, ${method}, ${password}, https://github.com/lhie1/Rules/blob/master/SSEncrypt.module?raw=true`
if (plugin != undefined) {
proxy += `, ${plugin}`
}
result[idx] = proxy
}
let outName = ''
if (group) {
outName = group
} else if (result.length === 1) {
outName = tag
} else {
outName = `批量ss节点${result.length}`
}
return { servers: result, sstag: outName }
}
module.exports = {
proxyFromConf: getServersFromConfFile,
proxyFromURL: decodeScheme,
proxyFromVmess: decodeVmess,
proxyFromSSR: decodeSSR
}

@ -1,87 +0,0 @@
let githubRawReg = /^https:\/\/raw\.githubusercontent\.com\/(.*?)\/(.*?)\/master\/(.*?)$/
const FILE = 'data.js'
function getRulesReplacement(content = '') {
let advanceSettings = content ? content : JSON.parse($file.read(FILE).string)
if (advanceSettings.customSettings) {
let cs = advanceSettings.customSettings;
let pat = cs.match(/\/\/\s*replacement\s*:\s*(.*?)[\n\r]/);
if (pat && pat[1]) {
return pat[1];
}
}
return null;
}
function getSoftwareType() {
let file = JSON.parse($file.read(FILE).string)
let workspace = file.workspace
let outputFormat = workspace.outputFormat
if (outputFormat === 'Surge 3 TF') {
return 0
} else if (outputFormat === 'Surge 2') {
return 2
} else if (outputFormat === 'Quantumult') {
return 3
}
return 1
}
function checkUpdate(oldSha, newSha) {
return Object.keys(newSha).some(i => oldSha[i] !== newSha[i])
}
function setFilesSha(sha) {
let file = JSON.parse($file.read(FILE).string)
file['repoSha'] = sha
$file.write({
data: $data({ "string": JSON.stringify(file) }),
path: FILE
})
}
function getFilesSha() {
let file = JSON.parse($file.read(FILE).string)
return file['repoSha'] || {}
}
function getGitHubFilesSha(params) {
params.handler({})
}
function getRepoInfo() {
let owner = 'lhie1';
let repoName = 'Rules';
let filePath = 'Auto';
let softwareType = getSoftwareType()
if (softwareType === 0) {
filePath = 'Auto_New'
} else if (softwareType === 3) {
filePath = 'Quantumult'
}
console.log(filePath)
let rulesRep = getRulesReplacement();
if (rulesRep) {
let reg = rulesRep.match(githubRawReg);
if (reg && reg.length === 4) {
owner = reg[1];
repoName = reg[2];
filePath = reg[3];
}
}
return { owner, repoName, filePath };
}
function getLatestCommitMessage(params) {
params.handler(null)
}
module.exports = {
checkUpdate: checkUpdate,
getGitHubFilesSha: getGitHubFilesSha,
setFilesSha: setFilesSha,
getFilesSha: getFilesSha,
getLatestCommitMessage: getLatestCommitMessage,
getRepoInfo: getRepoInfo
}

@ -1,14 +0,0 @@
const ruleUpdateUtil = require('scripts/ruleUpdateUtil');
function siriRun() {
}
function renderSiriUI() {
$intents.height = 100
}
module.exports = {
siriRun: siriRun,
renderSiriUI: renderSiriUI
}

@ -1,8 +0,0 @@
function pc(x) {
let sw = $device.info.screen.width
return x * sw / 100
}
module.exports = {
pc: pc
}

@ -1,49 +0,0 @@
module.exports = {
init: (address, port, clearFirst = true, debug = true) => {
var oldLog = console.log;
var oldInfo = console.info;
var oldWarn = console.warn;
var oldError = console.error;
let start = clearFirst
var socket = $socket.new(`ws://${address}:${port}`);
socket.open()
console.log = function (obj) {
if (debug) {
let msg = JSON.stringify({ type: 'log', args: Array.prototype.slice.call(arguments) });
start = sendMessage(start, socket, msg);
}
oldLog.apply(console, arguments);
}
console.info = function (obj) {
if (debug) {
let msg = JSON.stringify({ type: 'info', args: Array.prototype.slice.call(arguments) });
start = sendMessage(start, socket, msg);
}
oldInfo.apply(console, arguments);
}
console.warn = function (obj) {
if (debug) {
let msg = JSON.stringify({ type: 'warn', args: Array.prototype.slice.call(arguments) });
start = sendMessage(start, socket, msg);
}
oldWarn.apply(console, arguments);
}
console.error = function (obj) {
if (debug) {
let msg = JSON.stringify({ type: 'error', args: Array.prototype.slice.call(arguments) });
start = sendMessage(start, socket, msg);
}
oldError.apply(console, arguments);
}
}
}
function sendMessage(start, socket, msg) {
if (start) {
socket.send(JSON.stringify({ type: "_open" }));
start = false;
}
socket.send(msg);
return start;
}

@ -1,468 +0,0 @@
const ruleUpdateUtil = require('scripts/ruleUpdateUtil')
const updateUtil = require('scripts/updateUtil')
const loadingHint = "检查规则/脚本更新..."
const scriptName = $addin.current.name
const FILE = 'data.js'
let pm = function (method) {
return new Promise((resolve, reject) => {
method({
handler: res => {
resolve(res)
}
})
})
}
function getIfaData() {
return Object.keys($network.ifa_data).filter(i => i.indexOf('utun') > -1)
}
function vpnStatus() {
try {
// 支持最新TF$network.proxy_settings
let proxySettings = $network.proxy_settings
let proxyScoped = proxySettings['__SCOPED__']
let lans = Object.keys(proxyScoped)
return lans.find(i => i.indexOf('utun') > -1) ? 1 : 0
} catch (e) {
// 兼容旧版,需要手动设置状态
if ($cache.get("surgeOn")) {
let surgeOn = $cache.get('surgeOn')
let nowIfa = getIfaData()
let oldIfa = surgeOn.ifaKeys
if (nowIfa.length === oldIfa.length) {
return surgeOn.status ? 1 : 0
} else {
return surgeOn.status ? 0 : 1
}
}
return -1
}
}
function genSrugeLabel(status, isQuan) {
if (status === -1) {
return '长按设置'
} else if (status === 0) {
return isQuan ? '开启Quantumult' : '开启Surge'
} else {
return isQuan ? '关闭Quantumult' : '关闭Surge'
}
}
function requestHead(url) {
if (!/^https?:\/\//.test(url)) return Promise.resolve('')
return new Promise((resolve, reject) => {
$http.request({
method: "HEAD",
url: url,
header: {
// 'User-Agent': 'Quantumult'
},
handler: function (resp) {
let headerFields = resp.response.runtimeValue().$allHeaderFields().rawValue();
if ('Subscription-userinfo' in headerFields) {
resolve(headerFields['Subscription-userinfo'])
} else if ('subscription-userinfo' in headerFields) {
resolve(headerFields['subscription-userinfo'])
} else {
resolve('')
}
}
})
})
}
function parseUsage(usageStr) {
let uploadMatcher = usageStr.match(/upload=(\d+)(?:;|$)/)
let downloadMatcher = usageStr.match(/download=(\d+)(?:;|$)/)
let totalMatcher = usageStr.match(/total=(\d+)(?:;|$)/)
let upload = 0
let download = 0
let total = 0
if (uploadMatcher && uploadMatcher[1]) upload = uploadMatcher[1] * 1
if (downloadMatcher && downloadMatcher[1]) download = downloadMatcher[1] * 1
if (totalMatcher && totalMatcher[1]) total = totalMatcher[1] * 1
return {
upload: upload,
download: download,
total: total
}
}
function widgetSettings(file) {
let items = file.widgetSettings.split(/[\r\n]+/g).filter(i => /^.*?=\s*http/.test(i))
return items.map(i => {
let p = i.split(/=/)
return {
name: p[0].trim(),
url: p.slice(1).join('=').trim()
}
})
}
function renderTodayUI() {
let file = JSON.parse($file.read(FILE).string)
let workspace = file.workspace
let widget = widgetSettings(file);
let groupNames = workspace.serverData.map(i => i.title).concat(widget.map(i => i.name))
let groupURLs = workspace.serverData.map(i => i.url).concat(widget.map(i => i.url)).map(i => requestHead(i))
Promise.all(groupURLs).then(res => {
console.log(res)
let usageData = []
for (let idx in res) {
if (res[idx] === '') continue
let usage = parseUsage(res[idx])
const GB = Math.pow(1024, 3)
usageData.push({
groupName: {
text: `${groupNames[idx]}`
},
usageProgress: {
value: (usage.download + usage.upload) / usage.total
},
usageDetail: {
text: `${(usage.upload / GB).toFixed(2)}GB ↓ ${(usage.download / GB).toFixed(2)}GB ≡ ${((usage.total - usage.download - usage.upload) / GB).toFixed(2)}GB`
},
usageDetail2: {
text: `${groupNames[idx]} ( ${(usage.total / GB).toFixed(2)}GB )`
}
})
}
$("usageView").data = usageData
$("usageView").updateLayout(make => {
make.height.equalTo(usageData.length * 50)
})
$widget.height = 110 + (usageData.length * 50)
})
$widget.modeChanged = mode => {
if (mode === 1) {
$widget.height = 110 + ($("usageView").data.length * 50)
}
}
let outputFormat = workspace.outputFormat
let surge2 = outputFormat === 'Surge 2'
let isQuan = outputFormat === 'Quantumult'
let isLauncher = $app.widgetIndex < 0 || $app.widgetIndex > 2
let checks = [pm(ruleUpdateUtil.getGitHubFilesSha), pm(updateUtil.getLatestVersion)]
let vStatus = vpnStatus()
Promise.all(checks).then(res => {
let canUpdate = ruleUpdateUtil.checkUpdate(ruleUpdateUtil.getFilesSha(), res[0])
let newVersion = updateUtil.needUpdate(res[1], updateUtil.getCurVersion())
$("newTag").hidden = !canUpdate
$("newVersionTag").hidden = !newVersion
return canUpdate ? pm(ruleUpdateUtil.getLatestCommitMessage) : Promise.resolve()
}).then(res => {
let { owner, repoName, filePath } = ruleUpdateUtil.getRepoInfo()
$("updateStatus").text = res ? res.commit.message : `${owner}\/${repoName}`
})
let targetAppOn = $file.read("assets/today_surge.png")
let targetAppOff = $file.read("assets/today_surge_off.png")
if (isQuan) {
targetAppOn = $file.read("assets/today_quan.png")
targetAppOff = $file.read("assets/today_quan_off.png")
} else if (surge2) {
targetAppOn = $file.read("assets/today_surge2.png")
}
$ui.render({
props: {
id: "todayMainView",
title: "Surge3规则生成",
hideNavbar: true,
navBarHidden: true,
},
views: [{
type: "blur",
props: {
id: "close",
style: 1,
radius: 0,
hidden: !isLauncher
},
layout: (make, view) => {
make.width.height.equalTo(view.super).offset(10)
make.top.equalTo(view.super.top).offset(-10)
},
events: {
tapped: sender => {
$app.close(0.3)
}
}
}, {
type: "view",
props: {
id: "",
},
layout: (make, view) => {
make.height.equalTo(110)
make.width.equalTo(view.super).offset(-60)
make.centerX.equalTo(view.super)
},
views: [{
type: "label",
props: {
id: "updateStatus",
text: "Rules-lhie1 by Fndroid",
font: $font(12),
textColor: $rgba(50, 50, 50, .3)
},
layout: (make, view) => {
make.top.equalTo(view.super.top).offset(5)
make.centerX.equalTo(view.super)
}
}, {
type: "label",
props: {
id: "updateStatus",
text: loadingHint,
font: $font(12),
textColor: $rgba(50, 50, 50, .3)
},
layout: (make, view) => {
make.bottom.equalTo(view.super.bottom).offset(-5)
make.centerX.equalTo(view.super)
}
}, {
type: "image",
props: {
id: "pullBtn",
data: $file.read("assets/today_pull.png"),
radius: 25,
bgcolor: $rgba(255, 255, 255, 0)
},
layout: (make, view) => {
make.width.height.equalTo(55)
make.centerY.equalTo(view.super).offset(-10)
make.centerX.equalTo(view.super)
},
events: {
tapped: sender => {
$app.openURL(`jsbox://run?name=${encodeURIComponent(scriptName)}&auto=1`)
}
},
}, {
type: "image",
props: {
id: "surgeBtn",
data: vStatus === 0 ? targetAppOff : targetAppOn,
radius: 25,
bgcolor: $rgba(255, 255, 255, 0)
},
layout: (make, view) => {
make.width.height.equalTo(55)
make.centerY.equalTo(view.super).offset(-10)
// make.left.equalTo(view.prev.left).offset(-(sw / 3.5))
make.left.equalTo(view.super)
console.log('width', $widget.width)
},
events: {
tapped: sender => {
let url = `surge${surge2 ? "" : "3"}:///toggle?autoclose=true`
if (isQuan) {
url = 'quantumult://' + (vStatus === 0 ? 'start' : 'stop')
}
$app.openURL(url)
},
longPressed: sender => {
$ui.alert({
title: "初始设置",
message: '请选择当前VPN开关状态',
actions: [{
title: '已关闭',
handler: () => {
$cache.set("surgeOn", {
status: false,
ifaKeys: getIfaData()
})
}
}, {
title: '已开启',
handler: () => {
$cache.set("surgeOn", {
status: true,
ifaKeys: getIfaData()
})
}
}]
})
}
}
}, {
type: "image",
props: {
id: "jsboxBtn",
data: $file.read("assets/today_jsbox.png"),
radius: 25,
bgcolor: $rgba(255, 255, 255, 0)
},
layout: (make, view) => {
make.width.height.equalTo(50)
make.centerY.equalTo(view.super).offset(-10)
// make.right.equalTo(view.prev.prev.right).offset((sw / 3.5))
make.right.equalTo(view.super)
},
events: {
tapped: sender => {
$app.openURL(`jsbox://run?name=${encodeURIComponent(scriptName)}`)
}
}
}, {
type: "label",
props: {
text: "更新规则",
font: $font(12),
textColor: $rgba(50, 50, 50, .8),
align: $align.center
},
layout: (make, view) => {
make.height.equalTo(12)
make.top.equalTo($("pullBtn").bottom)
make.width.equalTo($("pullBtn").width)
make.centerX.equalTo($("pullBtn"))
}
}, {
type: "label",
props: {
id: "surgeLabel",
text: genSrugeLabel(vStatus, isQuan),
font: $font(12),
textColor: $rgba(50, 50, 50, .8),
align: $align.center
},
layout: (make, view) => {
make.height.equalTo(12)
make.top.equalTo(view.prev.top)
make.centerX.equalTo($("surgeBtn"))
}
}, {
type: "label",
props: {
text: "脚本设置",
font: $font(12),
textColor: $rgba(50, 50, 50, .8),
align: $align.center
},
layout: (make, view) => {
make.height.equalTo(12)
make.top.equalTo($("pullBtn").bottom)
make.width.equalTo($("pullBtn").width)
make.centerX.equalTo($("jsboxBtn"))
}
}, {
type: "image",
props: {
id: "newTag",
data: $file.read("assets/new_rules_tag.png"),
bgcolor: $rgba(255, 255, 255, 0),
hidden: true
},
layout: (make, view) => {
make.width.height.equalTo(15)
make.centerY.equalTo(view.super).offset(-20)
make.left.equalTo($("pullBtn").right).offset(-10)
}
}, {
type: "image",
props: {
id: "newVersionTag",
data: $file.read("assets/new_version_tag.png"),
bgcolor: $rgba(255, 255, 255, 0),
hidden: true
},
layout: (make, view) => {
make.width.height.equalTo(15)
make.centerY.equalTo(view.super).offset(-20)
make.left.equalTo($("jsboxBtn").right).offset(-10)
}
}, {
type: "image",
props: {
id: "closeBtn",
data: $file.read("assets/close_icon.png"),
bgcolor: $rgba(255, 255, 255, 0),
hidden: !isLauncher,
alpha: 0.7
},
layout: (make, view) => {
make.width.height.equalTo(20)
make.top.equalTo(view.super.top).offset(10)
make.right.equalTo(view.super.right).offset(-10)
},
events: {
tapped: sender => {
$app.close(.2)
}
}
}]
}, {
type: 'list',
props: {
id: "usageView",
data: [],
rowHeight: 50,
alwaysBounceVertical: false,
bgcolor: $color("clear"),
separatorHidden: true,
template: {
props: {
bgcolor: $color("clear")
},
views: [{
type: "progress",
props: {
id: 'usageProgress'
},
layout: function (make, view) {
make.centerY.equalTo(view.super).offset(-3);
make.centerX.equalTo(view.super);
make.height.equalTo(3)
make.width.equalTo(view.super).multipliedBy(1).offset(-50)
},
views: [{
type: 'label',
props: {
id: 'usageDetail',
align: $align.center,
font: $font("bold", 10),
textColor: $color("#595959")
},
layout: (make, view) => {
make.width.equalTo(view.super)
make.height.equalTo(20)
make.top.equalTo(view.super).offset(-20)
make.centerX.equalTo(view.super)
}
}, {
type: 'label',
props: {
id: 'usageDetail2',
align: $align.center,
font: $font("bold", 12),
textColor: $color("#595959")
},
layout: (make, view) => {
make.width.equalTo(view.super)
make.height.equalTo(23)
make.top.equalTo(view.super)
make.centerX.equalTo(view.super)
}
}]
}]
}
},
layout: (make, view) => {
make.top.equalTo(110)
make.width.equalTo(view.super)
make.height.width.equalTo(100)
}
}]
})
}
module.exports = {
renderTodayUI: renderTodayUI
}

@ -1,60 +0,0 @@
function getCurVersion() {
let version = $file.read("version.fndroid").string
return version
}
function needRestart() {
return $http.get({
url: 'https://raw.githubusercontent.com/Fndroid/jsbox_script/master/Rules-lhie1/restart.fndroid'
})
}
function getLatestVersion(params) {
$http.get({
url: 'https://raw.githubusercontent.com/Fndroid/jsbox_script/master/Rules-lhie1/version.fndroid' + '?t=' + new Date().getTime(),
handler: res => {
params.handler(res.data)
}
})
}
function updateScript(version) {
let url = 'https://raw.githubusercontent.com/Fndroid/jsbox_script/master/Rules-lhie1/.output/Rules-lhie1.box' + '?t=' + new Date().getTime()
const scriptName = $addin.current.name
let downloadBox = $http.download({
url: url
})
Promise.all([downloadBox, needRestart()]).then(res => {
let box = res[0].data
let restart = /true/.test(res[1].data)
$addin.save({
name: scriptName,
data: box,
handler: (success) => {
if (success) {
$ui.toast(`更新完成`)
if (restart) {
$delay(0.3, () => {
$addin.run(scriptName)
})
}
}
}
})
})
}
function needUpdate(nv, ov) {
let getVersionWeight = i => {
return i.split('.').map(i => i * 1).reduce((s, i) => s * 100 + i)
}
return getVersionWeight(nv) > getVersionWeight(ov)
}
module.exports = {
getCurVersion: getCurVersion,
getLatestVersion: getLatestVersion,
updateScript: updateScript,
needUpdate: needUpdate
}

@ -1,16 +0,0 @@
module.exports = {
Netflix: /(?=USER-AGENT,Argo\*,|DOMAIN-SUFFIX,netflix.com,|DOMAIN-SUFFIX,netflix.net,|DOMAIN-SUFFIX,nflxext.com,|DOMAIN-SUFFIX,nflximg.com,|DOMAIN-SUFFIX,nflximg.net,|DOMAIN-SUFFIX,nflxvideo.net,|IP-CIDR,23.246.0.0\/12,|IP-CIDR,37.77.0.0\/12,|IP-CIDR,45.57.0.0\/12,|IP-CIDR,64.120.128.0\/17,|IP-CIDR,66.197.128.0\/17,|IP-CIDR,108.175.0.0\/12,|IP-CIDR,185.2.0.0\/12,|IP-CIDR,185.9.188.0\/22,|IP-CIDR,192.173.64.0\/18,|IP-CIDR,198.38.0.0\/12,|IP-CIDR,198.45.0.0\/12,)[^\n]+/g,
Spotify: /(?=PROCESS-NAME,Spotify,|DOMAIN-SUFFIX,spoti.fi,|DOMAIN-KEYWORD,spotify,|USER-AGENT,\*Spotify\*,|DOMAIN-SUFFIX,scdn.co,|DOMAIN-SUFFIX,spotify.com,)[^\n]+/g,
YouTube: /(?=USER-AGENT,com.google.ios.youtube\*,|USER-AGENT,YouTube\*,|DOMAIN-SUFFIX,googlevideo.com,|DOMAIN-SUFFIX,youtube.com,|DOMAIN-KEYWORD,youtube,)[^\n]+/g,
MytvSUPER: /(?=DOMAIN-KEYWORD,nowtv100,|DOMAIN-KEYWORD,rthklive,|DOMAIN-SUFFIX,mytvsuper.com,|DOMAIN-SUFFIX,tvb.com,)[^\n]+/g,
BBC: /(?=USER-AGENT,BBCiPlayer\*,|USER-AGENT,BBCBeacon\*,|DOMAIN-KEYWORD,co.uk,|DOMAIN-KEYWORD,uk-live,|DOMAIN-SUFFIX,bbc.co,|DOMAIN-SUFFIX,bbc.com,|DOMAIN-SUFFIX,bbci.co.uk,|DOMAIN-SUFFIX,bbc.co.uk,|DOMAIN-SUFFIX,vod-hls-uk-live.akamaized.net,)[^\n]+/g,
LINE: /(?=DOMAIN-SUFFIX,lin.ee,|DOMAIN-SUFFIX,line.me,|DOMAIN-SUFFIX,line.naver.jp,|DOMAIN-SUFFIX,line-apps.com,|DOMAIN-SUFFIX,line-cdn.net,|DOMAIN-SUFFIX,line-scdn.net,|DOMAIN-SUFFIX,nhncorp.jp,|IP-CIDR,103.2.28.0\/22,|IP-CIDR,119.235.224.0\/21,|IP-CIDR,119.235.232.0\/23,|IP-CIDR,119.235.235.0\/24,|IP-CIDR,119.235.236.0\/23,|IP-CIDR,125.6.146.0\/24,|IP-CIDR,125.6.149.0\/24,|IP-CIDR,125.6.190.0\/24,|IP-CIDR,125.209.208.0\/20,|IP-CIDR,203.104.103.0\/24,|IP-CIDR,203.104.128.0\/20,|IP-CIDR,203.174.66.64\/26,|IP-CIDR,203.174.77.0\/24,)[^\n]+/g,
SoundCloud: /(?=USER-AGENT,SoundCloud\*,|DOMAIN-SUFFIX,soundcloud.com,|DOMAIN-SUFFIX,sndcdn.com,)[^\n]+/g,
Google: /(?=DOMAIN-SUFFIX,goo.gl,|DOMAIN-SUFFIX,gvt1.com,|DOMAIN-SUFFIX,youtu.be,|DOMAIN-SUFFIX,ytimg.com,|DOMAIN-SUFFIX,ggpht.com,|DOMAIN-KEYWORD,gmail,|DOMAIN-KEYWORD,google,|DOMAIN-KEYWORD,gstatic,|DOMAIN-KEYWORD,youtube,|USER-AGENT,WalletAPP.app\*,|USER-AGENT,Wallet\*iSL\*,|DOMAIN-SUFFIX,google.com,|DOMAIN-SUFFIX,googleapis.com,)[^\n]+/g,
PayPal: /(?=USER-AGENT,PayPal\*,|DOMAIN-KEYWORD,paypal,|DOMAIN-SUFFIX,paypal.com,)[^\n]+/g,
Microsoft: /(?=USER-AGENT,Microsoft\*,|USER-AGENT,OneDrive\*,|DOMAIN-KEYWORD,microsoft,|DOMAIN-SUFFIX,bing.com,|DOMAIN-SUFFIX,bing.net,|DOMAIN-SUFFIX,cloudappsecurity.com,|DOMAIN-SUFFIX,docs.com,|DOMAIN-SUFFIX,hotmail.com,|DOMAIN-SUFFIX,live.com,|DOMAIN-SUFFIX,live.net,|DOMAIN-SUFFIX,microsoft.com,|DOMAIN-SUFFIX,msedge.net,|DOMAIN-SUFFIX,msn.com,|DOMAIN-SUFFIX,office.com,|DOMAIN-SUFFIX,office.net,|DOMAIN-SUFFIX,office365.com,|DOMAIN-SUFFIX,onedrive.com,|DOMAIN-SUFFIX,onenote.com,|DOMAIN-SUFFIX,onenote.net,|DOMAIN-SUFFIX,onmicrosoft.com,|DOMAIN-SUFFIX,outlook.com,|DOMAIN-SUFFIX,outlookgroups.ms,|DOMAIN-SUFFIX,visualstudio.com,|DOMAIN-SUFFIX,windows.com,|DOMAIN-SUFFIX,windows.net,|DOMAIN-SUFFIX,windowsupdate.com,)[^\n]+/g,
YoutubeMusic: /(?=USER-AGENT,\*youtubemusic\*,|USER-AGENT,YouTubeMusic\*,)[^\n]+/g,
TVB: /(?=USER-AGENT,mytv\*,|DOMAIN-SUFFIX,mytvsuper.com,|DOMAIN-KEYWORD,nowtv100,|DOMAIN-KEYWORD,rthklive,|DOMAIN-SUFFIX,tvb.com,|USER-AGENT,OTT_iPhone\*,|USER-AGENT,Viu\*,|DOMAIN-SUFFIX,viu.com,)[^\n]+/g,
Vidol: /(?=USER-AGENT,Vidol\*,|DOMAIN-SUFFIX,vidol.tv,)[^\n]+/g,
Hulu: /(?=DOMAIN-SUFFIX,hulu.com,)[^\n]+/g
}

@ -1,3 +0,0 @@
"OK" = "OK";
"DONE" = "Done";
"HELLO_WORLD" = "Hello, World!";

@ -1,3 +0,0 @@
"OK" = "好的";
"DONE" = "完成";
"HELLO_WORLD" = "你好,世界!";

@ -1,546 +0,0 @@
**如打赏名单有遗漏请TG联系我加一下**
#### 2019.05.06
1. 修复规则仓库调整导致的不兼容问题 -v1.9.70
#### 2019.04.15
1. 修复“删除节点”逻辑问题 -v1.9.69
#### 2019.03.21
1. 修复Quantumult自定义url rewrite不生效问题 -v1.9.67
#### 2019.03.20
1. 修复规则仓库调整导致的不兼容问题 -v1.9.64
2. 修复当只有userHostName时配置文件出现第一行为空的问题 -v1.9.65
3. 修复Quantumult规则变化导致的问题 -v1.9.66
#### 2019.02.25
1. 修复规则仓库调整导致的不兼容问题 -v1.9.63
2. 修复ss节点密码带有@导致不能识别问题 -v1.9.63
#### 2019.02.07
1. 修复RuleSet注释无法识别问题 -v1.9.62
#### 2019.01.04
1. 修复进阶设置占位符滚动问题 -v1.9.61
#### 2019.01.01
1. 修复导出到Quantumult时RULE-SET转换错误问题 -v1.9.60
#### 2018.12.30
1. rename替换中使用``>``代替``,`` -v1.9.59
#### 2018.12.21
1. 支持导入混合类型订阅 -v1.9.58
#### 2018.12.20
1. 修复RULE-SET被注释依然转换问题 -v1.9.57
#### 2018.11.24
1. 修复非TF导出带有RULE-SET问题 -v1.9.56
#### 2018.11.19
1. 修复Quantumult和Surge TF无法使用replacement问题 -v1.9.54
#### 2018.11.17
1. 修复Widget布局问题 -v1.9.52
2. 自定义规则对所有导出支持RULE-SET -v1.9.52
#### 2018.11.16
1. 去除特殊代理 -v1.9.51
2. 去除RuleSet的时间戳 -v1.9.51
#### 2018.11.14
1. Quantumult所有Rejection部分使用最新Rewrite实现1x1像素过滤 -v1.9.50
2. 替换支付宝红包 -v1.9.50
#### 2018.11.08
1. 修复SSEncrypt.module失效问题 -v1.9.47
2. 修复重复策略名无法导出问题 -v1.9.48
#### 2018.11.03
1. 修复Quantumult问题 -v1.9.45
#### 2018.11.01
1. 允许添加空节点 -v1.9.43
#### 2018.10.30
1. 修复Shadowsocks订阅识别问题 -v1.9.39
2. 修复Quantumult规则导出问题 -v1.9.40
3. 修复SurgeTF规则导出问题 -v1.9.40
#### 2018.10.29
1. 调整Quantumult自定义规则位置于GEOIP,CN上面 -v1.9.38
#### 2018.10.27
1. 修复Quantumult所有LAN规则没有设置为DIRECT的问题 -v1.9.37
#### 2018.10.22
1. 修正备份问题 -v1.9.36
#### 2018.10.18
1. 恢复占位符重命名 -v1.9.35
1. 代理分组输入框提供所有占位符名称选择 -v1.9.35
#### 2018.10.17
1. 修复动画资源过大问题 -v1.9.34
#### 2018.10.15
1. 小组件流量显示调整,设置时等号两边可以留空格 -v1.9.33
#### 2018.10.14
1. 小组件规则更新信息根据APP显示 -v1.9.32
#### 2018.10.10
1. 修复Shadowsocks链接导入错误 -v1.9.30
2. 修复Shadowsocks订阅在Quantumult不生成的问题 -v1.9.31
#### 2018.10.05
1. 调整调试状态栏显示状态下UI被遮挡的问题 -v1.9.28
#### 2018.10.03
1. 替换占位符操作“重命名”为“删除节点” -v1.9.26
2. 修复ssid策略组名称问题 -v1.9.27
#### 2018.10.01
1. 调整RULE-SET中的URL添加时间戳强制刷新 -v1.9.24
2. 支持添加类型为round-robin的策略组配置如select对应Quantumult轮询 -v1.9.24
3. 修复规则替换问题 -v1.9.25
#### 2018.09.29
1. 生成Quantumult规则时自动生成对应的favorite -v1.9.22
2. 增加列表下拉更新节点 -v1.9.23
#### 2018.09.28
1. 修复Quantumult无法导出问题 -v1.9.15
2. 增加升级重启逻辑,仅在需要时重启脚本 -v1.9.16
3. 修复SSID设置失效问题 -v1.9.17
4. 增加导出Surge3 TF格式规则 -v1.9.18
5. 修复图标问题 -v1.9.19
6. 修复Bug -v1.9.21
#### 2018.09.25
1. 修复特殊代理设置后无法生成配置文件问题 -v1.9.13
#### 2018.09.24
1. 调整列表样式 -v1.9.12
#### 2018.09.20
1. 修复特殊代理设置无效问题 -v1.9.11
#### 2018.09.19
1. 调整Quantumult规则 -v1.9.9
#### 2018.09.16
1. 调整ss链接识别 -v1.9.5
2. 更改Quantulult导出的规则为Jacky Y维护的规则 -v1.9.7
3. 增加备份文件选择后的提示 -v1.9.7
#### 2018.09.13
1. 修复首次打开无法导入节点问题 -v1.9.4
2. 允许空白分组并在生成时清除其group信息 -v1.9.4
#### 2018.09.11
1. 新添加节点显示在最顶部 -v1.9.3
2. 修复v2ray订阅不生成问题 -v1.9.3
#### 2018.09.10
1. 支持导入v2订阅Quantumult和v2rayN去除对Shadowrocket和Kitsunebi的vmess格式支持 -v1.9.1
2. v2rayN的vmess默认加密方式改为chacha20-ietf-poly1305 -1.9.2
#### 2018.08.30
1. 修复bug -v1.8.4
#### 2018.08.28
1. 修复加载慢或无法加载的问题 -v1.8.2
#### 2018.08.26
1. 修复Bug -v1.7.89
2. 适配Quantumult的SSID suspend -v1.8.1
#### 2018.08.23
1. 调整导航栏样式 -v1.7.82
2. 增加Loading动画 -v1.7.83
#### 2018.08.22
1. 允许定义部分UI主界面和进阶设置颜色且不会因更新覆盖 -v1.7.80
#### 2018.08.21
1. 增加从Quantumult导出节点导出文件是选择JSBox即可 -v1.7.76
2. 恢复SSID设置 -1.7.77
#### 2018.08.20
1. 增加小组件流量显示自定义 -v1.7.75
#### 2018.08.19
1. 修复SSR订阅识别Quantumult无法联网问题 -v1.7.73
2. 修复Bug -v1.7.74
#### 2018.08.18
1. 增加节点编辑,去除重命名 -v1.7.71
2. 兼容某些不合规范的订阅 -v1.7.72
#### 2018.08.17
1. 特殊代理设置会在脚本启动时从Github更新地址https://github.com/Fndroid/specialReg -v1.7.70
#### 2018.08.13
1. 更新默认General配置更新需要清空进阶设置里的“常规” -v1.7.69
#### 2018.08.12
1. Emoji功能独立开关 -v1.7.66
2. 修复更新节点时批量节点被恢复问题 -v1.7.66
3. 部分机场订阅不规范无法识别问题 -v1.7.67
#### 2018.08.11
1. Emoji更新可以对所有节点生效 -v1.7.64
2. 修复bug -v1.7.65
#### 2018.08.09
1. 修复SSR转换时模块丢失问题 -v1.7.63
#### 2018.08.07
1. SSR链接导出到Surge时仅支持plain/origin兼容模式 -v1.7.62
2. 订阅识别不支持混合类型,仅仅会读取首个节点类型,不同的将被抛弃 -v1.7.62
#### 2018.08.06
1. 增加识别external类型Surge链接 -v1.7.61
2. 导出为Surge时尝试将链接转换为ss链接 -v1.7.61
#### 2018.08.05
1. 更新节点列表时可以带上国旗Emoji -v1.7.60
#### 2018.08.03
1. 修复拓展选择时注释被误识别的问题 -v1.7.59
2. 调整下载逻辑 -v1.7.59
#### 2018.08.01
1. 修复Quantumult添加后缀被误识别为混淆参数的问题 -v1.7.58
#### 2018.07.30
1. 增加后缀source-type=nn合法值为0-7用于Quantumult导出时设置订阅的OPTION选项 -v1.7.57
2. 增加307重定向识别 -v1.7.57
#### 2018.07.28
1. 修复某些设备小组件字体被遮挡的问题 -v1.7.56
#### 2018.07.27
1. 增加识别Shadowsocks订阅 -v1.7.54
2. 修复某些机场不能显示流量问题 -v1.7.54
3. 增加节点更新/识别失败提示 -v1.7.55
#### 2018.07.25
1. 调整小组件流量显示样式 -v1.7.52
2. 修复流量信息和组名不对应问题 -v1.7.53
#### 2018.07.24
1. 修复一个很难触发的Bug -v1.7.51
#### 2018.07.23
1. 界面调整 -v1.7.50
#### 2018.07.22
1. Quantumult调整REJECT规则部分解决某些广告不能屏蔽问题如腾讯视频 -v1.7.47
2. 删除昨晚加班解决的问题 -v1.7.48
3. 修复小Bug -v1.7.49
#### 2018.07.21
1. 修复Quantumult导出时自定义URLRewrite或URLReject丢失问题 -v1.7.44
2. 修复Quantumult导出时因节点名称不合法而丢弃的问题 -v1.7.45
#### 2018.07.20
1. 关于页面增加托管Bot入口 -v1.7.41
2. 长按“生成配置”按钮可以快速导出data.js文件 -v1.7.41
3. 小组件会根据配置使用Quantumult支持的流量显示方式显示流量信息 -v1.7.42
4. 调整小组件样式 -v1.7.43
#### 2018.07.19
1. 更新托管文件名获取逻辑 -v1.7.40
#### 2018.07.18
1. 调整小组件和启动器区分逻辑 -v1.7.38
2. 调整主界面UI -v1.7.38
3. 区分Surge2/3的图标 -v1.7.39
#### 2018.07.17
1. 生成Quantumult配置时如检测到SSR链接则在Favorite里生成对应订阅 -v1.7.37
#### 2018.07.16
1. 配置文件增加覆盖功能 -v1.7.36
#### 2018.07.15
1. 支持识别Kitsunebi和Shadowrocket的V2Ray链接 - v1.7.35
#### 2018.07.13
1. 修复SSR托管无法识别问题 - v1.7.32
2. 增加更新/导入识别结果提示 - v1.7.32
3. 增加都某些机场不规范订阅的识别 -v1.7.33
4. 调整节点列表样式 -v1.7.33
#### 2018.07.12
1. 导入Quantumult显示分组vmess除外 - v1.7.30
2. 支持识别SSR订阅和批量导入 - v1.7.31
3. 支持识别V2Ray订阅和批量导入 - v1.7.31
#### 2018.07.10
1. Quantumult最新TF已经修正obfs-host问题所以删除hack逻辑 - v1.7.28
2. 测试识别V2Ray链接vmess:// - v1.7.29
#### 2018.07.09
1. 修复下滑问题 - v1.7.27
2. 修复Quantumult无法获取配置DNS问题 - v1.7.27
3. 去除URL Scheme导出filter和rejection的特性因为Quantumult无法识别里面的Policy - v1.7.27
4. 支持使用特殊占位符``WidgetHeader``来配置Quantumult的Widget节点 - v1.7.27
#### 2018.07.08
1. hack解决圈obfs-host识别问题 - v1.7.26
2. 修复占位符会替换策略组名问题(占位符不要与节点名冲突)- v1.7.26
3. 增加Quantumult小组件开关 - v1.7.26
4. 导出为Quantumult时不选择导出则只更新filter和rejection两部分 - v1.7.26
#### 2018.07.07
1. 支持导出为Quantumult格式配置beta测试前请备份配置先 - v1.7.25
#### 2018.07.06
1. Surge开关使用最新API判断可不预先设置 - v1.7.24
#### 2018.07.05
1. 调整Surge开关状态逻辑如不能显示请反馈 - v1.7.22
2. Surge开关状态需要先进行初始设置长按图标即可 - v1.7.23
#### 2018.07.03
1. 小组件点击逻辑不变但会显示Surge开关状态
#### 2018.06.30
1. 修复Surge2不开启MITM时报错的问题
#### 2018.06.26
1. 增加占位符重命名
2. 修复批量导入节点在更新时会重复出现的问题
#### 2018.06.24
1. 关键字删除时,可以使用正则表达式,但除了``\s``(空格),这是分隔符
#### 2018.06.23
1. 列表不选择时默认为DIRECT改为默认全选
#### 2018.06.22
1. 调整右上角按钮,分别改为支付宝红包领取和备份当前配置
2. 调整UI
3. 备份改为存储到iCloud
4. 修复可以创建空占位符问题
#### 2018.06.21
1. ``// replacement``注释现在将会替换除General、Proxy Group和MITM的CA部分替换规则不需要重新生成证书外的其他内容
2. 修复部分情况下hostname为空的错误
#### 2018.06.20
1. 脚本右上角增加导出data.js文件选项方便后续生成订阅
#### 2018.06.19
1. 修复完成按钮无法保存后缀问题
#### 2018.06.18
1. rename注释可以识别等号但需要使用转义``\=``,每个替换的左边可以是一个正则表达式,右边为替换字符串
2. 适配新版TF脚本主界面右上角可以直接进入Surge
#### 2018.06.17
1. 直接导出到Surge的时间设置为10s10s后会关闭开启的内置服务器防止耗电过多问题
#### 2018.06.16
1. 为保证小组件快捷生成成功率取消生成后回到JSBox自动退出功能添加按钮手动操作
2. 小组件支持显示规则替换仓库信息仅支持Github托管master分支的配置
3. 修复rename和replacement注释因边界无法识别问题
#### 2018.06.15
1. 支持重命名默认策略组,需要在进阶设置中代理分组中添加注释:// rename: o1 = n1, o2 = n2
2. 支持替换lhie1的所有规则可以配合分组代理加载其他规则用法在进阶设置中代理规则添加注释// replacement: https://xxxx
#### 2018.06.14
1. 支持Surge2未测试因为我没有Surge2
2. 修复Surge2的跳转问题
#### 2018.06.13
1. 更新脚本简介
#### 2018.06.12
1. 更新方式改为静默更新,有新版本将不会再弹出提示更新而在后台静默更新,更新后可不重启脚本生成规则,修复因更新脚本造成打断操作而产生心里阴影面积过大的问题
2. 修复ProxyHeader占位符丢失问题
3. 去除重复后缀
4. 先进行占位符替换避免影响General的内容占位符命名请不要与Proxy Group里面的内容相同
#### 2018.06.11
1. 调整界面
2. 去除UPD设置选项增加节点后缀设置可用于UDP、TFO和插件设置
3. 修复undefined问题
#### 2018.06.10
1. 生成配置时检查特殊代理设置服务器是否存在,不存在则忽略避免报错
2. 删除**批量Auto**选项
3. 增加**策略组别**选项用于生成用于进阶设置的占位符和ProxyHeader类似
4. 修复无法删除策略组占位符问题
5. 增加特殊代理可选择策略组功能
6. 修复特殊代理direct无效问题
如担心数据丢失可对脚本内data.js文件进行备份
#### 2018.06.07
1. 修复脚本名称更改后升级和小组件异常问题
#### 2018.06.06
1. 删除导入节点时对组别进行保存的功能,因为有列表更新,所以貌似也没什么用
2. 默认给所有节点打开tfo开关明天做
#### 2018.06.03
1. 删除分组时可以选择按照关键字删除
2. 修复单个组别无法进行关键字删除的问题
#### 2018.06.02
1. 小组件/启动器在规则有更新时显示最新的commit message
#### 2018.05.27
1. 适配两种奇葩的机场托管节点识别(分别是在节点前面加空格和节点后面加逗号)
2. 修复部分ss节点无法识别问题
3. 修复windows下Shadowsocks二维码无法识别问题
4. 打开小组件或启动器时同时检查脚本和规则更新
#### 2018.05.26
1. 修复obfs识别问题
2. 调整更新等待时间
3. 区分启动器和小组件样式,启动器加入关闭按钮(性能模式启动器有效)
#### 2018.05.25
1. 修正不添加节点不能导出规则问题
2. 调整启动器启动样式,启动后点击上下两部分空白可退出脚本
#### 2018.05.22
1. 调整Widget样式
#### 2018.05.15
1. 增加一个特性在Surge里面将配置文件.conf分享到脚本脚本会将当前配置的自定义规则通过Surge的Extension添加的追加到进阶设置里。
> 1的识别的配置文件需要时脚本生成的且进阶设置的代理规则包括``# Custom``注释
#### 2018.05.13
1. 调整导入节点表现,当列表中已有该组时,会更新已有组
2. 导入节点增加“更新节点列表”选项,用于升级列表中所有组
> 上述12仅对1.6.19及以后导入的组别生效
#### 2018.05.12
1. 调整Widget样式去除启动器启动时的导航栏
2. 去除输入法上方的提示栏
3. 修复定义ProxyHeader失败问题
#### 2018.05.11
1. 增加对http/https/socks5/socks5-tls代理类型的支持统一理解为Surge链接
2. 增强剪贴板导入以行为单位支持批量ss链接、Surge链接和托管地址混合导入
#### 2018.05.10
1. 修复Safari拓展节点消失问题
2. 优化拉取速度,更新规则耗时更短
3. 当节点列表只有一个分组时节点倒序、批量Auto和删除节点将不会弹出菜单直接操作当前唯一组
#### 2018.05.09
1. 调整UI
2. 增加组别支持每次导入的节点会被分为一组可以根据组别进行倒序、Auto和删除操作
3. 取消对``固定脚本``的支持,列表外上滑将不再减少高度
4. 节点重命名选项可以对所在组别进行重命名
> 由于列表结构改变,原本节点列表的数据会丢失,请重新导入
#### 2018.05.08
1. 增加Safari拓展在页面添加规则
2. 修复图标过大导致JSBox启动器无法载入问题
3. 调整``全部Auto``,如果当前已经全选,则全部反选一次
4. 调整``导入节点``,如果列表中已经存在,则不会重复显示
5. 修复自动生成时选择取消Action Sheet时脚本不退出问题
#### 2018.05.07
1. 增加小组件支持
2. 修复批量导入时不能识别``\r``换行的问题
3. 自动生成后回到JSBox自动退出脚本
4. 特殊代理增加单独查看、清除按钮支持选择Direct
5. 调整小组件按钮位置,规则无更新时下方小字不再显示
#### 2018.05.06
1. .conf的所有配置项现在都可以在进阶配置中定义大部分设置可以使用``-(减号)``对lhie1配置剔除
2. 内部代码调整,异常请反馈
3. 部分UI调整
4. 增加特殊代理设置,左滑节点即可设置
5. 文档修正,感谢**Mornwind**的PR
#### 2018.05.05
1. 增加去自定义规则删除(优先级低于添加),写法:
![](https://github.com/Fndroid/jsbox_script/blob/master/imgs/Rules-lhie1_pre2.jpg?raw=true)
2. 修复进阶设置Gallery切换按钮不联动的问题
3. 增加关于页面
感谢**Mornwind、几位匿名赞赏者**的咖啡
#### 2018.05.04
1. 检测文件名不合法则采用Action Sheet分享
2. 由于无法检测脚本是否在``固定脚本``中运行,所以增加了上下滑动列表高度变化,虽然很突兀,但是没办法
3. 增加节点重命名和排序
4. 进阶设置界面调整DNS设置改为GeneralMITM默认为lhie1的不开启则为空
5. 修正Surge3跳转
6. 增加URL Scheme支持添加``auto=1``即可根据配置自动生成一次规则
#### 2018.05.03
1. 增加直接导出到Surge功能但有限制进入Surge后不要等待过长时间确定安装
2. 更改TestFlight选项名为UDP
3. 尝试突破Surge加载问题
4. 修复托管direct多余问题
感谢**Nicked、Mornwind**的咖啡
#### 2018.05.02
1. 增加批量导入ss节点剪贴板
2. 增加批量导入Surge节点剪贴板
3. 增加生成的过渡动画代替默认的loading
4. 修复TINYGIF的问题
5. 增加配置Proxy Group``ProxyHeader``为Auto节点名``Proxy Header``为所有节点名可更改Auto设置
6. 删除导入的相册选择选项如需此操作请更新JSBox
感谢**Wangsc1 M**的咖啡
#### 2018.05.01
1. 跳转到主应用运行
2. 调整界面(节点操作栏不再跟随滚动,开关说明)
3. 解除底部按钮固定适配iPhoneX屏幕
> 无法运行无解,我无法复现...
#### 2018.04.30 A
1. 每次退出脚本先保存环境,下次进入可以直接使用
2. 修改图标
3. 支持导入Surge节点
4. 修复``墙洞``托管无法识别问题
> 仅测试了cordcloud和rixCloud的托管导入其他如果不能导入的要不把托管借我测试再改密要不等别人借我测试
> 脚本无法启动问题已经提交JSBox作者调试
#### 2018.04.30
1. 修复TF无效节点无udp-relay=true的问题
2. 修复升级日志遮挡问题
3. 由于``$addin.save``无回调增加2s防止极端情况更新失败
4. 调整界面去掉Auto显示框改为列表显示形式
5. 增加``MITM``、``DNS``和``Rule Custom``设置
6. 修正配置可能丢失的问题
7. 修复输入框遮挡问题
8. 修正配置文件与wf部分区别
9. 增加MITM开关
> iOS 10.3.3如果能进入脚本请TG和我说一下
#### 2018.04.29
1. 不选Auto时Auto则为DIRECT生成后请手动选择Proxy
2. 保存的``托管``或``ss://``改为以名称显示(可能不能很好兼容上版本)
3. 增加倒序和全部添加到Auto功能
3. 调整界面大小
4. 修正无法识别托管文件名的问题

@ -61,18 +61,18 @@ Quantumult | [@Jacky Y](https://t.me/WatanabeMayu) | [Quantumult](https://t.me/q
---
### JSBox
### Subscription Converter
````
Surgehttps://xteko.com/redir?name=Rules-lhie1&url=https://raw.githubusercontent.com/lhie1/Rules/master/JSBox/Rules-lhie1.box
https://sub.dleris.best
````
---
### Remote Files
### Shadowrocket (Remote Files)
````
Shadowrockethttps://raw.githubusercontent.com/lhie1/Rules/master/Shadowrocket/Complete.conf
https://raw.githubusercontent.com/lhie1/Rules/master/Shadowrocket/Complete.conf
````
---

Loading…
Cancel
Save