| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102 |
- /**
- * 根据自己的习惯整合各个开发者而形成的工具包(@NobyDa, @chavyleung)
- * 兼容surge,quantumult x,loon,node环境
- * 并且加入一些好用的方法
- * 方法如下:
- * isEmpty: 判断字符串是否是空(undefined,null,空串)
- * getRequestUrl: 获取请求的url(目前仅支持surge和quanx)
- * getResponseBody: 获取响应体(目前仅支持surge和quanx)
- * boxJsJsonBuilder:构建最简默认boxjs配置json
- * randomString: 生成随机字符串
- * autoComplete: 自动补齐字符串
- * customReplace: 自定义替换
- * hash: 字符串做hash
- *
- * ⚠️当开启当且仅当执行失败的时候通知选项,请在执行失败的地方执行execFail()
- *
- * @param scriptName 脚本名,用于通知时候的标题
- * @param scriptId 每个脚本唯一的id,用于存储持久化的时候加入key
- * @param options 传入一些参数,目前参数如下;
- * [email protected]:6166(这个是默认值,本人surge调试脚本用,可自行修改)
- * target_boxjs_json_path=/Users/lowking/Desktop/Scripts/lowking.boxjs.json(生成boxjs配置的目标文件路径)
- * @constructor
- */
- function ToolKit(scriptName, scriptId, options) {
- return new (class {
- constructor(scriptName, scriptId, options) {
- this.tgEscapeCharMapping = { '&': '&', '#': '#' }
- this.userAgent = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15`
- this.prefix = `lk`
- this.name = scriptName
- this.id = scriptId
- this.data = null
- this.dataFile = this.getRealPath(`${this.prefix}${this.id}.dat`)
- this.boxJsJsonFile = this.getRealPath(`${this.prefix}${this.id}.boxjs.json`)
- //surge http api等一些扩展参数
- this.options = options
- //命令行入参
- this.isExecComm = false
- //默认脚本开关
- this.isEnableLog = this.getVal(`${this.prefix}IsEnableLog${this.id}`)
- this.isEnableLog = this.isEmpty(this.isEnableLog) ? true : JSON.parse(this.isEnableLog)
- this.isNotifyOnlyFail = this.getVal(`${this.prefix}NotifyOnlyFail${this.id}`)
- this.isNotifyOnlyFail = this.isEmpty(this.isNotifyOnlyFail) ? false : JSON.parse(this.isNotifyOnlyFail)
- //tg通知开关
- this.isEnableTgNotify = this.getVal(`${this.prefix}IsEnableTgNotify${this.id}`)
- this.isEnableTgNotify = this.isEmpty(this.isEnableTgNotify) ? false : JSON.parse(this.isEnableTgNotify)
- this.tgNotifyUrl = this.getVal(`${this.prefix}TgNotifyUrl${this.id}`)
- this.isEnableTgNotify = this.isEnableTgNotify ? !this.isEmpty(this.tgNotifyUrl) : this.isEnableTgNotify
- //计时部分
- this.costTotalStringKey = `${this.prefix}CostTotalString${this.id}`
- this.costTotalString = this.getVal(this.costTotalStringKey)
- this.costTotalString = this.isEmpty(this.costTotalString) ? `0,0` : this.costTotalString.replace("\"", "")
- this.costTotalMs = this.costTotalString.split(",")[0]
- this.execCount = this.costTotalString.split(",")[1]
- this.costTotalMs = this.isEmpty(this.costTotalMs) ? 0 : parseInt(this.costTotalMs)
- this.execCount = this.isEmpty(this.execCount) ? 0 : parseInt(this.execCount)
- this.logSeparator = '\n██'
- this.now = new Date()
- this.startTime = this.now.getTime()
- this.node = (() => {
- if (this.isNode()) {
- const request = require('request')
- return ({ request })
- } else {
- return (null)
- }
- })()
- this.execStatus = true
- this.notifyInfo = []
- this.log(`${this.name}, 开始执行!`)
- this.initCache()
- this.checkRecordRequestBody()
- this.execComm()
- }
- checkRecordRequestBody() {
- if (!this.isRequest()) {
- return;
- }
- const reqBody = $request.body;
- if (!reqBody) {
- return;
- }
- const path = $request.path;
- let cacheKey = this.id + "#" + path.replace("/", "_");
- cacheKey = cacheKey.replace("?", "#");
- if (this.isQuanX()) $prefs.setValueForKey(reqBody, cacheKey);
- if (this.isLoon() || this.isSurge()) $persistentStore.write(reqBody, cacheKey);
- if (this.isNode()) {
- this.node.fs.writeFileSync(
- `${cacheKey}.json`,
- reqBody,
- {
- flag: "w"
- },
- (err) => console.log(err)
- );
- }
- }
- getRequestBody() {
- const path = $request.path;
- let cacheKey = this.id + "#" + path.replace("/", "_");
- cacheKey = cacheKey.replace("?", "#");
- if (this.isSurge() || this.isLoon()) {
- return $persistentStore.read(cacheKey);
- }
- if (this.isQuanX()) {
- return $prefs.valueForKey(cacheKey);
- }
- if (this.isNode()) {
- const fpath = `${cacheKey}.json`;
- if (!this.node.fs.existsSync(fpath)) {
- return JSON.parse(
- this.node.fs.readFileSync(fpath)
- );
- }
- }
- }
- // persistence
- // initialize cache
- initCache() {
- const pKey = this.getPersistKey();
- if (this.isQuanX()) this.cache = JSON.parse($prefs.valueForKey(pKey) || "{}");
- if (this.isLoon() || this.isSurge())
- this.cache = JSON.parse($persistentStore.read(pKey) || "{}");
- if (this.isNode()) {
- // create a json for root cache
- let fpath = "root.json";
- if (!this.node.fs.existsSync(fpath)) {
- this.node.fs.writeFileSync(
- fpath,
- JSON.stringify({}),
- {
- flag: "wx"
- },
- (err) => console.log(err)
- );
- }
- this.root = {};
- // create a json file with the given name if not exists
- fpath = `${pKey}.json`;
- if (!this.node.fs.existsSync(fpath)) {
- this.node.fs.writeFileSync(
- fpath,
- JSON.stringify({}),
- {
- flag: "wx"
- },
- (err) => console.log(err)
- );
- this.cache = {};
- } else {
- this.cache = JSON.parse(
- this.node.fs.readFileSync(`${pKey}.json`)
- );
- }
- }
- }
- getPersistKey() {
- return `${this.id}#privateCache`;
- }
- // store cache
- persistCache() {
- const pKey = this.getPersistKey();
- const data = JSON.stringify(this.cache, null, 2);
- if (this.isQuanX()) $prefs.setValueForKey(data, pKey);
- if (this.isLoon() || this.isSurge()) $persistentStore.write(data, pKey);
- if (this.isNode()) {
- this.node.fs.writeFileSync(
- `${pKey}.json`,
- data,
- {
- flag: "w"
- },
- (err) => console.log(err)
- );
- this.node.fs.writeFileSync(
- "root.json",
- JSON.stringify(this.root, null, 2),
- {
- flag: "w"
- },
- (err) => console.log(err)
- );
- }
- }
- write(data, key) {
- this.log(`SET ${key}`);
- if (key.indexOf("#") !== -1) {
- key = key.substr(1);
- if (isSurge || this.isLoon()) {
- return $persistentStore.write(data, key);
- }
- if (this.isQuanX()) {
- return $prefs.setValueForKey(data, key);
- }
- if (this.isNode()) {
- this.root[key] = data;
- }
- } else {
- this.cache[key] = data;
- }
- this.persistCache();
- }
- read(key) {
- this.log(`READ ${key}`);
- if (key.indexOf("#") !== -1) {
- key = key.substr(1);
- if (this.isSurge() || this.isLoon()) {
- return $persistentStore.read(key);
- }
- if (this.isQuanX()) {
- return $prefs.valueForKey(key);
- }
- if (this.isNode()) {
- return this.root[key];
- }
- } else {
- return this.cache[key];
- }
- }
- delete(key) {
- this.log(`DELETE ${key}`);
- if (key.indexOf("#") !== -1) {
- key = key.substr(1);
- if (this.isSurge() || this.isLoon()) {
- return $persistentStore.write(null, key);
- }
- if (this.isQuanX()) {
- return $prefs.removeValueForKey(key);
- }
- if (this.isNode()) {
- delete this.root[key];
- }
- } else {
- delete this.cache[key];
- }
- this.persistCache();
- }
- //当执行命令的目录不是脚本所在目录时,自动把文件路径改成指令传入的路径并返回完整文件路径
- getRealPath(fileName) {
- if (this.isNode()) {
- let targetPath = process.argv.slice(1, 2)[0].split("/")
- targetPath[targetPath.length - 1] = fileName
- return targetPath.join("/")
- }
- return fileName
- }
- /**
- * http://boxjs.com/ => http://boxjs.com
- * http://boxjs.com/app/jd => http://boxjs.com
- */
- getUrlHost(url) {
- return url.slice(0, url.indexOf('/', 8))
- }
- /**
- * http://boxjs.com/ =>
- * http://boxjs.com/api/getdata => /api/getdata
- */
- getUrlPath(url) {
- // 如果以结尾, 去掉最后一个/
- const end = url.lastIndexOf('/') === url.length - 1 ? -1 : undefined
- // slice第二个参数传 undefined 会直接截到最后
- // indexOf第二个参数用来跳过前面的 "https://"
- return url.slice(url.indexOf('/', 8), end)
- }
- async execComm() {
- //支持node命令,实现发送手机测试
- if (this.isNode()) {
- this.comm = process.argv.slice(1)
- let isHttpApiErr = false
- if (this.comm[1] == "p") {
- this.isExecComm = true
- //phone
- this.log(`开始执行指令【${this.comm[1]}】=> 发送到手机测试脚本!`);
- if (this.isEmpty(this.options) || this.isEmpty(this.options.httpApi)) {
- this.log(`未设置options,使用默认值`)
- //设置默认值
- if (this.isEmpty(this.options)) {
- this.options = {}
- }
- this.options.httpApi = `[email protected]:6166`
- } else {
- //判断格式
- if (!/.*?@.*?:[0-9]+/.test(this.options.httpApi)) {
- isHttpApiErr = true
- this.log(`❌httpApi格式错误!格式:[email protected]:6166`)
- this.done()
- }
- }
- if (!isHttpApiErr) {
- this.callApi(this.comm[2])
- }
- }
- }
- }
- callApi(timeout) {
- // 直接用接收到文件路径,解决在不同目录下都可以使用 node xxxx/xxx.js p 指令发送脚本给手机执行
- // let fname = this.getCallerFileNameAndLine().split(":")[0].replace("[", "")
- let fname = this.comm[0]
- this.log(`获取【${fname}】内容传给手机`)
- let scriptStr = ''
- this.fs = this.fs ? this.fs : require('fs')
- this.path = this.path ? this.path : require('path')
- const curDirDataFilePath = this.path.resolve(fname)
- const rootDirDataFilePath = this.path.resolve(process.cwd(), fname)
- const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
- const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
- if (isCurDirDataFile || isRootDirDataFile) {
- const datPath = isCurDirDataFile ? curDirDataFilePath : rootDirDataFilePath
- try {
- scriptStr = this.fs.readFileSync(datPath)
- } catch (e) {
- scriptStr = ''
- }
- } else {
- scriptStr = ''
- }
- let options = {
- url: `http://${this.options.httpApi.split("@")[1]}/v1/scripting/evaluate`,
- headers: {
- "X-Key": `${this.options.httpApi.split("@")[0]}`
- },
- body: {
- "script_text": `${scriptStr}`,
- "mock_type": "cron",
- "timeout": (!this.isEmpty(timeout) && timeout > 5) ? timeout : 5
- },
- json: true
- }
- this.post(options, (_error, _response, _data) => {
- this.log(`已将脚本【${fname}】发给手机!`)
- this.done()
- })
- }
- getCallerFileNameAndLine() {
- let error
- try {
- throw Error('')
- } catch (err) {
- error = err
- }
- const stack = error.stack
- const stackArr = stack.split('\n')
- let callerLogIndex = 1
- if (callerLogIndex !== 0) {
- const callerStackLine = stackArr[callerLogIndex]
- this.path = this.path ? this.path : require('path')
- return `[${callerStackLine.substring(callerStackLine.lastIndexOf(this.path.sep) + 1, callerStackLine.lastIndexOf(':'))}]`
- } else {
- return '[-]'
- }
- }
- getFunName(fun) {
- var ret = fun.toString()
- ret = ret.substr('function '.length)
- ret = ret.substr(0, ret.indexOf('('))
- return ret
- }
- boxJsJsonBuilder(info, param) {
- if (this.isNode()) {
- let boxjsJsonPath = "/Users/lowking/Desktop/Scripts/lowking.boxjs.json"
- // 从传入参数param读取配置的boxjs的json文件路径
- if (param && param.hasOwnProperty("target_boxjs_json_path")) {
- boxjsJsonPath = param["target_boxjs_json_path"]
- }
- if (!this.fs.existsSync(boxjsJsonPath)) {
- return
- }
- if (!this.isJsonObject(info) || !this.isJsonObject(param)) {
- this.log("构建BoxJsJson传入参数格式错误,请传入json对象")
- return
- }
- this.log('using node')
- let needAppendKeys = ["settings", "keys"]
- const domain = 'https://raw.githubusercontent.com/Orz-3'
- let boxJsJson = {}
- let scritpUrl = '#lk{script_url}'
- if (param && param.hasOwnProperty('script_url')) {
- scritpUrl = this.isEmpty(param['script_url']) ? "#lk{script_url}" : param['script_url']
- }
- boxJsJson.id = `${this.prefix}${this.id}`
- boxJsJson.name = this.name
- boxJsJson.desc_html = `⚠️使用说明</br>详情【<a href='${scritpUrl}?raw=true'><font class='red--text'>点我查看</font></a>】`
- boxJsJson.icons = [`${domain}/mini/master/Alpha/${this.id.toLocaleLowerCase()}.png`, `${domain}/mini/master/Color/${this.id.toLocaleLowerCase()}.png`]
- boxJsJson.keys = []
- boxJsJson.settings = [
- {
- "id": `${this.prefix}IsEnableLog${this.id}`,
- "name": "开启/关闭日志",
- "val": true,
- "type": "boolean",
- "desc": "默认开启"
- },
- {
- "id": `${this.prefix}NotifyOnlyFail${this.id}`,
- "name": "只当执行失败才通知",
- "val": false,
- "type": "boolean",
- "desc": "默认关闭"
- },
- {
- "id": `${this.prefix}IsEnableTgNotify${this.id}`,
- "name": "开启/关闭Telegram通知",
- "val": false,
- "type": "boolean",
- "desc": "默认关闭"
- },
- {
- "id": `${this.prefix}TgNotifyUrl${this.id}`,
- "name": "Telegram通知地址",
- "val": "",
- "type": "text",
- "desc": "Tg的通知地址,如:https://api.telegram.org/bot-token/sendMessage?chat_id=-100140&parse_mode=Markdown&text="
- }
- ]
- boxJsJson.author = "#lk{author}"
- boxJsJson.repo = "#lk{repo}"
- boxJsJson.script = `${scritpUrl}?raw=true`
- // 除了settings和keys追加,其他的都覆盖
- if (!this.isEmpty(info)) {
- for (let i in needAppendKeys) {
- let key = needAppendKeys[i]
- if (!this.isEmpty(info[key])) {
- // 处理传入的每项设置
- if (key === 'settings') {
- for (let i = 0; i < info[key].length; i++) {
- let input = info[key][i]
- for (let j = 0; j < boxJsJson.settings.length; j++) {
- let def = boxJsJson.settings[j]
- if (input.id === def.id) {
- // id相同,就使用外部传入的配置
- boxJsJson.settings.splice(j, 1)
- }
- }
- }
- }
- boxJsJson[key] = boxJsJson[key].concat(info[key])
- }
- delete info[key]
- }
- }
- Object.assign(boxJsJson, info)
- if (this.isNode()) {
- this.fs = this.fs ? this.fs : require('fs')
- this.path = this.path ? this.path : require('path')
- const curDirDataFilePath = this.path.resolve(this.boxJsJsonFile)
- const rootDirDataFilePath = this.path.resolve(process.cwd(), this.boxJsJsonFile)
- const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
- const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
- const jsondata = JSON.stringify(boxJsJson, null, '\t')
- if (isCurDirDataFile) {
- this.fs.writeFileSync(curDirDataFilePath, jsondata)
- } else if (isRootDirDataFile) {
- this.fs.writeFileSync(rootDirDataFilePath, jsondata)
- } else {
- this.fs.writeFileSync(curDirDataFilePath, jsondata)
- }
- // 写到项目的boxjs订阅json中
- let boxjsJson = JSON.parse(this.fs.readFileSync(boxjsJsonPath))
- if (boxjsJson.hasOwnProperty("apps") && Array.isArray(boxjsJson["apps"]) && boxjsJson["apps"].length > 0) {
- let apps = boxjsJson.apps
- let targetIdx = apps.indexOf(apps.filter((app) => {
- return app.id == boxJsJson.id
- })[0])
- if (targetIdx >= 0) {
- boxjsJson.apps[targetIdx] = boxJsJson
- } else {
- boxjsJson.apps.push(boxJsJson)
- }
- let ret = JSON.stringify(boxjsJson, null, 2)
- if (!this.isEmpty(param)) {
- for (const key in param) {
- let val = ''
- if (param.hasOwnProperty(key)) {
- val = param[key]
- } else if (key === 'author') {
- val = '@lowking'
- } else if (key === 'repo') {
- val = 'https://github.com/lowking/Scripts'
- }
- ret = ret.replace(`#lk{${key}}`, val)
- }
- }
- // 全部处理完毕检查是否有漏掉未配置的参数,进行提醒
- const regex = /(?:#lk\{)(.+?)(?=\})/
- let m = regex.exec(ret)
- if (m !== null) {
- this.log('生成BoxJs还有未配置的参数,请参考https://github.com/lowking/Scripts/blob/master/util/example/ToolKitDemo.js#L17-L18传入参数:\n')
- }
- let loseParamSet = new Set()
- while ((m = regex.exec(ret)) !== null) {
- loseParamSet.add(m[1])
- ret = ret.replace(`#lk{${m[1]}}`, ``)
- }
- loseParamSet.forEach(p => {
- console.log(`${p} `)
- })
- this.fs.writeFileSync(boxjsJsonPath, ret)
- }
- }
- }
- }
- isJsonObject(obj) {
- return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == "[object object]" && !obj.length
- }
- appendNotifyInfo(info, type) {
- if (type == 1) {
- this.notifyInfo = info
- } else {
- this.notifyInfo.push(info)
- }
- }
- prependNotifyInfo(info) {
- this.notifyInfo.splice(0, 0, info)
- }
- execFail() {
- this.execStatus = false
- }
- isRequest() {
- return typeof $request != "undefined"
- }
- isSurge() {
- return typeof $httpClient != "undefined"
- }
- isQuanX() {
- return typeof $task != "undefined"
- }
- isLoon() {
- return typeof $loon != "undefined"
- }
- isJSBox() {
- return typeof $app != "undefined" && typeof $http != "undefined"
- }
- isStash() {
- return 'undefined' !== typeof $environment && $environment['stash-version']
- }
- isNode() {
- return typeof require == "function" && !this.isJSBox()
- }
- async sleep(time) {
- return new Promise((resolve) => setTimeout(resolve, time))
- }
- async wait(time) {
- return sleep(time);
- }
- async delay(time) {
- return sleep(time);
- }
- log(message) {
- if (this.isEnableLog) console.log(`${this.logSeparator}${message}`)
- }
- logErr(message) {
- this.execStatus = true
- if (this.isEnableLog) {
- console.log(`${this.logSeparator}${this.name}执行异常:`)
- console.log(message)
- console.log('\n' + `${message.message}`)
- }
- }
- msg(subtitle, message, openUrl, mediaUrl) {
- if (!this.isRequest() && this.isNotifyOnlyFail && this.execStatus) {
- //开启了当且仅当执行失败的时候通知,并且执行成功了,这时候不通知
- } else {
- if (this.isEmpty(message)) {
- if (Array.isArray(this.notifyInfo)) {
- message = this.notifyInfo.join("\n")
- } else {
- message = this.notifyInfo
- }
- }
- if (!this.isEmpty(message)) {
- if (this.isEnableTgNotify) {
- this.log(`${this.name}Tg通知开始`)
- //处理特殊字符
- for (let key in this.tgEscapeCharMapping) {
- if (!this.tgEscapeCharMapping.hasOwnProperty(key)) {
- continue
- }
- message = message.replace(key, this.tgEscapeCharMapping[key])
- }
- this.get({
- url: encodeURI(`${this.tgNotifyUrl}📌${this.name}` + '\n' + `${message}`)
- }, (_error, _statusCode, _body) => {
- this.log(`Tg通知完毕`)
- })
- } else {
- let options = {}
- const hasOpenUrl = !this.isEmpty(openUrl)
- const hasMediaUrl = !this.isEmpty(mediaUrl)
- if (this.isQuanX()) {
- if (hasOpenUrl) options["open-url"] = openUrl
- if (hasMediaUrl) options["media-url"] = mediaUrl
- $notify(this.name, subtitle, message, options)
- }
- if (this.isSurge() || this.isStash()) {
- if (hasOpenUrl) options["url"] = openUrl
- $notification.post(this.name, subtitle, message, options)
- }
- if (this.isNode()) this.log("⭐️" + this.name + "\n" + subtitle + "\n" + message)
- if (this.isJSBox()) $push.schedule({
- title: this.name,
- body: subtitle ? subtitle + "\n" + message : message
- })
- }
- }
- }
- }
- pushWxMsg(summary, content, url, callback = () => { }) {
- let data = {
- appToken: "AT_rTc93GQYIdMU8XLRnoJaSea8WkfhSzhX",
- content: content,
- summary: summary,
- contentType: 1,
- topicIds: [],
- uids: [
- "UID_6P4B00X6Zv8U2oKC0I2R09emxtqq"
- ],
- url: "",
- verifyPay: false
- };
- if (url) {
- data.url = url;
- }
- const headers = this.getJsonDoneHeaders();
- headers.Host = 'wxpusher.zjiecode.com';
- headers['Content-Type'] = 'application/json;charset=UTF-8';
- let options = {
- url: 'https://wxpusher.zjiecode.com/api/send/message',
- headers: headers,
- body: JSON.stringify(data),
- };
- this.post(options, callback);
- }
- getVal(key, defaultValue = "") {
- let value
- if (this.isSurge() || this.isLoon() || this.isStash()) {
- value = $persistentStore.read(key)
- } else if (this.isQuanX()) {
- value = $prefs.valueForKey(key)
- } else if (this.isNode()) {
- this.data = this.loadData()
- value = process.env[key] || this.data[key]
- } else {
- value = (this.data && this.data[key]) || null
- }
- return !value ? defaultValue : value
- }
- setVal(key, val) {
- if (this.isSurge() || this.isLoon() || this.isStash()) {
- return $persistentStore.write(val, key)
- } else if (this.isQuanX()) {
- return $prefs.setValueForKey(val, key)
- } else if (this.isNode()) {
- this.data = this.loadData()
- this.data[key] = val
- this.writeData()
- return true
- } else {
- return (this.data && this.data[key]) || null
- }
- }
- loadData() {
- if (this.isNode()) {
- this.fs = this.fs ? this.fs : require('fs')
- this.path = this.path ? this.path : require('path')
- const curDirDataFilePath = this.path.resolve(this.dataFile)
- const rootDirDataFilePath = this.path.resolve(process.cwd(), this.dataFile)
- const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
- const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
- if (isCurDirDataFile || isRootDirDataFile) {
- const datPath = isCurDirDataFile ? curDirDataFilePath : rootDirDataFilePath
- try {
- return JSON.parse(this.fs.readFileSync(datPath))
- } catch (e) {
- return {}
- }
- } else return {}
- } else return {}
- }
- writeData() {
- if (this.isNode()) {
- this.fs = this.fs ? this.fs : require('fs')
- this.path = this.path ? this.path : require('path')
- const curDirDataFilePath = this.path.resolve(this.dataFile)
- const rootDirDataFilePath = this.path.resolve(process.cwd(), this.dataFile)
- const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
- const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
- const jsondata = JSON.stringify(this.data)
- if (isCurDirDataFile) {
- this.fs.writeFileSync(curDirDataFilePath, jsondata)
- } else if (isRootDirDataFile) {
- this.fs.writeFileSync(rootDirDataFilePath, jsondata)
- } else {
- this.fs.writeFileSync(curDirDataFilePath, jsondata)
- }
- }
- }
- adapterStatus(response) {
- if (response) {
- if (response.status) {
- response["statusCode"] = response.status
- } else if (response.statusCode) {
- response["status"] = response.statusCode
- }
- }
- return response
- }
- get(options, callback = () => { }) {
- if (this.isQuanX()) {
- if (typeof options == "string") options = {
- url: options
- }
- options["method"] = "GET"
- $task.fetch(options).then(response => {
- callback(null, this.adapterStatus(response), response.body)
- }, reason => callback(reason.error, null, null))
- }
- if (this.isSurge() || this.isLoon() || this.isStash()) $httpClient.get(options, (error, response, body) => {
- callback(error, this.adapterStatus(response), body)
- })
- if (this.isNode()) {
- this.node.request(options, (error, response, body) => {
- callback(error, this.adapterStatus(response), body)
- })
- }
- if (this.isJSBox()) {
- if (typeof options == "string") options = {
- url: options
- }
- options["header"] = options["headers"]
- options["handler"] = function (resp) {
- let error = resp.error
- if (error) error = JSON.stringify(resp.error)
- let body = resp.data
- if (typeof body == "object") body = JSON.stringify(resp.data)
- callback(error, this.adapterStatus(resp.response), body)
- }
- $http.get(options)
- }
- }
- post(options, callback = () => { }) {
- if (this.isQuanX()) {
- if (typeof options == "string") options = {
- url: options
- }
- options["method"] = "POST"
- $task.fetch(options).then(response => {
- callback(null, this.adapterStatus(response), response.body)
- }, reason => callback(reason.error, null, null))
- }
- if (this.isSurge() || this.isLoon() || this.isStash()) {
- $httpClient.post(options, (error, response, body) => {
- callback(error, this.adapterStatus(response), body)
- })
- }
- if (this.isNode()) {
- this.node.request.post(options, (error, response, body) => {
- callback(error, this.adapterStatus(response), body)
- })
- }
- if (this.isJSBox()) {
- if (typeof options == "string") options = {
- url: options
- }
- options["header"] = options["headers"]
- options["handler"] = function (resp) {
- let error = resp.error
- if (error) error = JSON.stringify(resp.error)
- let body = resp.data
- if (typeof body == "object") body = JSON.stringify(resp.data)
- callback(error, this.adapterStatus(resp.response), body)
- }
- $http.post(options)
- }
- }
- put(options, callback = () => { }) {
- if (this.isQuanX()) {
- // no test
- if (typeof options == "string") options = {
- url: options
- }
- options["method"] = "PUT"
- $task.fetch(options).then(response => {
- callback(null, this.adapterStatus(response), response.body)
- }, reason => callback(reason.error, null, null))
- }
- if (this.isSurge() || this.isLoon() || this.isStash()) {
- options.method = "PUT"
- $httpClient.put(options, (error, response, body) => {
- callback(error, this.adapterStatus(response), body)
- })
- }
- if (this.isNode()) {
- options.method = "PUT"
- this.node.request.put(options, (error, response, body) => {
- callback(error, this.adapterStatus(response), body)
- })
- }
- if (this.isJSBox()) {
- // no test
- if (typeof options == "string") options = {
- url: options
- }
- options["header"] = options["headers"]
- options["handler"] = function (resp) {
- let error = resp.error
- if (error) error = JSON.stringify(resp.error)
- let body = resp.data
- if (typeof body == "object") body = JSON.stringify(resp.data)
- callback(error, this.adapterStatus(resp.response), body)
- }
- $http.post(options)
- }
- }
- costTime() {
- let info = `${this.name}执行完毕!`
- if (this.isNode() && this.isExecComm) {
- info = `指令【${this.comm[1]}】执行完毕!`
- }
- const endTime = new Date().getTime()
- const ms = endTime - this.startTime
- const costTime = ms / 1000
- this.execCount++
- this.costTotalMs += ms
- this.log(`${info}耗时【${costTime}】秒\n总共执行【${this.execCount}】次,平均耗时【${((this.costTotalMs / this.execCount) / 1000).toFixed(4)}】秒`)
- this.setVal(this.costTotalStringKey, JSON.stringify(`${this.costTotalMs},${this.execCount}`))
- // this.setVal(this.execCountKey, JSON.stringify(0))
- // this.setVal(this.costTotalMsKey, JSON.stringify(0))
- }
- done(value = {}) {
- this.costTime()
- if (this.isSurge() || this.isQuanX() || this.isLoon() || this.isStash()) {
- $done(value)
- }
- }
- getRequestUrl() {
- return $request.url
- }
- getResponseBody() {
- if ($response) {
- return $response.body
- }
- }
- isGetCookie(reg) {
- return !!($request.method != 'OPTIONS' && this.getRequestUrl().match(reg))
- }
- isEmpty(obj) {
- return typeof obj == "undefined" || obj == null || obj == "" || obj == "null" || obj == "undefined" || obj.length === 0
- }
- randomString(len) {
- len = len || 32
- var $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
- var maxPos = $chars.length
- var pwd = ''
- for (let i = 0; i < len; i++) {
- pwd += $chars.charAt(Math.floor(Math.random() * maxPos))
- }
- return pwd
- }
- /**
- * 自动补齐字符串
- * @param str 原始字符串
- * @param prefix 前缀
- * @param suffix 后缀
- * @param fill 补齐用字符
- * @param len 目标补齐长度,不包含前后缀
- * @param direction 方向:0往后补齐
- * @param ifCode 是否打码
- * @param clen 打码长度
- * @param startIndex 起始坐标
- * @param cstr 打码字符
- * @returns {*}
- */
- autoComplete(str, prefix, suffix, fill, len, direction, ifCode, clen, startIndex, cstr) {
- str += ''
- if (str.length < len) {
- while (str.length < len) {
- if (direction == 0) {
- str += fill
- } else {
- str = fill + str
- }
- }
- }
- if (ifCode) {
- let temp = ''
- for (var i = 0; i < clen; i++) {
- temp += cstr
- }
- str = str.substring(0, startIndex) + temp + str.substring(clen + startIndex)
- }
- str = prefix + str + suffix
- return this.toDBC(str)
- }
- /**
- * @param str 源字符串 "#{code}, #{value}"
- * @param param 用于替换的数据,结构如下
- * @param prefix 前缀 "#{"
- * @param suffix 后缀 "}"
- * {
- * "code": 1,
- * "value": 2
- * }
- * 按上面的传入,输出为"1, 2"
- * 对应的#{code}用param里面code的值替换,#{value}也是
- * @returns {*|void|string}
- */
- customReplace(str, param, prefix, suffix) {
- try {
- if (this.isEmpty(prefix)) {
- prefix = "#{"
- }
- if (this.isEmpty(suffix)) {
- suffix = "}"
- }
- for (let i in param) {
- str = str.replace(`${prefix}${i}${suffix}`, param[i])
- }
- } catch (e) {
- this.logErr(e)
- }
- return str
- }
- toDBC(txtstring) {
- var tmp = ""
- for (var i = 0; i < txtstring.length; i++) {
- if (txtstring.charCodeAt(i) == 32) {
- tmp = tmp + String.fromCharCode(12288)
- } else if (txtstring.charCodeAt(i) < 127) {
- tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248)
- }
- }
- return tmp
- }
- hash(str) {
- let h = 0,
- i,
- chr
- for (i = 0; i < str.length; i++) {
- chr = str.charCodeAt(i)
- h = (h << 5) - h + chr
- h |= 0 // Convert to 32bit integer
- }
- return String(h)
- }
- /**
- * formatDate y:年 M:月 d:日 q:季 H:时 m:分 s:秒 S:毫秒
- */
- formatDate(date, format) {
- let o = {
- 'M+': date.getMonth() + 1,
- 'd+': date.getDate(),
- 'H+': date.getHours(),
- 'm+': date.getMinutes(),
- 's+': date.getSeconds(),
- 'q+': Math.floor((date.getMonth() + 3) / 3),
- 'S': date.getMilliseconds()
- }
- if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
- for (let k in o)
- if (new RegExp('(' + k + ')').test(format))
- format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
- return format
- }
- objToQueryStr(obj, encode) {
- let str = ''
- for (const key in obj) {
- let value = obj[key]
- if (value != null && value !== '') {
- if (typeof value === 'object') {
- value = JSON.stringify(value)
- } else if (encode) {
- value = encodeURIComponent(value)
- }
- str += `${key}=${value}&`
- }
- }
- str = str.substring(0, str.length - 1)
- return str
- }
- parseQueryStr(str) {
- let obj = {}
- if (str.indexOf("?") > -1) {
- str = str.split("?")[1]
- }
- let arr = str.split("&")
- for (let i = 0; i < arr.length; i++) {
- let kv = arr[i].split("=")
- obj[kv[0]] = kv[1]
- }
- return obj
- }
- deepClone(obj, newObj) {
- newObj = newObj || {};
- for (let key in obj) {
- if (typeof obj[key] == 'object') {
- newObj[key] = (obj[key].constructor === Array) ? [] : {}
- this.deepClone(obj[key], newObj[key]);
- } else {
- newObj[key] = obj[key]
- }
- }
- return newObj;
- }
- getBaseDoneHeaders(mixHeaders = {}) {
- return Object.assign(
- {
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Methods': 'POST,GET,OPTIONS,PUT,DELETE',
- 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
- },
- mixHeaders
- )
- }
- getHtmlDoneHeaders() {
- return this.getBaseDoneHeaders({
- 'Content-Type': 'text/html;charset=UTF-8'
- })
- }
- getJsonDoneHeaders() {
- return this.getBaseDoneHeaders({
- 'Content-Type': 'text/json; charset=utf-8',
- 'Connection': 'keep-alive'
- })
- }
- })(scriptName, scriptId, options)
- }
|