|
|
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
|
|
|
}
|