MagicJS3.js 67 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784
  1. function MagicJS(scriptName = "MagicJS", logLevel = "INFO") {
  2. const MagicEnvironment = () => {
  3. const isLoon = typeof $loon !== "undefined";
  4. const isQuanX = typeof $task !== "undefined";
  5. const isNode = typeof module !== "undefined";
  6. const isSurge = typeof $httpClient !== "undefined" && !isLoon;
  7. const isStorm = typeof $storm !== "undefined";
  8. const isStash = typeof $environment !== "undefined" && typeof $environment["stash-build"] !== "undefined";
  9. const isSurgeLike = isSurge || isLoon || isStorm || isStash;
  10. const isScriptable = typeof importModule !== "undefined";
  11. return {
  12. isLoon: isLoon,
  13. isQuanX: isQuanX,
  14. isNode: isNode,
  15. isSurge: isSurge,
  16. isStorm: isStorm,
  17. isStash: isStash,
  18. isSurgeLike: isSurgeLike,
  19. isScriptable: isScriptable,
  20. get name() {
  21. if (isLoon) {
  22. return "Loon";
  23. } else if (isQuanX) {
  24. return "QuantumultX";
  25. } else if (isNode) {
  26. return "NodeJS";
  27. } else if (isSurge) {
  28. return "Surge";
  29. } else if (isScriptable) {
  30. return "Scriptable";
  31. } else {
  32. return "unknown";
  33. }
  34. },
  35. get build() {
  36. if (isSurge) {
  37. return $environment["surge-build"];
  38. } else if (isStash) {
  39. return $environment["stash-build"];
  40. } else if (isStorm) {
  41. return $storm.buildVersion;
  42. }
  43. },
  44. get language() {
  45. if (isSurge || isStash) {
  46. return $environment["language"];
  47. }
  48. },
  49. get version() {
  50. if (isSurge) {
  51. return $environment["surge-version"];
  52. } else if (isStash) {
  53. return $environment["stash-version"];
  54. } else if (isStorm) {
  55. return $storm.appVersion;
  56. } else if (isNode) {
  57. return process.version;
  58. }
  59. },
  60. get system() {
  61. if (isSurge) {
  62. return $environment["system"];
  63. } else if (isNode) {
  64. return process.platform;
  65. }
  66. },
  67. get systemVersion() {
  68. if (isStorm) {
  69. return $storm.systemVersion;
  70. }
  71. },
  72. get deviceName() {
  73. if (isStorm) {
  74. return $storm.deviceName;
  75. }
  76. }
  77. };
  78. };
  79. const MagicLogger = (scriptName, logLevel = "INFO") => {
  80. let _level = logLevel;
  81. let logSeparator = '\n';
  82. const logLevels = {
  83. SNIFFER: 6,
  84. DEBUG: 5,
  85. INFO: 4,
  86. NOTIFY: 3,
  87. WARNING: 2,
  88. ERROR: 1,
  89. CRITICAL: 0,
  90. NONE: -1
  91. };
  92. const logEmoji = {
  93. SNIFFER: "",
  94. DEBUG: "",
  95. INFO: "",
  96. NOTIFY: "",
  97. WARNING: "❗ ",
  98. ERROR: "❌ ",
  99. CRITICAL: "❌ ",
  100. NONE: ""
  101. };
  102. const _log = (msg, level = "INFO") => {
  103. // let level = "INFO";
  104. // if (logs.length > 1) {
  105. // let lastParam = logs[logs.length-1];
  106. // let leveArr = ["SNIFFER","DEBUG","INFO","NOTIFY","WARNING","ERROR","RETRY"];
  107. // if(typeof(lastParam) == 'string' && leveArr.indexOf(lastParam) > 0){
  108. // level = lastParam;
  109. // logs.length = logs.length-1;
  110. // }
  111. // }
  112. // let msg = logs.join('\n');
  113. if (!(logLevels[_level] < logLevels[level.toUpperCase()])) console.log(`██[${scriptName}][${level}]`+''+`${logEmoji[level.toUpperCase()]}${msg}`+'\n'+``);
  114. };
  115. const setLevel = logLevel => {
  116. _level = logLevel;
  117. };
  118. return {
  119. getLevel: () => {
  120. return _level;
  121. },
  122. setLevel: setLevel,
  123. sniffer: (...logs) => {
  124. let msg = logs.join(logSeparator);
  125. _log(msg, "SNIFFER");
  126. },
  127. log: (...logs) => {
  128. let msg = logs.join(logSeparator);
  129. console.log(`██[${scriptName}]`+''+`${msg}`+'\n'+``);
  130. },
  131. debug: (...logs) => {
  132. let msg = logs.join(logSeparator);
  133. _log(msg, "DEBUG");
  134. },
  135. info: (...logs) => {
  136. let msg = logs.join(logSeparator);
  137. _log(msg, "INFO");
  138. },
  139. notify: (...logs) => {
  140. let msg = logs.join(logSeparator);
  141. _log(msg, "NOTIFY");
  142. },
  143. warning: (...logs) => {
  144. let msg = logs.join(logSeparator);
  145. _log(msg, "WARNING");
  146. },
  147. error: (...logs) => {
  148. let msg = logs.join(logSeparator);
  149. _log(msg, "ERROR");
  150. },
  151. retry: (...logs) => {
  152. let msg = logs.join(logSeparator);
  153. _log(msg, "RETRY");
  154. }
  155. };
  156. };
  157. return new class {
  158. constructor(scriptName, logLevel) {
  159. this._startTime = Date.now();
  160. this.version = "3.0.0";
  161. this.scriptName = scriptName;
  162. this.env = MagicEnvironment();
  163. this.logger = MagicLogger(scriptName, logLevel);
  164. this.http = typeof MagicHttp === "function" ? MagicHttp(this.env, this.logger) : undefined;
  165. this.data = typeof MagicData === "function" ? MagicData(this.env, this.logger) : undefined;
  166. this.notification = typeof MagicNotification === "function" ? MagicNotification(this.scriptName, this.env, this.logger, this.http) : undefined;
  167. this.utils = typeof MagicUtils === "function" ? MagicUtils(this.env, this.logger) : undefined;
  168. this.qinglong = typeof MagicQingLong === "function" ? MagicQingLong(this.env, this.data, this.logger) : undefined;
  169. if (typeof this.data !== "undefined") {
  170. let magicLoglevel = this.data.read("magic_loglevel");
  171. const barkUrl = this.data.read("magic_bark_url");
  172. if (magicLoglevel) {
  173. this.logger.setLevel(magicLoglevel.toUpperCase());
  174. }
  175. if (barkUrl) {
  176. this.notification.setBark(barkUrl);
  177. }
  178. }
  179. this.logger.info(`${scriptName}, 开始执行!`);
  180. // this.checkRecordRequestBody();
  181. }
  182. get isRequest() {
  183. return typeof $request !== "undefined";
  184. }
  185. get isStrictRequest() {
  186. return typeof $request !== "undefined" && typeof $response === "undefined";
  187. }
  188. get isResponse() {
  189. return typeof $response !== "undefined";
  190. }
  191. get isDebug() {
  192. return this.logger.level === "DEBUG";
  193. }
  194. get request() {
  195. return typeof $request !== "undefined" ? $request : undefined;
  196. }
  197. get response() {
  198. if (typeof $response !== "undefined") {
  199. if ($response.hasOwnProperty("status")) $response["statusCode"] = $response["status"];
  200. if ($response.hasOwnProperty("statusCode")) $response["status"] = $response["statusCode"];
  201. return $response;
  202. } else {
  203. return undefined;
  204. }
  205. }
  206. log(...logs){
  207. this.logger.log(logs);
  208. }
  209. toStr(data, defVal=null){
  210. try{
  211. return JSON.stringify(data);
  212. }catch{
  213. return defVal;
  214. }
  215. }
  216. toObj(str, defVal=null){
  217. try{
  218. return JSON.parse(str);
  219. }catch{
  220. return defVal;
  221. }
  222. }
  223. checkRecordRequestBody() {
  224. if (!this.isRequest) {
  225. return;
  226. }
  227. const reqBody = $request.body;
  228. if (!reqBody) {
  229. return;
  230. }
  231. const env = this.env;
  232. const path = $request.path;
  233. let cacheKey = this.scriptName + "#" + path.replace("/", "_");
  234. cacheKey = cacheKey.replace("?", "#");
  235. if (env.isQuanX) $prefs.setValueForKey(reqBody, cacheKey);
  236. if (env.isLoon || env.isSurge) $persistentStore.write(reqBody, cacheKey);
  237. if (env.isNode) {
  238. const node_fs = require("fs");
  239. node_fs.writeFileSync(
  240. `${cacheKey}.json`,
  241. reqBody,
  242. {
  243. flag: "w"
  244. },
  245. (err) => console.log(err)
  246. );
  247. }
  248. }
  249. getRequestBody() {
  250. const env = this.env;
  251. const path = $request.path;
  252. let cacheKey = this.scriptName + "#" + path.replace("/", "_");
  253. cacheKey = cacheKey.replace("?", "#");
  254. if (env.isSurge || env.isLoon) {
  255. return $persistentStore.read(cacheKey);
  256. }
  257. if (env.isQuanX) {
  258. return $prefs.valueForKey(cacheKey);
  259. }
  260. if (env.isNode) {
  261. const fpath = `${cacheKey}.json`;
  262. const node_fs = require("fs");
  263. if (!node_fs.existsSync(fpath)) {
  264. return JSON.parse(
  265. node_fs.readFileSync(fpath)
  266. );
  267. }
  268. }
  269. }
  270. getResponseBody() {
  271. if ($response) {
  272. return $response.body;
  273. }
  274. }
  275. // 解析cookie字符串的函数
  276. parseCookies(cookieString) {
  277. let dict = {};
  278. cookieString && cookieString.split(';').forEach(function(cookie) {
  279. let parts = cookie.split('=');
  280. dict[parts.shift().trim()] = decodeURI(parts.join('='));
  281. });
  282. return dict;
  283. }
  284. // 系列化为cookie字符串
  285. serializeCookies(cookieData) {
  286. const parts = [];
  287. for(let key in cookieData){
  288. let value = cookieData[key];
  289. let cookiePart = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
  290. parts.push(cookiePart);
  291. }
  292. return parts.join('; ');
  293. }
  294. parseSetCookies(cookieString) {
  295. const attribKeys = ['Expires', 'Max-Age', 'Domain', 'Path', 'HttpOnly', 'SameSite'];
  296. const parts = cookieString.split(';');
  297. let mainKey = null;
  298. const retDict = {};
  299. const retData = [];
  300. parts.forEach(part => {
  301. let pstr = part.trim();
  302. let aKey = null;
  303. let aValue = true;
  304. if (pstr.includes('=')) {
  305. let kvdata = pstr.split('=');
  306. aKey = kvdata[0].trim();
  307. aValue = kvdata[1].trim();
  308. } else {
  309. aKey = pstr;
  310. }
  311. if (attribKeys.includes(aKey)) {
  312. if (retDict[mainKey]) {
  313. retDict[mainKey][aKey] = aValue;
  314. let attribs = retDict[mainKey].attribs;
  315. attribs[aKey] = aValue;
  316. }
  317. } else {
  318. mainKey = aKey;
  319. let attribs = {};
  320. if (mainKey.includes(',')) {
  321. const keys = mainKey.split(',');
  322. keys.forEach(key => {
  323. const k = key.trim();
  324. if (attribKeys.includes(k)) {
  325. attribs[k] = true;
  326. } else {
  327. mainKey = k;
  328. }
  329. });
  330. }
  331. retDict[mainKey] = { name: mainKey, value: aValue, attribs: attribs };
  332. retData.push(retDict[mainKey]);
  333. }
  334. });
  335. return retData;
  336. }
  337. objToQueryStr(obj, encode) {
  338. let str = '';
  339. for (const key in obj) {
  340. let value = obj[key];
  341. if (value != null && value !== '') {
  342. if (typeof value === 'object') {
  343. value = JSON.stringify(value);
  344. } else if (encode) {
  345. value = encodeURIComponent(value);
  346. }
  347. str += `${key}=${value}&`;
  348. }
  349. }
  350. str = str.substring(0, str.length - 1);
  351. return str;
  352. }
  353. parseQueryStr(str) {
  354. let obj = {};
  355. if (str.indexOf("?") > -1) {
  356. str = str.split("?")[1];
  357. }
  358. let arr = str.split("&");
  359. for (let i = 0; i < arr.length; i++) {
  360. let kv = arr[i].split("=");
  361. obj[kv[0]] = kv[1];
  362. }
  363. return obj;
  364. }
  365. deepClone(obj, newObj) {
  366. newObj = newObj || {};
  367. for (let key in obj) {
  368. if (typeof obj[key] == 'object') {
  369. newObj[key] = (obj[key].constructor === Array) ? [] : {};
  370. this.deepClone(obj[key], newObj[key]);
  371. } else {
  372. newObj[key] = obj[key];
  373. }
  374. }
  375. return newObj;
  376. }
  377. /**
  378. * formatDate y:年 M:月 d:日 q:季 H:时 m:分 s:秒 S:毫秒
  379. */
  380. formatDate(date, format) {
  381. let o = {
  382. 'M+': date.getMonth() + 1,
  383. 'd+': date.getDate(),
  384. 'H+': date.getHours(),
  385. 'm+': date.getMinutes(),
  386. 's+': date.getSeconds(),
  387. 'q+': Math.floor((date.getMonth() + 3) / 3),
  388. 'S': date.getMilliseconds()
  389. }
  390. if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
  391. for (let k in o)
  392. if (new RegExp('(' + k + ')').test(format))
  393. format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
  394. return format
  395. }
  396. /**
  397. * parseDate 字符串格式,默认'yyyy-MM-dd',支持如下:y、M、d、H、m、s、S,不支持w和q
  398. */
  399. parseDate(str, format) {
  400. format = format || 'yyyy-MM-dd';
  401. let obj = { y: 0, M: 1, d: 0, H: 0, h: 0, m: 0, s: 0, S: 0 };
  402. format.replace(/([^yMdHmsS]*?)(([yMdHmsS])\3*)([^yMdHmsS]*?)/g, function (m, $1, $2, $3, $4, idx, old) {
  403. str = str.replace(new RegExp($1 + '(\\d{' + $2.length + '})' + $4), function (_m, _$1) {
  404. obj[$3] = parseInt(_$1);
  405. return '';
  406. });
  407. return '';
  408. });
  409. obj.M--; // 月份是从0开始的,所以要减去1
  410. let date = new Date(obj.y, obj.M, obj.d, obj.H, obj.m, obj.s);
  411. if (obj.S !== 0) date.setMilliseconds(obj.S); // 如果设置了毫秒
  412. return date;
  413. }
  414. costTime() {
  415. let info = `${this.scriptName}执行完毕!`
  416. // if (this.isNode && this.isExecComm) {
  417. // info = `指令【${this.comm[1]}】执行完毕!`
  418. // }
  419. this._endTime = new Date().getTime();
  420. const ms = this._endTime - this._startTime;
  421. const costTime = ms / 1000;
  422. this.logger.info(`${info}耗时【${costTime}】秒`);
  423. }
  424. done = (value = {}) => {
  425. // this._endTime = Date.now();
  426. // let span = (this._endTime - this._startTime) / 1e3;
  427. // this.logger.info(`SCRIPT COMPLETED: ${span} S.`);
  428. this.costTime();
  429. if (typeof $done !== "undefined") {
  430. $done(value);
  431. }
  432. };
  433. }(scriptName, logLevel);
  434. }
  435. function MagicHttp(env, logger) {
  436. const phoneUA = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Mobile/15E148 Safari/604.1";
  437. const computerUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36 Edg/84.0.522.59";
  438. let axiosInstance;
  439. if (env.isNode) {
  440. const axios = require("axios");
  441. axiosInstance = axios.create();
  442. }
  443. class InterceptorManager {
  444. constructor(isRequest = true) {
  445. this.handlers = [];
  446. this.isRequest = isRequest;
  447. }
  448. use(fulfilled, rejected, options) {
  449. if (typeof fulfilled === "function") {
  450. logger.debug(`Register fulfilled ${fulfilled.name}`);
  451. }
  452. if (typeof rejected === "function") {
  453. logger.debug(`Register rejected ${rejected.name}`);
  454. }
  455. this.handlers.push({
  456. fulfilled: fulfilled,
  457. rejected: rejected,
  458. synchronous: options && typeof options.synchronous === "boolean" ? options.synchronous : false,
  459. runWhen: options ? options.runWhen : null
  460. });
  461. return this.handlers.length - 1;
  462. }
  463. eject(id) {
  464. if (this.handlers[id]) {
  465. this.handlers[id] = null;
  466. }
  467. }
  468. forEach(fn) {
  469. this.handlers.forEach(element => {
  470. if (element !== null) {
  471. fn(element);
  472. }
  473. });
  474. }
  475. }
  476. function paramsToQueryString(config) {
  477. let _config = {
  478. ...config
  479. };
  480. if (!!_config.params) {
  481. if (!env.isNode) {
  482. let qs = Object.keys(_config.params).map(key => {
  483. const encodeKey = encodeURIComponent(key);
  484. _config.url = _config.url.replace(new RegExp(`${key}=[^&]*`, "ig"), "");
  485. _config.url = _config.url.replace(new RegExp(`${encodeKey}=[^&]*`, "ig"), "");
  486. return `${encodeKey}=${encodeURIComponent(_config.params[key])}`;
  487. }).join("&");
  488. if (_config.url.indexOf("?") < 0) _config.url += "?";
  489. if (!/(&|\?)$/g.test(_config.url)) {
  490. _config.url += "&";
  491. }
  492. _config.url += qs;
  493. delete _config.params;
  494. logger.debug(`Params to QueryString: ${_config.url}`);
  495. }
  496. }
  497. return _config;
  498. }
  499. const mergeConfig = (method, configOrUrl) => {
  500. let config = typeof configOrUrl === "object" ? {
  501. headers: {},
  502. ...configOrUrl
  503. } : {
  504. url: configOrUrl,
  505. headers: {}
  506. };
  507. if (!config.method) {
  508. config["method"] = method;
  509. }
  510. config = paramsToQueryString(config);
  511. if (config["rewrite"] === true) {
  512. if (env.isSurge) {
  513. config.headers["X-Surge-Skip-Scripting"] = false;
  514. delete config["rewrite"];
  515. } else if (env.isQuanX) {
  516. config["hints"] = false;
  517. delete config["rewrite"];
  518. }
  519. }
  520. if (env.isSurgeLike) {
  521. const contentType = config.headers["content-type"] || config.headers["Content-Type"];
  522. if (config["method"] !== "GET" && contentType && contentType.indexOf("application/json") >= 0 && config.body instanceof Array) {
  523. config.body = JSON.stringify(config.body);
  524. logger.debug(`Convert Array object to String: ${config.body}`);
  525. }
  526. } else if (env.isQuanX) {
  527. if (config.hasOwnProperty("body") && typeof config["body"] !== "string") config["body"] = JSON.stringify(config["body"]);
  528. config["method"] = method;
  529. } else if (env.isNode) {
  530. if (method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE") {
  531. config.data = config.data || config.body;
  532. } else if (method === "GET") {
  533. config.params = config.params || config.body;
  534. }
  535. delete config.body;
  536. }
  537. return config;
  538. };
  539. const modifyResponse = (resp, config = null) => {
  540. if (resp) {
  541. let _resp = {
  542. ...resp,
  543. config: resp.config || config,
  544. status: resp.statusCode || resp.status,
  545. body: resp.body || resp.data,
  546. headers: resp.headers || resp.header
  547. };
  548. if (typeof _resp.body === "string") {
  549. try {
  550. _resp.body = JSON.parse(_resp.body);
  551. } catch {}
  552. }
  553. delete _resp.data;
  554. return _resp;
  555. } else {
  556. return resp;
  557. }
  558. };
  559. const convertHeadersToLowerCase = headers => {
  560. return Object.keys(headers).reduce((acc, key) => {
  561. acc[key.toLowerCase()] = headers[key];
  562. return acc;
  563. }, {});
  564. };
  565. const convertHeadersToCamelCase = headers => {
  566. return Object.keys(headers).reduce((acc, key) => {
  567. const newKey = key.split("-").map(word => word[0].toUpperCase() + word.slice(1)).join("-");
  568. acc[newKey] = headers[key];
  569. return acc;
  570. }, {});
  571. };
  572. const raiseExceptionByStatusCode = (resp, config = null) => {
  573. if (!!resp && resp.status >= 400) {
  574. logger.debug(`Raise exception when status code is ${resp.status}`);
  575. return {
  576. name: "RequestException",
  577. message: `Request failed with status code ${resp.status}`,
  578. config: config || resp.config,
  579. response: resp
  580. };
  581. }
  582. };
  583. const interceptors = {
  584. request: new InterceptorManager(),
  585. response: new InterceptorManager(false)
  586. };
  587. let requestInterceptorChain = [];
  588. let responseInterceptorChain = [];
  589. let synchronousRequestInterceptors = true;
  590. function interceptConfig(config) {
  591. config = paramsToQueryString(config);
  592. logger.debug(`HTTP ${config["method"].toUpperCase()}:`+'\n'+`${JSON.stringify(config)}`);
  593. return config;
  594. }
  595. function interceptResponse(resp) {
  596. try {
  597. resp = !!resp ? modifyResponse(resp) : resp;
  598. logger.sniffer(`HTTP ${resp.config["method"].toUpperCase()}:`+'\n'+`${JSON.stringify(resp.config)}`+'\n'+`STATUS CODE:`+'\n'+`${resp.status}`+'\n'+`RESPONSE:`+'\n'+`${typeof resp.body === "object" ? JSON.stringify(resp.body) : resp.body}`);
  599. const err = raiseExceptionByStatusCode(resp);
  600. if (!!err) {
  601. return Promise.reject(err);
  602. }
  603. return resp;
  604. } catch (err) {
  605. logger.error(err);
  606. return resp;
  607. }
  608. }
  609. const registerInterceptors = config => {
  610. try {
  611. requestInterceptorChain = [];
  612. responseInterceptorChain = [];
  613. interceptors.request.forEach(interceptor => {
  614. if (typeof interceptor.runWhen === "function" && interceptor.runWhen(config) === false) {
  615. return;
  616. }
  617. synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
  618. requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
  619. });
  620. interceptors.response.forEach(interceptor => {
  621. responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
  622. });
  623. } catch (err) {
  624. logger.error(`Failed to register interceptors: ${err}.`);
  625. }
  626. };
  627. const request = (method, config) => {
  628. let dispatchRequest;
  629. const _method = method.toUpperCase();
  630. config = mergeConfig(_method, config);
  631. if (env.isNode) {
  632. dispatchRequest = axiosInstance;
  633. } else {
  634. if (env.isSurgeLike) {
  635. dispatchRequest = config => {
  636. return new Promise((resolve, reject) => {
  637. $httpClient[method.toLowerCase()](config, (err, resp, body) => {
  638. if (err) {
  639. let newErr = {
  640. name: err.name || err,
  641. message: err.message || err,
  642. stack: err.stack || err,
  643. config: config,
  644. response: modifyResponse(resp)
  645. };
  646. reject(newErr);
  647. } else {
  648. resp.config = config;
  649. resp.body = body;
  650. resolve(resp);
  651. }
  652. });
  653. });
  654. };
  655. } else {
  656. dispatchRequest = config => {
  657. return new Promise((resolve, reject) => {
  658. $task.fetch(config).then(resp => {
  659. resp = modifyResponse(resp, config);
  660. const err = raiseExceptionByStatusCode(resp, config);
  661. if (err) {
  662. return Promise.reject(err);
  663. }
  664. resolve(resp);
  665. }).catch(err => {
  666. let newErr = {
  667. name: err.message || err.error,
  668. message: err.message || err.error,
  669. stack: err.error,
  670. config: config,
  671. response: !!err.response ? modifyResponse(err.response) : null
  672. };
  673. reject(newErr);
  674. });
  675. });
  676. };
  677. }
  678. }
  679. let promise;
  680. registerInterceptors(config);
  681. const defaultRequestInterceptors = [ interceptConfig, undefined ];
  682. const defaultResponseInterceptors = [ interceptResponse, undefined ];
  683. if (!synchronousRequestInterceptors) {
  684. logger.debug("Interceptors are executed in asynchronous mode");
  685. let chain = [ dispatchRequest, undefined ];
  686. Array.prototype.unshift.apply(chain, defaultRequestInterceptors);
  687. Array.prototype.unshift.apply(chain, requestInterceptorChain);
  688. chain = chain.concat(defaultResponseInterceptors);
  689. chain = chain.concat(responseInterceptorChain);
  690. promise = Promise.resolve(config);
  691. while (chain.length) {
  692. try {
  693. let onFulfilled = chain.shift();
  694. let onRejected = chain.shift();
  695. if (!env.isNode && config["timeout"] && onFulfilled === dispatchRequest) {
  696. onFulfilled = requestTimeout;
  697. }
  698. if (typeof onFulfilled === "function") {
  699. logger.debug(`Executing request fulfilled ${onFulfilled.name}`);
  700. }
  701. if (typeof onRejected === "function") {
  702. logger.debug(`Executing request rejected ${onRejected.name}`);
  703. }
  704. promise = promise.then(onFulfilled, onRejected);
  705. } catch (err) {
  706. logger.error(`request exception: ${err}`);
  707. }
  708. }
  709. return promise;
  710. } else {
  711. logger.debug("Interceptors are executed in synchronous mode");
  712. Array.prototype.unshift.apply(requestInterceptorChain, defaultRequestInterceptors);
  713. requestInterceptorChain = requestInterceptorChain.concat([ interceptConfig, undefined ]);
  714. while (requestInterceptorChain.length) {
  715. let onFulfilled = requestInterceptorChain.shift();
  716. let onRejected = requestInterceptorChain.shift();
  717. try {
  718. if (typeof onFulfilled === "function") {
  719. logger.debug(`Executing request fulfilled ${onFulfilled.name}`);
  720. }
  721. config = onFulfilled(config);
  722. } catch (error) {
  723. if (typeof onRejected === "function") {
  724. logger.debug(`Executing request rejected ${onRejected.name}`);
  725. }
  726. onRejected(error);
  727. break;
  728. }
  729. }
  730. try {
  731. if (!env.isNode && config["timeout"]) {
  732. promise = requestTimeout(config);
  733. } else {
  734. promise = dispatchRequest(config);
  735. }
  736. } catch (err) {
  737. return Promise.reject(err);
  738. }
  739. Array.prototype.unshift.apply(responseInterceptorChain, defaultResponseInterceptors);
  740. while (responseInterceptorChain.length) {
  741. promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());
  742. }
  743. return promise;
  744. }
  745. function requestTimeout(config) {
  746. try {
  747. const timer = new Promise((_, reject) => {
  748. setTimeout(() => {
  749. let err = {
  750. message: `timeout of ${config["timeout"]}ms exceeded.`,
  751. config: config
  752. };
  753. reject(err);
  754. }, config["timeout"]);
  755. });
  756. return Promise.race([ dispatchRequest(config), timer ]);
  757. } catch (err) {
  758. logger.error(`Request Timeout exception: ${err}.`);
  759. }
  760. }
  761. };
  762. return {
  763. request: request,
  764. interceptors: interceptors,
  765. convertHeadersToLowerCase: convertHeadersToLowerCase,
  766. convertHeadersToCamelCase: convertHeadersToCamelCase,
  767. modifyResponse: modifyResponse,
  768. get: configOrUrl => {
  769. return request("GET", configOrUrl);
  770. },
  771. post: configOrUrl => {
  772. return request("POST", configOrUrl);
  773. },
  774. put: configOrUrl => {
  775. return request("PUT", configOrUrl);
  776. },
  777. patch: configOrUrl => {
  778. return request("PATCH", configOrUrl);
  779. },
  780. delete: configOrUrl => {
  781. return request("DELETE", configOrUrl);
  782. },
  783. head: configOrUrl => {
  784. return request("HEAD", configOrUrl);
  785. },
  786. options: configOrUrl => {
  787. return request("OPTIONS", configOrUrl);
  788. }
  789. };
  790. }
  791. function MagicData(env, logger) {
  792. let node = {
  793. fs: undefined,
  794. data: {}
  795. };
  796. if (env.isNode) {
  797. node.fs = require("fs");
  798. try {
  799. node.fs.accessSync("./magic.json", node.fs.constants.R_OK | node.fs.constants.W_OK);
  800. } catch (err) {
  801. node.fs.writeFileSync("./magic.json", "{}", {
  802. encoding: "utf8"
  803. });
  804. }
  805. node.data = require("./magic.json");
  806. }
  807. const defaultValueComparator = (oldVal, newVal) => {
  808. if (typeof newVal === "object") {
  809. return false;
  810. } else {
  811. return oldVal === newVal;
  812. }
  813. };
  814. const _typeConvertor = val => {
  815. if (val === "true") {
  816. return true;
  817. } else if (val === "false") {
  818. return false;
  819. } else if (typeof val === "undefined") {
  820. return null;
  821. } else {
  822. return val;
  823. }
  824. };
  825. const _valConvertor = (val, default_, session, read_no_session) => {
  826. if (session) {
  827. try {
  828. if (typeof val === "string") val = JSON.parse(val);
  829. if (val["magic_session"] === true) {
  830. val = val[session];
  831. } else {
  832. val = null;
  833. }
  834. } catch {
  835. val = null;
  836. }
  837. }
  838. if (typeof val === "string" && val !== "null") {
  839. try {
  840. val = JSON.parse(val);
  841. } catch {}
  842. }
  843. if (read_no_session === false && !!val && val["magic_session"] === true) {
  844. val = null;
  845. }
  846. if ((val === null || typeof val === "undefined") && default_ !== null && typeof default_ !== "undefined") {
  847. val = default_;
  848. }
  849. val = _typeConvertor(val);
  850. return val;
  851. };
  852. const convertToObject = obj => {
  853. if (typeof obj === "string") {
  854. let data = {};
  855. try {
  856. data = JSON.parse(obj);
  857. const type = typeof data;
  858. if (type !== "object" || data instanceof Array || type === "bool" || data === null) {
  859. data = {};
  860. }
  861. } catch {}
  862. return data;
  863. } else if (obj instanceof Array || obj === null || typeof obj === "undefined" || obj !== obj || typeof obj === "boolean") {
  864. return {};
  865. } else {
  866. return obj;
  867. }
  868. };
  869. const readForNode = (key, default_ = null, session = "", read_no_session = false, externalData = null) => {
  870. let data = externalData || node.data;
  871. if (!!data && typeof data[key] !== "undefined" && data[key] !== null) {
  872. val = data[key];
  873. } else {
  874. val = !!session ? {} : null;
  875. }
  876. val = _valConvertor(val, default_, session, read_no_session);
  877. return val;
  878. };
  879. const read = (key, default_ = null, session = "", read_no_session = false, externalData = null) => {
  880. let val = "";
  881. if (externalData || env.isNode) {
  882. val = readForNode(key, default_, session, read_no_session, externalData);
  883. } else {
  884. if (env.isSurgeLike) {
  885. val = $persistentStore.read(key);
  886. } else if (env.isQuanX) {
  887. val = $prefs.valueForKey(key);
  888. }
  889. val = _valConvertor(val, default_, session, read_no_session);
  890. }
  891. logger.debug(`READ DATA [${key}]${!!session ? `[${session}]` : ""} <${typeof val}>`+'\n'+`${JSON.stringify(val)}`);
  892. return val;
  893. };
  894. const writeForNode = (key, val, session = "", externalData = null) => {
  895. let data = externalData || node.data;
  896. data = convertToObject(data);
  897. if (!!session) {
  898. let obj = convertToObject(data[key]);
  899. obj["magic_session"] = true;
  900. obj[session] = val;
  901. data[key] = obj;
  902. } else {
  903. data[key] = val;
  904. }
  905. if (externalData !== null) {
  906. externalData = data;
  907. }
  908. return data;
  909. };
  910. const write = (key, val, session = "", externalData = null) => {
  911. if (typeof val === "undefined" || val !== val) {
  912. return false;
  913. }
  914. if (!env.isNode && (typeof val === "boolean" || typeof val === "number")) {
  915. val = String(val);
  916. }
  917. let data = "";
  918. if (externalData || env.isNode) {
  919. data = writeForNode(key, val, session, externalData);
  920. } else {
  921. if (!session) {
  922. data = val;
  923. } else {
  924. if (env.isSurgeLike) {
  925. data = !!$persistentStore.read(key) ? $persistentStore.read(key) : data;
  926. } else if (env.isQuanX) {
  927. data = !!$prefs.valueForKey(key) ? $prefs.valueForKey(key) : data;
  928. }
  929. data = convertToObject(data);
  930. data["magic_session"] = true;
  931. data[session] = val;
  932. }
  933. }
  934. if (!!data && typeof data === "object") {
  935. data = JSON.stringify(data, null, 4);
  936. }
  937. logger.debug(`WRITE DATA [${key}]${session ? `[${session}]` : ""} <${typeof val}>`+'\n'+`${JSON.stringify(val)}`);
  938. if (!externalData) {
  939. if (env.isSurgeLike) {
  940. return $persistentStore.write(data, key);
  941. } else if (env.isQuanX) {
  942. return $prefs.setValueForKey(data, key);
  943. } else if (env.isNode) {
  944. try {
  945. node.fs.writeFileSync("./magic.json", data);
  946. return true;
  947. } catch (err) {
  948. logger.error(err);
  949. return false;
  950. }
  951. }
  952. }
  953. return true;
  954. };
  955. const update = (key, val, session, comparator = defaultValueComparator, externalData = null) => {
  956. val = _typeConvertor(val);
  957. const oldValue = read(key, null, session, false, externalData);
  958. if (comparator(oldValue, val) === true) {
  959. return false;
  960. } else {
  961. const result = write(key, val, session, externalData);
  962. let newVal = read(key, null, session, false, externalData);
  963. if (comparator === defaultValueComparator && typeof newVal === "object") {
  964. return result;
  965. }
  966. return comparator(val, newVal);
  967. }
  968. };
  969. const delForNode = (key, session, externalData) => {
  970. let data = externalData || node.data;
  971. data = convertToObject(data);
  972. if (!!session) {
  973. obj = convertToObject(data[key]);
  974. delete obj[session];
  975. data[key] = obj;
  976. } else {
  977. delete data[key];
  978. }
  979. if (!!externalData) {
  980. externalData = data;
  981. }
  982. return data;
  983. };
  984. const del = (key, session = "", externalData = null) => {
  985. let data = {};
  986. if (externalData || env.isNode) {
  987. data = delForNode(key, session, externalData);
  988. if (!externalData) {
  989. node.fs.writeFileSync("./magic.json", JSON.stringify(data, null, 4));
  990. } else {
  991. externalData = data;
  992. }
  993. } else {
  994. if (!session) {
  995. if (env.isStorm) {
  996. return $persistentStore.remove(key);
  997. } else if (env.isSurgeLike) {
  998. return $persistentStore.write(null, key);
  999. } else if (env.isQuanX) {
  1000. return $prefs.removeValueForKey(key);
  1001. }
  1002. } else {
  1003. if (env.isSurgeLike) {
  1004. data = $persistentStore.read(key);
  1005. } else if (env.isQuanX) {
  1006. data = $prefs.valueForKey(key);
  1007. }
  1008. data = convertToObject(data);
  1009. delete data[session];
  1010. const json = JSON.stringify(data, null, 4);
  1011. write(key, json);
  1012. }
  1013. }
  1014. logger.debug(`DELETE KEY [${key}]${!!session ? `[${session}]` : ""}`);
  1015. };
  1016. const allSessionNames = (key, externalData = null) => {
  1017. let _sessions = [];
  1018. let data = read(key, null, null, true, externalData);
  1019. data = convertToObject(data);
  1020. if (data["magic_session"] !== true) {
  1021. _sessions = [];
  1022. } else {
  1023. _sessions = Object.keys(data).filter(key => key !== "magic_session");
  1024. }
  1025. logger.debug(`READ ALL SESSIONS [${key}] <${typeof _sessions}>`+'\n'+`${JSON.stringify(_sessions, null, 4)}`);
  1026. return _sessions;
  1027. };
  1028. const allSessions = (key, externalData = null) => {
  1029. let _sessions = {};
  1030. let data = read(key, null, null, true, externalData);
  1031. data = convertToObject(data);
  1032. if (data["magic_session"] === true) {
  1033. _sessions = {
  1034. ...data
  1035. };
  1036. delete _sessions["magic_session"];
  1037. }
  1038. logger.debug(`READ ALL SESSIONS [${key}] <${typeof _sessions}>`+'\n'+`${JSON.stringify(_sessions, null, 4)}`);
  1039. return _sessions;
  1040. };
  1041. return {
  1042. read: read,
  1043. write: write,
  1044. del: del,
  1045. update: update,
  1046. allSessions: allSessions,
  1047. allSessionNames: allSessionNames,
  1048. defaultValueComparator: defaultValueComparator,
  1049. convertToObject: convertToObject
  1050. };
  1051. }
  1052. function MagicNotification(scriptName, env, logger, http) {
  1053. let _barkUrl = null;
  1054. let _barkKey = null;
  1055. let notifyInfo = []
  1056. const setBark = url => {
  1057. try {
  1058. let _url = url.replace(/\/+$/g, "");
  1059. _barkUrl = `${/^https?:\/\/([^/]*)/.exec(_url)[0]}/push`;
  1060. _barkKey = /\/([^\/]+)\/?$/.exec(_url)[1];
  1061. } catch (ex) {
  1062. logger.error(`Bark url error: ${ex}.`);
  1063. }
  1064. };
  1065. function appendNotifyInfo(info, type) {
  1066. if (type == 1) {
  1067. notifyInfo = info
  1068. } else {
  1069. notifyInfo.push(info)
  1070. }
  1071. }
  1072. function prependNotifyInfo(info) {
  1073. notifyInfo.splice(0, 0, info)
  1074. }
  1075. function msg(subtitle, message, openUrl, mediaUrl) {
  1076. let opts = {};
  1077. if(openUrl){
  1078. opts["open-url"] = openUrl;
  1079. }
  1080. if(mediaUrl){
  1081. opts["media-url"] = mediaUrl;
  1082. }
  1083. if(!message || message.length == 0){
  1084. if (Array.isArray(notifyInfo)) {
  1085. message = notifyInfo.join("\n");
  1086. } else {
  1087. message = notifyInfo;
  1088. }
  1089. }
  1090. if(message && message.length > 0){
  1091. post(scriptName, "", message, opts);
  1092. }
  1093. }
  1094. function post(title = scriptName, subTitle = "", body = "", opts = "") {
  1095. const _adaptOpts = _opts => {
  1096. try {
  1097. let newOpts = {};
  1098. if (typeof _opts === "string") {
  1099. if(_opts.length > 0){
  1100. if (env.isLoon){
  1101. newOpts = {
  1102. openUrl: _opts
  1103. };
  1104. }else if (env.isQuanX){
  1105. newOpts = {
  1106. "open-url": _opts
  1107. };
  1108. }else if (env.isSurge){
  1109. newOpts = {
  1110. url: _opts
  1111. };
  1112. }
  1113. }
  1114. } else if (typeof _opts === "object") {
  1115. if (env.isLoon) {
  1116. newOpts["openUrl"] = !!_opts["open-url"] ? _opts["open-url"] : "";
  1117. newOpts["mediaUrl"] = !!_opts["media-url"] ? _opts["media-url"] : "";
  1118. } else if (env.isQuanX) {
  1119. newOpts = !!_opts["open-url"] || !!_opts["media-url"] ? _opts : {};
  1120. } else if (env.isSurge) {
  1121. let openUrl = _opts["open-url"] || _opts["openUrl"];
  1122. newOpts = openUrl ? {
  1123. url: openUrl
  1124. } : {};
  1125. }
  1126. }
  1127. return newOpts;
  1128. } catch (err) {
  1129. logger.error(`通知选项转换失败${err}`);
  1130. }
  1131. return _opts;
  1132. };
  1133. opts = _adaptOpts(opts);
  1134. if (arguments.length === 1) {
  1135. title = scriptName;
  1136. subTitle = "", body = arguments[0];
  1137. }
  1138. logger.notify('\n'+`title:${title}`+'\n'+`subTitle:${subTitle}`+'\n'+`body:${body}`+'\n'+`options:${typeof opts === "object" ? JSON.stringify(opts) : opts}`);
  1139. if (env.isSurge) {
  1140. $notification.post(title, subTitle, body, opts);
  1141. } else if (env.isLoon) {
  1142. if (!!opts) $notification.post(title, subTitle, body, opts); else $notification.post(title, subTitle, body);
  1143. } else if (env.isQuanX) {
  1144. $notify(title, subTitle, body, opts);
  1145. }
  1146. if (_barkUrl && _barkKey) {
  1147. bark(title, subTitle, body);
  1148. }
  1149. }
  1150. function debug(title = scriptName, subTitle = "", body = "", opts = "") {
  1151. if (logger.getLevel() === "DEBUG") {
  1152. if (arguments.length === 1) {
  1153. title = scriptName;
  1154. subTitle = "";
  1155. body = arguments[0];
  1156. }
  1157. this.post(title, subTitle, body, opts);
  1158. }
  1159. }
  1160. function bark(title = scriptName, subTitle = "", body = "", opts = "") {
  1161. if (typeof http === "undefined" || typeof http.post === "undefined") {
  1162. throw "Bark notification needs to import MagicHttp module.";
  1163. }
  1164. let options = {
  1165. url: _barkUrl,
  1166. headers: {
  1167. "content-type": "application/json; charset=utf-8"
  1168. },
  1169. body: {
  1170. title: title,
  1171. body: subTitle ? `${subTitle}`+'\n'+`${body}` : body,
  1172. device_key: _barkKey
  1173. }
  1174. };
  1175. http.post(options).catch(ex => {
  1176. logger.error(`Bark notify error: ${ex}`);
  1177. });
  1178. }
  1179. return {
  1180. post: post,
  1181. debug: debug,
  1182. bark: bark,
  1183. setBark: setBark,
  1184. appendNotifyInfo: appendNotifyInfo,
  1185. prependNotifyInfo: prependNotifyInfo,
  1186. msg: msg,
  1187. };
  1188. }
  1189. function MagicUtils(env, logger) {
  1190. const retry = (fn, retries = 5, interval = 0, callback = null) => {
  1191. return (...args) => {
  1192. return new Promise((resolve, reject) => {
  1193. function _retry(...args) {
  1194. Promise.resolve().then(() => fn.apply(this, args)).then(result => {
  1195. if (typeof callback === "function") {
  1196. Promise.resolve().then(() => callback(result)).then(() => {
  1197. resolve(result);
  1198. }).catch(ex => {
  1199. if (retries >= 1) {
  1200. if (interval > 0) setTimeout(() => _retry.apply(this, args), interval); else _retry.apply(this, args);
  1201. } else {
  1202. reject(ex);
  1203. }
  1204. retries--;
  1205. });
  1206. } else {
  1207. resolve(result);
  1208. }
  1209. }).catch(ex => {
  1210. logger.error(ex);
  1211. if (retries >= 1 && interval > 0) {
  1212. setTimeout(() => _retry.apply(this, args), interval);
  1213. } else if (retries >= 1) {
  1214. _retry.apply(this, args);
  1215. } else {
  1216. reject(ex);
  1217. }
  1218. retries--;
  1219. });
  1220. }
  1221. _retry.apply(this, args);
  1222. });
  1223. };
  1224. };
  1225. const formatTime = (time, fmt = "yyyy-MM-dd hh:mm:ss") => {
  1226. let o = {
  1227. "M+": time.getMonth() + 1,
  1228. "d+": time.getDate(),
  1229. "h+": time.getHours(),
  1230. "m+": time.getMinutes(),
  1231. "s+": time.getSeconds(),
  1232. "q+": Math.floor((time.getMonth() + 3) / 3),
  1233. S: time.getMilliseconds()
  1234. };
  1235. if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (time.getFullYear() + "").substr(4 - RegExp.$1.length));
  1236. for (let k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
  1237. return fmt;
  1238. };
  1239. const now = () => {
  1240. return formatTime(new Date(), "yyyy-MM-dd hh:mm:ss");
  1241. };
  1242. const today = () => {
  1243. return formatTime(new Date(), "yyyy-MM-dd");
  1244. };
  1245. const sleep = time => {
  1246. return new Promise(resolve => setTimeout(resolve, time));
  1247. };
  1248. const assert = (val, msg = null) => {
  1249. if (env.isNode) {
  1250. const _assert = require("assert");
  1251. if (msg) _assert(val, msg); else _assert(val);
  1252. } else {
  1253. if (val !== true) {
  1254. let err = `AssertionError: ${msg || "The expression evaluated to a falsy value."}`;
  1255. logger.error(err);
  1256. }
  1257. }
  1258. };
  1259. return {
  1260. retry: retry,
  1261. formatTime: formatTime,
  1262. now: now,
  1263. today: today,
  1264. sleep: sleep,
  1265. assert: assert
  1266. };
  1267. }
  1268. function MagicQingLong(env, data, logger) {
  1269. let qlUrl = "";
  1270. let qlName = "";
  1271. let qlClient = "";
  1272. let qlSecret = "";
  1273. let qlPwd = "";
  1274. let qlToken = "";
  1275. const magicJsonFileName = "magic.json";
  1276. const timeout = 3e3;
  1277. const http = (() => MagicHttp(env, logger))();
  1278. const init = (url, clientId, clientSecret, username, password) => {
  1279. qlUrl = url;
  1280. qlClient = clientId;
  1281. qlSecret = clientSecret;
  1282. qlName = username;
  1283. qlPwd = password;
  1284. };
  1285. function readQingLongConfig(config) {
  1286. qlUrl = qlUrl || data.read("magic_qlurl");
  1287. qlToken = qlToken || data.read("magic_qltoken");
  1288. logger.debug(`QingLong url: ${qlUrl}`+'\n'+`QingLong token: ${qlToken}`);
  1289. return config;
  1290. }
  1291. function setBaseUrlAndTimeout(config) {
  1292. if (!qlUrl) {
  1293. qlUrl = data.read("magic_qlurl");
  1294. }
  1295. if (config.url.indexOf(qlUrl) < 0) {
  1296. config.url = `${qlUrl}${config.url}`;
  1297. }
  1298. return {
  1299. ...config,
  1300. timeout: timeout
  1301. };
  1302. }
  1303. function setTimestamp(config) {
  1304. config.params = {
  1305. ...config.params,
  1306. t: Date.now()
  1307. };
  1308. return config;
  1309. }
  1310. async function setAuthorization(config) {
  1311. qlToken = qlToken || data.read("magic_qltoken", "");
  1312. if (!qlToken) {
  1313. await getToken();
  1314. }
  1315. config.headers["authorization"] = `Bearer ${qlToken}`;
  1316. return config;
  1317. }
  1318. function switchClientMode(config) {
  1319. qlClient = qlClient || data.read("magic_qlclient");
  1320. if (!!qlClient) {
  1321. config.url = config.url.replace("/api/", "/open/");
  1322. }
  1323. return config;
  1324. }
  1325. async function refreshToken(error) {
  1326. try {
  1327. const message = error.message || error.error || JSON.stringify(error);
  1328. if ((message.indexOf("NSURLErrorDomain") >= 0 && message.indexOf("-1012") >= 0 || !!error.response && error.response.status === 401) && !!error.config && error.config.refreshToken !== true) {
  1329. logger.warning(`QingLong Panel token has expired`);
  1330. logger.info("Refreshing the QingLong Panel token");
  1331. await getToken();
  1332. error.config["refreshToken"] = true;
  1333. logger.info("Call the previous method again");
  1334. return await http.request(error.config.method, error.config);
  1335. } else {
  1336. return Promise.reject(error);
  1337. }
  1338. } catch (ex) {
  1339. return Promise.reject(ex);
  1340. }
  1341. }
  1342. http.interceptors.request.use(setBaseUrlAndTimeout, undefined);
  1343. http.interceptors.request.use(switchClientMode, undefined, {
  1344. runWhen: config => {
  1345. return config.url.indexOf("api/user/login") < 0 && config.url.indexOf("open/auth/token") < 0;
  1346. }
  1347. });
  1348. http.interceptors.request.use(setAuthorization, undefined, {
  1349. runWhen: config => {
  1350. return config.url.indexOf("api/user/login") < 0 && config.url.indexOf("open/auth/token") < 0;
  1351. }
  1352. });
  1353. http.interceptors.request.use(setTimestamp, undefined, {
  1354. runWhen: config => {
  1355. return config.url.indexOf("open/auth/token") < 0;
  1356. }
  1357. });
  1358. http.interceptors.request.use(readQingLongConfig, undefined);
  1359. http.interceptors.response.use(undefined, refreshToken);
  1360. async function getToken() {
  1361. qlClient = qlClient || data.read("magic_qlclient");
  1362. qlSecret = qlSecret || data.read("magic_qlsecrt");
  1363. qlName = qlName || data.read("magic_qlname");
  1364. qlPwd = qlPwd || data.read("magic_qlpwd");
  1365. if (qlUrl && qlClient && qlSecret) {
  1366. logger.info("Get token from QingLong Panel");
  1367. await http.get({
  1368. url: `/open/auth/token`,
  1369. headers: {
  1370. "content-type": "application/json"
  1371. },
  1372. params: {
  1373. client_id: qlClient,
  1374. client_secret: qlSecret
  1375. }
  1376. }).then(resp => {
  1377. if (Object.keys(resp.body).length > 0 && resp.body.data && resp.body.data.token) {
  1378. logger.info("Successfully logged in to QingLong Panel");
  1379. qlToken = resp.body.data.token;
  1380. data.write("magic_qltoken", qlToken);
  1381. } else {
  1382. throw new Error("Get QingLong Panel token failed.");
  1383. }
  1384. }).catch(err => {
  1385. logger.error(`Error logging in to QingLong Panel.`+'\n'+`${err.message || err}`);
  1386. });
  1387. } else if (qlUrl && qlName && qlPwd) {
  1388. await http.post({
  1389. url: `/api/user/login`,
  1390. headers: {
  1391. "content-type": "application/json"
  1392. },
  1393. body: {
  1394. username: qlName,
  1395. password: qlPwd
  1396. }
  1397. }).then(resp => {
  1398. logger.info("Successfully logged in to QingLong Panel");
  1399. qlToken = resp.body.data.token;
  1400. data.write("magic_qltoken", qlToken);
  1401. }).catch(err => {
  1402. logger.error(`Error logging in to QingLong Panel.`+'\n'+`${err.message || err}`);
  1403. });
  1404. }
  1405. return qlToken;
  1406. }
  1407. async function setEnv(name, value, id = null) {
  1408. qlUrl = qlUrl || data.read("magic_qlurl");
  1409. if (id === null) {
  1410. let envIds = await setEnvs([ {
  1411. name: name,
  1412. value: value
  1413. } ]);
  1414. if (!!envIds && envIds.length === 1) {
  1415. return envIds[0];
  1416. }
  1417. } else {
  1418. await http.put({
  1419. url: `/api/envs`,
  1420. headers: {
  1421. "content-type": "application/json"
  1422. },
  1423. body: {
  1424. name: name,
  1425. value: value,
  1426. id: id
  1427. }
  1428. }).then(resp => {
  1429. if (resp.body.code === 200) {
  1430. logger.debug(`QINGLONG UPDATE ENV ${name} <${typeof value}> (${id})`+'\n'+`${JSON.stringify(value)}`);
  1431. return true;
  1432. } else {
  1433. logger.error(`Error adding environment variable from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1434. }
  1435. }).catch(err => {
  1436. logger.error(`Error adding environment variable from QingLong Panel.`+'\n'+`${err.message || err}`);
  1437. return false;
  1438. });
  1439. }
  1440. }
  1441. async function setEnvs(envs) {
  1442. let envIds = [];
  1443. await http.post({
  1444. url: `/api/envs`,
  1445. headers: {
  1446. "content-type": "application/json"
  1447. },
  1448. body: envs
  1449. }).then(resp => {
  1450. if (resp.body.code === 200) {
  1451. resp.body.data.forEach(element => {
  1452. logger.debug(`QINGLONG ADD ENV ${element.name} <${typeof element.value}> (${element.id})`+'\n'+`${JSON.stringify(element)}`);
  1453. envIds.push(element.id);
  1454. });
  1455. } else {
  1456. logger.error(`Error adding environments variable from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1457. }
  1458. }).catch(err => {
  1459. logger.error(`Error adding environments variable from QingLong Panel.`+'\n'+`${err.message || err}`);
  1460. });
  1461. return envIds;
  1462. }
  1463. async function delEnvs(ids) {
  1464. return await http.delete({
  1465. url: `/api/envs`,
  1466. headers: {
  1467. accept: "application/json",
  1468. "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
  1469. connection: "keep-alive",
  1470. "content-type": "application/json;charset=UTF-8",
  1471. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36 Edg/102.0.1245.30"
  1472. },
  1473. body: ids
  1474. }).then(resp => {
  1475. if (resp.body.code === 200) {
  1476. logger.debug(`QINGLONG DELETE ENV IDS: ${ids}`);
  1477. return true;
  1478. } else {
  1479. logger.error(`Error deleting environments variable from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1480. return false;
  1481. }
  1482. }).catch(err => {
  1483. logger.error(`Error deleting environments variable from QingLong Panel.`+'\n'+`${err.message || err}`);
  1484. });
  1485. }
  1486. async function getEnvs(name = null, searchValue = "", retired = 0) {
  1487. let envs = [];
  1488. await http.get({
  1489. url: `/api/envs`,
  1490. headers: {
  1491. "content-type": "application/json"
  1492. },
  1493. params: {
  1494. searchValue: searchValue
  1495. }
  1496. }).then(resp => {
  1497. if (resp.body.code === 200) {
  1498. const allEnvs = resp.body.data;
  1499. if (!!name) {
  1500. let _envs = [];
  1501. for (const env of allEnvs) {
  1502. if (env.name === name) {
  1503. envs.push(env);
  1504. }
  1505. }
  1506. envs = _envs;
  1507. }
  1508. envs = allEnvs;
  1509. } else {
  1510. throw new Error(`Error reading environment variable from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1511. }
  1512. }).catch(err => {
  1513. throw new Error(`Error reading environments variable from QingLong Panel.`+'\n'+`${err.message || err}`);
  1514. });
  1515. return envs;
  1516. }
  1517. async function getEnv(id) {
  1518. let env = null;
  1519. const allEnvs = await getEnvs();
  1520. for (const _env of allEnvs) {
  1521. if (_env.id === id) {
  1522. env = _env;
  1523. break;
  1524. }
  1525. }
  1526. return env;
  1527. }
  1528. async function disableEnvs(ids) {
  1529. let result = false;
  1530. await http.put({
  1531. url: `/api/envs/disable`,
  1532. headers: {
  1533. accept: "application/json",
  1534. "accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
  1535. connection: "keep-alive",
  1536. "content-type": "application/json;charset=UTF-8",
  1537. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36 Edg/102.0.1245.30"
  1538. },
  1539. body: ids
  1540. }).then(resp => {
  1541. if (resp.body.code === 200) {
  1542. logger.debug(`QINGLONG DISABLED ENV IDS: ${ids}`);
  1543. result = true;
  1544. } else {
  1545. logger.error(`Error disabling environments variable from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1546. }
  1547. }).catch(err => {
  1548. logger.error(`Error disabling environments variable from QingLong Panel.`+'\n'+`${err.message || err}`);
  1549. });
  1550. return result;
  1551. }
  1552. async function enableEnvs(ids) {
  1553. let result = false;
  1554. await http.put({
  1555. url: `/api/envs/enable`,
  1556. headers: {
  1557. accept: "application/json",
  1558. "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
  1559. connection: "keep-alive",
  1560. "content-type": "application/json;charset=UTF-8",
  1561. "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36 Edg/102.0.1245.30"
  1562. },
  1563. body: ids
  1564. }).then(resp => {
  1565. if (resp.body.code === 200) {
  1566. logger.debug(`QINGLONG ENABLED ENV IDS: ${ids}`);
  1567. result = true;
  1568. } else {
  1569. logger.error(`Error enabling environments variable from Qilong panel.`+'\n'+`${JSON.stringify(resp)}`);
  1570. }
  1571. }).catch(err => {
  1572. logger.error(`Error enabling environments variable from Qilong panel.`+'\n'+`${err.message || err}`);
  1573. });
  1574. return result;
  1575. }
  1576. async function addScript(name, path = "", content = "") {
  1577. let result = false;
  1578. await http.post({
  1579. url: `/api/scripts`,
  1580. headers: {
  1581. "content-type": "application/json"
  1582. },
  1583. body: {
  1584. filename: name,
  1585. path: path,
  1586. content: content
  1587. }
  1588. }).then(resp => {
  1589. if (resp.body.code === 200) {
  1590. result = true;
  1591. } else {
  1592. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1593. }
  1594. }).catch(err => {
  1595. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${err.message || err}`);
  1596. });
  1597. return result;
  1598. }
  1599. async function getScript(name, path = "") {
  1600. let content = "";
  1601. await http.get({
  1602. url: `/api/scripts/${name}`,
  1603. params: {
  1604. path: path
  1605. }
  1606. }).then(resp => {
  1607. if (resp.body.code === 200) {
  1608. content = resp.body.data;
  1609. } else {
  1610. throw new Error(`Error reading data from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1611. }
  1612. }).catch(err => {
  1613. throw new Error(`Error reading data from QingLong Panel.`+'\n'+`${err.message || err}`);
  1614. });
  1615. return content;
  1616. }
  1617. async function editScript(name, path = "", content = "") {
  1618. let result = false;
  1619. await http.put({
  1620. url: `/api/scripts`,
  1621. headers: {
  1622. "content-type": "application/json"
  1623. },
  1624. body: {
  1625. filename: name,
  1626. path: path,
  1627. content: content
  1628. }
  1629. }).then(resp => {
  1630. if (resp.body.code === 200) {
  1631. result = true;
  1632. } else {
  1633. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1634. }
  1635. }).catch(err => {
  1636. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${err.message || err}`);
  1637. });
  1638. return result;
  1639. }
  1640. async function delScript(name, path = "") {
  1641. let result = false;
  1642. await http.delete({
  1643. url: `/api/scripts`,
  1644. headers: {
  1645. "content-type": "application/json"
  1646. },
  1647. body: {
  1648. filename: name,
  1649. path: path
  1650. }
  1651. }).then(resp => {
  1652. if (resp.body.code === 200) {
  1653. result = true;
  1654. } else {
  1655. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${JSON.stringify(resp)}`);
  1656. }
  1657. }).catch(err => {
  1658. logger.error(`Error reading data from QingLong Panel.`+'\n'+`${err.message || err}`);
  1659. });
  1660. return result;
  1661. }
  1662. async function write(key, val, session = "") {
  1663. let qlContent = await getScript(magicJsonFileName, "");
  1664. let qlData = data.convertToObject(qlContent);
  1665. let writeResult = data.write(key, val, session, qlData);
  1666. qlContent = JSON.stringify(qlData, null, 4);
  1667. let editResult = await editScript(magicJsonFileName, "", qlContent);
  1668. return editResult && writeResult;
  1669. }
  1670. async function batchWrite(...args) {
  1671. let qlContent = await getScript(magicJsonFileName, "");
  1672. let qlData = data.convertToObject(qlContent);
  1673. for (let arg of args) {
  1674. data.write(arg[0], arg[1], typeof arg[2] !== "undefined" ? arg[2] : "", qlData);
  1675. }
  1676. qlContent = JSON.stringify(qlData, null, 4);
  1677. return await editScript(magicJsonFileName, "", qlContent);
  1678. }
  1679. async function update(key, val, session, comparator = data.defaultValueComparator) {
  1680. let qlContent = await getScript(magicJsonFileName, "");
  1681. let qlData = data.convertToObject(qlContent);
  1682. const updateResult = data.update(key, val, session, comparator, qlData);
  1683. let editScriptResult = false;
  1684. if (updateResult === true) {
  1685. qlContent = JSON.stringify(qlData, null, 4);
  1686. editScriptResult = await editScript(magicJsonFileName, "", qlContent);
  1687. }
  1688. return updateResult && editScriptResult;
  1689. }
  1690. async function batchUpdate(...args) {
  1691. let qlContent = await getScript(magicJsonFileName, "");
  1692. let qlData = data.convertToObject(qlContent);
  1693. for (let arg of args) {
  1694. data.update(arg[0], arg[1], typeof arg[2] !== "undefined" ? arg[2] : "", typeof arg[3] !== "undefined" ? arg["comparator"] : data.defaultValueComparator, qlData);
  1695. }
  1696. qlContent = JSON.stringify(qlData, null, 4);
  1697. return await editScript(magicJsonFileName, "", qlContent);
  1698. }
  1699. async function read(key, val, session = "", read_no_session = false) {
  1700. let qlContent = await getScript(magicJsonFileName, "");
  1701. let qlData = data.convertToObject(qlContent);
  1702. return data.read(key, val, session, read_no_session, qlData);
  1703. }
  1704. async function batchRead(...args) {
  1705. let qlContent = await getScript(magicJsonFileName, "");
  1706. let qlData = data.convertToObject(qlContent);
  1707. let results = [];
  1708. for (let arg of args) {
  1709. const result = data.read(arg[0], arg[1], typeof arg[2] !== "undefined" ? arg[2] : "", typeof arg[3] === "boolean" ? arg[3] : false, qlData);
  1710. results.push(result);
  1711. }
  1712. return results;
  1713. }
  1714. async function del(key, session = "") {
  1715. let qlContent = await getScript(magicJsonFileName, "");
  1716. let qlData = data.convertToObject(qlContent);
  1717. const delResult = data.del(key, session, qlData);
  1718. qlContent = JSON.stringify(qlData, null, 4);
  1719. const editResult = await editScript(magicJsonFileName, "", qlContent);
  1720. return delResult && editResult;
  1721. }
  1722. async function batchDel(...args) {
  1723. let qlContent = await getScript(magicJsonFileName, "");
  1724. let qlData = data.convertToObject(qlContent);
  1725. for (let arg of args) {
  1726. data.del(arg[0], typeof arg[1] !== "undefined" ? arg[1] : "", qlData);
  1727. }
  1728. qlContent = JSON.stringify(qlData, null, 4);
  1729. return await editScript(magicJsonFileName, "", qlContent);
  1730. }
  1731. async function allSessionNames(key) {
  1732. let qlContent = await getScript(magicJsonFileName, "");
  1733. let qlData = data.convertToObject(qlContent);
  1734. return data.allSessionNames(key, qlData);
  1735. }
  1736. async function allSessions(key) {
  1737. let qlContent = await getScript(magicJsonFileName, "");
  1738. let qlData = data.convertToObject(qlContent);
  1739. return data.allSessions(key, qlData);
  1740. }
  1741. return {
  1742. url: qlUrl || data.read("magic_qlurl"),
  1743. init: init,
  1744. getToken: getToken,
  1745. setEnv: setEnv,
  1746. setEnvs: setEnvs,
  1747. getEnv: getEnv,
  1748. getEnvs: getEnvs,
  1749. delEnvs: delEnvs,
  1750. disableEnvs: disableEnvs,
  1751. enableEnvs: enableEnvs,
  1752. addScript: addScript,
  1753. getScript: getScript,
  1754. editScript: editScript,
  1755. delScript: delScript,
  1756. write: write,
  1757. read: read,
  1758. del: del,
  1759. update: update,
  1760. batchWrite: batchWrite,
  1761. batchRead: batchRead,
  1762. batchUpdate: batchUpdate,
  1763. batchDel: batchDel,
  1764. allSessions: allSessions,
  1765. allSessionNames: allSessionNames
  1766. };
  1767. }