MagicJS.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. function MagicJS(e = "MagicJS", t = "INFO") {
  2. const s = {
  3. accept: "Accept",
  4. "accept-ch": "Accept-CH",
  5. "accept-charset": "Accept-Charset",
  6. "accept-features": "Accept-Features",
  7. "accept-encoding": "Accept-Encoding",
  8. "accept-language": "Accept-Language",
  9. "accept-ranges": "Accept-Ranges",
  10. "access-control-allow-credentials": "Access-Control-Allow-Credentials",
  11. "access-control-allow-origin": "Access-Control-Allow-Origin",
  12. "access-control-allow-methods": "Access-Control-Allow-Methods",
  13. "access-control-allow-headers": "Access-Control-Allow-Headers",
  14. "access-control-max-age": "Access-Control-Max-Age",
  15. "access-control-expose-headers": "Access-Control-Expose-Headers",
  16. "access-control-request-method": "Access-Control-Request-Method",
  17. "access-control-request-headers": "Access-Control-Request-Headers",
  18. age: "Age",
  19. allow: "Allow",
  20. alternates: "Alternates",
  21. authorization: "Authorization",
  22. "cache-control": "Cache-Control",
  23. connection: "Connection",
  24. "content-encoding": "Content-Encoding",
  25. "content-language": "Content-Language",
  26. "content-length": "Content-Length",
  27. "content-location": "Content-Location",
  28. "content-md5": "Content-MD5",
  29. "content-range": "Content-Range",
  30. "content-security-policy": "Content-Security-Policy",
  31. "content-type": "Content-Type",
  32. cookie: "Cookie",
  33. dnt: "DNT",
  34. date: "Date",
  35. etag: "ETag",
  36. expect: "Expect",
  37. expires: "Expires",
  38. from: "From",
  39. host: "Host",
  40. "if-match": "If-Match",
  41. "if-modified-since": "If-Modified-Since",
  42. "if-none-match": "If-None-Match",
  43. "if-range": "If-Range",
  44. "if-unmodified-since": "If-Unmodified-Since",
  45. "last-event-id": "Last-Event-ID",
  46. "last-modified": "Last-Modified",
  47. link: "Link",
  48. location: "Location",
  49. "max-forwards": "Max-Forwards",
  50. negotiate: "Negotiate",
  51. origin: "Origin",
  52. pragma: "Pragma",
  53. "proxy-authenticate": "Proxy-Authenticate",
  54. "proxy-authorization": "Proxy-Authorization",
  55. range: "Range",
  56. referer: "Referer",
  57. "retry-after": "Retry-After",
  58. "sec-websocket-extensions": "Sec-Websocket-Extensions",
  59. "sec-websocket-key": "Sec-Websocket-Key",
  60. "sec-websocket-origin": "Sec-Websocket-Origin",
  61. "sec-websocket-protocol": "Sec-Websocket-Protocol",
  62. "sec-websocket-version": "Sec-Websocket-Version",
  63. server: "Server",
  64. "set-cookie": "Set-Cookie",
  65. "set-cookie2": "Set-Cookie2",
  66. "strict-transport-security": "Strict-Transport-Security",
  67. tcn: "TCN",
  68. te: "TE",
  69. trailer: "Trailer",
  70. "transfer-encoding": "Transfer-Encoding",
  71. upgrade: "Upgrade",
  72. "user-agent": "User-Agent",
  73. "variant-vary": "Variant-Vary",
  74. vary: "Vary",
  75. via: "Via",
  76. warning: "Warning",
  77. "www-authenticate": "WWW-Authenticate",
  78. "x-content-duration": "X-Content-Duration",
  79. "x-content-security-policy": "X-Content-Security-Policy",
  80. "x-dnsprefetch-control": "X-DNSPrefetch-Control",
  81. "x-frame-options": "X-Frame-Options",
  82. "x-requested-with": "X-Requested-With",
  83. "x-surge-skip-scripting": "X-Surge-Skip-Scripting"
  84. };
  85. return new class {
  86. constructor() {
  87. this.version = "2.2.3.3";
  88. this.scriptName = e;
  89. this.logLevels = {
  90. DEBUG: 5,
  91. INFO: 4,
  92. NOTIFY: 3,
  93. WARNING: 2,
  94. ERROR: 1,
  95. CRITICAL: 0,
  96. NONE: -1
  97. };
  98. this.isLoon = typeof $loon !== "undefined";
  99. this.isQuanX = typeof $task !== "undefined";
  100. this.isJSBox = typeof $drive !== "undefined";
  101. this.isNode = typeof module !== "undefined" && !this.isJSBox;
  102. this.isSurge = typeof $httpClient !== "undefined" && !this.isLoon;
  103. this.platform = this.getPlatform();
  104. this.node = {
  105. request: undefined,
  106. fs: undefined,
  107. data: {}
  108. };
  109. this.iOSUserAgent = "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";
  110. this.pcUserAgent = "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";
  111. this.logLevel = t;
  112. this._unifiedPushUrl = "";
  113. if (this.isNode) {
  114. this.node.fs = require("fs");
  115. this.node.request = require("request");
  116. try {
  117. this.node.fs.accessSync("./magic.json", this.node.fs.constants.R_OK | this.node.fs.constants.W_OK);
  118. } catch (e) {
  119. this.node.fs.writeFileSync("./magic.json", "{}", {
  120. encoding: "utf8"
  121. });
  122. }
  123. this.node.data = require("./magic.json");
  124. } else if (this.isJSBox) {
  125. if (!$file.exists("drive://MagicJS")) {
  126. $file.mkdir("drive://MagicJS");
  127. }
  128. if (!$file.exists("drive://MagicJS/magic.json")) {
  129. $file.write({
  130. data: $data({
  131. string: "{}"
  132. }),
  133. path: "drive://MagicJS/magic.json"
  134. });
  135. }
  136. }
  137. }
  138. set unifiedPushUrl(e) {
  139. this._unifiedPushUrl = !!e ? e.replace(/\/+$/g, "") : "";
  140. }
  141. set logLevel(e) {
  142. this._logLevel = typeof e === "string" ? e.toUpperCase() : "DEBUG";
  143. }
  144. get logLevel() {
  145. return this._logLevel;
  146. }
  147. get isRequest() {
  148. return typeof $request !== "undefined" && typeof $response === "undefined";
  149. }
  150. get isResponse() {
  151. return typeof $response !== "undefined";
  152. }
  153. get request() {
  154. return typeof $request !== "undefined" ? $request : undefined;
  155. }
  156. get response() {
  157. if (typeof $response !== "undefined") {
  158. if ($response.hasOwnProperty("status")) $response["statusCode"] = $response["status"];
  159. if ($response.hasOwnProperty("statusCode")) $response["status"] = $response["statusCode"];
  160. return $response;
  161. } else {
  162. return undefined;
  163. }
  164. }
  165. getPlatform() {
  166. if (this.isSurge) return "Surge"; else if (this.isQuanX) return "QuantumultX"; else if (this.isLoon) return "Loon"; else if (this.isJSBox) return "JSBox"; else if (this.isNode) return "Node.js"; else return "unknown";
  167. }
  168. read(e, t = "") {
  169. let s = "";
  170. if (this.isSurge || this.isLoon) {
  171. s = $persistentStore.read(e);
  172. } else if (this.isQuanX) {
  173. s = $prefs.valueForKey(e);
  174. } else if (this.isNode) {
  175. s = this.node.data;
  176. } else if (this.isJSBox) {
  177. s = $file.read("drive://MagicJS/magic.json").string;
  178. }
  179. try {
  180. if (this.isNode) s = s[e];
  181. if (this.isJSBox) s = JSON.parse(s)[e];
  182. if (!!t) {
  183. if (typeof s === "string") s = JSON.parse(s);
  184. s = !!s && typeof s === "object" ? s[t] : null;
  185. }
  186. } catch (i) {
  187. this.logError(i);
  188. s = !!t ? {} : null;
  189. this.del(e);
  190. }
  191. if (typeof s === "undefined") s = null;
  192. try {
  193. if (!!s && typeof s === "string") s = JSON.parse(s);
  194. } catch (e) {}
  195. this.logDebug(`READ DATA [${e}]${!!t ? `[${t}]` : ""}(${typeof s})\n${JSON.stringify(s)}`);
  196. return s;
  197. }
  198. write(e, t, s = "") {
  199. let i = !!s ? {} : "";
  200. if (!!s && (this.isSurge || this.isLoon)) {
  201. i = $persistentStore.read(e);
  202. } else if (!!s && this.isQuanX) {
  203. i = $prefs.valueForKey(e);
  204. } else if (this.isNode) {
  205. i = this.node.data;
  206. } else if (this.isJSBox) {
  207. i = JSON.parse($file.read("drive://MagicJS/magic.json").string);
  208. }
  209. if (!!s) {
  210. try {
  211. if (typeof i === "string") i = JSON.parse(i);
  212. i = typeof i === "object" && !!i ? i : {};
  213. } catch (t) {
  214. this.logError(t);
  215. this.del(e);
  216. i = {};
  217. }
  218. if (this.isJSBox || this.isNode) {
  219. if (!i.hasOwnProperty(e) || typeof i[e] !== "object" || i[e] === null) {
  220. i[e] = {};
  221. }
  222. if (!i[e].hasOwnProperty(s)) {
  223. i[e][s] = null;
  224. }
  225. if (typeof t === "undefined") {
  226. delete i[e][s];
  227. } else {
  228. i[e][s] = t;
  229. }
  230. } else {
  231. if (typeof t === "undefined") {
  232. delete i[s];
  233. } else {
  234. i[s] = t;
  235. }
  236. }
  237. } else {
  238. if (this.isNode || this.isJSBox) {
  239. if (typeof t === "undefined") {
  240. delete i[e];
  241. } else {
  242. i[e] = t;
  243. }
  244. } else {
  245. if (typeof t === "undefined") {
  246. i = null;
  247. } else {
  248. i = t;
  249. }
  250. }
  251. }
  252. if (typeof i === "object") i = JSON.stringify(i);
  253. if (this.isSurge || this.isLoon) {
  254. $persistentStore.write(i, e);
  255. } else if (this.isQuanX) {
  256. $prefs.setValueForKey(i, e);
  257. } else if (this.isNode) {
  258. this.node.fs.writeFileSync("./magic.json", i);
  259. } else if (this.isJSBox) {
  260. $file.write({
  261. data: $data({
  262. string: i
  263. }),
  264. path: "drive://MagicJS/magic.json"
  265. });
  266. }
  267. this.logDebug(`WRITE DATA [${e}]${!!s ? `[${s}]` : ""}(${typeof t})\n${JSON.stringify(t)}`);
  268. }
  269. del(e, t = "") {
  270. this.logDebug(`DELETE KEY [${e}]${!!t ? `[${t}]` : ""}`);
  271. this.write(e, null, t);
  272. }
  273. notify(e = this.scriptName, t = "", s = "", i = "") {
  274. let o = e => {
  275. let t = {};
  276. if (this.isSurge || this.isQuanX || this.isLoon) {
  277. if (typeof e === "string") {
  278. if (this.isLoon) t = {
  279. openUrl: e
  280. }; else if (this.isQuanX) t = {
  281. "open-url": e
  282. }; else if (this.isSurge) t = {
  283. url: e
  284. };
  285. } else if (typeof e === "object") {
  286. let s = {
  287. Surge: {
  288. openUrl: "url",
  289. "open-url": "url"
  290. },
  291. Loon: {
  292. url: "openUrl",
  293. "open-url": "openUrl",
  294. "media-url": "mediaUrl"
  295. },
  296. QuantumultX: {
  297. url: "open-url",
  298. openUrl: "open-url",
  299. mediaUrl: "media-url"
  300. }
  301. };
  302. let i = Object.keys(e);
  303. for (let o = 0; o < i.length; o++) {
  304. if (!!s[this.platform][i[o]]) {
  305. t[s[this.platform][i[o]]] = e[i[o]];
  306. } else {
  307. t[i[o]] = e[i[o]];
  308. }
  309. }
  310. }
  311. }
  312. return t;
  313. };
  314. i = o(i);
  315. this.logNotify(`title:${e}\nsubTitle:${t}\nbody:${s}\noptions:${typeof i === "object" ? JSON.stringify(i) : i}`);
  316. if (arguments.length == 1) {
  317. e = this.scriptName;
  318. t = "", s = arguments[0];
  319. }
  320. if (!!this._unifiedPushUrl) {
  321. let i = encodeURI(`${e}/${t}${!!t ? "\n" : ""}${s}`);
  322. this.get(`${this._unifiedPushUrl}/${i}`, () => {});
  323. }
  324. if (this.isSurge || this.isLoon) {
  325. $notification.post(e, t, s, i);
  326. } else if (this.isQuanX) {
  327. $notify(e, t, s, i);
  328. } else if (this.isJSBox) {
  329. let i = {
  330. title: e,
  331. body: !!t ? `${t}\n${s}` : s
  332. };
  333. $push.schedule(i);
  334. }
  335. }
  336. log(e, t = "INFO") {
  337. if (!(this.logLevels[this._logLevel] < this.logLevels[t.toUpperCase()])) console.log(`[${t}] [${this.scriptName}]\n${e}\n`);
  338. }
  339. logDebug(e) {
  340. this.log(e, "DEBUG");
  341. }
  342. logInfo(e) {
  343. this.log(e, "INFO");
  344. }
  345. logNotify(e) {
  346. this.log(e, "NOTIFY");
  347. }
  348. logWarning(e) {
  349. this.log(e, "WARNING");
  350. }
  351. logError(e) {
  352. this.log(e, "ERROR");
  353. }
  354. adapterHttpOptions(e, t) {
  355. let i = typeof e === "object" ? Object.assign({}, e) : {
  356. url: e,
  357. headers: {}
  358. };
  359. if (i.hasOwnProperty("header") && !i.hasOwnProperty("headers")) {
  360. i["headers"] = i["header"];
  361. delete i["header"];
  362. }
  363. if (typeof i.headers === "object" && !!s) {
  364. for (let e in i.headers) {
  365. if (s[e]) {
  366. i.headers[s[e]] = i.headers[e];
  367. delete i.headers[e];
  368. }
  369. }
  370. }
  371. if (!!!i.headers || typeof i.headers !== "object" || !!!i.headers["User-Agent"]) {
  372. if (!!!i.headers || typeof i.headers !== "object") i.headers = {};
  373. if (this.isNode) i.headers["User-Agent"] = this.pcUserAgent; else i.headers["User-Agent"] = this.iOSUserAgent;
  374. }
  375. let o = false;
  376. if (typeof i["opts"] === "object" && (i["opts"]["hints"] === true || i["opts"]["Skip-Scripting"] === true) || typeof i["headers"] === "object" && i["headers"]["X-Surge-Skip-Scripting"] === true) {
  377. o = true;
  378. }
  379. if (!o) {
  380. if (this.isSurge) i.headers["X-Surge-Skip-Scripting"] = false; else if (this.isLoon) i.headers["X-Requested-With"] = "XMLHttpRequest"; else if (this.isQuanX) {
  381. if (typeof i["opts"] !== "object") i.opts = {};
  382. i.opts["hints"] = false;
  383. }
  384. }
  385. if (!this.isSurge || o) delete i.headers["X-Surge-Skip-Scripting"];
  386. if (!this.isQuanX && i.hasOwnProperty("opts")) delete i["opts"];
  387. if (this.isQuanX && i.hasOwnProperty("opts")) delete i["opts"]["Skip-Scripting"];
  388. if (t === "GET" && !this.isNode && !!i.body) {
  389. let e = Object.keys(i.body).map(e => {
  390. if (typeof i.body === "undefined") return "";
  391. return `${encodeURIComponent(e)}=${encodeURIComponent(i.body[e])}`;
  392. }).join("&");
  393. if (i.url.indexOf("?") < 0) i.url += "?";
  394. if (i.url.lastIndexOf("&") + 1 != i.url.length && i.url.lastIndexOf("?") + 1 != i.url.length) i.url += "&";
  395. i.url += e;
  396. delete i.body;
  397. }
  398. if (this.isQuanX) {
  399. if (i.hasOwnProperty("body") && typeof i["body"] !== "string") i["body"] = JSON.stringify(i["body"]);
  400. i["method"] = t;
  401. } else if (this.isNode) {
  402. delete i.headers["Accept-Encoding"];
  403. if (typeof i.body === "object") {
  404. if (t === "GET") {
  405. i.qs = i.body;
  406. delete i.body;
  407. } else if (t === "POST") {
  408. i["json"] = true;
  409. i.body = i.body;
  410. }
  411. }
  412. } else if (this.isJSBox) {
  413. i["header"] = i["headers"];
  414. delete i["headers"];
  415. }
  416. return i;
  417. }
  418. get(e, t) {
  419. let s = this.adapterHttpOptions(e, "GET");
  420. this.logDebug(`HTTP GET: ${JSON.stringify(s)}`);
  421. if (this.isSurge || this.isLoon) {
  422. $httpClient.get(s, t);
  423. } else if (this.isQuanX) {
  424. $task.fetch(s).then(e => {
  425. e["status"] = e.statusCode;
  426. t(null, e, e.body);
  427. }, e => t(e.error, null, null));
  428. } else if (this.isNode) {
  429. return this.node.request.get(s, t);
  430. } else if (this.isJSBox) {
  431. s["handler"] = e => {
  432. let s = e.error ? JSON.stringify(e.error) : undefined;
  433. let i = typeof e.data === "object" ? JSON.stringify(e.data) : e.data;
  434. t(s, e.response, i);
  435. };
  436. $http.get(s);
  437. }
  438. }
  439. post(e, t) {
  440. let s = this.adapterHttpOptions(e, "POST");
  441. this.logDebug(`HTTP POST: ${JSON.stringify(s)}`);
  442. if (this.isSurge || this.isLoon) {
  443. $httpClient.post(s, t);
  444. } else if (this.isQuanX) {
  445. $task.fetch(s).then(e => {
  446. e["status"] = e.statusCode;
  447. t(null, e, e.body);
  448. }, e => {
  449. t(e.error, null, null);
  450. });
  451. } else if (this.isNode) {
  452. return this.node.request.post(s, t);
  453. } else if (this.isJSBox) {
  454. s["handler"] = e => {
  455. let s = e.error ? JSON.stringify(e.error) : undefined;
  456. let i = typeof e.data === "object" ? JSON.stringify(e.data) : e.data;
  457. t(s, e.response, i);
  458. };
  459. $http.post(s);
  460. }
  461. }
  462. done(e = {}) {
  463. if (typeof $done !== "undefined") {
  464. $done(e);
  465. }
  466. }
  467. isToday(e) {
  468. if (e == null) {
  469. return false;
  470. } else {
  471. let t = new Date();
  472. if (typeof e == "string") {
  473. e = new Date(e);
  474. }
  475. if (t.getFullYear() == e.getFullYear() && t.getMonth() == e.getMonth() && t.getDay() == e.getDay()) {
  476. return true;
  477. } else {
  478. return false;
  479. }
  480. }
  481. }
  482. isNumber(e) {
  483. return parseFloat(e).toString() === "NaN" ? false : true;
  484. }
  485. attempt(e, t = null) {
  486. return e.then(e => {
  487. return [ null, e ];
  488. }).catch(e => {
  489. this.logError(e);
  490. return [ e, t ];
  491. });
  492. }
  493. retry(e, t = 5, s = 0, i = null) {
  494. return (...o) => {
  495. return new Promise((r, n) => {
  496. function a(...o) {
  497. Promise.resolve().then(() => e.apply(this, o)).then(e => {
  498. if (typeof i === "function") {
  499. Promise.resolve().then(() => i(e)).then(() => {
  500. r(e);
  501. }).catch(e => {
  502. this.logError(e);
  503. if (t >= 1 && s > 0) {
  504. setTimeout(() => a.apply(this, o), s);
  505. } else if (t >= 1) {
  506. a.apply(this, o);
  507. } else {
  508. n(e);
  509. }
  510. t--;
  511. });
  512. } else {
  513. r(e);
  514. }
  515. }).catch(e => {
  516. this.logError(e);
  517. if (t >= 1 && s > 0) {
  518. setTimeout(() => a.apply(this, o), s);
  519. } else if (t >= 1) {
  520. a.apply(this, o);
  521. } else {
  522. n(e);
  523. }
  524. t--;
  525. });
  526. }
  527. a.apply(this, o);
  528. });
  529. };
  530. }
  531. formatTime(e, t = "yyyy-MM-dd hh:mm:ss") {
  532. var s = {
  533. "M+": e.getMonth() + 1,
  534. "d+": e.getDate(),
  535. "h+": e.getHours(),
  536. "m+": e.getMinutes(),
  537. "s+": e.getSeconds(),
  538. "q+": Math.floor((e.getMonth() + 3) / 3),
  539. S: e.getMilliseconds()
  540. };
  541. if (/(y+)/.test(t)) t = t.replace(RegExp.$1, (e.getFullYear() + "").substr(4 - RegExp.$1.length));
  542. for (let e in s) if (new RegExp("(" + e + ")").test(t)) t = t.replace(RegExp.$1, RegExp.$1.length == 1 ? s[e] : ("00" + s[e]).substr(("" + s[e]).length));
  543. return t;
  544. }
  545. now() {
  546. return this.formatTime(new Date(), "yyyy-MM-dd hh:mm:ss");
  547. }
  548. today() {
  549. return this.formatTime(new Date(), "yyyy-MM-dd");
  550. }
  551. sleep(e) {
  552. return new Promise(t => setTimeout(t, e));
  553. }
  554. }(e);
  555. }