ToolKit.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  1. /**
  2. * 根据自己的习惯整合各个开发者而形成的工具包(@NobyDa, @chavyleung)
  3. * 兼容surge,quantumult x,loon,node环境
  4. * 并且加入一些好用的方法
  5. * 方法如下:
  6. * isEmpty: 判断字符串是否是空(undefined,null,空串)
  7. * getRequestUrl: 获取请求的url(目前仅支持surge和quanx)
  8. * getResponseBody: 获取响应体(目前仅支持surge和quanx)
  9. * boxJsJsonBuilder:构建最简默认boxjs配置json
  10. * randomString: 生成随机字符串
  11. * autoComplete: 自动补齐字符串
  12. * customReplace: 自定义替换
  13. * hash: 字符串做hash
  14. *
  15. * ⚠️当开启当且仅当执行失败的时候通知选项,请在执行失败的地方执行execFail()
  16. *
  17. * @param scriptName 脚本名,用于通知时候的标题
  18. * @param scriptId 每个脚本唯一的id,用于存储持久化的时候加入key
  19. * @param options 传入一些参数,目前参数如下;
  20. * [email protected]:6166(这个是默认值,本人surge调试脚本用,可自行修改)
  21. * target_boxjs_json_path=/Users/lowking/Desktop/Scripts/lowking.boxjs.json(生成boxjs配置的目标文件路径)
  22. * @constructor
  23. */
  24. function ToolKit(scriptName, scriptId, options) {
  25. return new (class {
  26. constructor(scriptName, scriptId, options) {
  27. this.tgEscapeCharMapping = { '&': '&', '#': '#' }
  28. 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`
  29. this.prefix = `lk`
  30. this.name = scriptName
  31. this.id = scriptId
  32. this.data = null
  33. this.dataFile = this.getRealPath(`${this.prefix}${this.id}.dat`)
  34. this.boxJsJsonFile = this.getRealPath(`${this.prefix}${this.id}.boxjs.json`)
  35. //surge http api等一些扩展参数
  36. this.options = options
  37. //命令行入参
  38. this.isExecComm = false
  39. //默认脚本开关
  40. this.isEnableLog = this.getVal(`${this.prefix}IsEnableLog${this.id}`)
  41. this.isEnableLog = this.isEmpty(this.isEnableLog) ? true : JSON.parse(this.isEnableLog)
  42. this.isNotifyOnlyFail = this.getVal(`${this.prefix}NotifyOnlyFail${this.id}`)
  43. this.isNotifyOnlyFail = this.isEmpty(this.isNotifyOnlyFail) ? false : JSON.parse(this.isNotifyOnlyFail)
  44. //tg通知开关
  45. this.isEnableTgNotify = this.getVal(`${this.prefix}IsEnableTgNotify${this.id}`)
  46. this.isEnableTgNotify = this.isEmpty(this.isEnableTgNotify) ? false : JSON.parse(this.isEnableTgNotify)
  47. this.tgNotifyUrl = this.getVal(`${this.prefix}TgNotifyUrl${this.id}`)
  48. this.isEnableTgNotify = this.isEnableTgNotify ? !this.isEmpty(this.tgNotifyUrl) : this.isEnableTgNotify
  49. //计时部分
  50. this.costTotalStringKey = `${this.prefix}CostTotalString${this.id}`
  51. this.costTotalString = this.getVal(this.costTotalStringKey)
  52. this.costTotalString = this.isEmpty(this.costTotalString) ? `0,0` : this.costTotalString.replace("\"", "")
  53. this.costTotalMs = this.costTotalString.split(",")[0]
  54. this.execCount = this.costTotalString.split(",")[1]
  55. this.costTotalMs = this.isEmpty(this.costTotalMs) ? 0 : parseInt(this.costTotalMs)
  56. this.execCount = this.isEmpty(this.execCount) ? 0 : parseInt(this.execCount)
  57. this.logSeparator = '\n██'
  58. this.now = new Date()
  59. this.startTime = this.now.getTime()
  60. this.node = (() => {
  61. if (this.isNode()) {
  62. const request = require('request')
  63. return ({ request })
  64. } else {
  65. return (null)
  66. }
  67. })()
  68. this.execStatus = true
  69. this.notifyInfo = []
  70. this.log(`${this.name}, 开始执行!`)
  71. this.execComm()
  72. }
  73. //当执行命令的目录不是脚本所在目录时,自动把文件路径改成指令传入的路径并返回完整文件路径
  74. getRealPath(fileName) {
  75. if (this.isNode()) {
  76. let targetPath = process.argv.slice(1, 2)[0].split("/")
  77. targetPath[targetPath.length - 1] = fileName
  78. return targetPath.join("/")
  79. }
  80. return fileName
  81. }
  82. async execComm() {
  83. //支持node命令,实现发送手机测试
  84. if (this.isNode()) {
  85. this.comm = process.argv.slice(1)
  86. let isHttpApiErr = false
  87. if (this.comm[1] == "p") {
  88. this.isExecComm = true
  89. //phone
  90. this.log(`开始执行指令【${this.comm[1]}】=> 发送到手机测试脚本!`)
  91. if (this.isEmpty(this.options) || this.isEmpty(this.options.httpApi)) {
  92. this.log(`未设置options,使用默认值`)
  93. //设置默认值
  94. if (this.isEmpty(this.options)) {
  95. this.options = {}
  96. }
  97. this.options.httpApi = `[email protected]:6166`
  98. } else {
  99. //判断格式
  100. if (!/.*?@.*?:[0-9]+/.test(this.options.httpApi)) {
  101. isHttpApiErr = true
  102. this.log(`❌httpApi格式错误!格式:[email protected]:6166`)
  103. this.done()
  104. }
  105. }
  106. if (!isHttpApiErr) {
  107. this.callApi(this.comm[2])
  108. }
  109. }
  110. }
  111. }
  112. callApi(timeout) {
  113. // 直接用接收到文件路径,解决在不同目录下都可以使用 node xxxx/xxx.js p 指令发送脚本给手机执行
  114. // let fname = this.getCallerFileNameAndLine().split(":")[0].replace("[", "")
  115. let fname = this.comm[0]
  116. this.log(`获取【${fname}】内容传给手机`)
  117. let scriptStr = ''
  118. this.fs = this.fs ? this.fs : require('fs')
  119. this.path = this.path ? this.path : require('path')
  120. const curDirDataFilePath = this.path.resolve(fname)
  121. const rootDirDataFilePath = this.path.resolve(process.cwd(), fname)
  122. const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
  123. const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
  124. if (isCurDirDataFile || isRootDirDataFile) {
  125. const datPath = isCurDirDataFile ? curDirDataFilePath : rootDirDataFilePath
  126. try {
  127. scriptStr = this.fs.readFileSync(datPath)
  128. } catch (e) {
  129. scriptStr = ''
  130. }
  131. } else {
  132. scriptStr = ''
  133. }
  134. let options = {
  135. url: `http://${this.options.httpApi.split("@")[1]}/v1/scripting/evaluate`,
  136. headers: {
  137. "X-Key": `${this.options.httpApi.split("@")[0]}`
  138. },
  139. body: {
  140. "script_text": `${scriptStr}`,
  141. "mock_type": "cron",
  142. "timeout": (!this.isEmpty(timeout) && timeout > 5) ? timeout : 5
  143. },
  144. json: true
  145. }
  146. this.post(options, (_error, _response, _data) => {
  147. this.log(`已将脚本【${fname}】发给手机!`)
  148. this.done()
  149. })
  150. }
  151. getCallerFileNameAndLine() {
  152. let error
  153. try {
  154. throw Error('')
  155. } catch (err) {
  156. error = err
  157. }
  158. const stack = error.stack
  159. const stackArr = stack.split('\n')
  160. let callerLogIndex = 1
  161. if (callerLogIndex !== 0) {
  162. const callerStackLine = stackArr[callerLogIndex]
  163. this.path = this.path ? this.path : require('path')
  164. return `[${callerStackLine.substring(callerStackLine.lastIndexOf(this.path.sep) + 1, callerStackLine.lastIndexOf(':'))}]`
  165. } else {
  166. return '[-]'
  167. }
  168. }
  169. getFunName(fun) {
  170. var ret = fun.toString()
  171. ret = ret.substr('function '.length)
  172. ret = ret.substr(0, ret.indexOf('('))
  173. return ret
  174. }
  175. boxJsJsonBuilder(info, param) {
  176. if (this.isNode()) {
  177. let boxjsJsonPath = "/Users/lowking/Desktop/Scripts/lowking.boxjs.json"
  178. // 从传入参数param读取配置的boxjs的json文件路径
  179. if (param && param.hasOwnProperty("target_boxjs_json_path")) {
  180. boxjsJsonPath = param["target_boxjs_json_path"]
  181. }
  182. if (!this.fs.existsSync(boxjsJsonPath)) {
  183. return
  184. }
  185. if (!this.isJsonObject(info) || !this.isJsonObject(param)) {
  186. this.log("构建BoxJsJson传入参数格式错误,请传入json对象")
  187. return
  188. }
  189. this.log('using node')
  190. let needAppendKeys = ["settings", "keys"]
  191. const domain = 'https://raw.githubusercontent.com/Orz-3'
  192. let boxJsJson = {}
  193. let scritpUrl = '#lk{script_url}'
  194. if (param && param.hasOwnProperty('script_url')) {
  195. scritpUrl = this.isEmpty(param['script_url']) ? "#lk{script_url}" : param['script_url']
  196. }
  197. boxJsJson.id = `${this.prefix}${this.id}`
  198. boxJsJson.name = this.name
  199. boxJsJson.desc_html = `⚠️使用说明</br>详情【<a href='${scritpUrl}?raw=true'><font class='red--text'>点我查看</font></a>】`
  200. boxJsJson.icons = [`${domain}/mini/master/Alpha/${this.id.toLocaleLowerCase()}.png`, `${domain}/mini/master/Color/${this.id.toLocaleLowerCase()}.png`]
  201. boxJsJson.keys = []
  202. boxJsJson.settings = [
  203. {
  204. "id": `${this.prefix}IsEnableLog${this.id}`,
  205. "name": "开启/关闭日志",
  206. "val": true,
  207. "type": "boolean",
  208. "desc": "默认开启"
  209. },
  210. {
  211. "id": `${this.prefix}NotifyOnlyFail${this.id}`,
  212. "name": "只当执行失败才通知",
  213. "val": false,
  214. "type": "boolean",
  215. "desc": "默认关闭"
  216. },
  217. {
  218. "id": `${this.prefix}IsEnableTgNotify${this.id}`,
  219. "name": "开启/关闭Telegram通知",
  220. "val": false,
  221. "type": "boolean",
  222. "desc": "默认关闭"
  223. },
  224. {
  225. "id": `${this.prefix}TgNotifyUrl${this.id}`,
  226. "name": "Telegram通知地址",
  227. "val": "",
  228. "type": "text",
  229. "desc": "Tg的通知地址,如:https://api.telegram.org/bot-token/sendMessage?chat_id=-100140&parse_mode=Markdown&text="
  230. }
  231. ]
  232. boxJsJson.author = "#lk{author}"
  233. boxJsJson.repo = "#lk{repo}"
  234. boxJsJson.script = `${scritpUrl}?raw=true`
  235. // 除了settings和keys追加,其他的都覆盖
  236. if (!this.isEmpty(info)) {
  237. for (let i in needAppendKeys) {
  238. let key = needAppendKeys[i]
  239. if (!this.isEmpty(info[key])) {
  240. // 处理传入的每项设置
  241. if (key === 'settings') {
  242. for (let i = 0; i < info[key].length; i++) {
  243. let input = info[key][i]
  244. for (let j = 0; j < boxJsJson.settings.length; j++) {
  245. let def = boxJsJson.settings[j]
  246. if (input.id === def.id) {
  247. // id相同,就使用外部传入的配置
  248. boxJsJson.settings.splice(j, 1)
  249. }
  250. }
  251. }
  252. }
  253. boxJsJson[key] = boxJsJson[key].concat(info[key])
  254. }
  255. delete info[key]
  256. }
  257. }
  258. Object.assign(boxJsJson, info)
  259. if (this.isNode()) {
  260. this.fs = this.fs ? this.fs : require('fs')
  261. this.path = this.path ? this.path : require('path')
  262. const curDirDataFilePath = this.path.resolve(this.boxJsJsonFile)
  263. const rootDirDataFilePath = this.path.resolve(process.cwd(), this.boxJsJsonFile)
  264. const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
  265. const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
  266. const jsondata = JSON.stringify(boxJsJson, null, '\t')
  267. if (isCurDirDataFile) {
  268. this.fs.writeFileSync(curDirDataFilePath, jsondata)
  269. } else if (isRootDirDataFile) {
  270. this.fs.writeFileSync(rootDirDataFilePath, jsondata)
  271. } else {
  272. this.fs.writeFileSync(curDirDataFilePath, jsondata)
  273. }
  274. // 写到项目的boxjs订阅json中
  275. let boxjsJson = JSON.parse(this.fs.readFileSync(boxjsJsonPath))
  276. if (boxjsJson.hasOwnProperty("apps") && Array.isArray(boxjsJson["apps"]) && boxjsJson["apps"].length > 0) {
  277. let apps = boxjsJson.apps
  278. let targetIdx = apps.indexOf(apps.filter((app) => {
  279. return app.id == boxJsJson.id
  280. })[0])
  281. if (targetIdx >= 0) {
  282. boxjsJson.apps[targetIdx] = boxJsJson
  283. } else {
  284. boxjsJson.apps.push(boxJsJson)
  285. }
  286. let ret = JSON.stringify(boxjsJson, null, 2)
  287. if (!this.isEmpty(param)) {
  288. for (const key in param) {
  289. let val = ''
  290. if (param.hasOwnProperty(key)) {
  291. val = param[key]
  292. } else if (key === 'author') {
  293. val = '@lowking'
  294. } else if (key === 'repo') {
  295. val = 'https://github.com/lowking/Scripts'
  296. }
  297. ret = ret.replace(`#lk{${key}}`, val)
  298. }
  299. }
  300. // 全部处理完毕检查是否有漏掉未配置的参数,进行提醒
  301. const regex = /(?:#lk\{)(.+?)(?=\})/
  302. let m = regex.exec(ret)
  303. if (m !== null) {
  304. this.log(`生成BoxJs还有未配置的参数,请参考https://github.com/lowking/Scripts/blob/master/util/example/ToolKitDemo.js#L17-L18传入参数:\n`)
  305. }
  306. let loseParamSet = new Set()
  307. while ((m = regex.exec(ret)) !== null) {
  308. loseParamSet.add(m[1])
  309. ret = ret.replace(`#lk{${m[1]}}`, ``)
  310. }
  311. loseParamSet.forEach(p => {
  312. console.log(`${p} `)
  313. })
  314. this.fs.writeFileSync(boxjsJsonPath, ret)
  315. }
  316. }
  317. }
  318. }
  319. isJsonObject(obj) {
  320. return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == "[object object]" && !obj.length
  321. }
  322. appendNotifyInfo(info, type) {
  323. if (type == 1) {
  324. this.notifyInfo = info
  325. } else {
  326. this.notifyInfo.push(info)
  327. }
  328. }
  329. prependNotifyInfo(info) {
  330. this.notifyInfo.splice(0, 0, info)
  331. }
  332. execFail() {
  333. this.execStatus = false
  334. }
  335. isRequest() {
  336. return typeof $request != "undefined"
  337. }
  338. isSurge() {
  339. return typeof $httpClient != "undefined"
  340. }
  341. isQuanX() {
  342. return typeof $task != "undefined"
  343. }
  344. isLoon() {
  345. return typeof $loon != "undefined"
  346. }
  347. isJSBox() {
  348. return typeof $app != "undefined" && typeof $http != "undefined"
  349. }
  350. isStash() {
  351. return 'undefined' !== typeof $environment && $environment['stash-version']
  352. }
  353. isNode() {
  354. return typeof require == "function" && !this.isJSBox()
  355. }
  356. sleep(time) {
  357. return new Promise((resolve) => setTimeout(resolve, time))
  358. }
  359. log(message) {
  360. if (this.isEnableLog) console.log(`${this.logSeparator}${message}`)
  361. }
  362. logErr(message) {
  363. this.execStatus = true
  364. if (this.isEnableLog) {
  365. console.log(`${this.logSeparator}${this.name}执行异常:`)
  366. console.log(message)
  367. console.log(`\n${message.message}`)
  368. }
  369. }
  370. msg(subtitle, message, openUrl, mediaUrl) {
  371. if (!this.isRequest() && this.isNotifyOnlyFail && this.execStatus) {
  372. //开启了当且仅当执行失败的时候通知,并且执行成功了,这时候不通知
  373. } else {
  374. if (this.isEmpty(message)) {
  375. if (Array.isArray(this.notifyInfo)) {
  376. message = this.notifyInfo.join("\n")
  377. } else {
  378. message = this.notifyInfo
  379. }
  380. }
  381. if (!this.isEmpty(message)) {
  382. if (this.isEnableTgNotify) {
  383. this.log(`${this.name}Tg通知开始`)
  384. //处理特殊字符
  385. for (let key in this.tgEscapeCharMapping) {
  386. if (!this.tgEscapeCharMapping.hasOwnProperty(key)) {
  387. continue
  388. }
  389. message = message.replace(key, this.tgEscapeCharMapping[key])
  390. }
  391. this.get({
  392. url: encodeURI(`${this.tgNotifyUrl}📌${this.name}\n${message}`)
  393. }, (_error, _statusCode, _body) => {
  394. this.log(`Tg通知完毕`)
  395. })
  396. } else {
  397. let options = {}
  398. const hasOpenUrl = !this.isEmpty(openUrl)
  399. const hasMediaUrl = !this.isEmpty(mediaUrl)
  400. if (this.isQuanX()) {
  401. if (hasOpenUrl) options["open-url"] = openUrl
  402. if (hasMediaUrl) options["media-url"] = mediaUrl
  403. $notify(this.name, subtitle, message, options)
  404. }
  405. if (this.isSurge() || this.isStash()) {
  406. if (hasOpenUrl) options["url"] = openUrl
  407. $notification.post(this.name, subtitle, message, options)
  408. }
  409. if (this.isNode()) this.log("⭐️" + this.name + "\n" + subtitle + "\n" + message)
  410. if (this.isJSBox()) $push.schedule({
  411. title: this.name,
  412. body: subtitle ? subtitle + "\n" + message : message
  413. })
  414. }
  415. }
  416. }
  417. }
  418. getVal(key, defaultValue = "") {
  419. let value
  420. if (this.isSurge() || this.isLoon() || this.isStash()) {
  421. value = $persistentStore.read(key)
  422. } else if (this.isQuanX()) {
  423. value = $prefs.valueForKey(key)
  424. } else if (this.isNode()) {
  425. this.data = this.loadData()
  426. value = process.env[key] || this.data[key]
  427. } else {
  428. value = (this.data && this.data[key]) || null
  429. }
  430. return !value ? defaultValue : value
  431. }
  432. setVal(key, val) {
  433. if (this.isSurge() || this.isLoon() || this.isStash()) {
  434. return $persistentStore.write(val, key)
  435. } else if (this.isQuanX()) {
  436. return $prefs.setValueForKey(val, key)
  437. } else if (this.isNode()) {
  438. this.data = this.loadData()
  439. this.data[key] = val
  440. this.writeData()
  441. return true
  442. } else {
  443. return (this.data && this.data[key]) || null
  444. }
  445. }
  446. loadData() {
  447. if (this.isNode()) {
  448. this.fs = this.fs ? this.fs : require('fs')
  449. this.path = this.path ? this.path : require('path')
  450. const curDirDataFilePath = this.path.resolve(this.dataFile)
  451. const rootDirDataFilePath = this.path.resolve(process.cwd(), this.dataFile)
  452. const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
  453. const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
  454. if (isCurDirDataFile || isRootDirDataFile) {
  455. const datPath = isCurDirDataFile ? curDirDataFilePath : rootDirDataFilePath
  456. try {
  457. return JSON.parse(this.fs.readFileSync(datPath))
  458. } catch (e) {
  459. return {}
  460. }
  461. } else return {}
  462. } else return {}
  463. }
  464. writeData() {
  465. if (this.isNode()) {
  466. this.fs = this.fs ? this.fs : require('fs')
  467. this.path = this.path ? this.path : require('path')
  468. const curDirDataFilePath = this.path.resolve(this.dataFile)
  469. const rootDirDataFilePath = this.path.resolve(process.cwd(), this.dataFile)
  470. const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath)
  471. const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath)
  472. const jsondata = JSON.stringify(this.data)
  473. if (isCurDirDataFile) {
  474. this.fs.writeFileSync(curDirDataFilePath, jsondata)
  475. } else if (isRootDirDataFile) {
  476. this.fs.writeFileSync(rootDirDataFilePath, jsondata)
  477. } else {
  478. this.fs.writeFileSync(curDirDataFilePath, jsondata)
  479. }
  480. }
  481. }
  482. adapterStatus(response) {
  483. if (response) {
  484. if (response.status) {
  485. response["statusCode"] = response.status
  486. } else if (response.statusCode) {
  487. response["status"] = response.statusCode
  488. }
  489. }
  490. return response
  491. }
  492. get(options, callback = () => { }) {
  493. if (this.isQuanX()) {
  494. if (typeof options == "string") options = {
  495. url: options
  496. }
  497. options["method"] = "GET"
  498. $task.fetch(options).then(response => {
  499. callback(null, this.adapterStatus(response), response.body)
  500. }, reason => callback(reason.error, null, null))
  501. }
  502. if (this.isSurge() || this.isLoon() || this.isStash()) $httpClient.get(options, (error, response, body) => {
  503. callback(error, this.adapterStatus(response), body)
  504. })
  505. if (this.isNode()) {
  506. this.node.request(options, (error, response, body) => {
  507. callback(error, this.adapterStatus(response), body)
  508. })
  509. }
  510. if (this.isJSBox()) {
  511. if (typeof options == "string") options = {
  512. url: options
  513. }
  514. options["header"] = options["headers"]
  515. options["handler"] = function (resp) {
  516. let error = resp.error
  517. if (error) error = JSON.stringify(resp.error)
  518. let body = resp.data
  519. if (typeof body == "object") body = JSON.stringify(resp.data)
  520. callback(error, this.adapterStatus(resp.response), body)
  521. }
  522. $http.get(options)
  523. }
  524. }
  525. post(options, callback = () => { }) {
  526. if (this.isQuanX()) {
  527. if (typeof options == "string") options = {
  528. url: options
  529. }
  530. options["method"] = "POST"
  531. $task.fetch(options).then(response => {
  532. callback(null, this.adapterStatus(response), response.body)
  533. }, reason => callback(reason.error, null, null))
  534. }
  535. if (this.isSurge() || this.isLoon() || this.isStash()) {
  536. $httpClient.post(options, (error, response, body) => {
  537. callback(error, this.adapterStatus(response), body)
  538. })
  539. }
  540. if (this.isNode()) {
  541. this.node.request.post(options, (error, response, body) => {
  542. callback(error, this.adapterStatus(response), body)
  543. })
  544. }
  545. if (this.isJSBox()) {
  546. if (typeof options == "string") options = {
  547. url: options
  548. }
  549. options["header"] = options["headers"]
  550. options["handler"] = function (resp) {
  551. let error = resp.error
  552. if (error) error = JSON.stringify(resp.error)
  553. let body = resp.data
  554. if (typeof body == "object") body = JSON.stringify(resp.data)
  555. callback(error, this.adapterStatus(resp.response), body)
  556. }
  557. $http.post(options)
  558. }
  559. }
  560. put(options, callback = () => { }) {
  561. if (this.isQuanX()) {
  562. // no test
  563. if (typeof options == "string") options = {
  564. url: options
  565. }
  566. options["method"] = "PUT"
  567. $task.fetch(options).then(response => {
  568. callback(null, this.adapterStatus(response), response.body)
  569. }, reason => callback(reason.error, null, null))
  570. }
  571. if (this.isSurge() || this.isLoon() || this.isStash()) {
  572. options.method = "PUT"
  573. $httpClient.put(options, (error, response, body) => {
  574. callback(error, this.adapterStatus(response), body)
  575. })
  576. }
  577. if (this.isNode()) {
  578. options.method = "PUT"
  579. this.node.request.put(options, (error, response, body) => {
  580. callback(error, this.adapterStatus(response), body)
  581. })
  582. }
  583. if (this.isJSBox()) {
  584. // no test
  585. if (typeof options == "string") options = {
  586. url: options
  587. }
  588. options["header"] = options["headers"]
  589. options["handler"] = function (resp) {
  590. let error = resp.error
  591. if (error) error = JSON.stringify(resp.error)
  592. let body = resp.data
  593. if (typeof body == "object") body = JSON.stringify(resp.data)
  594. callback(error, this.adapterStatus(resp.response), body)
  595. }
  596. $http.post(options)
  597. }
  598. }
  599. costTime() {
  600. let info = `${this.name}执行完毕!`
  601. if (this.isNode() && this.isExecComm) {
  602. info = `指令【${this.comm[1]}】执行完毕!`
  603. }
  604. const endTime = new Date().getTime()
  605. const ms = endTime - this.startTime
  606. const costTime = ms / 1000
  607. this.execCount++
  608. this.costTotalMs += ms
  609. this.log(`${info}耗时【${costTime}】秒\n总共执行【${this.execCount}】次,平均耗时【${((this.costTotalMs / this.execCount) / 1000).toFixed(4)}】秒`)
  610. this.setVal(this.costTotalStringKey, JSON.stringify(`${this.costTotalMs},${this.execCount}`))
  611. // this.setVal(this.execCountKey, JSON.stringify(0))
  612. // this.setVal(this.costTotalMsKey, JSON.stringify(0))
  613. }
  614. done(value = {}) {
  615. this.costTime()
  616. if (this.isSurge() || this.isQuanX() || this.isLoon() || this.isStash()) {
  617. $done(value)
  618. }
  619. }
  620. getRequestUrl() {
  621. return $request.url
  622. }
  623. getResponseBody() {
  624. return $response.body
  625. }
  626. isGetCookie(reg) {
  627. return !!($request.method != 'OPTIONS' && this.getRequestUrl().match(reg))
  628. }
  629. isEmpty(obj) {
  630. return typeof obj == "undefined" || obj == null || obj == "" || obj == "null" || obj == "undefined" || obj.length === 0
  631. }
  632. randomString(len) {
  633. len = len || 32
  634. var $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
  635. var maxPos = $chars.length
  636. var pwd = ''
  637. for (let i = 0; i < len; i++) {
  638. pwd += $chars.charAt(Math.floor(Math.random() * maxPos))
  639. }
  640. return pwd
  641. }
  642. /**
  643. * 自动补齐字符串
  644. * @param str 原始字符串
  645. * @param prefix 前缀
  646. * @param suffix 后缀
  647. * @param fill 补齐用字符
  648. * @param len 目标补齐长度,不包含前后缀
  649. * @param direction 方向:0往后补齐
  650. * @param ifCode 是否打码
  651. * @param clen 打码长度
  652. * @param startIndex 起始坐标
  653. * @param cstr 打码字符
  654. * @returns {*}
  655. */
  656. autoComplete(str, prefix, suffix, fill, len, direction, ifCode, clen, startIndex, cstr) {
  657. str += ``
  658. if (str.length < len) {
  659. while (str.length < len) {
  660. if (direction == 0) {
  661. str += fill
  662. } else {
  663. str = fill + str
  664. }
  665. }
  666. }
  667. if (ifCode) {
  668. let temp = ``
  669. for (var i = 0; i < clen; i++) {
  670. temp += cstr
  671. }
  672. str = str.substring(0, startIndex) + temp + str.substring(clen + startIndex)
  673. }
  674. str = prefix + str + suffix
  675. return this.toDBC(str)
  676. }
  677. /**
  678. * @param str 源字符串 "#{code}, #{value}"
  679. * @param param 用于替换的数据,结构如下
  680. * @param prefix 前缀 "#{"
  681. * @param suffix 后缀 "}"
  682. * {
  683. * "code": 1,
  684. * "value": 2
  685. * }
  686. * 按上面的传入,输出为"1, 2"
  687. * 对应的#{code}用param里面code的值替换,#{value}也是
  688. * @returns {*|void|string}
  689. */
  690. customReplace(str, param, prefix, suffix) {
  691. try {
  692. if (this.isEmpty(prefix)) {
  693. prefix = "#{"
  694. }
  695. if (this.isEmpty(suffix)) {
  696. suffix = "}"
  697. }
  698. for (let i in param) {
  699. str = str.replace(`${prefix}${i}${suffix}`, param[i])
  700. }
  701. } catch (e) {
  702. this.logErr(e)
  703. }
  704. return str
  705. }
  706. toDBC(txtstring) {
  707. var tmp = ""
  708. for (var i = 0; i < txtstring.length; i++) {
  709. if (txtstring.charCodeAt(i) == 32) {
  710. tmp = tmp + String.fromCharCode(12288)
  711. } else if (txtstring.charCodeAt(i) < 127) {
  712. tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248)
  713. }
  714. }
  715. return tmp
  716. }
  717. hash(str) {
  718. let h = 0,
  719. i,
  720. chr
  721. for (i = 0; i < str.length; i++) {
  722. chr = str.charCodeAt(i)
  723. h = (h << 5) - h + chr
  724. h |= 0 // Convert to 32bit integer
  725. }
  726. return String(h)
  727. }
  728. /**
  729. * formatDate y:年 M:月 d:日 q:季 H:时 m:分 s:秒 S:毫秒
  730. */
  731. formatDate(date, format) {
  732. let o = {
  733. 'M+': date.getMonth() + 1,
  734. 'd+': date.getDate(),
  735. 'H+': date.getHours(),
  736. 'm+': date.getMinutes(),
  737. 's+': date.getSeconds(),
  738. 'q+': Math.floor((date.getMonth() + 3) / 3),
  739. 'S': date.getMilliseconds()
  740. }
  741. if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
  742. for (let k in o)
  743. if (new RegExp('(' + k + ')').test(format))
  744. format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
  745. return format
  746. }
  747. })(scriptName, scriptId, options)
  748. }