KeyJoker Auto Task

KeyJoker Auto Task,修改自https://greasyfork.org/zh-CN/scripts/383411

当前为 2021-11-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name KeyJoker Auto Task
  3. // @namespace KeyJokerAutoTask
  4. // @version 1.0.6
  5. // @description KeyJoker Auto Task,修改自https://greasyfork.org/zh-CN/scripts/383411
  6. // @author 祭夜
  7. // @icon https://www.jysafe.cn/assets/images/avatar.jpg
  8. // @include *://www.keyjoker.com/entries*
  9. // @include *://assets.hcaptcha.com/*
  10. // @include *://discord.com/channels/@me?keyjokertask=storageAuth
  11. // @include *://www.twitch.tv/settings/profile?keyjokertask=storageAuth
  12. // @include *://twitter.com/settings/account?keyjokertask=storageAuth
  13. // @include *?keyjokertask=*
  14. // @supportURL https://www.jysafe.cn/4332.air
  15. // @homepage https://github.com/jiyeme/keyjokerScript/
  16. // @run-at document-start
  17. // @grant GM_registerMenuCommand
  18. // @grant GM_unregisterMenuCommand
  19. // @grant GM_addStyle
  20. // @grant GM_xmlhttpRequest
  21. // @grant GM_setClipboard
  22. // @grant GM_setValue
  23. // @grant GM_getValue
  24. // @grant GM_listValues
  25. // @grant GM_deleteValue
  26. // @grant GM_openInTab
  27. // @grant GM_log
  28. // @grant GM_notification
  29. // @connect hcaptcha.com
  30. // @connect store.steampowered.com
  31. // @connect steamcommunity.com
  32. // @connect twitter.com
  33. // @connect facebook.com
  34. // @connect discord.com
  35. // @connect twitch.tv
  36. // @connect tumblr.com
  37. // @connect spotify.com
  38. // @connect jysafe.cn
  39. // @require https://cdn.staticfile.org/jquery/3.3.1/jquery.min.js
  40. // @require https://cdn.jsdelivr.net/gh/jiyeme/keyjokerScript@e1f9bc6ca24cf7e8f734bd910306737449a26830/keyjoker.ext.js
  41. // ==/UserScript==
  42.  
  43. (function() {
  44. 'use strict';
  45. const debug = true;
  46. const discordAuth = GM_getValue('discordAuth') || {
  47. authorization: "",
  48. status:0,
  49. updateTime: 0
  50. }
  51. // steam信息
  52. const steamInfo = GM_getValue('steamInfo') || {
  53. userName: '',
  54. steam64Id: '',
  55. communitySessionID: '',
  56. storeSessionID: '',
  57. comUpdateTime: 0,
  58. storeUpdateTime: 0
  59. }
  60. const twitchAuth = GM_getValue('twitchAuth') || {
  61. "auth-token": "",
  62. status:0,
  63. updateTime: 0
  64. }
  65. const twitterAuth = GM_getValue('twitterAuth') || {
  66. authorization: "AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA",
  67. ct0: '',
  68. status: 0,
  69. updateTime: 0
  70. }
  71. const ignoreList = GM_getValue('ignoreList') || [];
  72.  
  73. const jq = $;
  74. let completeCheck = null;
  75.  
  76. // 0-未动作|200-成功取得|401未登录|603正在取得
  77. const getAuthStatus = {
  78. discord: false,
  79. spotify: false,
  80. steamStore: 0,
  81. steamCom: 0,
  82. tumblr: false,
  83. twitch: false,
  84. twitter: 0
  85. }
  86. var checkSwitchId = null;
  87. const noticeFrame = {
  88. loadFrame: ()=>{
  89. log.log("loadFrame");
  90. jq('body').append(`<style>
  91. .fuck-task-logs li{display:list-item !important;float:none !important}
  92. #extraBtn .el-badge.item{margin-bottom:4px !important}
  93. #extraBtn .el-badge.item sup{padding-right:0 !important}
  94. .fuck-task-logs{width:auto;max-width:50%;max-height:50%;z-index:99999999999 !important}
  95. .fuck-task-logs .el-notification__group{width:100%}
  96. .fuck-task-logs .el-notification__title{text-align:center}
  97. .fuck-task-logs .el-notification__content{overflow:auto;max-height:230px}
  98. font.start{color:black;}
  99. font.success{color:green;}
  100. font.error{color:red;}
  101. font.warning{color:#00f;}
  102. font.wait{color:#9c27b0;}
  103. [class^=el-icon-]{font-family:element-icons !important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}
  104. .el-icon-brush:before{content:"\\e76e"}
  105. .el-icon-document:before{content:"\\e785"}
  106. .el-icon-refresh:before{content:"\\e6d0"}
  107. .el-icon-s-promotion:before{content:"\\e7ba"}
  108. .el-icon-setting:before{content:"\\e6ca"}
  109. .el-icon-video-play:before{content:"\\e7c0"}
  110. .el-notification{display:-webkit-box;display:-ms-flexbox;display:flex;padding:14px 26px 14px 13px;border-radius:8px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #ebeef5;position:fixed;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;overflow:hidden}
  111. .el-notification__group{margin-left:13px;margin-right:8px}
  112. .el-notification__title{font-weight:700;font-size:16px;color:#303133;margin:0}
  113. .el-notification__content{font-size:14px;line-height:21px;margin:6px 0 0;color:#606266;text-align:justify}
  114. .el-notification__content p{margin:0}
  115. .el-badge{position:relative;vertical-align:middle;display:inline-block}
  116. .el-badge__content{background-color:#f56c6c;border-radius:10px;color:#fff;display:inline-block;font-size:12px;height:18px;line-height:18px;padding:0 6px;text-align:center;white-space:nowrap;border:1px solid #fff}
  117. .el-badge__content.is-fixed{position:absolute;top:10px;right:10px;-webkit-transform:translateY(-50%) translateX(100%);transform:translateY(-50%) translateX(100%)}
  118. .el-badge__content.is-fixed.is-dot{right:8px}
  119. .el-badge__content.is-dot{height:8px;width:8px;padding:0;right:0;border-radius:50%}
  120. .el-button{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}
  121. .el-button{-webkit-box-sizing:border-box}
  122. .el-button{display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;background:#fff;border:1px solid #dcdfe6;color:#606266;-webkit-appearance:none;text-align:center;box-sizing:border-box;outline:0;margin:0;-webkit-transition:.1s;transition:.1s;font-weight:500;padding:12px 20px;font-size:14px;border-radius:4px}
  123. .el-button:focus,.el-button:hover{color:#409eff;border-color:#c6e2ff;background-color:#ecf5ff}
  124. .el-button:active{color:#3a8ee6;border-color:#3a8ee6;outline:0}
  125. .el-button::-moz-focus-inner{border:0}
  126. .el-button.is-circle{border-radius:50%;padding:15px}
  127. #extraBtn .el-button.is-circle{padding:8px !important}
  128. @font-face{font-family:element-icons;src:url(https://cdn.bootcss.com/element-ui/2.12.0/theme-chalk/fonts/element-icons.woff) format("woff"),url(https://cdn.bootcss.com/element-ui/2.12.0/theme-chalk/fonts/element-icons.ttf) format("truetype");font-weight:400;font-display:auto;font-style:normal}
  129. </style>
  130. <div role="alert" class="el-notification fuck-task-logs right" style="bottom: 16px; z-index: 2000;">
  131. <div class="el-notification__group">
  132. <h2 id="extraBtn" class="el-notification__title">
  133. <div class="el-badge item"><button id="checkUpdate" type="button" class="el-button el-button--default is-circle"
  134. title="检查更新">
  135. <i class="el-icon-refresh"></i>
  136. </button><sup class="el-badge__content el-badge__content--undefined is-fixed is-dot"
  137. style="display: none;"></sup></div>
  138. <div class="el-badge item"><button id="fuck" type="button" class="el-button el-button--default is-circle" title="强制做任务">
  139. <i class="el-icon-video-play"></i>
  140. </button><sup class="el-badge__content el-badge__content--undefined is-fixed is-dot" style="display: none;"></sup>
  141. </div>
  142. <div class="el-badge item"><button id="changeLog" type="button" class="el-button el-button--default is-circle"
  143. title="查看更新内容">
  144. <i class="el-icon-document"></i>
  145. </button><sup class="el-badge__content el-badge__content--undefined is-fixed is-dot" style="display: none;"></sup></div>
  146. <div class="el-badge item"><button id="clearNotice" type="button" class="el-button el-button--default is-circle"
  147. title="清空执行日志">
  148. <i class="el-icon-brush"></i>
  149. </button><sup class="el-badge__content el-badge__content--undefined is-fixed is-dot"
  150. style="display: none;"></sup></div>
  151. <div class="el-badge item"><button id="report" type="button" class="el-button el-button--default is-circle"
  152. title="提交建议/BUG">
  153. <i class="el-icon-s-promotion"></i>
  154. </button><sup class="el-badge__content el-badge__content--undefined is-fixed is-dot"
  155. style="display: none;"></sup></div>
  156. </h2>
  157. <h2 class="el-notification__title">任务执行日志</h2>
  158. <div class="el-notification__content" style="">
  159. <p></p>
  160. </li>
  161. </div>
  162. </div>
  163. </div>`)
  164. },
  165. addNotice: function(data){
  166. switch(data.type)
  167. {
  168. case "taskStatus":
  169. jq('.el-notification__content').append('<li>' + data.task.task.name + ' <a href="' + data.task.data.url + '" target="_blank">' + (data.task.data.name||data.task.data.username) + '</a>|<font id="' + data.task.id + '" class="' + data.status +'">' + data.status +'</font></li>');
  170. break;
  171. case "msg":
  172. jq('.el-notification__content').append("<li>" + data.msg + "</li>");
  173. break;
  174. case "authVerify":
  175. jq('.el-notification__content').append('<li>' + data.name + ' |<font id="' + data.status.id + '" class="' + data.status.class +'">' + data.status.text + '</font></li>');
  176. break;
  177. default:
  178. jq('.el-notification__content').append("<li>" + data + "</li>");
  179. break;
  180. }
  181. },
  182. clearNotice:()=>{
  183. jq('.el-notification__content li').remove();
  184. },
  185. updateNotice: function(id, result){
  186. jq('font#' + id).removeClass()
  187. jq('font#' + id).addClass(result.class)
  188. jq('font#' + id).text(result.text)
  189. },
  190. }
  191.  
  192. const log = ((...data)=>{
  193.  
  194. if(debug){
  195. return console
  196. }else{
  197. return {}
  198. }
  199. })();
  200. const HTTP = (function(){
  201. // [修改自https://greasyfork.org/zh-CN/scripts/370650]
  202. const httpRequest = function (e) {
  203. const requestObj = {}
  204. requestObj.url = e.url
  205. requestObj.method = e.method.toUpperCase()
  206. requestObj.timeout = e.timeout || 30000
  207. if (e.dataType) requestObj.dataType = e.dataType
  208. if (e.responseType) requestObj.responseType = e.responseType
  209. if (e.headers) requestObj.headers = e.headers
  210. if (e.binary) requestObj.binary = e.binary;
  211. if (e.data) requestObj.data = e.data
  212. if (e.cookie) requestObj.cookie = e.cookie
  213. if (e.anonymous) requestObj.anonymous = e.anonymous
  214. if (e.onload) requestObj.onload = e.onload
  215. if (e.fetch) requestObj.fetch = e.fetch
  216. if (e.onreadystatechange) requestObj.onreadystatechange = e.onreadystatechange
  217. requestObj.ontimeout = e.ontimeout || function (data) {
  218. log.info('请求超时:', data)
  219. if (e.status) e.status.error('Error:Timeout(0)')
  220. if (e.r) e.r({ result: 'error', statusText: 'Timeout', status: 0, option: e })
  221. }
  222. requestObj.onabort = e.onabort || function (data) {
  223. log.info('请求终止:', data)
  224. if (e.status) e.status.error('Error:Aborted(0)')
  225. if (e.r) e.r({ result: 'error', statusText: 'Aborted', status: 0, option: e })
  226. }
  227. requestObj.onerror = e.onerror || function (data) {
  228. log.info('请求出错:', data)
  229. if (e.status) e.status.error('Error:Error(0)')
  230. if (e.r) e.r({ result: 'error', statusText: 'Error', status: 0, option: e })
  231. }
  232. log.info('发送请求:', requestObj)
  233. GM_xmlhttpRequest(requestObj);
  234. }
  235. function get(url, data={}, e = {}){
  236. return new Promise((resolve, reject)=>{
  237. e.url = url;
  238. e.method = "GET";
  239. e.data = data;
  240. e.onload = resolve;
  241. e.onerror = reject;
  242. httpRequest(e)
  243. })
  244. }
  245. function post(url, data={}, e = {}){
  246. return new Promise((resolve, reject)=>{
  247. e.url = url;
  248. e.method = "POST";
  249. e.data = data;
  250. e.onload = resolve;
  251. e.onerror = reject
  252. httpRequest(e);
  253. })
  254. }
  255. function put(url, data={}, e = {}){
  256. return new Promise((resolve, reject)=>{
  257. e.url = url;
  258. e.method = "PUT";
  259. e.data = data;
  260. e.onload = resolve
  261. e.onerror = reject
  262. httpRequest(e);
  263. })
  264. }
  265. return {
  266. GET: get,
  267. POST: post,
  268. PUT: put
  269. }
  270. })();
  271. try{
  272. const checkTask = {
  273. reLoad: function (time,sum){
  274. let date=new Date();
  275. let hour=date.getHours();
  276. let min=date.getMinutes()<10?("0"+date.getMinutes()):date.getMinutes();
  277. if(GM_getValue("start")==1){
  278. jq(".border-bottom").text(hour+":"+min+" 执行新任务检测");
  279. jq.ajax({
  280. url:"/entries/load",
  281. type:"get",
  282. headers:{'x-csrf-token': jq('meta[name="csrf-token"]').attr('content')},
  283. success:(data,status,xhr)=>{
  284. data.actions = data.actions.filter(e=>ignoreList.indexOf(e.id)===-1)
  285. if(data && (data.actions && (data.actions.length > sum) )){
  286. log.log(data);
  287. let date=new Date();
  288. let hour=date.getHours();
  289. let min=date.getMinutes()<10?("0"+date.getMinutes()):date.getMinutes();
  290. jq(".border-bottom").text(hour+":"+min+" 检测到新任务(暂停检测)");
  291. /*GM_notification({
  292. title: "keyjoker新任务",
  293. text: "keyjoker网站更新"+(data.actions.length-sum)+"个新任务!",
  294. image: "https://www.keyjoker.com/favicon-32x32.png",
  295. timeout: 0,
  296. onclick: function(){
  297. //location.reload(true);
  298. }
  299. });*/
  300. // 重载列表
  301. noticeFrame.clearNotice();
  302. func.reLoadTaskList().then(()=>{
  303. ignoreList.forEach(id=>{
  304. const ele = jq(`a[href='https://www.keyjoker.com/entries/open/${id}']`)
  305. if(ele.length > 0){
  306. ele[0].parentNode.parentNode.parentNode.parentNode.remove()
  307. }
  308. })
  309. func.do_task(data);
  310. });
  311. GM_setValue("start", 0);
  312. checkSwitch();
  313. }else{
  314. setTimeout(()=>{
  315. this.reLoad(time,sum);
  316. },time);
  317. }
  318. },
  319. error:(err)=>{
  320. window.location.reload(true);
  321. }
  322. });
  323. }
  324. },
  325. setTime: function (){
  326. let time=prompt('请输入获取任务信息的时间间隔(单位:秒):');
  327. if(!isNaN(time)){
  328. GM_setValue("time",parseInt(time));
  329. }
  330. },
  331. start: function (r = null){
  332. let time = GM_getValue("time");
  333. if(!time){
  334. time=60;
  335. }
  336. if(confirm("是否以时间间隔" + time + "秒进行任务检测?")){
  337. GM_setValue("start",1);
  338. if(r)r();
  339. this.next();
  340. }
  341. },
  342. next: function (){
  343. // 初始化凭证获取状态
  344. getAuthStatus.discord = false;
  345. getAuthStatus.spotify = false;
  346. getAuthStatus.steamStore = 0;
  347. getAuthStatus.steamCom = 0;
  348. getAuthStatus.tumblr = false;
  349. getAuthStatus.twitch = false;
  350. getAuthStatus.twitter = 0;
  351. let time = GM_getValue("time");
  352. if(!time){
  353. time=60;
  354. }
  355. let sum=jq(".list-complete-item").length;
  356. if(sum>0){
  357. this.reLoad(time*1000,sum);
  358. }else{
  359. this.reLoad(time*1000,0);
  360. }
  361. }
  362. }
  363. const DISCORD = (()=>{
  364. const AuthUpdate = (update = false)=>{
  365. return new Promise((resolve, reject)=>{
  366. if (new Date().getTime() - discordAuth.updateTime < 30 * 60 * 1000 && discordAuth.status == 200 && !update) {
  367. log.info("DISCORD: 直接使用未过期的Auth")
  368. resolve(200)
  369. return;
  370. }
  371. if(false == getAuthStatus.discord || true === update)
  372. {
  373. getAuthStatus.discord = true;
  374. GM_openInTab("https://discord.com/channels/@me?keyjokertask=storageAuth", true);
  375. }
  376. let i = 0;
  377. let check = setInterval(()=>{
  378. i++;
  379. if(GM_getValue("discordAuth") && new Date().getTime() - GM_getValue("discordAuth").updateTime <= 10 * 1000)
  380. {
  381. if(GM_getValue("discordAuth").status != 200)
  382. {
  383. clearInterval(check);
  384. reject(GM_getValue("discordAuth").status)
  385. return;
  386. }
  387. discordAuth.authorization = GM_getValue("discordAuth").authorization
  388. discordAuth.updateTime = GM_getValue("discordAuth").updateTime
  389. discordAuth.status = GM_getValue("discordAuth").status;
  390. clearInterval(check);
  391. resolve(discordAuth.status)
  392. }
  393. if(i >= 10)
  394. {
  395. clearInterval(check);
  396. reject(408)
  397. }
  398. }, 1000)
  399. })
  400. }
  401. const doJoinServer = (server)=>{
  402. return HTTP.POST('https://discord.com/api/v9/invites/' + server, null, {
  403. headers: {
  404. referer: 'https://discord.com/invite/' + server,
  405. authorization: discordAuth.authorization
  406. },
  407. anonymous: true,
  408. responseType: 'json'
  409. }).then(res=>{
  410. if (res.status === 200) {
  411. log.log({ result: 'success', statusText: res.statusText, status: res.status })
  412. return Promise.resolve(200);
  413. } else {
  414. log.error("状态码异常:", res);
  415. log.info(res.responseText)
  416. return Promise.reject(res.status);
  417. }
  418. })
  419. }
  420. const JoinServer = async (r, server)=>{
  421. log.info("DISCORD: 准备加入服务器:", server)
  422. try{
  423. log.info("DISCORD: 更新凭证:", server)
  424. const auth = await AuthUpdate()
  425.  
  426. // https://discord.com/api/v9/invites/EVgxm7TTvD
  427. log.info("DISCORD: 加入服务器:", server)
  428. const ret = await doJoinServer(server)
  429. log.info('DISCORD: ret', ret)
  430. r(ret)
  431. }catch(e){
  432. log.error("DISCORD: 加入服务器出错:", e)
  433. r(e);
  434. return;
  435. }
  436. }
  437. const LeaveServer = (r, serverId)=>{
  438. AuthUpdate((ret)=>{
  439. if(ret != 200)
  440. {
  441. r(ret);
  442. return;
  443. }
  444. jq.ajax({
  445. url: 'https://discord.com/api/v6/users/@me/guilds/' + serverId,
  446. method: 'DELETE',
  447. headers: { authorization: discordAuth.authorization, "content-type": "application/json"},
  448. onload: (response) => {
  449. if (response.status === 604) {
  450. log.log({ result: 'success', statusText: response.statusText, status: response.status })
  451. r(604);
  452. } else {
  453. log.error(response);
  454. r(601);
  455. }
  456. },
  457. error:(res)=>{
  458. log.error(res);
  459. r(601);
  460. },
  461. anonymous:true
  462. })
  463. })
  464. }
  465. return {
  466. AuthUpdate: AuthUpdate,
  467. JoinServer: JoinServer,
  468. LeaveServer: LeaveServer
  469. }
  470. })();
  471. const SPOTIFY = (()=>{
  472. const GetUserInfo = async (r)=>{
  473. r(603)
  474. const accessToken = GetAccessToken()
  475. return HTTP.GET('https://api.spotify.com/v1/me', null, {
  476. headers:{authorization: "Bearer " + accessToken},
  477. anonymous:true
  478. }).then((res, accessToken)=>{
  479. if (res.status === 200) {
  480. r(200, accessToken, JSON.parse(res.responseText).id);
  481. } else {
  482. log.error(res)
  483. r(401);
  484. }
  485. })
  486. }
  487. const GetAccessToken = function(){
  488. return HTTP.GET('https://open.spotify.com/get_access_token?reason=transport&productType=web_player', null)
  489. .then(res=>{
  490. if (res.status === 200) {
  491. return Promise.resolve(JSON.parse(res.responseText).accessToken);
  492. } else {
  493. log.log(res);
  494. return Promise.reject(401);
  495. }
  496. })
  497. }
  498. const SaveAuto = (r, data, del = false)=>{
  499. GetUserInfo((status, accessToken = null, userId = null)=>{
  500. if(status != 200)
  501. {
  502. r(status);
  503. return;
  504. }
  505. let putUrl = "";
  506. new Promise((resolve, reject)=>{
  507. switch(data.type)
  508. {
  509. case "album":
  510. putUrl = "https://spclient.wg.spotify.com/collection-view/v1/collection/albums/" + userId + "?base62ids=" + data.id + "&model=bookmark";
  511. resolve(putUrl);
  512. break;
  513. case "track":
  514. HTTP.GET('https://api.spotify.com/v1/tracks?ids=' + data.id + '&market=from_token', null, {
  515. headers:{authorization: "Bearer " + accessToken},
  516. anonymous:true
  517. }).then(res=>{
  518. if(res.status == 200)
  519. {
  520. let temp = JSON.parse(res.response);
  521. putUrl = "https://spclient.wg.spotify.com/collection-view/v1/collection/albums/" + userId + "?base62ids=" + temp.tracks[0].album.id + "&model=bookmark";
  522. resolve(putUrl);
  523. }else
  524. {
  525. log.error(res);
  526. reject(601);
  527. }
  528. }).catch(err=>{
  529. log.error(err);
  530. reject(601);
  531. })
  532. break;
  533. default:
  534. log.error("spotifySaveAuto未知类型:", data);
  535. r(601);
  536. return;
  537. break;
  538. }
  539. }).then((putUrl)=>{
  540. log.log(putUrl)
  541. jq.ajax({
  542. type: !del?'PUT':"DELETE",
  543. url: putUrl,
  544. headers: {authorization: "Bearer " + accessToken},
  545. success: function(data){
  546. log.log(data);
  547. r(200);
  548. },
  549. error: function(data){
  550. log.error(data);
  551. r(601);
  552. },
  553. anonymous:true
  554. });
  555. })
  556. });
  557. }
  558. const Follow = (r, data, del = false)=>{
  559. GetUserInfo((status, accessToken = null)=>{
  560. if(status != 200)
  561. {
  562. r(status)
  563. return;
  564. }
  565. let putUrl = "";
  566. switch(data.type)
  567. {
  568. case "artist":
  569. putUrl = "https://api.spotify.com/v1/me/following?type=artist&ids=" + data.id;
  570. break;
  571. case "playlist":
  572. putUrl = "https://api.spotify.com/v1/playlists/" + data.id + "/followers"
  573. break;
  574. case "user":
  575. putUrl = "https://api.spotify.com/v1/me/following?type=user&ids=" + data.id;
  576. break;
  577. default:
  578. log.error(data);
  579. r(601);
  580. return;
  581. break;
  582. }
  583. jq.ajax({
  584. type: !del?'PUT':"DELETE",
  585. url: putUrl,
  586. headers: {authorization: "Bearer " + accessToken},
  587. success: function(data){
  588. r(200);
  589. },
  590. error: function(data){
  591. log.error(data);
  592. r(604);
  593. },
  594. anonymous:true
  595. });
  596. });
  597. }
  598. return {
  599. GetAccessToken: GetAccessToken,
  600. SaveAuto: SaveAuto,
  601. Follow: Follow
  602. }
  603. })();
  604. const STEAM = (()=>{
  605. const InfoUpdate = async (type = 'all', forceUpdate = false)=> {
  606. if (type === 'community' || type === 'all') {
  607. await getComAuth(forceUpdate)
  608. }
  609. if (type === 'store' || type === 'all') {
  610. await getStoreAuth(forceUpdate)
  611. }
  612. }
  613. const getComAuth = (forceUpdate = false)=>{
  614. if (new Date().getTime() - steamInfo.comUpdateTime > 10 * 60 * 1000 || forceUpdate) {
  615. getAuthStatus.steamCom = 603;
  616. HTTP.GET('https://steamcommunity.com/my')
  617. .then(res=>{
  618. if (res.status === 200) {
  619. if (jq(res.responseText).find('a[href*="/login/home"]').length > 0) {
  620. getAuthStatus.steamCom = 401;
  621. return Promise.reject(401);
  622. } else {
  623. const steam64Id = res.responseText.match(/g_steamID = "(.+?)";/);
  624. const communitySessionID = res.responseText.match(/g_sessionID = "(.+?)";/);
  625. const userName = res.responseText.match(/steamcommunity.com\/id\/(.+?)\/friends\//);
  626. if (steam64Id) steamInfo.steam64Id = steam64Id[1];
  627. if (communitySessionID) steamInfo.communitySessionID = communitySessionID[1];
  628. if (userName) steamInfo.userName = userName[1];
  629. getAuthStatus.steamCom = 200;
  630. steamInfo.comUpdateTime = new Date().getTime();
  631. GM_setValue('steamInfo', steamInfo);
  632. return Promise.resolve(200);
  633. }
  634. } else {
  635. log.error(res);
  636. getAuthStatus.steamCom = 601;
  637. return Promise.reject(601);
  638. }
  639. })
  640. } else {
  641. return Promise.resolve(200);
  642. }
  643. }
  644. const getStoreAuth = (forceUpdate = false)=>{
  645. if (new Date().getTime() - steamInfo.storeUpdateTime > 10 * 60 * 1000 || forceUpdate) {
  646. getAuthStatus.steamStore = 603;
  647. return HTTP.GET('https://store.steampowered.com/stats/', null )
  648. .then(res=>{
  649. if (res.status === 200) {
  650. if (jq(res.responseText).find('a[href*="/login/"]').length > 0) {
  651. log.log(res)
  652. getAuthStatus.steamStore = 401;
  653. return Promise.reject(401)
  654. } else {
  655. const storeSessionID = res.responseText.match(/g_sessionID = "(.+?)";/)
  656. if (storeSessionID) steamInfo.storeSessionID = storeSessionID[1]
  657. getAuthStatus.steamStore = 200;
  658. steamInfo.storeUpdateTime = new Date().getTime();
  659. GM_setValue('steamInfo', steamInfo);
  660. return Promise.resolve(200);
  661. }
  662. } else {
  663. log.error(res);
  664. getAuthStatus.steamStore = 601;
  665. return Promise.reject(601);
  666. }
  667. })
  668. } else {
  669. return Promise.resolve(200)
  670. }
  671. }
  672.  
  673. const Rep = async (r, id)=>{
  674. try{
  675. const check = await RepHisCheck(id)
  676. HTTP.POST('https://steamcommunity.com/comment/Profile/post/' + id + '/-1/',jq.param({comment:'+rep',count:6,sessionid:steamInfo.communitySessionID,feature2:-1}), {
  677. headers:{'content-type': 'application/x-www-form-urlencoded'},
  678. }).then(res=>{
  679. if(res.status == 200)
  680. {
  681. let ret = JSON.parse(res.response)
  682. if(ret.success == true)r(200);
  683. else{
  684. log.error("发送评论失败", res);
  685. r(601);
  686. }
  687. }else{
  688. log.error("评论返回值异常", res);
  689. r(601);
  690. }
  691. }).catch(err=>{
  692. log.error("请求发送异常", err);
  693. r(601);
  694. })
  695. }catch(e){
  696. r(e);
  697. return;
  698. }
  699. }
  700. const RepHisCheck = async function (id){
  701. const auth = await InfoUpdate( "community")
  702. return HTTP.GET("https://steamcommunity.com/profiles/" + id)
  703. .then(res=>{
  704. if(res.status == 200)
  705. {
  706. let comments = res.responseText.match(/commentthread_comments([\s\S]*)commentthread_footer/);
  707. log.log(comments);
  708. if(comments != null)
  709. {
  710. if(comments[1].includes(steamInfo.steam64Id) || steamInfo.userName?comments[1].includes(steamInfo.userName):false)
  711. {
  712. return Promise.resolve(200, true);
  713. }
  714. else if(!res.responseText.includes("commentthread_textarea"))
  715. {
  716. return Promise.reject(605)
  717. }else{
  718. return Promise.resolve(200, false);
  719. }
  720. }
  721. else return Promise.reject(605);
  722. }else{
  723. log.error("检查评论记录返回异常", res);
  724. return Promise.reject(601);
  725. }
  726. })
  727.  
  728. }
  729. const isGroupExist = (url)=>{
  730. return HTTP.GET(url).then(res=>{
  731. const html = res.responseText;
  732. return Promise.resolve(html.indexOf('已被移除。') !== -1 || html.indexOf('无法检索到该指定 URL 的组') !== -1)
  733. })
  734. }
  735. const JoinGroupAuto = async function (r, url) {
  736. try{
  737. const auth = await InfoUpdate('community')
  738. HTTP.POST(url, jq.param({ action: 'join', sessionID: steamInfo.communitySessionID }), {
  739. headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
  740. }).then(res=>{
  741. if (res.status === 200 && !res.responseText.includes('grouppage_join_area')) {
  742. if(res.responseText.match(/<h3>(.+?)<\/h3>/) && res.responseText.match(/<h3>(.+?)<\/h3>/)[1] != "您已经是该组的成员了。")
  743. {
  744. log.error("STEAM.JoinGroupAuto1", res);
  745. r(404);
  746. }else r(200);
  747. } else {
  748. log.error("STEAM.JoinGroupAuto2", res);
  749. r(601);
  750. }
  751. })
  752. }catch(e){
  753. let exist = await isGroupExist(url)
  754. r(!exist?404:e);
  755. return;
  756. }
  757.  
  758. }
  759. const LeaveGroup = function (r, url) {
  760. let groupName = url.split('s/')[1];
  761. GetGroupID(groupName, (groupName, groupId) => {
  762. var postUrl = "";
  763. postUrl = (steamInfo.userName) ? 'https://steamcommunity.com/id/' + steamInfo.userName + '/home_process' : 'https://steamcommunity.com/profiles/' + steamInfo.steam64Id + '/home_process'
  764. HTTP.POST(postUrl, jq.param({ sessionID: steamInfo.communitySessionID, action: 'leaveGroup', groupId: groupId }), {
  765. headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
  766. }).then(res=>{
  767. if (res.status === 200 && res.finalUrl.includes('groups') && jq(res.responseText.toLowerCase()).find(`a[href='https://steamcommunity.com/groups/${groupName.toLowerCase()}']`).length === 0) {
  768. r(200);
  769. } else {
  770. log.error(res);
  771. r(601);
  772. }
  773. })
  774. })
  775. }
  776. const GetGroupID = async function (groupName, callback) {
  777. try{
  778. const auth = InfoUpdate()
  779. HTTP.GET('https://steamcommunity.com/groups/' + groupName, null, {
  780. headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
  781. }).then(res=>{
  782. log.log(res)
  783. if (res.status === 200) {
  784. const groupId = res.responseText.match(/OpenGroupChat\( '([0-9]+)'/)
  785. if (groupId === null) {
  786. log.error(res)
  787. } else {
  788. if (groupId[1] !== false && callback) callback(groupName, groupId[1]);
  789. }
  790. } else {
  791. log.error(res)
  792. }
  793. })
  794. }catch(e){
  795. callback(e);
  796. return;
  797. }
  798. }
  799. const AddWishlistAuto = async function (r, gameId) {
  800. try{
  801. const auth = await InfoUpdate('store')
  802.  
  803. HTTP.POST('https://store.steampowered.com/api/addtowishlist', jq.param({ sessionid: steamInfo.storeSessionID, appid: gameId }), {
  804. headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
  805. dataType: 'json'
  806. }).then(res=>{
  807. log.log(res)
  808. if (res.status === 200 && res.response && res.response.success === true) {
  809. r(200)
  810. } else {
  811. HTTP.GET('https://store.steampowered.com/app/' + gameId)
  812. .then(res=>{
  813. log.log(res)
  814. if (res.status === 200) {
  815. if (res.responseText.includes('class="queue_actions_ctn"') && res.responseText.includes('已在库中')) {
  816. r(200)
  817. } else if ((res.responseText.includes('class="queue_actions_ctn"') && !res.responseText.includes('add_to_wishlist_area" style="display: none;"')) || !res.responseText.includes('class="queue_actions_ctn"')) {
  818. log.error(res);
  819. r(601);
  820. } else {
  821. r(200);
  822. }
  823. } else {
  824. log.error(res);
  825. r(601);
  826. }
  827. })
  828. return Promise.resolve({ result: 'error', statusText: res.statusText, status: res.status })
  829. }
  830. })
  831. }catch(e){
  832. log.error(e);
  833. r(601);
  834. }
  835. }
  836. const RemoveWishlistAuto = function (r, gameId) {
  837. this.steamInfoUpdate(() => {
  838. new Promise(resolve => {
  839. HTTP.POST('https://store.steampowered.com/api/removefromwishlist', jq.param({ sessionid: steamInfo.storeSessionID, appid: gameId }), {
  840. headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
  841. dataType: 'json',
  842. onabort: response => { resolve({ result: 'error', statusText: response.statusText, status: response.status }) },
  843. ontimeout: response => { resolve({ result: 'error', statusText: response.statusText, status: response.status }) },
  844. r: resolve
  845. }).then(res=>{
  846. if (res.status === 200 && res.response && res.response.success === true) {
  847. resolve({ result: 'success', statusText: res.statusText, status: res.status })
  848. } else {
  849. resolve({ result: 'error', statusText: res.statusText, status: res.status })
  850. }
  851. })
  852. }).then(result => {
  853. if (result.result === 'success') {
  854. r(200);
  855. } else {
  856. HTTP.GET('https://store.steampowered.com/app/' + gameId)
  857. .then(res=>{
  858. if (res.status === 200) {
  859. if (res.responseText.includes('class="queue_actions_ctn"') && (res.responseText.includes('已在库中') || res.responseText.includes('添加至您的愿望单'))) {
  860. r(200);
  861. } else {
  862. log.error(res);
  863. r(601);
  864. }
  865. } else {
  866. log.error(res);
  867. r(601);
  868. }
  869. })
  870. }
  871. }).catch(err => {
  872. log.error(err);
  873. r(601);
  874. })
  875. })
  876. }
  877. return {
  878. InfoUpdate: InfoUpdate,
  879. Rep: Rep,
  880. JoinGroup: JoinGroupAuto,
  881. LeaveGroup: LeaveGroup,
  882. AddWishlist: AddWishlistAuto
  883. }
  884. })();
  885. const TUMBLR = (()=>{
  886. const FollowAuto = async function(r, name){
  887. try{
  888. r(603)
  889. const key = await AuthUpdate()
  890. HTTP.POST( 'https://www.tumblr.com/svc/follow', jq.param({'data[tumblelog]': name}), {
  891. headers: {"x-tumblr-form-key": key, "Content-Type": "application/x-www-form-urlencoded"},
  892. }).then(res=>{
  893. if(res.status == 200)
  894. {
  895. r(200);
  896. }else{
  897. log.error(res);
  898. r(601);
  899. }
  900. })
  901. }catch(e){
  902. r(e);
  903. return;
  904. }
  905. }
  906. const UnfollowAuto = function(r, name){
  907. AuthUpdate((status, key = false)=>{
  908. if(status != 200)
  909. {
  910. r(status);
  911. return;
  912. }
  913. if(false != key){
  914. HTTP.POST('https://www.tumblr.com/svc/unfollow', jq.param({'data[tumblelog]': name}), {
  915. headers: {"x-tumblr-form-key": key, "Content-Type": "application/x-www-form-urlencoded"},
  916. }).then(res=>{
  917. if(res.status == 200)
  918. {
  919. r(200);
  920. }else{
  921. log.error(res);
  922. r(601);
  923. }
  924. })
  925. }else{
  926. r(601);
  927. }
  928. })
  929. }
  930. const AuthUpdate = function(update = false){
  931. return HTTP.GET('https://www.tumblr.com/dashboard/iframe')
  932. .then(res=>{
  933. if(res.status == 200)
  934. {
  935. let key = res.responseText.match(/id="tumblr_form_key" content="(.+?)">/)
  936. if(key){
  937. key = key[1]
  938. if(-1 != key.indexOf("!123") && -1 !=key.indexOf("|") )
  939. {
  940. return Promise.reject(401);
  941. }else return Promise.resolve(key);
  942. }
  943. else{
  944. log.error("tumblrGetKey->get key failed.", res);
  945. return Promise.reject(601);
  946. }
  947. }else{
  948. log.error(res);
  949. return Promise.reject(601);
  950. }
  951. })
  952. }
  953. return {
  954. AuthUpdate: AuthUpdate,
  955. Follow: FollowAuto,
  956. UnfollowAuto: UnfollowAuto
  957. }
  958. })();
  959. const TWITCH = (()=>{
  960. const FollowAuto = async function(r, channelId){
  961. try{
  962. const auth = await AuthUpdate()
  963. HTTP.POST( 'https://gql.twitch.tv/gql','[{"operationName":"FollowButton_FollowUser","variables":{"input":{"disableNotifications":false,"targetID":"' + channelId + '"}},"extensions":{"persistedQuery":{"version":1,"sha256Hash":"3efee1acda90efdff9fef6e6b4a29213be3ee490781c5b54469717b6131ffdfe"}}}]', {
  964. headers: { Authorization: "OAuth " + twitchAuth["auth-token"]},
  965. }).then(res=>{
  966. if (res.status === 200) {
  967. r(200);
  968. } else if(res.status === 401){
  969. twitchAuth.updateTime = 0;
  970. GM_setValue("twitchAuth", null);
  971. r(401);
  972. }else{
  973. log.error(res);
  974. r(601);
  975. }
  976. })
  977. }catch(e){
  978. r(status);
  979. return;
  980. }
  981. }
  982. const UnfollowAuto = function(r, channelId){
  983. AuthUpdate((status)=>{
  984. if(status != 200)
  985. {
  986. r(status);
  987. return;
  988. }
  989. HTTP.POST('https://gql.twitch.tv/gql', '[{"operationName":"FollowButton_UnfollowUser","variables":{"input":{"targetID":"' + channelId + '"}},"extensions":{"persistedQuery":{"version":1,"sha256Hash":"d7fbdb4e9780dcdc0cc1618ec783309471cd05a59584fc3c56ea1c52bb632d41"}}}]', {
  990. headers: { Authorization: "OAuth " + twitchAuth["auth-token"]},
  991. }).then(res=>{
  992. if (res.status === 200) {
  993. r(200);
  994. } else if(res.status === 401){
  995. twitchAuth.updateTime = 0;
  996. GM_setValue("twitchAuth", null);
  997. r(401);
  998. }else{
  999. log.error(res);
  1000. r(601);
  1001. }
  1002. })
  1003. })
  1004. }
  1005. // 弃用
  1006. const twitchGetId = function(r, channels){
  1007. HTTP.GET('https://api.twitch.tv/api/channels/' + channels + '/access_token?oauth_token=' + GM_getValue("twitchAuth").match(/auth-token=(.+?); /)[1] + '&need_https=true&platform=web&player_type=site&player_backend=mediaplayer', null, {
  1008. anonymous:true
  1009. }) .then(res=>{
  1010. if (res.status === 200) {
  1011. let rep = JSON.parse(JSON.parse(res.responseText).token);
  1012. r(rep.channel_id);
  1013. } else {
  1014. log.error(res);
  1015. r('error')
  1016. }
  1017. })
  1018. }
  1019. const AuthUpdate = function(forceUpdate = false){
  1020. return new Promise((resolve, reject)=>{
  1021. if (new Date().getTime() - twitchAuth.updateTime < 30 * 60 * 1000 && twitchAuth.status === 200 && !forceUpdate) {
  1022. resolve(200)
  1023. return
  1024. }
  1025. if(false == getAuthStatus.twitch || true === forceUpdate)
  1026. {
  1027. getAuthStatus.twitch = true;
  1028. GM_openInTab("https://www.twitch.tv/settings/profile?keyjokertask=storageAuth", true);
  1029. }
  1030. let i = 0;
  1031. let check = setInterval(()=>{
  1032. i++;
  1033. if(GM_getValue("twitchAuth") && new Date().getTime() - GM_getValue("twitchAuth").updateTime <= 10 * 1000)
  1034. {
  1035. if(GM_getValue("twitchAuth").status != 200)
  1036. {
  1037. clearInterval(check);
  1038. reject(401);
  1039. return;
  1040. }
  1041. twitchAuth["auth-token"] = GM_getValue('twitchAuth')["auth-token"];
  1042. twitchAuth.updateTime = GM_getValue('twitchAuth').updateTime;
  1043. twitchAuth.status = GM_getValue('twitchAuth').status;
  1044. clearInterval(check);
  1045. resolve(200)
  1046. }
  1047. if(i >= 10)
  1048. {
  1049. clearInterval(check);
  1050. reject(408)
  1051. }
  1052. }, 1000)
  1053. })
  1054. }
  1055. return {
  1056. AuthUpdate: AuthUpdate,
  1057. FollowAuto: FollowAuto,
  1058. UnfollowAuto: UnfollowAuto
  1059. }
  1060. })();
  1061. const TWITTER = (()=>{
  1062. const FollowAuto = async function(r, data){
  1063. try{
  1064. const auth = await AuthUpdate()
  1065. const uinfo = await GetUserInfo(data.username)
  1066. let userId = uinfo[1]
  1067. HTTP.POST('https://api.twitter.com/1.1/friendships/create.json',
  1068. jq.param({
  1069. include_profile_interstitial_type: 1,
  1070. include_blocking: 1,
  1071. include_blocked_by: 1,
  1072. include_followed_by: 1,
  1073. include_want_retweets: 1,
  1074. include_mute_edge: 1,
  1075. include_can_dm: 1,
  1076. include_can_media_tag: 1,
  1077. skip_status: 1,
  1078. id: userId
  1079. }), {
  1080. headers: { authorization: "Bearer " + twitterAuth.authorization, 'Content-Type': 'application/x-www-form-urlencoded', 'x-csrf-token':twitterAuth.ct0},
  1081. }).then(res=>{
  1082. if (res.status === 200) {
  1083. r(200);
  1084. } else {
  1085. log.error(res);
  1086. twitterAuth.updateTime = 0;
  1087. GM_setValue("twitterAuth", twitterAuth);
  1088. r(601);
  1089. }
  1090. })
  1091. }catch(e){
  1092. log.error("TWITTER: 关注出错:", e)
  1093. r(e);
  1094. return;
  1095. }
  1096. }
  1097. const UnfollowAuto = function(r, data){
  1098. AuthUpdate((status)=>{
  1099. if(status != 200)
  1100. {
  1101. r(status);
  1102. return;
  1103. }
  1104. GetUserInfo((userId)=>{
  1105. log.log(userId)
  1106. if("error" == userId)
  1107. {
  1108. r(601);
  1109. return;
  1110. }
  1111. HTTP.POST('https://api.twitter.com/1.1/friendships/destroy.json', jq.param({ include_profile_interstitial_type: 1,include_blocking: 1,include_blocked_by: 1,include_followed_by: 1,include_want_retweets: 1,include_mute_edge: 1,include_can_dm: 1,include_can_media_tag: 1,skip_status: 1,id: userId}), {
  1112. headers: { authorization: "Bearer " + twitterAuth.authorization, 'Content-Type': 'application/x-www-form-urlencoded', 'x-csrf-token':twitterAuth.ct0},
  1113. }).then(res=>{
  1114. if (res.status === 200) {
  1115. r(200);
  1116. } else {
  1117. log.error(res);
  1118. twitterAuth.updateTime = 0;
  1119. GM_setValue("twitterAuth", twitterAuth);
  1120. r(601);
  1121. }
  1122. })
  1123. }, data.username)
  1124. })
  1125. }
  1126. const RetweetAuto = async function(r, url){
  1127. let retweetId = url.split("status/")[1];
  1128. try{
  1129. const auth = await AuthUpdate()
  1130. HTTP.POST( 'https://api.twitter.com/1.1/statuses/retweet.json', jq.param({ tweet_mode: "extended",id: retweetId}), {
  1131. headers: { authorization: "Bearer " + twitterAuth.authorization, 'Content-Type': 'application/x-www-form-urlencoded', 'x-csrf-token':twitterAuth.ct0},
  1132. }).then(res=>{
  1133. if (res.status === 200 || (res.status === 403 && res.responseText == '{"errors":[{"code":327,"message":"You have already retweeted this Tweet."}]}')) {
  1134. r(200);
  1135. } else {
  1136. twitterAuth.updateTime = 0;
  1137. GM_setValue("twitterAuth", twitterAuth);
  1138. r(601);
  1139. }
  1140. })
  1141. }catch(e){
  1142. log.error("TWITTER: 转推出错:", e)
  1143. r(e);
  1144. return;
  1145. }
  1146. }
  1147. const GetUserInfo = function(userName){
  1148. if(debug)log.log("====twitterGetUserInfo====");
  1149. return HTTP.GET('https://api.twitter.com/graphql/-xfUfZsnR_zqjFd-IfrN5A/UserByScreenName?variables=%7B%22screen_name%22%3A%22' + userName + '%22%2C%22withHighlightedLabel%22%3Atrue%7D', null, {
  1150. headers: { authorization: "Bearer " + twitterAuth.authorization, "content-type": "application/json"},
  1151. anonymous:true
  1152. }).then(res=>{
  1153. if (res.status === 200) {
  1154. return Promise.resolve([200, JSON.parse(res.responseText).data.user.rest_id]);
  1155. } else {
  1156. log.error(res);
  1157. return Promise.reject(601);
  1158. }
  1159. })
  1160. }
  1161. const AuthUpdate = function(update = false){
  1162. return new Promise((resolve, reject)=>{
  1163. if(new Date().getTime() - twitterAuth.updateTime < 30 * 60 * 1000 && !update){
  1164. // 未过期,不强制更新
  1165. resolve(200)
  1166. return;
  1167. }
  1168.  
  1169. if(false == getAuthStatus.twitter || true === update)
  1170. {
  1171. getAuthStatus.twitter = true;
  1172. GM_openInTab("https://twitter.com/settings/account?keyjokertask=storageAuth", true);
  1173. }
  1174. let i = 0;
  1175. let check = setInterval(()=>{
  1176. i++;
  1177. if(GM_getValue("twitterAuth") && new Date().getTime() - GM_getValue("twitterAuth").updateTime <= 10 * 1000)
  1178. {
  1179. if(GM_getValue("twitterAuth").status != 200)
  1180. {
  1181. clearInterval(check);
  1182. reject(GM_getValue("twitterAuth").status)
  1183. return;
  1184. }
  1185. twitterAuth.ct0 = GM_getValue("twitterAuth").ct0;
  1186. twitterAuth.updateTime = GM_getValue("twitterAuth").updateTime
  1187. twitterAuth.status = GM_getValue("twitterAuth").status;
  1188. clearInterval(check);
  1189. resolve(twitterAuth.status)
  1190. }
  1191. if(i >= 10)
  1192. {
  1193. clearInterval(check);
  1194. reject(408)
  1195. }
  1196. }, 1000)
  1197. })
  1198. }
  1199. // 推特取得用户页面响应头(OK)
  1200. /*twitterAP:function(r){
  1201. HTTP.GET( 'https://twitter.com/settings/account?k')
  1202. .then(res=>{
  1203. if (res.status === 200) {
  1204. log.log(res)
  1205. r(200, res.responseHeaders)
  1206. } else {
  1207. log.error(res);
  1208. r(601);
  1209. }
  1210. }).catch(err=>{
  1211. log.error(res);
  1212. r(601);
  1213. })
  1214. },*/
  1215.  
  1216. return {
  1217. FollowAuto: FollowAuto,
  1218. RetweetAuto: RetweetAuto,
  1219. AuthUpdate: AuthUpdate
  1220. }
  1221. })();
  1222. const func = {
  1223. // twitch & discord 凭证存储,人机验证处理
  1224. appHandle: function(){
  1225. switch(location.hostname)
  1226. {
  1227. case "www.twitch.tv":
  1228. if(location.search == "?keyjokertask=storageAuth")
  1229. {
  1230. let cookie = document.cookie + ";"
  1231. twitchAuth.updateTime = new Date().getTime();
  1232. if(cookie.match(/auth-token=(.+?);/) != null)
  1233. {
  1234. twitchAuth["auth-token"] = cookie.match(/auth-token=(.+?);/)[1]
  1235. twitchAuth.status = 200;
  1236. }else twitchAuth.status = 401;
  1237. GM_setValue("twitchAuth", twitchAuth)
  1238. log.log(twitchAuth)
  1239. window.close();
  1240. }
  1241. window.close();
  1242. break;
  1243. case "discord.com":
  1244. if(location.search == "?keyjokertask=storageAuth")
  1245. {
  1246. discordAuth.authorization = JSON.parse(localStorage.getItem("token"));
  1247. if(discordAuth.authorization == null)discordAuth.status = 401;
  1248. else discordAuth.status = 200;
  1249. discordAuth.updateTime = new Date().getTime();
  1250. GM_setValue("discordAuth", discordAuth);
  1251. log.log(discordAuth)
  1252. window.close();
  1253. }
  1254. break;
  1255. case "twitter.com":
  1256. if(location.search == "?keyjokertask=storageAuth")
  1257. {
  1258. let m = document.cookie.match(/ct0=(.+?);/);
  1259. if(m != null && m[1])
  1260. {
  1261. twitterAuth.status = 200;
  1262. twitterAuth.ct0 = m[1];
  1263. twitterAuth.updateTime = new Date().getTime()
  1264. }else{
  1265. twitterAuth.status = 401;
  1266. }
  1267. GM_setValue("twitterAuth", twitterAuth)
  1268. window.close();
  1269. }
  1270. break;
  1271. case "www.keyjoker.com":
  1272. if(location.search == "?keyjokertask=unbindDiscord")
  1273. {
  1274. jq(document).ready(()=>{
  1275. log.log("keyjoker unbindDiscord")
  1276. const auto = jq("h4:contains('Discord')")[0].parentNode
  1277. //auto.nextElementSibling.firstChild.click()
  1278. const modal = auto.parentNode.parentNode.parentNode.nextElementSibling
  1279. if(modal.id.indexOf('delete-identity-')===0) modal.firstChild.firstChild.lastChild.firstChild.lastChild.click()
  1280. else location.href = "https://www.keyjoker.com/socialite/discord"
  1281. })
  1282. }
  1283. break;
  1284. case "assets.hcaptcha.com":
  1285. // 人机验证
  1286. func.hcaptcha2();
  1287. break;
  1288. default :
  1289. break;
  1290. }
  1291. },
  1292. authVerify:function(){
  1293. // noticeFrame.loadFrame();
  1294. noticeFrame.addNotice("检查各项凭证");
  1295. noticeFrame.addNotice({type: "authVerify", name: "<a href=\"https://discord.com/login/\" target=\"_blank\">Discord</a> Auth", status:{id: "discord", class: "wait", text:"ready"}});
  1296. noticeFrame.addNotice({type: "authVerify", name: "<a href=\"https://accounts.spotify.com/login/\" target=\"_blank\">Spotify</a> Auth&nbsp;", status:{id: "spotify", class: "wait", text:"ready"}});
  1297. noticeFrame.addNotice({type: "authVerify", name: "<a href=\"https://steamcommunity.com/login/\" target=\"_blank\">Steam</a> Auth&nbsp;&nbsp;", status:{id: "steam", class: "wait", text:"ready"}});
  1298. noticeFrame.addNotice({type: "authVerify", name: "<a href=\"https://www.tumblr.com/login\" target=\"_blank\">Tumblr</a> Auth", status:{id: "tumblr", class: "wait", text:"ready"}});
  1299. noticeFrame.addNotice({type: "authVerify", name: "<a href=\"https://www.twitch.tv/login\" target=\"_blank\">Twitch</a> Auth&nbsp;", status:{id: "twitch", class: "wait", text:"ready"}});
  1300. noticeFrame.addNotice({type: "authVerify", name: "<a href=\"https://twitter.com/login/\" target=\"_blank\">Twitter</a> Auth", status:{id: "twitter", class: "wait", text:"ready"}});
  1301.  
  1302. DISCORD.AuthUpdate(true).then(res=>{
  1303. this.statusReact(res, "discord");
  1304. }).catch(err=>{
  1305. this.statusReact(err, "discord");
  1306. });
  1307. SPOTIFY.GetAccessToken().then((res)=>{
  1308. log.info("spotify", res)
  1309. this.statusReact(200, "spotify");
  1310. }).catch(err=>{
  1311. this.statusReact(err, "spotify");
  1312. });
  1313. STEAM.InfoUpdate("all", true).then((res)=>{
  1314. log.info("STEAM", res)
  1315. this.statusReact(200, "steam");
  1316. }).catch(err=>{
  1317. this.statusReact(err, "steam");
  1318. });
  1319. TUMBLR.AuthUpdate(true).then((status)=>{
  1320. this.statusReact(200, "tumblr");
  1321. }).catch(err=>{
  1322. this.statusReact(err, "tumblr");
  1323. });
  1324. TWITCH.AuthUpdate(true).then((status)=>{
  1325. this.statusReact(status, "twitch");
  1326. }).catch(err=>{
  1327. this.statusReact(err, "twitch");
  1328. });
  1329. TWITTER.AuthUpdate(true).then((status)=>{
  1330. this.statusReact(status, "twitter");
  1331. }).catch(err=>{
  1332. this.statusReact(err, "twitter");
  1333. });
  1334. },
  1335. statusReact: (code, id)=>{
  1336. const result = {
  1337. 200: {
  1338. class:"success",
  1339. text: 'success'
  1340. },
  1341. 601: {
  1342. class:"error",
  1343. text: 'error'
  1344. },
  1345. 401: {
  1346. class:"error", text:"Not Login"
  1347. },
  1348. 408: {class:"error", text:"Time Out"},
  1349. 603: {class:"wait", text:"Getting Auth"},
  1350. 604: {class:"error", text:"Network Error"},
  1351. 605: {class:"error", text:"评论区未找到"},
  1352. }
  1353. log.info(code)
  1354. noticeFrame.updateNotice(id, result[code])
  1355. },
  1356. checkUpdate: function(){
  1357. noticeFrame.addNotice({type:"msg",msg:"正在检查版本信息...(当前版本:" + GM_info.script.version + ")"})
  1358. HTTP.GET('https://task.jysafe.cn/keyjoker/script/update.php?type=ver', null, {headers:{action: "keyjoker"}})
  1359. .then(res=>{
  1360. const resp = JSON.parse(res.response)
  1361. if(resp.status != 200)
  1362. {
  1363. noticeFrame.addNotice({type:"msg", msg:"异常!<font class=\"error\">" + resp.msg + "</font>"})
  1364. return;
  1365. }
  1366. if(this.checkVersion(resp.ver))
  1367. {
  1368. noticeFrame.addNotice({type:"msg", msg:"发现新版本!<font class=\"success\">" + resp.ver + "=>" + resp.msg + "</font>"})
  1369. }else{
  1370. noticeFrame.addNotice({type:"msg",msg:"当前已是最新版本!"})
  1371. }
  1372. }).catch(err=>{
  1373. log.error(err);
  1374. noticeFrame.addNotice({type:"msg", msg:"请求异常!请至控制台查看详情!"})
  1375. })
  1376. },
  1377. checkVersion: function(ver){
  1378. let new_ver = ver.split('.');
  1379. let old_ver = GM_info.script.version.split('.');
  1380. for(var i=0; i<new_ver.length && i<old_ver.length; i++){
  1381. let _new = parseInt(new_ver[i]);
  1382. let _old = parseInt(old_ver[i]);
  1383. if(_new >_old){
  1384. // 需更新
  1385. return 1;
  1386. }else if(_new == _old){
  1387. continue;
  1388. }else{
  1389. break;
  1390. }
  1391. }
  1392. return 0;
  1393. },
  1394. // 做任务
  1395. do_task: function(data){
  1396. for(const task of data.actions)
  1397. {
  1398. noticeFrame.addNotice({type: "taskStatus", task:task, status:'wait'});
  1399. this.runDirectUrl(task.redirect_url);
  1400. let react = (code)=>{
  1401. switch(code)
  1402. {
  1403. case 200:
  1404. noticeFrame.updateNotice(task.id, {class:"success",text:'success'})
  1405. this.redeemAuto(task.redirect_url);
  1406. break;
  1407. case 601:
  1408. noticeFrame.updateNotice(task.id, {class:"error",text:'error'})
  1409. break;
  1410. case 401:
  1411. noticeFrame.updateNotice(task.id, {class:"error", text:"Not Login"})
  1412. break;
  1413. case 404:
  1414. {
  1415. // 创建一个新的 div 元素
  1416. let ignoreBtn = document.createElement("button");
  1417. ignoreBtn.innerText = '忽略'
  1418. ignoreBtn.style.background = 'red'
  1419. ignoreBtn.className = 'btn btn-primary'
  1420. ignoreBtn.addEventListener('click', e=>{
  1421. log.log(e)
  1422. log.log(task.id)
  1423. ignoreList.push(task.id)
  1424. GM_setValue('ignoreList', ignoreList)
  1425. })
  1426. jq(`a[href='https://www.keyjoker.com/entries/open/${task.id}']`)[0].parentNode.append(ignoreBtn)
  1427. noticeFrame.updateNotice(task.id, {class:"error", text:"目标不存在"})
  1428. }
  1429. break;
  1430. case 408:
  1431. noticeFrame.updateNotice(task.id, {class:"error", text:"Time Out"})
  1432. break;
  1433. case 603:
  1434. noticeFrame.updateNotice(task.id, {class:"wait", text:"Getting Auth"})
  1435. break;
  1436. case 604:
  1437. noticeFrame.updateNotice(task.id, {class:"error", text:"Network Error"})
  1438. break;
  1439. case 605:
  1440. noticeFrame.updateNotice(task.id, {class:"error", text:"评论区未找到"})
  1441. break;
  1442. default:
  1443. noticeFrame.updateNotice(task.id, {class:"error", text:"Unknown Error"})
  1444. log.error("React Unknown Error--->", code)
  1445. break;
  1446. }
  1447. }
  1448.  
  1449. switch(task.task.name)
  1450. {
  1451. case "Join Steam Group":
  1452. STEAM.JoinGroup(react, task.data.url);
  1453. break;
  1454. case "Rep Steam Account":
  1455. STEAM.Rep(react, task.data.id);
  1456. break;
  1457. case "Wishlist Steam Game":
  1458. STEAM.AddWishlist(react, task.data.id);
  1459. break;
  1460. case "Follow Twitter Account":
  1461. TWITTER.FollowAuto(react, task.data);
  1462. break;
  1463. case "Join Discord Server":
  1464. var server = task.data.url.split(".gg/")[1];
  1465. DISCORD.JoinServer(react, server)
  1466. break;
  1467. case "Retweet Twitter Tweet":
  1468. TWITTER.RetweetAuto(react, task.data.url);
  1469. break;
  1470. case "Save Spotify Album":
  1471. SPOTIFY.SaveAuto(react, task.data);
  1472. break;
  1473. case "Follow Spotify Account":
  1474. SPOTIFY.Follow(react, task.data);
  1475. break;
  1476. case "Follow Twitch Channel":
  1477. TWITCH.FollowAuto(react, task.data.id);
  1478. break;
  1479. case "Follow Tumblr Blog":
  1480. TUMBLR.Follow(react, task.data.name);
  1481. break;
  1482. default:
  1483. noticeFrame.updateNotice(task.id, {class:"error", text:"Unknow Type:" + task.task.name})
  1484. log.error("未指定操作" + task.task.name)
  1485. break;
  1486. }
  1487. }
  1488.  
  1489. let i = 0;
  1490.  
  1491. // 清除上次残留线程
  1492. if(null != completeCheck)clearInterval(completeCheck);
  1493. let discordCheck = true;
  1494. completeCheck = setInterval(()=>{
  1495. i++;
  1496. //if(i >= 50)clearInterval(completeCheck);
  1497. //else
  1498. jq('button[class="btn btn-primary"]').click();
  1499.  
  1500. if(1 == jq('#fraud-warning-modal[style!="display: none;"]').length){
  1501. // 有弹窗,模拟点击OK
  1502. const ele = jq('button.btn.btn-secondary[type!="button"]')
  1503. jq(".modal-backdrop, .fade, .show").remove();
  1504. if(ele.length > 0)ele[0].click();
  1505. }
  1506. if( document.getElementById("toast-container")){
  1507. // 操作不存在
  1508. if(document.getElementById("toast-container").textContent == "This action does not exist."){
  1509. jq('.card').remove();
  1510. }
  1511. // check discord error [Could not refresh Discord information, please try again.]
  1512. if(discordCheck == true && document.getElementById("toast-container").textContent == "Could not refresh Discord information, please try again.")
  1513. {
  1514. discordCheck = false;
  1515. GM_openInTab("https://www.keyjoker.com/account/identities?keyjokertask=unbindDiscord", true)
  1516. }
  1517. }
  1518. if(jq(".list-complete-item").length == 0)
  1519. {
  1520. clearInterval(completeCheck);
  1521. noticeFrame.addNotice({type:"msg", msg:"任务似乎已完成,恢复监测!"});
  1522. GM_setValue("start", 1);
  1523. checkSwitch();
  1524. checkTask.next();
  1525. }
  1526. }, 5 * 1000)
  1527. },
  1528. // 人机验证出现图片时的处理
  1529. hC_get_c: function(r){
  1530. new Promise((resolve, reject)=>{
  1531. HTTP.GET( 'https://hcaptcha.com/checksiteconfig?host=dashboard.hcaptcha.com&sitekey=e4b28873-6852-49c0-9784-7231f004b96b&sc=1&swa=1', null, {
  1532. headers: { 'Content-Type': 'application/json; charset=utf-8'},
  1533. }).then(res=>{
  1534. let ret = JSON.parse(res.response);
  1535. if(ret.pass){
  1536. GM_log(ret);
  1537. resolve(ret.c);
  1538. }else{
  1539. reject();
  1540. }
  1541. })
  1542. }).then((c)=>{
  1543. r(c);
  1544. }).catch((err)=>{
  1545. log.error(err);
  1546. //let text = document.getElementsByClassName("prompt-text")[0].innerText;
  1547. //document.getElementsByClassName("prompt-text")[0].innerText = text + "\n免验证Cookie获取异常,请手动进行验证";
  1548. });
  1549.  
  1550. },
  1551. hC_getcaptcha: function(r){
  1552. this.hC_get_c((c)=>{
  1553. new Promise((resolve, reject)=>{
  1554. HTTP.POST('https://hcaptcha.com/getcaptcha', jq.param({
  1555. sitekey:'e4b28873-6852-49c0-9784-7231f004b96b',
  1556. host:'dashboard.hcaptcha.com',
  1557. n:'暂未实现获取方案',
  1558. c:JSON.stringify(c)
  1559. }), {
  1560. headers: { 'Content-Type': 'application/x-www-form-urlencoded'},
  1561. })
  1562. }).then((res)=>{
  1563. let rep = JSON.parse(res.response);
  1564. let c = rep.generated_pass_UUID
  1565. r(c);
  1566. }).catch((err)=>{
  1567. log.error(err);
  1568. r(false)
  1569. //let text = document.getElementsByClassName("prompt-text")[0].innerText;
  1570. //document.getElementsByClassName("prompt-text")[0].innerText = text + "\ngetcaptcha failed!";
  1571. });
  1572. });
  1573. },
  1574. hcaptcha2: function () {
  1575. return false;
  1576. let hcaptcha2Click=setInterval(()=>{
  1577. if(document.getElementsByClassName("challenge-container").length != 0 && document.getElementsByClassName("challenge-container")[0].children.length != 0)
  1578. {
  1579. clearInterval(hcaptcha2Click);
  1580. log.log("open hcaptcha");
  1581. let text = document.getElementsByClassName("prompt-text")[0].innerText;
  1582. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n正在自动获取免验证Cookie";
  1583. //$(".task-grid").remove();
  1584. //$(".challenge-example").remove();
  1585.  
  1586. HTTP.GET('https://accounts.hcaptcha.com/user', null, {
  1587. headers: { 'Content-Type': 'application/json'},
  1588. }).then((csrf)=>{
  1589. this.hC_getcaptcha((token)=>{
  1590. if(token == false)
  1591. {
  1592. document.getElementsByClassName("prompt-text")[0].innerText = text + "\ntoken 获取失败";
  1593. return ;
  1594. }
  1595. HTTP.POST('https://accounts.hcaptcha.com/accessibility/get_cookie', JSON.stringify({token:token}), {
  1596. headers: { 'Content-Type': 'application/json','x-csrf-token':csrf},
  1597. }).then(res=>{
  1598. let response = res
  1599. if(response.status == 200)
  1600. {
  1601. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n免验证Cookie获取成功,请重新点击验证框";
  1602. }else if(response.status == 401)
  1603. {
  1604. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n当前账号或IP的免验证Cookie获取次数已达上限,请更换hcaptcha账号或IP";
  1605. // setTimeout(()=>{window.open("https://dashboard.hcaptcha.com/welcome_accessibility")}, 3000);
  1606. }else if(response.status == 500)
  1607. {
  1608. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n未登录hCaptcha";
  1609. // setTimeout(()=>{window.open("https://dashboard.hcaptcha.com/welcome_accessibility")}, 3000);
  1610. }else{
  1611. log.error(response);
  1612. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n发生未知错误,已将数据记录至控制台";
  1613. }
  1614. }).catch(err=>{
  1615. log.error(err)
  1616. })
  1617. });
  1618. }).catch((err)=>{
  1619. if(err.status == 401)
  1620. {
  1621. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n未登录hCaptcha";
  1622. // setTimeout(()=>{window.open("https://dashboard.hcaptcha.com/welcome_accessibility")}, 3000);
  1623. }else{
  1624. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n未知错误";
  1625. }
  1626. log.error(err);
  1627. });
  1628. }
  1629. },1000);
  1630. },
  1631. // OK
  1632. redeemAuto: function(redirect_url){
  1633. if(0 != jq('a[href="' + redirect_url + '"]').length)jq('a[href="' + redirect_url + '"]').next().click();
  1634. },
  1635. reLoadTaskList: function(r){
  1636. return new Promise((resolve, reject)=>{
  1637. // 重载任务列表
  1638. if(2 == document.getElementsByClassName('row').length)
  1639. {
  1640. jq('.row')[1].remove();
  1641. jq('.layout-container').append('<entries-component></entries-component>');
  1642. if(true == GM_getValue("offlineMode") && typeof offlineData === "object")
  1643. {
  1644. offlineData["app.js"]();
  1645. resolve();
  1646. }else{
  1647. jq.getScript("/js/app.js", (rep,status)=>{
  1648. if("success" == status)resolve();
  1649. else log.error(status, rep);
  1650. });
  1651. }
  1652. }else
  1653. {
  1654. resolve();
  1655. }
  1656. })
  1657. },
  1658. reset: function (){
  1659. if(!confirm("你确定要执行重置操作?"))return;
  1660. noticeFrame.addNotice({type:"msg",msg:"正在重置设置"})
  1661. const listValues = GM_listValues()
  1662. for (const value of listValues) {
  1663. if(value == "currentVer")continue;
  1664. noticeFrame.addNotice({type:"msg",msg:"<font class=\"error\">正在删除:" + value + "</font>"})
  1665. GM_deleteValue(value)
  1666. }
  1667. noticeFrame.addNotice({type:"msg",msg:"设置重置完毕"})
  1668. },
  1669.  
  1670. runDirectUrl:function(direct_url){
  1671. GM_log("====访问跳转链接====")
  1672. HTTP.GET(direct_url, null, {
  1673. headers: {'x-csrf-token': jq('meta[name="csrf-token"]').attr('content')},
  1674. })
  1675. },
  1676. test: function(){
  1677. // GM_openInTab("https://discord.com/channels/@me?keyjokertask=storageAuth", true);
  1678. DISCORD.JoinServer(log.info, 'QbYvb2qwDT')
  1679. }
  1680. }
  1681. // ============Start===========
  1682. if(location.pathname == "/entries"){
  1683. window.onload=()=>{
  1684. log.info("KJ main")
  1685. if(document.getElementsByClassName("nav-item active").length != 0 && document.getElementsByClassName("nav-item active")[0].innerText == "Earn Credits" && document.getElementById("logout-form")){
  1686. noticeFrame.loadFrame();
  1687. // 事件绑定
  1688. eventBind();
  1689. //let isStart=setInterval(()=>{
  1690. if(GM_getValue("start")==1){
  1691. //clearInterval(isStart);
  1692. checkTask.next();
  1693. }
  1694. //},1000);
  1695. checkUpdate();
  1696. }else{
  1697. if(jq('.container').length > 0)
  1698. {
  1699. let i = 0;
  1700. let check = setInterval(()=>{
  1701. i++;
  1702. if(jq('.container')[0].innerText == "Whoops, looks like something went wrong.")location.href = location.pathname
  1703. if(i >= 10)clearInterval(check)
  1704. }, 1000);
  1705. }
  1706. }
  1707. }
  1708. }else{
  1709. func.appHandle();
  1710. }
  1711. function checkUpdate(){
  1712. // 隔一段时间检测新版本
  1713. if(new Date().getTime() - (GM_getValue("lastCheckUpdate") || 0) > 6 * 60 * 60 * 1000)
  1714. {
  1715. func.checkUpdate();
  1716. GM_setValue("lastCheckUpdate", new Date().getTime())
  1717. }
  1718. }
  1719. function eventBind(){
  1720. jq('button#checkUpdate').click(()=>{func.checkUpdate()})
  1721. jq('button#fuck').click(function(){
  1722. checkTask.start(()=>{jq('.card').remove();})
  1723. })
  1724. jq('button#clearNotice').click(function(){
  1725. noticeFrame.clearNotice()
  1726. })
  1727. jq('button#changeLog').click(function(){
  1728. noticeFrame.addNotice({type:"msg", msg:"获取日志中..."})
  1729. HTTP.GET( 'https://task.jysafe.cn/keyjoker/script/update.php?type=changelog&ver=' + GM_info.script.version, null, {
  1730. headers:{action: "keyjoker"},
  1731. }).then(res=>{
  1732. let ret = JSON.parse(res.response)
  1733. if(ret.status != 200)
  1734. {
  1735. noticeFrame.addNotice({type:"msg", msg:"异常!<font class=\"error\">" + ret.msg + "</font>"})
  1736. }else
  1737. {
  1738. noticeFrame.addNotice({type:"msg", msg:"<font class=\"success\">" + ret.msg + "</font>"})
  1739. }
  1740. }).catch(err=>{
  1741. log.error(err);
  1742. noticeFrame.addNotice({type:"msg", msg:"<font class=\"error\">请求异常!请至控制台查看详情!</font>"})
  1743. })
  1744. })
  1745. jq('button#report').click(function(){
  1746. noticeFrame.addNotice({type:"msg",msg:"目前提供以下反馈渠道:"})
  1747. noticeFrame.addNotice({type:"msg",msg:"<a href=\"https://www.jysafe.cn/4332.air\" target=\"_blank\">博客页面</a>"})
  1748. noticeFrame.addNotice({type:"msg",msg:"<a href=\"https://github.com/jiyeme/keyjokerScript/issues/new/choose\" target=\"_blank\">GitHub</a>"})
  1749. noticeFrame.addNotice({type:"msg",msg:"<a href=\"https://keylol.com/t660181-1-1\" target=\"_blank\">其乐社区</a>"})
  1750. })
  1751. // 版本升级后显示一次更新日志
  1752. if(GM_getValue("currentVer") != GM_info.script.version)
  1753. {
  1754. HTTP.GET('https://task.jysafe.cn/keyjoker/script/update.php?type=changelog&ver=' + GM_info.script.version, null, {
  1755. headers:{action: "keyjoker"},
  1756. anonymous:true
  1757. }).then(res=>{
  1758. let ret = JSON.parse(res.response)
  1759. if(ret.status != 200)
  1760. {
  1761. noticeFrame.addNotice({type:"msg", msg:"异常!<font class=\"error\">" + ret.msg + "</font>"})
  1762. }else
  1763. {
  1764. noticeFrame.addNotice({type:"msg", msg:"<font class=\"success\">" + ret.msg + "</font>"})
  1765. }
  1766. }).catch(err=>{
  1767. log.error(err);
  1768. noticeFrame.addNotice({type:"msg", msg:"更新日志获取异常!请至控制台查看详情!"})
  1769. })
  1770. GM_setValue("currentVer", GM_info.script.version)
  1771. }
  1772. }
  1773. GM_registerMenuCommand("设置时间间隔", checkTask.setTime);
  1774. GM_registerMenuCommand("重置设置", func.reset);
  1775. GM_registerMenuCommand("凭证检测",()=>{
  1776. func.authVerify();
  1777. });
  1778. function offlineSwitch(id){
  1779. GM_unregisterMenuCommand(id);
  1780. if(true == GM_getValue("offlineMode")){
  1781. let id = GM_registerMenuCommand("进入稳定模式",()=>{
  1782. GM_setValue("offlineMode", false);
  1783. offlineSwitch(id);
  1784. });
  1785. }else{
  1786. let id = GM_registerMenuCommand("进入极速模式",()=>{
  1787. GM_setValue("offlineMode", true);
  1788. offlineSwitch(id);
  1789. });
  1790. }
  1791. }
  1792. function checkSwitch(){
  1793. GM_unregisterMenuCommand(checkSwitchId);
  1794. if(1 == GM_getValue("start")){
  1795. checkSwitchId = GM_registerMenuCommand("停止检测",()=>{
  1796. let date=new Date();
  1797. let hour=date.getHours();
  1798. let min=date.getMinutes()<10?("0"+date.getMinutes()):date.getMinutes();
  1799. GM_setValue("start",0);
  1800. jq(".border-bottom").text(hour + ":" + min + " 停止执行新任务检测");
  1801. checkSwitch();
  1802. });
  1803. }else{
  1804. checkSwitchId = GM_registerMenuCommand("开始检测",()=>{
  1805. checkTask.start();
  1806. checkSwitch();
  1807. });
  1808. }
  1809. }
  1810. checkSwitch(null);
  1811. offlineSwitch(null);
  1812. if(debug)
  1813. {
  1814. GM_registerMenuCommand("Test",()=>{
  1815. func.test();
  1816. });
  1817. }
  1818. } catch (e) {
  1819. setTimeout(() => {
  1820. noticeFrame.addNotice({ type: 'msg', msg:"<font class\"error\">任务脚本执行期间发生预期之外的错误,详情见控制台</font>" })
  1821. }, 500)
  1822. console.log('%c%s', 'color:white;background:red', e.stack)
  1823. }
  1824. })();