KeyJoker Auto Task

KeyJoker Auto Task Script

  1. // ==UserScript==
  2. // @name KeyJoker Auto Task
  3. // @namespace KeyJokerAutoTask
  4. // @version 1.6.1
  5. // @description KeyJoker Auto Task Script
  6. // @description:zh-cn KeyJoker 的任务自动化脚本
  7. // @author 祭夜
  8. // @icon https://www.jysafe.cn/assets/images/avatar.jpg
  9. // @match *://www.keyjoker.com/entries*
  10. // @match *://assets.hcaptcha.com/*
  11. // @match https://www.twitch.tv/settings/profile?keyjokertask=*
  12. // @match https://twitter.com/settings/account?keyjokertask=*
  13. // @match http://localhost:3001*
  14. // @match https://msojocs.github.io/keyjoker-script*
  15. // @supportURL https://greasyfork.org/zh-CN/scripts/406476-keyjoker-auto-task/feedback
  16. // @homepage https://github.com/msojocs/keyjoker-script/
  17. // @run-at document-start
  18. // @grant GM_registerMenuCommand
  19. // @grant GM_unregisterMenuCommand
  20. // @grant GM_addStyle
  21. // @grant GM_xmlhttpRequest
  22. // @grant GM_setClipboard
  23. // @grant GM_setValue
  24. // @grant GM_getValue
  25. // @grant GM_listValues
  26. // @grant GM_deleteValue
  27. // @grant GM_openInTab
  28. // @grant GM_log
  29. // @grant GM_notification
  30. // @grant GM_getResourceText
  31. // @connect hcaptcha.com
  32. // @connect store.steampowered.com
  33. // @connect steamcommunity.com
  34. // @connect twitter.com
  35. // @connect facebook.com
  36. // @connect discord.com
  37. // @connect twitch.tv
  38. // @connect tumblr.com
  39. // @connect spotify.com
  40. // @connect task.jysafe.cn
  41. // @connect raw.fastgit.org
  42. // @connect 127.0.0.1
  43. // @resource iconfont https://at.alicdn.com/t/font_3156299_07qky93uxv0e.css
  44. // @require https://lib.baomitu.com/jquery/3.3.1/jquery.min.js
  45. // @require https://lib.baomitu.com/i18next/21.3.0/i18next.min.js
  46. // @require https://lib.baomitu.com/jquery-i18next/1.2.1/jquery-i18next.min.js
  47. // @require https://unpkg.com/i18next-http-backend@1.3.2/i18nextHttpBackend.min.js
  48. // @require https://cdn.jsdelivr.net/gh/msojocs/keyjoker-script@9a84040672898ece9d677e72c7617f95d7c92c86/keyjoker.ext.js
  49. // ==/UserScript==
  50. // @require http://task.jysafe.cn/keyjoker/script/keyjoker6.ext.js
  51.  
  52. (function() {
  53. 'use strict';
  54. const debug = false;
  55.  
  56. const languagePrefix = "https://cdn.jsdelivr.net/gh/msojocs/keyjoker-script@master/locales"
  57. const KJConfig = GM_getValue('KJConfig') || {
  58. language: navigator.language
  59. }
  60. // iconfont
  61. GM_addStyle(GM_getResourceText('iconfont'))
  62.  
  63. const discordAuth = GM_getValue('discordAuth') || {
  64. enable: false,
  65. authorization: "",
  66. status:0,
  67. updateTime: 0
  68. }
  69. // steam信息
  70. const steamConfig = GM_getValue('steamInfo') || {
  71. userName: '',
  72. steam64Id: '',
  73. communitySessionID: '',
  74. storeSessionID: '',
  75. comUpdateTime: 0,
  76. storeUpdateTime: 0
  77. }
  78. const twitchConfig = GM_getValue('twitchAuth') || {
  79. "auth-token": "",
  80. status:0,
  81. updateTime: 0
  82. }
  83. const twitterConfig = GM_getValue('twitterAuth') || {
  84. authorization: "AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA",
  85. ct0: '',
  86. status: 0,
  87. updateTime: 0
  88. }
  89. const ignoreList = GM_getValue('ignoreList') || [];
  90.  
  91. const jq = $;
  92. const kjData = offlineData;
  93. unsafeWindow.jq = jq
  94. let completeCheck = null;
  95.  
  96. // 监听处理器hook
  97. window.pro_elt_addEventListener=Element.prototype.addEventListener;
  98. Element.prototype.addEventListener=function(){
  99. if(!this.eventList) this.eventList={};
  100. if(!this.eventList[arguments[0]]) this.eventList[arguments[0]]=[];
  101. this.eventList[arguments[0]].push(arguments[1]);
  102.  
  103. // fix dropdown
  104. if(this.id === 'user-dropdown' && this.eventList?.click?.length === 1)return;
  105.  
  106. window.pro_elt_addEventListener.apply(this,arguments);
  107. };
  108.  
  109. // 0-未动作|200-成功取得|401未登录|603正在取得
  110. const getAuthStatus = {
  111. discord: false,
  112. spotify: false,
  113. steamStore: 0,
  114. steamCom: 0,
  115. // tumblr: false,
  116. twitch: false,
  117. twitter: 0
  118. }
  119. var checkSwitchId = null;
  120. const noticeFrame = {
  121. loadFrame: ()=>{
  122. log.log("loadFrame");
  123. jq('body').append(`<style>
  124. .hidden{display:none!important}
  125. .fuck-task-logs li{display:list-item !important;float:none !important}
  126. #extraBtn .el-badge.item{margin-bottom:4px !important}
  127. #extraBtn .el-badge.item sup{padding-right:0 !important}
  128. .fuck-task-logs{width:auto;max-width:50%;max-height:50%;z-index:99999999999 !important}
  129. .fuck-task-logs .el-notification__group{width:100%}
  130. .fuck-task-logs .el-notification__title{text-align:center}
  131. .fuck-task-logs .el-notification__content{overflow:auto;max-height:230px}
  132. font.start{color:black;}
  133. font.success{color:green;}
  134. font.error{color:red;}
  135. font.warning{color:#00f;}
  136. font.wait{color:#9c27b0;}
  137. .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}
  138. .el-notification__group{margin-left:13px;margin-right:8px}
  139. .el-notification__title{font-weight:700;font-size:16px;color:#303133;margin:0}
  140. .el-notification__content{font-size:14px;line-height:21px;margin:6px 0 0;color:#606266;text-align:justify}
  141. .el-notification__content p{margin:0}
  142. .el-badge{position:relative;vertical-align:middle;display:inline-block}
  143. .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}
  144. .el-badge__content.is-fixed{position:absolute;top:10px;right:10px;-webkit-transform:translateY(-50%) translateX(100%);transform:translateY(-50%) translateX(100%)}
  145. .el-badge__content.is-fixed.is-dot{right:8px}
  146. .el-badge__content.is-dot{height:8px;width:8px;padding:0;right:0;border-radius:50%}
  147. .el-button{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}
  148. .el-button{-webkit-box-sizing:border-box}
  149. .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}
  150. .el-button:focus,.el-button:hover{color:#409eff;border-color:#c6e2ff;background-color:#ecf5ff}
  151. .el-button:active{color:#3a8ee6;border-color:#3a8ee6;outline:0}
  152. .el-button::-moz-focus-inner{border:0}
  153. .el-button.is-circle{border-radius:50%;padding:15px}
  154. #extraBtn .el-button.is-circle{padding:8px !important}
  155. </style>
  156.  
  157. <div role="alert" class="el-notification fuck-task-logs right" style="bottom: 16px; z-index: 2000;">
  158. <div class="notification el-notification__group">
  159. <h2 id="extraBtn" class="el-notification__title">
  160.  
  161. <div class="el-badge item">
  162. <button id="checkUpdate" type="button" class="el-button el-button--default is-circle" data-i18n="[title]notification.checkUpdate" title="检查更新">
  163. <i class="iconfont icon-update"></i>
  164. </button>
  165. <sup class="el-badge__content el-badge__content--undefined is-fixed is-dot" style="display: none;"></sup>
  166. </div>
  167.  
  168. <div class="el-badge item">
  169. <button id="fuck" type="button" class="el-button el-button--default is-circle" data-i18n="[title]notification.startTask" title="开始做任务">
  170. <i class="iconfont icon-Start-01"></i>
  171. </button>
  172. <sup class="el-badge__content el-badge__content--undefined is-fixed is-dot" style="display: none;"></sup>
  173. </div>
  174. <div class="el-badge item hidden" >
  175. <button id="pause-fuck" type="button" class="el-button el-button--default is-circle" data-i18n="[title]notification.pauseTask" title="暂停做任务">
  176. <i class="iconfont icon-Stop"></i>
  177. </button>
  178. <sup class="el-badge__content el-badge__content--undefined is-fixed is-dot" style="display: none;"></sup>
  179. </div>
  180. <div class="el-badge item hidden" >
  181. <button id="stop-fuck" type="button" class="el-button el-button--default is-circle" data-i18n="[title]notification.stopTask" title="停止做任务">
  182. <i class="iconfont icon-Stop"></i>
  183. </button>
  184. <sup class="el-badge__content el-badge__content--undefined is-fixed is-dot" style="display: none;"></sup>
  185. </div>
  186.  
  187. <div class="el-badge item"><button id="changeLog" type="button" class="el-button el-button--default is-circle" data-i18n="[title]notification.viewChangelog" title="查看更新内容">
  188. <i class="iconfont icon-text"></i>
  189. </button>
  190. <sup class="el-badge__content el-badge__content--undefined is-fixed is-dot" style="display: none;"></sup>
  191. </div>
  192.  
  193. <div class="el-badge item">
  194. <button type="button" id="setting" class="el-button el-button--default is-circle" data-i18n="[title]notification.setting" title="设置">
  195. <i class="iconfont icon-setting"></i>
  196. </button>
  197. <sup class="el-badge__content el-badge__content--undefined is-fixed is-dot" style="display: none;"></sup>
  198. </div>
  199.  
  200. <div class="el-badge item">
  201. <button id="clearNotice" type="button" class="el-button el-button--default is-circle" data-i18n="[title]notification.clearLog" title="清空执行日志">
  202. <i class="iconfont icon-clear"></i>
  203. </button>
  204. <sup class="el-badge__content el-badge__content--undefined is-fixed is-dot" style="display: none;"></sup>
  205. </div>
  206.  
  207. <div class="el-badge item">
  208. <button id="report" type="button" class="el-button el-button--default is-circle" data-i18n="[title]notification.bugReport" title="提交建议/BUG">
  209. <i class="iconfont icon-bug-report"></i>
  210. </button>
  211. <sup class="el-badge__content el-badge__content--undefined is-fixed is-dot" style="display: none;"></sup>
  212. </div>
  213. </h2>
  214. <h2 class="el-notification__title" data-i18n="notification.logForRunning">任务执行日志</h2>
  215. <div class="el-notification__content">
  216. <span class="${!debug?'hidden':''}" data-i18n="test.one" data-i18n-options='{"a": "123"}'>test</span>
  217. <p></p>
  218. </div>
  219. </div>
  220. </div>
  221. `)
  222. },
  223. // 添加
  224. addNotice: function(data){
  225. switch(data.type)
  226. {
  227. case "taskStatus":
  228. 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>`);
  229. break;
  230. case "msg":
  231. jq('.el-notification__content').append(`<li>${data.msg}</li>`);
  232. break;
  233. case "authVerify":
  234. jq('.el-notification__content').append(`<li>${data.name} |<font id="${data.status.id}" class="${data.status.class}">${data.status.text}</font></li>`);
  235. break;
  236. default:
  237. jq('.el-notification__content').append(`<li>${data}</li>`);
  238. break;
  239. }
  240. if(jq('.notification').localize)jq('.notification').localize();
  241. },
  242. // 清空
  243. clearNotice:()=>{
  244. jq('.el-notification__content li').remove();
  245. },
  246. // 更新
  247. updateNotice: function(id, result){
  248. jq(`font#${id}`).removeClass()
  249. jq(`font#${id}`).addClass(result.class)
  250. jq(`font#${id}`).text(result.text)
  251. },
  252. }
  253. const KJModal = {
  254. show: (config)=>{
  255. const html = `<div id="custom-modal" tabindex="-1" role="dialog" aria-labelledby="fraud-warning-modal-title" class="modal fade show" style="display: block; padding-right: 15px;" aria-modal="true">
  256. <div role="document" class="modal-dialog modal-dialog-centered">
  257. <div class="modal-content">
  258. <div class="modal-header">
  259. <h5 id="fraud-warning-modal-title" class="modal-title" data-i18n="modal.${config?.title ?? 'title'}">${config?.title ?? 'title'}</h5>
  260. <button id="custom-modal-close" type="button" data-dismiss="modal" aria-label="Close" class="close"><span aria-hidden="true">×</span></button>
  261. </div>
  262. <div class="modal-body">${config?.content ?? 'content'}</div>
  263. <div class="modal-footer">
  264. <button id="custom-modal-cancel" class="btn btn-secondary" data-i18n="modal.${config?.cancelText??'cancel'}">${config?.cancelText??'Cancel'}</button>
  265. <button id="custom-modal-confirm" class="btn btn-primary" data-i18n="modal.${config?.comfirText??'confirm'}">${config?.comfirText??'Okay'}</button>
  266. </div>
  267. </div><!--modal-content-->
  268. </div><!--document-->
  269. </div>
  270. <div class="modal-backdrop fade show"></div>`
  271.  
  272. return new Promise((resolve, reject)=>{
  273. if(jq('#custom-modal').length === 1){
  274. jq('#custom-modal').remove()
  275. jq('.modal-backdrop, .fade, .show').remove()
  276. }
  277. const ele = jq('body').append(html)
  278.  
  279. jq('#custom-modal').localize(config?.options ?? null)
  280.  
  281. jq('#custom-modal-close').click(()=>{
  282. jq('#custom-modal').remove()
  283. jq('.modal-backdrop, .fade, .show').remove()
  284. reject()
  285. })
  286. jq('#custom-modal-cancel').click(()=>{
  287. jq('#custom-modal').remove()
  288. jq('.modal-backdrop, .fade, .show').remove()
  289. reject()
  290. })
  291. jq('#custom-modal-confirm').click(()=>{
  292. jq('#custom-modal').remove()
  293. jq('.modal-backdrop, .fade, .show').remove()
  294. resolve()
  295. })
  296. })
  297. }
  298. }
  299. const log = (()=>{
  300. const log = (...data)=>{
  301. if(debug)console.log("KJ", ...data)
  302. }
  303. const info = (...data)=>{
  304. if(debug)console.info("KJ", ...data)
  305. }
  306. const error = (...data)=>{
  307. console.error("KJ", ...data)
  308. }
  309. const warn = (...data)=>{
  310. if(debug)console.warn("KJ", ...data)
  311. }
  312. return {
  313. log,
  314. info,
  315. warn,
  316. error
  317. }
  318. })();
  319. const HTTP = (function(){
  320. // [修改自https://greasyfork.org/zh-CN/scripts/370650]
  321. const httpRequest = function (e) {
  322. const requestObj = {}
  323. requestObj.url = e.url
  324. requestObj.method = e.method.toUpperCase()
  325. requestObj.timeout = e.timeout || 30000
  326. if (e.responseType) requestObj.responseType = e.responseType
  327. if (e.headers) requestObj.headers = e.headers
  328. if (e.binary) requestObj.binary = e.binary;
  329. if (e.data) requestObj.data = e.data
  330. if (e.cookie) requestObj.cookie = e.cookie
  331. if (e.anonymous) requestObj.anonymous = e.anonymous
  332. if (e.onload) requestObj.onload = e.onload
  333. if (e.fetch) requestObj.fetch = e.fetch
  334. if (e.onreadystatechange) requestObj.onreadystatechange = e.onreadystatechange
  335. requestObj.onerror = e.onerror || function (data) {
  336. log.info('请求出错:', data)
  337. }
  338. requestObj.ontimeout = e.ontimeout || function (data) {
  339. log.info('请求超时:', data)
  340. e.onerror({reason: 'ontimeout', status: 408, data})
  341. }
  342. requestObj.onabort = e.onabort || function (data) {
  343. log.info('请求终止:', data)
  344. e.onerror({reason: 'abort', data})
  345. }
  346. log.info('发送请求:', requestObj)
  347. GM_xmlhttpRequest(requestObj);
  348. }
  349. function get(url, data={}, e = {}){
  350. return new Promise((resolve, reject)=>{
  351. e.url = url;
  352. e.method = "GET";
  353. e.data = data;
  354. e.onload = resolve;
  355. e.onerror = reject;
  356. httpRequest(e)
  357. })
  358. }
  359. function post(url, data={}, e = {}){
  360. return new Promise((resolve, reject)=>{
  361. e.url = url;
  362. e.method = "POST";
  363. e.data = data;
  364. e.onload = resolve;
  365. e.onerror = reject
  366. httpRequest(e);
  367. })
  368. }
  369. function put(url, data={}, e = {}){
  370. return new Promise((resolve, reject)=>{
  371. e.url = url;
  372. e.method = "PUT";
  373. e.data = data;
  374. e.onload = resolve
  375. e.onerror = reject
  376. httpRequest(e);
  377. })
  378. }
  379. return {
  380. GET: get,
  381. POST: post,
  382. PUT: put
  383. }
  384. })();
  385. try{
  386. const checkTask = {
  387. reLoad: function (time){
  388. let date=new Date();
  389. let hour=date.getHours();
  390. let min=date.getMinutes()<10?("0"+date.getMinutes()):date.getMinutes();
  391. if(GM_getValue("start")==1){
  392. jq(".border-bottom > #checkTime").text(`${hour}:${min}`);
  393. log.info(`检测:${parseInt(new Date().getTime()/1000)}`)
  394. jq.ajax({
  395. url:"/entries/load",
  396. type:"get",
  397. headers:{'x-csrf-token': jq('meta[name="csrf-token"]').attr('content')},
  398. success:(data,status,xhr)=>{
  399. // 忽略处理,不做的任务处理
  400. const disabledTask = GM_getValue('taskDisabled') || {}
  401. log.log(disabledTask)
  402. // 过滤出不在忽略列表且要做的任务
  403. log.log('actions before filter', data.actions)
  404. data.actions = data.actions.filter(e=>ignoreList.indexOf(e.id)===-1 && !disabledTask[e.task.provider.icon])
  405. log.log('actions after filter', data.actions)
  406.  
  407. log.info("检测是否新增")
  408. if(data && (data.actions && (data.actions.length > 0) )){
  409. log.info("检测是否新增", "是")
  410. log.log(data);
  411. let date=new Date();
  412. let hour=date.getHours();
  413. let min=date.getMinutes()<10?("0"+date.getMinutes()):date.getMinutes();
  414. jq(".border-bottom").html(`${hour}:${min} <span data-i18n='message.newTaskAvailable'>检测到新任务(暂停检测)</span>`);
  415.  
  416. // 清空提示
  417. noticeFrame.clearNotice();
  418. // 关闭检测开关
  419. GM_setValue("start", 0);
  420. // 菜单显示更新
  421. checkSwitch();
  422.  
  423. log.info("更新列表")
  424. kjData.loadData.actions = data.actions
  425. kjData.loadData.reward = data.reward
  426. kjData.loadData.isLoading = false
  427.  
  428. log.info("做任务")
  429. func.do_task(data);
  430. }else{
  431. log.info("检测是否新增", "否")
  432. setTimeout(()=>this.reLoad(time), time);
  433. }
  434. },
  435. error:(err)=>{
  436. window.location.reload(true);
  437. }
  438. });
  439. }
  440. },
  441. setTime: function (){
  442. let time=prompt('请输入获取任务信息的时间间隔(单位:秒):');
  443. if(!isNaN(time)){
  444. GM_setValue("time",parseInt(time));
  445. }
  446. },
  447. start: function (r = null){
  448. let time = GM_getValue("time");
  449. if(!time){
  450. time=60;
  451. }
  452.  
  453. KJModal.show({
  454. title: 'exeConfirm',
  455. content: `<span data-i18n="modal.exeConfirm1" data-i18n-options='{"time": ${time}}'></span>`,
  456. }).then(()=>{
  457. log.log('确认')
  458. if(GM_getValue('start') === 1)return;
  459. GM_setValue("start",1);
  460. if(r)r();
  461. this.next();
  462. }).catch(()=>{
  463. log.log('取消')
  464. })
  465. },
  466. next: function (){
  467. if(kjData.loadData)kjData.loadData.actions = []
  468. //kjData.loadData.isLoading = true
  469. jq(".border-bottom").html("<span id='checkTime'></span><span data-i18n='message.executing'>执行新任务检测</span>");
  470. jq(".border-bottom").localize && jq(".border-bottom").localize()
  471. // 关闭弹窗提示
  472. document.cookie = "fraud_warning_notice=1; expires=Sun, 1 Jan 2030 00:00:00 UTC; path=/"
  473. // 初始化凭证获取状态
  474. getAuthStatus.spotify = false;
  475. getAuthStatus.steamStore = 0;
  476. getAuthStatus.steamCom = 0;
  477. // getAuthStatus.tumblr = false;
  478. getAuthStatus.twitch = false;
  479. getAuthStatus.twitter = 0;
  480.  
  481. // 切换按钮
  482. jq('#fuck').parent().addClass('hidden')
  483. jq('#pause-fuck').parent().addClass('hidden')
  484. jq('#stop-fuck').parent().removeClass('hidden')
  485.  
  486. let time = GM_getValue("time");
  487. if(!time){
  488. time=60;
  489. }
  490. this.reLoad(time*1000);
  491. },
  492.  
  493. }
  494. // 模拟点击
  495. const DISCORD = (()=>{
  496. // 在KJ界面执行
  497. const JoinServer = (r, data)=>{
  498. log.info("加入discord", data.url)
  499. const url = data.url;
  500. GM_openInTab(`${url}?keyjokertask=joinDiscord&taskid=${data.id}`, true);
  501. let before = GM_getValue("discord") || {}
  502. before[data.id] = 0
  503. GM_setValue("discord", before)
  504. let checkInterval;
  505. const checkDiscordTaskStatus = ()=>{
  506. let status = GM_getValue("discord") || {}
  507. if(status[data.id] !== 0){
  508. r(status[data.id])
  509. clearInterval(checkInterval)
  510. }
  511. }
  512. checkInterval = setInterval(checkDiscordTaskStatus, 1000)
  513. }
  514. // 在Discord邀请页面执行
  515. const JoinServer2 = ()=>{
  516. log.info('JoinServer2')
  517. window.onbeforeunload = window.onunload = ()=>{
  518. log.info('溜了溜了')
  519. window.close()
  520. }
  521. let status = GM_getValue("discord") || {}
  522. const clickAction = ()=>{
  523. let search = location.search
  524. if(search == null){
  525. log.info("discord", "search获取失败")
  526. return;
  527. }
  528. let match = search.match(/taskid=(\d+)/)
  529. if(match == null){
  530. log.info("discord", "taskid获取失败")
  531. return;
  532. }
  533. let id = match[1]
  534.  
  535. if(jq("input[name='username']").length === 1 || jq("input[name='email']").length === 1){
  536. // 未登录
  537. log.info("discord", "未登录")
  538. status[id]= 401
  539. }else if(jq('button').length === 2){
  540. status[id]= 404
  541. log.info("discord", "服务器不存在")
  542. }else if(jq('button').length === 1){
  543. status[id]= 200
  544. log.info("discord", "加入服务器")
  545. jq('button').click()
  546. setTimeout(window.close, 1000)
  547. }
  548. GM_setValue("discord", status)
  549. }
  550. setInterval(clickAction, 1000)
  551. }
  552. return {
  553. JoinServer: JoinServer,
  554. JoinServer2: JoinServer2,
  555. }
  556. })();
  557. // 自动化
  558. const DISCORD2 = (()=>{
  559. const AuthUpdate = (update = false)=>{
  560. return new Promise((resolve, reject)=>{
  561. if (new Date().getTime() - discordAuth.updateTime < 30 * 60 * 1000 && discordAuth.status == 200 && !update) {
  562. log.info("DISCORD: 直接使用未过期的Auth")
  563. resolve(200)
  564. return;
  565. }
  566. if(false == getAuthStatus.discord || true === update)
  567. {
  568. getAuthStatus.discord = true;
  569. const tab = GM_openInTab("https://discord.com/channels/@me?keyjokertask=storageAuth", {active: false, insert: true, setParent: true});
  570. tab.onclose = ()=>{
  571. if(GM_getValue("discordAuth") && new Date().getTime() - GM_getValue("discordAuth").updateTime <= 10 * 1000)
  572. {
  573. if(GM_getValue("discordAuth").status != 200)
  574. {
  575. reject(GM_getValue("discordAuth").status)
  576. return;
  577. }
  578. discordAuth.authorization = GM_getValue("discordAuth").authorization
  579. discordAuth.updateTime = GM_getValue("discordAuth").updateTime
  580. discordAuth.status = GM_getValue("discordAuth").status;
  581. resolve(discordAuth.status)
  582. }
  583. }
  584. }
  585. })
  586. }
  587. const getServerInfo = async(server)=>{
  588. // https://discord.com/api/v9/invites/h9frErUaV4?with_counts=true&with_expiration=true
  589. return HTTP.GET(`https://discord.com/api/v9/invites/${server}`, {
  590. with_counts: true,
  591. with_expiration: true
  592. }, {
  593. headers: {
  594. referer: 'https://discord.com/invite/' + server,
  595. authorization: discordAuth.authorization,
  596. 'x-super-properties': discordAuth.xSuperProperties,
  597. 'x-fingerprint': discordAuth.xFingerprint,
  598. },
  599. responseType: 'json'
  600. }).then(res=>{
  601. if(res.status == 200)return Promise.resolve(res.response)
  602. else return Promise.reject(res.status)
  603. })
  604. }
  605. const doJoinServer = (server, info)=>{
  606. const xContextProperties = {
  607. "location":"Accept Invite Page",
  608. "location_guild_id":info.guild.id,
  609. "location_channel_id":info.channel.id,
  610. "location_channel_type":info.channel.type
  611. };
  612. return HTTP.POST(`https://discord.com/api/v6/invites/${server}`, "{}", {
  613. headers: {
  614. 'content-type': 'application/json',
  615. referer: 'https://discord.com/invite/' + server,
  616. authorization: discordAuth.authorization,
  617. 'x-super-properties': discordAuth.xSuperProperties,
  618. 'x-fingerprint': discordAuth.xFingerprint,
  619. 'x-context-properties': window.btoa(JSON.stringify(xContextProperties))
  620. },
  621. overrideMimeType: 'application/json',
  622. responseType: 'json'
  623. }).then(res=>{
  624. if (res.status === 200) {
  625. log.log({ result: 'success', statusText: res.statusText, status: res.status })
  626. return Promise.resolve(200);
  627. } else {
  628. log.error("状态码异常:", res);
  629. log.info(res.responseText)
  630. return Promise.reject(res.status);
  631. }
  632. })
  633. }
  634. const JoinServer = async (r, server)=>{
  635. log.info("DISCORD: 准备加入服务器:", server)
  636. try{
  637. log.info("DISCORD: 更新凭证:", server)
  638. const auth = await AuthUpdate()
  639.  
  640. log.info("DISCORD: 加入服务器:", server)
  641. const serverInfo = await getServerInfo(server)
  642. const ret = await doJoinServer(server, serverInfo)
  643. log.info('DISCORD: ret', ret)
  644. r(ret)
  645. }catch(e){
  646. log.error("DISCORD: 加入服务器出错:", e)
  647. r(e);
  648. return;
  649. }
  650. }
  651. const LeaveServer = (r, serverId)=>{
  652. AuthUpdate((ret)=>{
  653. if(ret != 200)
  654. {
  655. r(ret);
  656. return;
  657. }
  658. jq.ajax({
  659. url: 'https://discord.com/api/v6/users/@me/guilds/' + serverId,
  660. method: 'DELETE',
  661. headers: { authorization: discordAuth.authorization, "content-type": "application/json"},
  662. onload: (response) => {
  663. if (response.status === 604) {
  664. log.log({ result: 'success', statusText: response.statusText, status: response.status })
  665. r(604);
  666. } else {
  667. log.error(response);
  668. r(601);
  669. }
  670. },
  671. error:(res)=>{
  672. log.error(res);
  673. r(601);
  674. },
  675. anonymous:true
  676. })
  677. })
  678. }
  679. return {
  680. AuthUpdate: AuthUpdate,
  681. JoinServer: JoinServer,
  682. LeaveServer: LeaveServer
  683. }
  684. })();
  685. const SPOTIFY = (()=>{
  686. const GetUserInfo = async (r)=>{
  687. r(603)
  688. const accessToken = await GetAccessToken()
  689. return HTTP.GET('https://api.spotify.com/v1/me', null, {
  690. headers:{authorization: "Bearer " + accessToken},
  691. anonymous:true
  692. }).then((res, accessToken)=>{
  693. if (res.status === 200) {
  694. r(200, accessToken, JSON.parse(res.responseText).id);
  695. } else {
  696. log.error(res)
  697. r(401);
  698. }
  699. }).catch(err=>{
  700. log.error("SPOTIFY.GetUserInfo error", err)
  701. r(408)
  702. })
  703. }
  704. const GetAccessToken = function(){
  705. return HTTP.GET('https://open.spotify.com/get_access_token?reason=transport&productType=web_player', null, {responseType: 'json'})
  706. .then(res=>{
  707. //log.log(res)
  708. if(res.status != 200){
  709. return Promise.reject(401);
  710. }
  711. const resp = res.response
  712. if (!resp.isAnonymous) {
  713. return Promise.resolve(JSON.parse(res.responseText).accessToken);
  714. } else {
  715. log.error(res);
  716. return Promise.reject(401);
  717. }
  718. }).catch(err=>{
  719. log.error('SPOTIFY.GetAccessToken', err)
  720. return Promise.reject(err.status)
  721. })
  722. }
  723. const SaveAuto = (r, data, del = false)=>{
  724. GetUserInfo((status, accessToken = null, userId = null)=>{
  725. if(status != 200)
  726. {
  727. r(status);
  728. return;
  729. }
  730. let putUrl = "";
  731. new Promise((resolve, reject)=>{
  732. switch(data.type)
  733. {
  734. case "album":
  735. putUrl = "https://spclient.wg.spotify.com/collection-view/v1/collection/albums/" + userId + "?base62ids=" + data.id + "&model=bookmark";
  736. resolve(putUrl);
  737. break;
  738. case "track":
  739. HTTP.GET('https://api.spotify.com/v1/tracks?ids=' + data.id + '&market=from_token', null, {
  740. headers:{authorization: "Bearer " + accessToken},
  741. anonymous:true
  742. }).then(res=>{
  743. if(res.status == 200)
  744. {
  745. let temp = JSON.parse(res.response);
  746. putUrl = "https://spclient.wg.spotify.com/collection-view/v1/collection/albums/" + userId + "?base62ids=" + temp.tracks[0].album.id + "&model=bookmark";
  747. resolve(putUrl);
  748. }else
  749. {
  750. log.error(res);
  751. reject(601);
  752. }
  753. }).catch(err=>{
  754. log.error(err);
  755. reject(601);
  756. })
  757. break;
  758. default:
  759. log.error("spotifySaveAuto未知类型:", data);
  760. r(601);
  761. return;
  762. break;
  763. }
  764. }).then((putUrl)=>{
  765. log.log(putUrl)
  766. jq.ajax({
  767. type: !del?'PUT':"DELETE",
  768. url: putUrl,
  769. headers: {authorization: "Bearer " + accessToken},
  770. success: function(data){
  771. log.log(data);
  772. r(200);
  773. },
  774. error: function(data){
  775. log.error(data);
  776. r(601);
  777. },
  778. anonymous:true
  779. });
  780. })
  781. });
  782. }
  783. const Follow = (r, data, del = false)=>{
  784. GetUserInfo((status, accessToken = null)=>{
  785. if(status != 200)
  786. {
  787. r(status)
  788. return;
  789. }
  790. let putUrl = "";
  791. switch(data.type)
  792. {
  793. case "artist":
  794. putUrl = "https://api.spotify.com/v1/me/following?type=artist&ids=" + data.id;
  795. break;
  796. case "playlist":
  797. putUrl = "https://api.spotify.com/v1/playlists/" + data.id + "/followers"
  798. break;
  799. case "user":
  800. putUrl = "https://api.spotify.com/v1/me/following?type=user&ids=" + data.id;
  801. break;
  802. default:
  803. log.error(data);
  804. r(601);
  805. return;
  806. break;
  807. }
  808. jq.ajax({
  809. type: !del?'PUT':"DELETE",
  810. url: putUrl,
  811. headers: {authorization: "Bearer " + accessToken},
  812. success: function(data){
  813. r(200);
  814. },
  815. error: function(data){
  816. log.error(data);
  817. r(604);
  818. },
  819. anonymous:true
  820. });
  821. });
  822. }
  823. return {
  824. GetAccessToken: GetAccessToken,
  825. SaveAuto: SaveAuto,
  826. Follow: Follow
  827. }
  828. })();
  829. const STEAM = (()=>{
  830. const InfoUpdate = async (type = 'all', forceUpdate = false)=> {
  831. if (type === 'community' || type === 'all') {
  832. await getComAuth(forceUpdate)
  833. }
  834. if (type === 'store' || type === 'all') {
  835. await getStoreAuth(forceUpdate)
  836. }
  837. }
  838. const getComAuth = (forceUpdate = false)=>{
  839. if (new Date().getTime() - steamConfig.comUpdateTime > 10 * 60 * 1000 || forceUpdate) {
  840. getAuthStatus.steamCom = 603;
  841. HTTP.GET('https://steamcommunity.com/my')
  842. .then(res=>{
  843. if (res.status === 200) {
  844. if (jq(res.responseText).find('a[href*="/login/home"]').length > 0) {
  845. getAuthStatus.steamCom = 401;
  846. return Promise.reject(401);
  847. } else {
  848. const steam64Id = res.responseText.match(/g_steamID = "(.+?)";/);
  849. const communitySessionID = res.responseText.match(/g_sessionID = "(.+?)";/);
  850. const userName = res.responseText.match(/steamcommunity.com\/id\/(.+?)\/friends\//);
  851. if (steam64Id) steamConfig.steam64Id = steam64Id[1];
  852. if (communitySessionID) steamConfig.communitySessionID = communitySessionID[1];
  853. if (userName) steamConfig.userName = userName[1];
  854. getAuthStatus.steamCom = 200;
  855. steamConfig.comUpdateTime = new Date().getTime();
  856. GM_setValue('steamInfo', steamConfig);
  857. return Promise.resolve(200);
  858. }
  859. } else {
  860. log.error(res);
  861. getAuthStatus.steamCom = 601;
  862. return Promise.reject(601);
  863. }
  864. })
  865. } else {
  866. return Promise.resolve(200);
  867. }
  868. }
  869. const getStoreAuth = (forceUpdate = false)=>{
  870. if (new Date().getTime() - steamConfig.storeUpdateTime > 10 * 60 * 1000 || forceUpdate) {
  871. getAuthStatus.steamStore = 603;
  872. return HTTP.GET('https://store.steampowered.com/stats/', null )
  873. .then(res=>{
  874. if (res.status === 200) {
  875. if (jq(res.responseText).find('a[href*="/login/"]').length > 0) {
  876. log.log(res)
  877. getAuthStatus.steamStore = 401;
  878. return Promise.reject(401)
  879. } else {
  880. const storeSessionID = res.responseText.match(/g_sessionID = "(.+?)";/)
  881. if (storeSessionID) steamConfig.storeSessionID = storeSessionID[1]
  882. getAuthStatus.steamStore = 200;
  883. steamConfig.storeUpdateTime = new Date().getTime();
  884. GM_setValue('steamInfo', steamConfig);
  885. return Promise.resolve(200);
  886. }
  887. } else {
  888. log.error(res);
  889. getAuthStatus.steamStore = 601;
  890. return Promise.reject(601);
  891. }
  892. })
  893. } else {
  894. return Promise.resolve(200)
  895. }
  896. }
  897.  
  898. const Rep = async (r, id)=>{
  899. try{
  900. const check = await RepHisCheck(id)
  901. HTTP.POST('https://steamcommunity.com/comment/Profile/post/' + id + '/-1/',jq.param({comment:'+rep',count:6,sessionid:steamConfig.communitySessionID,feature2:-1}), {
  902. headers:{'content-type': 'application/x-www-form-urlencoded'},
  903. }).then(res=>{
  904. if(res.status == 200)
  905. {
  906. let ret = JSON.parse(res.response)
  907. if(ret.success == true)r(200);
  908. else{
  909. log.error("发送评论失败", res);
  910. r(601);
  911. }
  912. }else{
  913. log.error("评论返回值异常", res);
  914. r(601);
  915. }
  916. }).catch(err=>{
  917. log.error("请求发送异常", err);
  918. r(601);
  919. })
  920. }catch(e){
  921. r(e);
  922. return;
  923. }
  924. }
  925. const RepHisCheck = async function (id){
  926. const auth = await InfoUpdate( "community")
  927. return HTTP.GET("https://steamcommunity.com/profiles/" + id)
  928. .then(res=>{
  929. if(res.status == 200)
  930. {
  931. let comments = res.responseText.match(/commentthread_comments([\s\S]*)commentthread_footer/);
  932. log.log(comments);
  933. if(comments != null)
  934. {
  935. if(comments[1].includes(steamConfig.steam64Id) || steamConfig.userName?comments[1].includes(steamConfig.userName):false)
  936. {
  937. return Promise.resolve(200, true);
  938. }
  939. else if(!res.responseText.includes("commentthread_textarea"))
  940. {
  941. return Promise.reject(605)
  942. }else{
  943. return Promise.resolve(200, false);
  944. }
  945. }
  946. else return Promise.reject(605);
  947. }else{
  948. log.error("检查评论记录返回异常", res);
  949. return Promise.reject(601);
  950. }
  951. })
  952.  
  953. }
  954. const isGroupExist = (url)=>{
  955. return HTTP.GET(url).then(res=>{
  956. const html = res.responseText;
  957. return Promise.resolve(html.indexOf('已被移除。') !== -1 || html.indexOf('无法检索到该指定 URL 的组') !== -1)
  958. })
  959. }
  960. const JoinGroupAuto = async function (r, url) {
  961. try{
  962. const auth = await InfoUpdate('community')
  963. HTTP.POST(url, jq.param({ action: 'join', sessionID: steamConfig.communitySessionID }), {
  964. headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
  965. }).then(res=>{
  966. if (res.status === 200 && !res.responseText.includes('grouppage_join_area')) {
  967. if(res.responseText.match(/<h3>(.+?)<\/h3>/) && res.responseText.match(/<h3>(.+?)<\/h3>/)[1] != "您已经是该组的成员了。")
  968. {
  969. log.error("STEAM.JoinGroupAuto1", res);
  970. r(404);
  971. }else r(200);
  972. } else {
  973. log.error("STEAM.JoinGroupAuto2", res);
  974. r(601);
  975. }
  976. })
  977. }catch(e){
  978. let exist = await isGroupExist(url)
  979. r(!exist?404:e);
  980. return;
  981. }
  982.  
  983. }
  984. const LeaveGroup = function (r, url) {
  985. let groupName = url.split('s/')[1];
  986. GetGroupID(groupName, (groupName, groupId) => {
  987. var postUrl = "";
  988. postUrl = (steamConfig.userName) ? 'https://steamcommunity.com/id/' + steamConfig.userName + '/home_process' : 'https://steamcommunity.com/profiles/' + steamConfig.steam64Id + '/home_process'
  989. HTTP.POST(postUrl, jq.param({ sessionID: steamConfig.communitySessionID, action: 'leaveGroup', groupId: groupId }), {
  990. headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
  991. }).then(res=>{
  992. if (res.status === 200 && res.finalUrl.includes('groups') && jq(res.responseText.toLowerCase()).find(`a[href='https://steamcommunity.com/groups/${groupName.toLowerCase()}']`).length === 0) {
  993. r(200);
  994. } else {
  995. log.error(res);
  996. r(601);
  997. }
  998. })
  999. })
  1000. }
  1001. const GetGroupID = async function (groupName, callback) {
  1002. try{
  1003. const auth = InfoUpdate()
  1004. HTTP.GET('https://steamcommunity.com/groups/' + groupName, null, {
  1005. headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
  1006. }).then(res=>{
  1007. log.log(res)
  1008. if (res.status === 200) {
  1009. const groupId = res.responseText.match(/OpenGroupChat\( '([0-9]+)'/)
  1010. if (groupId === null) {
  1011. log.error(res)
  1012. } else {
  1013. if (groupId[1] !== false && callback) callback(groupName, groupId[1]);
  1014. }
  1015. } else {
  1016. log.error(res)
  1017. }
  1018. })
  1019. }catch(e){
  1020. callback(e);
  1021. return;
  1022. }
  1023. }
  1024. const AddWishlistAuto = async function (r, gameId) {
  1025. try{
  1026. const auth = await InfoUpdate('store')
  1027.  
  1028. HTTP.POST('https://store.steampowered.com/api/addtowishlist', jq.param({ sessionid: steamConfig.storeSessionID, appid: gameId }), {
  1029. headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
  1030. responseType: 'json'
  1031. }).then(res=>{
  1032. log.log(res)
  1033. if (res.status === 200 && res.response && res.response.success === true) {
  1034. r(200)
  1035. } else {
  1036. HTTP.GET('https://store.steampowered.com/app/' + gameId)
  1037. .then(res=>{
  1038. log.log(res)
  1039. if (res.status === 200) {
  1040. if (res.responseText.includes('class="queue_actions_ctn"') && res.responseText.includes('已在库中')) {
  1041. r(200)
  1042. } 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"')) {
  1043. log.error(res);
  1044. r(601);
  1045. } else {
  1046. r(200);
  1047. }
  1048. } else {
  1049. log.error(res);
  1050. r(601);
  1051. }
  1052. })
  1053. return Promise.resolve({ result: 'error', statusText: res.statusText, status: res.status })
  1054. }
  1055. })
  1056. }catch(e){
  1057. log.error(e);
  1058. r(601);
  1059. }
  1060. }
  1061. const RemoveWishlistAuto = function (r, gameId) {
  1062. this.steamInfoUpdate(() => {
  1063. new Promise(resolve => {
  1064. HTTP.POST('https://store.steampowered.com/api/removefromwishlist', jq.param({ sessionid: steamConfig.storeSessionID, appid: gameId }), {
  1065. headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
  1066. responseType: 'json',
  1067. onabort: response => { resolve({ result: 'error', statusText: response.statusText, status: response.status }) },
  1068. ontimeout: response => { resolve({ result: 'error', statusText: response.statusText, status: response.status }) },
  1069. r: resolve
  1070. }).then(res=>{
  1071. if (res.status === 200 && res.response && res.response.success === true) {
  1072. resolve({ result: 'success', statusText: res.statusText, status: res.status })
  1073. } else {
  1074. resolve({ result: 'error', statusText: res.statusText, status: res.status })
  1075. }
  1076. })
  1077. }).then(result => {
  1078. if (result.result === 'success') {
  1079. r(200);
  1080. } else {
  1081. HTTP.GET('https://store.steampowered.com/app/' + gameId)
  1082. .then(res=>{
  1083. if (res.status === 200) {
  1084. if (res.responseText.includes('class="queue_actions_ctn"') && (res.responseText.includes('已在库中') || res.responseText.includes('添加至您的愿望单'))) {
  1085. r(200);
  1086. } else {
  1087. log.error(res);
  1088. r(601);
  1089. }
  1090. } else {
  1091. log.error(res);
  1092. r(601);
  1093. }
  1094. })
  1095. }
  1096. }).catch(err => {
  1097. log.error(err);
  1098. r(601);
  1099. })
  1100. })
  1101. }
  1102. return {
  1103. InfoUpdate: InfoUpdate,
  1104. Rep: Rep,
  1105. JoinGroup: JoinGroupAuto,
  1106. LeaveGroup: LeaveGroup,
  1107. AddWishlist: AddWishlistAuto
  1108. }
  1109. })();
  1110. const TWITCH = (()=>{
  1111. const FollowAuto = async function(r, channelId){
  1112. try{
  1113. const auth = await AuthUpdate()
  1114. HTTP.POST( 'https://gql.twitch.tv/gql','[{"operationName":"FollowButton_FollowUser","variables":{"input":{"disableNotifications":false,"targetID":"' + channelId + '"}},"extensions":{"persistedQuery":{"version":1,"sha256Hash":"3efee1acda90efdff9fef6e6b4a29213be3ee490781c5b54469717b6131ffdfe"}}}]', {
  1115. headers: { Authorization: "OAuth " + twitchConfig["auth-token"]},
  1116. }).then(res=>{
  1117. if (res.status === 200) {
  1118. r(200);
  1119. } else if(res.status === 401){
  1120. twitchConfig.updateTime = 0;
  1121. GM_setValue("twitchAuth", null);
  1122. r(401);
  1123. }else{
  1124. log.error(res);
  1125. r(601);
  1126. }
  1127. })
  1128. }catch(e){
  1129. r(status);
  1130. return;
  1131. }
  1132. }
  1133. const UnfollowAuto = function(r, channelId){
  1134. AuthUpdate((status)=>{
  1135. if(status != 200)
  1136. {
  1137. r(status);
  1138. return;
  1139. }
  1140. HTTP.POST('https://gql.twitch.tv/gql', '[{"operationName":"FollowButton_UnfollowUser","variables":{"input":{"targetID":"' + channelId + '"}},"extensions":{"persistedQuery":{"version":1,"sha256Hash":"d7fbdb4e9780dcdc0cc1618ec783309471cd05a59584fc3c56ea1c52bb632d41"}}}]', {
  1141. headers: { Authorization: "OAuth " + twitchConfig["auth-token"]},
  1142. }).then(res=>{
  1143. if (res.status === 200) {
  1144. r(200);
  1145. } else if(res.status === 401){
  1146. twitchConfig.updateTime = 0;
  1147. GM_setValue("twitchAuth", null);
  1148. r(401);
  1149. }else{
  1150. log.error(res);
  1151. r(601);
  1152. }
  1153. })
  1154. })
  1155. }
  1156. // 弃用
  1157. const twitchGetId = function(r, channels){
  1158. 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, {
  1159. anonymous:true
  1160. }) .then(res=>{
  1161. if (res.status === 200) {
  1162. let rep = JSON.parse(JSON.parse(res.responseText).token);
  1163. r(rep.channel_id);
  1164. } else {
  1165. log.error(res);
  1166. r('error')
  1167. }
  1168. })
  1169. }
  1170. const AuthUpdate = function(forceUpdate = false){
  1171. return new Promise((resolve, reject)=>{
  1172. if (new Date().getTime() - twitchConfig.updateTime < 30 * 60 * 1000 && twitchConfig.status === 200 && !forceUpdate) {
  1173. resolve(200)
  1174. return
  1175. }
  1176. if(false == getAuthStatus.twitch || true === forceUpdate)
  1177. {
  1178. getAuthStatus.twitch = true;
  1179. const tab = GM_openInTab("https://www.twitch.tv/settings/profile?keyjokertask=storageAuth", {active: false, insert: true, setParent: true});
  1180. tab.onclose = ()=>{
  1181. if(GM_getValue("twitchAuth") && new Date().getTime() - GM_getValue("twitchAuth").updateTime <= 10 * 1000)
  1182. {
  1183. if(GM_getValue("twitchAuth").status != 200)
  1184. {
  1185. reject(401);
  1186. return;
  1187. }
  1188. twitchConfig["auth-token"] = GM_getValue('twitchAuth')["auth-token"];
  1189. twitchConfig.updateTime = GM_getValue('twitchAuth').updateTime;
  1190. twitchConfig.status = GM_getValue('twitchAuth').status;
  1191. resolve(200)
  1192. }
  1193. }
  1194. }
  1195. })
  1196. }
  1197. return {
  1198. AuthUpdate: AuthUpdate,
  1199. FollowAuto: FollowAuto,
  1200. UnfollowAuto: UnfollowAuto
  1201. }
  1202. })();
  1203. const TWITTER = (()=>{
  1204. const FollowAuto = async function(r, data){
  1205. try{
  1206. const auth = await AuthUpdate()
  1207. const uinfo = await GetUserInfo(data.username)
  1208. let userId = uinfo[1]
  1209. HTTP.POST('https://api.twitter.com/1.1/friendships/create.json',
  1210. jq.param({
  1211. include_profile_interstitial_type: 1,
  1212. include_blocking: 1,
  1213. include_blocked_by: 1,
  1214. include_followed_by: 1,
  1215. include_want_retweets: 1,
  1216. include_mute_edge: 1,
  1217. include_can_dm: 1,
  1218. include_can_media_tag: 1,
  1219. skip_status: 1,
  1220. id: userId
  1221. }), {
  1222. headers: { authorization: "Bearer " + twitterConfig.authorization, 'Content-Type': 'application/x-www-form-urlencoded', 'x-csrf-token':twitterConfig.ct0},
  1223. }).then(res=>{
  1224. if (res.status === 200) {
  1225. r(200);
  1226. } else {
  1227. log.error(res);
  1228. twitterConfig.updateTime = 0;
  1229. GM_setValue("twitterAuth", twitterConfig);
  1230. r(601);
  1231. }
  1232. })
  1233. }catch(e){
  1234. log.error("TWITTER: 关注出错:", e)
  1235. r(e);
  1236. return;
  1237. }
  1238. }
  1239. const UnfollowAuto = function(r, data){
  1240. AuthUpdate((status)=>{
  1241. if(status != 200)
  1242. {
  1243. r(status);
  1244. return;
  1245. }
  1246. GetUserInfo((userId)=>{
  1247. log.log(userId)
  1248. if("error" == userId)
  1249. {
  1250. r(601);
  1251. return;
  1252. }
  1253. 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}), {
  1254. headers: { authorization: "Bearer " + twitterConfig.authorization, 'Content-Type': 'application/x-www-form-urlencoded', 'x-csrf-token':twitterConfig.ct0},
  1255. }).then(res=>{
  1256. if (res.status === 200) {
  1257. r(200);
  1258. } else {
  1259. log.error(res);
  1260. twitterConfig.updateTime = 0;
  1261. GM_setValue("twitterAuth", twitterConfig);
  1262. r(601);
  1263. }
  1264. })
  1265. }, data.username)
  1266. })
  1267. }
  1268. const RetweetAuto = async function(r, url){
  1269. let retweetId = url.split("status/")[1];
  1270. try{
  1271. const auth = await AuthUpdate()
  1272. HTTP.POST( 'https://api.twitter.com/1.1/statuses/retweet.json', jq.param({ tweet_mode: "extended",id: retweetId}), {
  1273. headers: { authorization: "Bearer " + twitterConfig.authorization, 'Content-Type': 'application/x-www-form-urlencoded', 'x-csrf-token':twitterConfig.ct0},
  1274. }).then(res=>{
  1275. if (res.status === 200 || (res.status === 403 && res.responseText == '{"errors":[{"code":327,"message":"You have already retweeted this Tweet."}]}')) {
  1276. r(200);
  1277. } else {
  1278. twitterConfig.updateTime = 0;
  1279. GM_setValue("twitterAuth", twitterConfig);
  1280. r(601);
  1281. }
  1282. })
  1283. }catch(e){
  1284. log.error("TWITTER: 转推出错:", e)
  1285. r(e);
  1286. return;
  1287. }
  1288. }
  1289. const GetUserInfo = function(userName){
  1290. if(debug)log.log("====twitterGetUserInfo====");
  1291. 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, {
  1292. headers: { authorization: "Bearer " + twitterConfig.authorization, "content-type": "application/json"},
  1293. anonymous:true
  1294. }).then(res=>{
  1295. if (res.status === 200) {
  1296. return Promise.resolve([200, JSON.parse(res.responseText).data.user.rest_id]);
  1297. } else {
  1298. log.error(res);
  1299. return Promise.reject(601);
  1300. }
  1301. })
  1302. }
  1303. const AuthUpdate = function(update = false){
  1304. return new Promise((resolve, reject)=>{
  1305. if(new Date().getTime() - twitterConfig.updateTime < 30 * 60 * 1000 && !update){
  1306. // 未过期,不强制更新
  1307. resolve(200)
  1308. return;
  1309. }
  1310.  
  1311. if(false == getAuthStatus.twitter || true === update)
  1312. {
  1313. getAuthStatus.twitter = true;
  1314. const tab = GM_openInTab("https://twitter.com/settings/account?keyjokertask=storageAuth", {active: false, insert: true, setParent: true});
  1315. tab.onclose = ()=>{
  1316. log.log("twitter tab closed")
  1317. const auth = GM_getValue("twitterAuth");
  1318. // 10s之内
  1319. if(GM_getValue("twitterAuth") && new Date().getTime() - auth.updateTime <= 10 * 1000)
  1320. {
  1321. if(auth.status != 200)
  1322. {
  1323. reject(auth.status)
  1324. return;
  1325. }
  1326. twitterConfig.ct0 = auth.ct0;
  1327. twitterConfig.updateTime = auth.updateTime
  1328. twitterConfig.status = auth.status;
  1329. resolve(twitterConfig.status)
  1330. }else{
  1331. reject(408)
  1332. }
  1333.  
  1334. }
  1335. }
  1336. })
  1337. }
  1338. // 推特取得用户页面响应头(OK)
  1339. /*twitterAP:function(r){
  1340. HTTP.GET( 'https://twitter.com/settings/account?k')
  1341. .then(res=>{
  1342. if (res.status === 200) {
  1343. log.log(res)
  1344. r(200, res.responseHeaders)
  1345. } else {
  1346. log.error(res);
  1347. r(601);
  1348. }
  1349. }).catch(err=>{
  1350. log.error(res);
  1351. r(601);
  1352. })
  1353. },*/
  1354.  
  1355. return {
  1356. FollowAuto: FollowAuto,
  1357. RetweetAuto: RetweetAuto,
  1358. AuthUpdate: AuthUpdate
  1359. }
  1360. })();
  1361. const func = {
  1362. // 部分站点凭证存储处理,人机验证处理
  1363. appHandle: function(){
  1364. switch(location.hostname)
  1365. {
  1366. case "www.twitch.tv":
  1367. if(location.search == "?keyjokertask=storageAuth")
  1368. {
  1369. let cookie = document.cookie + ";"
  1370. twitchConfig.updateTime = new Date().getTime();
  1371. if(cookie.match(/auth-token=(.+?);/) != null)
  1372. {
  1373. twitchConfig["auth-token"] = cookie.match(/auth-token=(.+?);/)[1]
  1374. twitchConfig.status = 200;
  1375. }else twitchConfig.status = 401;
  1376. GM_setValue("twitchAuth", twitchConfig)
  1377. log.log(twitchConfig)
  1378. window.close();
  1379. }
  1380. window.close();
  1381. break;
  1382. case "discord.com":
  1383. if(location.search.includes("?keyjokertask=joinDiscord"))
  1384. {
  1385. // 模拟点击
  1386. log.info("discord", "ready")
  1387. jq(document).ready(DISCORD.JoinServer2);
  1388. }else if(location.search == "?keyjokertask=storageAuth"){
  1389.  
  1390. // test
  1391. const temp = JSON.parse(localStorage.getItem("token") || "{}")
  1392. const language = navigator.language||navigator.userLanguage;
  1393. const browserInfo = func.getBrowserInfo()
  1394. const xSuperProperties = {
  1395. "os": temp.os || navigator.userAgentData.platform,
  1396. "browser": temp.browser || (browserInfo[0].slice(0,1).toUpperCase() + browserInfo[0].slice(1).toLowerCase()),
  1397. "device": temp.device || "",
  1398. "system_locale": temp.system_locale || language,
  1399. "browser_user_agent":navigator.userAgent,
  1400. "browser_version":browserInfo[1],
  1401. "os_version":"",
  1402. "referrer":"",
  1403. "referring_domain":"",
  1404. "referrer_current":"",
  1405. "referring_domain_current":"",
  1406. "release_channel":"stable",
  1407. "client_build_number":111330,
  1408. "client_event_source":null
  1409. }
  1410. log.log(xSuperProperties)
  1411.  
  1412. const xFingerprint = localStorage.getItem("fingerprint") || "";
  1413. // test
  1414. discordAuth.xSuperProperties = window.btoa(JSON.stringify(xSuperProperties))
  1415. discordAuth.xFingerprint = xFingerprint.replaceAll('"', '')
  1416.  
  1417. discordAuth.authorization = JSON.parse(localStorage.getItem("token"));
  1418. if(discordAuth.authorization == null)discordAuth.status = 401;
  1419. else discordAuth.status = 200;
  1420. discordAuth.updateTime = new Date().getTime();
  1421. GM_setValue("discordAuth", discordAuth);
  1422. log.log(discordAuth)
  1423.  
  1424. window.close();
  1425. }
  1426. break;
  1427. case "twitter.com":
  1428. // https://twitter.com/settings/account?keyjokertask=storageAuth
  1429. if(location.search === "?keyjokertask=storageAuth")
  1430. {
  1431. log.log('twitter auth store')
  1432. log.log(location.href)
  1433. // 注册地址变换事件
  1434. const _historyWrap = function(type) {
  1435. const orig = history[type];
  1436. const e = new Event(type);
  1437. return function() {
  1438. const rv = orig.apply(this, arguments);
  1439. e.arguments = arguments;
  1440. window.dispatchEvent(e);
  1441. return rv;
  1442. };
  1443. };
  1444. history.replaceState = _historyWrap('replaceState');
  1445. // 监听地址变换
  1446. window.addEventListener('replaceState', function(e) {
  1447. log.log(location.href)
  1448. console.log('change replaceState', e);
  1449. if(location.href.endsWith('login')){
  1450. // 切换到登陆页面
  1451. twitterConfig.status = 401;
  1452. GM_setValue("twitterAuth", twitterConfig)
  1453. unsafeWindow.close();
  1454. }
  1455. });
  1456.  
  1457. // 检测cookie有效性
  1458. let m = document.cookie.match(/ct0=(.+?);/);
  1459. HTTP.GET('https://twitter.com/i/api/1.1/users/email_phone_info.json', null, {
  1460. headers: {
  1461. authorization: `Bearer ${twitterConfig.authorization}`,
  1462. 'x-csrf-token': m[1]
  1463. },
  1464. responseType: 'json'
  1465. })
  1466. .then(res=>{
  1467. log.log(res)
  1468. twitterConfig.updateTime = new Date().getTime()
  1469. if(res.status === 200){
  1470. // 未登录时,页面地址会发生变更
  1471. if(m != null && m[1])
  1472. {
  1473. twitterConfig.status = 200;
  1474. twitterConfig.ct0 = m[1];
  1475. }else{
  1476. twitterConfig.status = 401;
  1477. }
  1478. }else{
  1479. twitterConfig.status = 401;
  1480. }
  1481. GM_setValue("twitterAuth", twitterConfig)
  1482. unsafeWindow.close();
  1483. }).catch(err=>{
  1484. log.err(err)
  1485. twitterConfig.status = 401;
  1486. GM_setValue("twitterAuth", twitterConfig)
  1487. unsafeWindow.close();
  1488. })
  1489. }
  1490. break;
  1491. case "www.keyjoker.com":
  1492. if(location.search == "?keyjokertask=unbindDiscord")
  1493. {
  1494. jq(document).ready(()=>{
  1495. log.log("keyjoker unbindDiscord")
  1496. const auto = jq("h4:contains('Discord')")[0].parentNode
  1497. //auto.nextElementSibling.firstChild.click()
  1498. const modal = auto.parentNode.parentNode.parentNode.nextElementSibling
  1499. if(modal.id.indexOf('delete-identity-')===0) modal.firstChild.firstChild.lastChild.firstChild.lastChild.click()
  1500. else location.href = "https://www.keyjoker.com/socialite/discord"
  1501. })
  1502. }
  1503. break;
  1504. case "assets.hcaptcha.com":
  1505. // 人机验证
  1506. func.hcaptcha2();
  1507. break;
  1508. default :
  1509. unsafeWindow.kj = {
  1510. get: GM_getValue,
  1511. set: GM_setValue
  1512. }
  1513. break;
  1514. }
  1515. },
  1516. authVerify:function(){
  1517. noticeFrame.clearNotice();
  1518. noticeFrame.addNotice("<span data-i18n=\"notification.checkAuth\">检查各项凭证</span>");
  1519. if(discordAuth.enable)noticeFrame.addNotice({type: "authVerify", name: "<a href=\"https://discord.com/login/\" target=\"_blank\">Discord</a> Auth", status:{id: "discord", class: "wait", text:"ready"}});
  1520. 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"}});
  1521. 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"}});
  1522. // noticeFrame.addNotice({type: "authVerify", name: "<a href=\"https://www.tumblr.com/login\" target=\"_blank\">Tumblr</a> Auth", status:{id: "tumblr", class: "wait", text:"ready"}});
  1523. 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"}});
  1524. noticeFrame.addNotice({type: "authVerify", name: "<a href=\"https://twitter.com/login/\" target=\"_blank\">Twitter</a> Auth", status:{id: "twitter", class: "wait", text:"ready"}});
  1525.  
  1526. if(discordAuth.enable){
  1527. DISCORD2.AuthUpdate(true).then(res=>{
  1528. this.statusReact(res, "discord");
  1529. }).catch(err=>{
  1530. this.statusReact(err, "discord");
  1531. });
  1532. }
  1533. SPOTIFY.GetAccessToken().then((res)=>{
  1534. log.info("spotify", res)
  1535. this.statusReact(200, "spotify");
  1536. }).catch(err=>{
  1537. this.statusReact(err, "spotify");
  1538. });
  1539. STEAM.InfoUpdate("all", true).then((res)=>{
  1540. log.info("STEAM", res)
  1541. this.statusReact(200, "steam");
  1542. }).catch(err=>{
  1543. this.statusReact(err, "steam");
  1544. });
  1545. // TUMBLR.AuthUpdate(true).then((status)=>{
  1546. // this.statusReact(200, "tumblr");
  1547. // }).catch(err=>{
  1548. // this.statusReact(err, "tumblr");
  1549. // });
  1550. TWITCH.AuthUpdate(true).then((status)=>{
  1551. this.statusReact(status, "twitch");
  1552. }).catch(err=>{
  1553. this.statusReact(err, "twitch");
  1554. });
  1555. TWITTER.AuthUpdate(true).then((status)=>{
  1556. this.statusReact(status, "twitter");
  1557. }).catch(err=>{
  1558. this.statusReact(err, "twitter");
  1559. });
  1560. },
  1561. statusReact: (code, id)=>{
  1562. const result = {
  1563. 0:{class:"error", text:"Time Out"},
  1564. 200: {
  1565. class:"success",
  1566. text: 'success'
  1567. },
  1568. 601: {
  1569. class:"error",
  1570. text: 'error'
  1571. },
  1572. 401: {
  1573. class:"error", text:"Not Login"
  1574. },
  1575. 408: {class:"error", text:"Time Out"},
  1576. 603: {class:"wait", text:"Getting Auth"},
  1577. 604: {class:"error", text:"Network Error"},
  1578. 605: {class:"error", text:"评论区未找到"},
  1579. }
  1580. log.info('statusReact', id, code)
  1581. if(result[code]) noticeFrame.updateNotice(id, result[code])
  1582. },
  1583. checkUpdate: function(){
  1584. noticeFrame.addNotice({type:"msg",msg:"<span data-i18n=\"notification.checkingUpdate\">正在检查版本信息...(当前版本:</span>" + GM_info.script.version + ")"})
  1585. HTTP.GET('https://task.jysafe.cn/keyjoker/script/update.php?type=ver', null, {headers:{action: "keyjoker"}})
  1586. .then(res=>{
  1587. const resp = JSON.parse(res.response)
  1588. if(resp.status != 200)
  1589. {
  1590. noticeFrame.addNotice({type:"msg", msg:"异常!<font class=\"error\">" + resp.msg + "</font>"})
  1591. return;
  1592. }
  1593. if(func.checkVersion(resp.ver))
  1594. {
  1595. noticeFrame.addNotice({type:"msg", msg:"<span data-i18n=\"notification.newVersionFound\">发现新版本!</span><font class=\"success\">" + resp.ver + "=>" + resp.msg + "</font>"})
  1596. }else{
  1597. noticeFrame.addNotice({type:"msg",msg:"<span data-i18n=\"notification.youAreInLatest\">当前已是最新版本!</span>"})
  1598. }
  1599. }).catch(err=>{
  1600. log.error(err);
  1601. noticeFrame.addNotice({type:"msg", msg:"<span data-i18n=\"notification.errGoConsole\" style=\"color:red\">请求异常!请至控制台查看详情!</span>"})
  1602. })
  1603. },
  1604. checkVersion: function(ver){
  1605. let new_ver = ver.split('.');
  1606. let old_ver = GM_info.script.version.split('.');
  1607. for(var i=0; i<new_ver.length && i<old_ver.length; i++){
  1608. let _new = parseInt(new_ver[i]);
  1609. let _old = parseInt(old_ver[i]);
  1610. if(_new >_old){
  1611. // 需更新
  1612. return 1;
  1613. }else if(_new == _old){
  1614. continue;
  1615. }else{
  1616. break;
  1617. }
  1618. }
  1619. return 0;
  1620. },
  1621. getTaskReplace: async function(task){
  1622. log.log('task', task)
  1623. const res = await HTTP.GET(`https://raw.fastgit.org/msojocs/keyjoker-script/master/task-replace/${task.task.provider.icon}.json`, null, {
  1624. responseType: 'json'
  1625. })
  1626. log.log('res', res)
  1627. const resp = res.response
  1628. log.log('resp', resp)
  1629. return resp[task.data.name]
  1630. },
  1631. // 做单个任务
  1632. do_task_single: function(task, retry=false){
  1633. retry || noticeFrame.addNotice({type: "taskStatus", task:task, status:'wait'});
  1634. retry && noticeFrame.updateNotice(task.id, {class:"wait", text:"重试中"})
  1635. this.runDirectUrl(task.redirect_url);
  1636. let react = (code)=>{
  1637. switch(code)
  1638. {
  1639. case 200:
  1640. noticeFrame.updateNotice(task.id, {class:"success",text:'success'})
  1641. this.redeemAuto(task.redirect_url);
  1642. break;
  1643. case 601:
  1644. noticeFrame.updateNotice(task.id, {class:"error",text:'error'})
  1645. break;
  1646. case 401:
  1647. noticeFrame.updateNotice(task.id, {class:"error", text:"Not Login"})
  1648. break;
  1649. case 404:
  1650. {
  1651. // 创建一个新的 div 元素
  1652. let ignoreBtn = document.createElement("button");
  1653. ignoreBtn.innerText = '忽略'
  1654. ignoreBtn.style.background = 'red'
  1655. ignoreBtn.style.color = 'white'
  1656. ignoreBtn.style['margin-left'] = '10px'
  1657. ignoreBtn.className = 'btn'
  1658. ignoreBtn.addEventListener('click', e=>{
  1659. log.info("点击忽略")
  1660. log.log(e)
  1661. log.log(task.id)
  1662. log.log(kjData.loadData)
  1663. kjData.loadData.actions = kjData.loadData.actions.filter(a=>a.id!==task.id)
  1664. if(!ignoreList.includes(task.id)){
  1665. ignoreList.push(task.id)
  1666. }
  1667. GM_setValue('ignoreList', ignoreList)
  1668. })
  1669. if(jq(`a[href='https://www.keyjoker.com/entries/open/24301']`)[0].parentNode.children.length === 2){
  1670. jq(`a[href='https://www.keyjoker.com/entries/open/${task.id}']`)[0].parentNode.append(ignoreBtn)
  1671. }
  1672. noticeFrame.updateNotice(task.id, {class:"error", text:"目标不存在"})
  1673.  
  1674. task.try = task?.try ?? 0
  1675. task.try++
  1676. if(task.try <= 1){
  1677. log.log('test')
  1678. this.getTaskReplace(task)
  1679. .then(url=>{
  1680. log.log('获取到的新任务链接:', url)
  1681. if(url){
  1682. task.data.url = url
  1683. setTimeout(()=>{
  1684. this.do_task_single(task, true)
  1685. }, 5000)
  1686. }
  1687. })
  1688. }
  1689. }
  1690. break;
  1691. case 408:
  1692. noticeFrame.updateNotice(task.id, {class:"error", text:"Time Out"})
  1693. break;
  1694. case 603:
  1695. noticeFrame.updateNotice(task.id, {class:"wait", text:"Getting Auth"})
  1696. break;
  1697. case 604:
  1698. noticeFrame.updateNotice(task.id, {class:"error", text:"Network Error"})
  1699. break;
  1700. case 605:
  1701. noticeFrame.updateNotice(task.id, {class:"error", text:"评论区未找到"})
  1702. break;
  1703. default:
  1704. noticeFrame.updateNotice(task.id, {class:"error", text:"Unknown Error"})
  1705. log.error("React Unknown Error--->", code)
  1706. break;
  1707. }
  1708. }
  1709.  
  1710. switch(task.task.name)
  1711. {
  1712. case "Join Steam Group":
  1713. STEAM.JoinGroup(react, task.data.url);
  1714. break;
  1715. case "Rep Steam Account":
  1716. STEAM.Rep(react, task.data.id);
  1717. break;
  1718. case "Wishlist Steam Game":
  1719. STEAM.AddWishlist(react, task.data.id);
  1720. break;
  1721. case "Follow Twitter Account":
  1722. TWITTER.FollowAuto(react, task.data);
  1723. break;
  1724. case "Join Discord Server":
  1725. if(!discordAuth.enable){
  1726. DISCORD.JoinServer(react, task.data)
  1727. }else{
  1728. let server = task.data.url.split(".gg/")[1];
  1729. DISCORD2.JoinServer(react, server)
  1730. }
  1731. break;
  1732. case "Retweet Twitter Tweet":
  1733. TWITTER.RetweetAuto(react, task.data.url);
  1734. break;
  1735. case "Save Spotify Album":
  1736. SPOTIFY.SaveAuto(react, task.data);
  1737. break;
  1738. case "Follow Spotify Account":
  1739. SPOTIFY.Follow(react, task.data);
  1740. break;
  1741. case "Follow Twitch Channel":
  1742. TWITCH.FollowAuto(react, task.data.id);
  1743. break;
  1744. //case "Follow Tumblr Blog":
  1745. //TUMBLR.Follow(react, task.data.name);
  1746. //break;
  1747. default:
  1748. noticeFrame.updateNotice(task.id, {class:"error", text:"Unknow Type:" + task.task.name})
  1749. log.error("未指定操作" + task.task.name)
  1750. break;
  1751. }
  1752. },
  1753. // 做任务
  1754. do_task: function(data){
  1755. log.info("遍历做任务")
  1756. for(const task of data.actions)
  1757. {
  1758. this.do_task_single(task)
  1759. }
  1760. log.info("遍历完毕")
  1761.  
  1762. let i = 0;
  1763.  
  1764. // 清除上次残留线程
  1765. if(null != completeCheck)clearInterval(completeCheck);
  1766. let discordCheck = true;
  1767. const completeCheckFunc = ()=>{
  1768. log.info("检测任务是否完成", "start")
  1769. i++;
  1770. //if(i >= 50)clearInterval(completeCheck);
  1771. //else
  1772. log.info("点击redeem按钮")
  1773. // 点击redeem按钮
  1774. jq('.card-body button[class="btn btn-primary"]').click();
  1775. // 除遮罩
  1776. jq(".modal-backdrop, .fade, .show").remove();
  1777. /*
  1778. if(1 == jq('#fraud-warning-modal[style!="display: none;"]').length){
  1779. log.info("有弹窗,模拟点击OK")
  1780. const ele = jq('button.btn.btn-secondary[type!="button"]')
  1781. if(ele.length > 0)ele[0].click();
  1782. }*/
  1783. if( document.getElementById("toast-container")){
  1784. if(document.getElementById("toast-container").textContent == "This action does not exist."){
  1785. log.info("任务操作不存在")
  1786. jq('.card').remove();
  1787. }
  1788. // check discord error [Could not refresh Discord information, please try again.]
  1789. if(discordCheck == true && (document.getElementById("toast-container").textContent == "Could not verify server information from Discord, please try again." || document.getElementById("toast-container").textContent == "Could not refresh Discord information, please try again."))
  1790. {
  1791. log.info("Discord 身份过期")
  1792. discordCheck = false;
  1793. GM_openInTab("https://www.keyjoker.com/account/identities?keyjokertask=unbindDiscord", false)
  1794. }
  1795. }
  1796. if(jq(".list-complete-item").length == 0)
  1797. {
  1798. clearInterval(completeCheck);
  1799. noticeFrame.addNotice({type:"msg", msg:"<span data-i18n='notification.taskOK'>任务似乎已完成,恢复监测!</span>"});
  1800. GM_setValue("start", 1);
  1801. checkSwitch();
  1802. checkTask.next();
  1803. }
  1804. log.info("检测任务是否完成", "end")
  1805. }
  1806. completeCheck = setInterval(completeCheckFunc, 5 * 1000)
  1807. },
  1808. // 人机验证出现图片时的处理
  1809. hC_get_c: function(r){
  1810. new Promise((resolve, reject)=>{
  1811. HTTP.GET( 'https://hcaptcha.com/checksiteconfig?host=dashboard.hcaptcha.com&sitekey=e4b28873-6852-49c0-9784-7231f004b96b&sc=1&swa=1', null, {
  1812. headers: { 'Content-Type': 'application/json; charset=utf-8'},
  1813. }).then(res=>{
  1814. let ret = JSON.parse(res.response);
  1815. if(ret.pass){
  1816. GM_log(ret);
  1817. resolve(ret.c);
  1818. }else{
  1819. reject();
  1820. }
  1821. })
  1822. }).then((c)=>{
  1823. r(c);
  1824. }).catch((err)=>{
  1825. log.error(err);
  1826. //let text = document.getElementsByClassName("prompt-text")[0].innerText;
  1827. //document.getElementsByClassName("prompt-text")[0].innerText = text + "\n免验证Cookie获取异常,请手动进行验证";
  1828. });
  1829.  
  1830. },
  1831. hC_getcaptcha: function(r){
  1832. this.hC_get_c((c)=>{
  1833. new Promise((resolve, reject)=>{
  1834. HTTP.POST('https://hcaptcha.com/getcaptcha', jq.param({
  1835. sitekey:'e4b28873-6852-49c0-9784-7231f004b96b',
  1836. host:'dashboard.hcaptcha.com',
  1837. n:'暂未实现获取方案',
  1838. c:JSON.stringify(c)
  1839. }), {
  1840. headers: { 'Content-Type': 'application/x-www-form-urlencoded'},
  1841. })
  1842. }).then((res)=>{
  1843. let rep = JSON.parse(res.response);
  1844. let c = rep.generated_pass_UUID
  1845. r(c);
  1846. }).catch((err)=>{
  1847. log.error(err);
  1848. r(false)
  1849. //let text = document.getElementsByClassName("prompt-text")[0].innerText;
  1850. //document.getElementsByClassName("prompt-text")[0].innerText = text + "\ngetcaptcha failed!";
  1851. });
  1852. });
  1853. },
  1854. hcaptcha2: function () {
  1855. return false;
  1856. let hcaptcha2Click=setInterval(()=>{
  1857. if(document.getElementsByClassName("challenge-container").length != 0 && document.getElementsByClassName("challenge-container")[0].children.length != 0)
  1858. {
  1859. clearInterval(hcaptcha2Click);
  1860. log.log("open hcaptcha");
  1861. let text = document.getElementsByClassName("prompt-text")[0].innerText;
  1862. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n正在自动获取免验证Cookie";
  1863. //$(".task-grid").remove();
  1864. //$(".challenge-example").remove();
  1865.  
  1866. HTTP.GET('https://accounts.hcaptcha.com/user', null, {
  1867. headers: { 'Content-Type': 'application/json'},
  1868. }).then((csrf)=>{
  1869. this.hC_getcaptcha((token)=>{
  1870. if(token == false)
  1871. {
  1872. document.getElementsByClassName("prompt-text")[0].innerText = text + "\ntoken 获取失败";
  1873. return ;
  1874. }
  1875. HTTP.POST('https://accounts.hcaptcha.com/accessibility/get_cookie', JSON.stringify({token:token}), {
  1876. headers: { 'Content-Type': 'application/json','x-csrf-token':csrf},
  1877. }).then(res=>{
  1878. let response = res
  1879. if(response.status == 200)
  1880. {
  1881. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n免验证Cookie获取成功,请重新点击验证框";
  1882. }else if(response.status == 401)
  1883. {
  1884. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n当前账号或IP的免验证Cookie获取次数已达上限,请更换hcaptcha账号或IP";
  1885. // setTimeout(()=>{window.open("https://dashboard.hcaptcha.com/welcome_accessibility")}, 3000);
  1886. }else if(response.status == 500)
  1887. {
  1888. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n未登录hCaptcha";
  1889. // setTimeout(()=>{window.open("https://dashboard.hcaptcha.com/welcome_accessibility")}, 3000);
  1890. }else{
  1891. log.error(response);
  1892. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n发生未知错误,已将数据记录至控制台";
  1893. }
  1894. }).catch(err=>{
  1895. log.error(err)
  1896. })
  1897. });
  1898. }).catch((err)=>{
  1899. if(err.status == 401)
  1900. {
  1901. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n未登录hCaptcha";
  1902. // setTimeout(()=>{window.open("https://dashboard.hcaptcha.com/welcome_accessibility")}, 3000);
  1903. }else{
  1904. document.getElementsByClassName("prompt-text")[0].innerText = text + "\n未知错误";
  1905. }
  1906. log.error(err);
  1907. });
  1908. }
  1909. },1000);
  1910. },
  1911. // OK
  1912. redeemAuto: function(redirect_url){
  1913. if(0 != jq('a[href="' + redirect_url + '"]').length)jq('a[href="' + redirect_url + '"]').next().click();
  1914. },
  1915. reset: function (){
  1916. if(!confirm("你确定要执行重置操作?"))return;
  1917. noticeFrame.addNotice({type:"msg",msg:"正在重置设置"})
  1918. const listValues = GM_listValues()
  1919. for (const value of listValues) {
  1920. if(value == "currentVer")continue;
  1921. noticeFrame.addNotice({type:"msg",msg:"<font class=\"error\">正在删除:" + value + "</font>"})
  1922. GM_deleteValue(value)
  1923. }
  1924. noticeFrame.addNotice({type:"msg",msg:"设置重置完毕"})
  1925. },
  1926. getBrowserInfo: function() {
  1927. let agent = navigator.userAgent.toLowerCase();
  1928. //console.log(agent);
  1929. // let system = agent.split(' ')[1].split(' ')[0].split('(')[1];
  1930. let REGSTR_EDGE = /edge\/[\d.]+/gi;
  1931. let REGSTR_IE = /trident\/[\d.]+/gi;
  1932. let OLD_IE = /msie\s[\d.]+/gi;
  1933. let REGSTR_FF = /firefox\/[\d.]+/gi;
  1934. let REGSTR_CHROME = /chrome\/[\d.]+/gi;
  1935. let REGSTR_SAF = /safari\/[\d.]+/gi;
  1936. let REGSTR_OPERA = /opr\/[\d.]+/gi;
  1937. let REGSTR_QQ = /qqbrowser\/[\d.]+/gi;
  1938. let REGSTR_METASR = /metasr\+/gi;
  1939. let REGSTR_WECHAT = /MicroMessenger\/[\d.]+/gi;
  1940.  
  1941. if (agent.indexOf('trident') > 0) {
  1942. // IE
  1943. return [agent.match(REGSTR_IE)[0].split('/')[0], agent.match(REGSTR_IE)[0].split('/')[1]];
  1944. } else if (agent.indexOf('msie') > 0) {
  1945. // OLD_IE
  1946. return [agent.match(OLD_IE)[0].split('/')[0], agent.match(OLD_IE)[0].split('/')[1]];
  1947. } else if (agent.indexOf('edge') > 0) {
  1948. // Edge
  1949. return [agent.match(REGSTR_EDGE)[0].split('/')[0], agent.match(REGSTR_EDGE)[0].split('/')[1]];
  1950. } else if (agent.indexOf('firefox') > 0) {
  1951. // firefox
  1952. return [agent.match(REGSTR_FF)[0].split('/')[0], agent.match(REGSTR_FF)[0].split('/')[1]];
  1953. } else if (agent.indexOf('opr') > 0) {
  1954. // Opera
  1955. return [agent.match(REGSTR_OPERA)[0].split('/')[0], agent.match(REGSTR_OPERA)[0].split('/')[1]];
  1956. } else if (agent.indexOf('safari') > 0 && agent.indexOf('chrome') < 0) {
  1957. // Safari
  1958. return [agent.match(REGSTR_SAF)[0].split('/')[0], agent.match(REGSTR_SAF)[0].split('/')[1]];
  1959. } else if (agent.indexOf('qqbrowse') > 0) {
  1960. // qqbrowse
  1961. return [agent.match(REGSTR_QQ)[0].split('/')[0], agent.match(REGSTR_QQ)[0].split('/')[1]];
  1962. } else if (agent.indexOf('metasr') > 0) {
  1963. // metasr火狐
  1964. return 'metasr';
  1965. } else if (agent.indexOf('micromessenger') > 0) {
  1966. // 微信内置浏览器
  1967. return [agent.match(REGSTR_WECHAT)[0].split('/')[0], agent.match(REGSTR_WECHAT)[0].split('/')[1]];
  1968. } else if (agent.indexOf('chrome') > 0) {
  1969. // Chrome
  1970. return [agent.match(REGSTR_CHROME)[0].split('/')[0], agent.match(REGSTR_CHROME)[0].split('/')[1]];
  1971. } else {
  1972. return ['chrome', '97.0.4692.71'];
  1973. }
  1974. },
  1975. runDirectUrl:function(direct_url){
  1976. GM_log("====访问跳转链接====")
  1977. HTTP.GET(direct_url, null, {
  1978. headers: {'x-csrf-token': jq('meta[name="csrf-token"]').attr('content')},
  1979. })
  1980. },
  1981. test: function(){
  1982. // GM_openInTab("https://discord.com/channels/@me?keyjokertask=storageAuth", true);
  1983. DISCORD2.JoinServer(log.info, 'h9frErUaV4')
  1984. //console.log(func.getBrowserVersion())
  1985. }
  1986. }
  1987. // ============Start===========
  1988. if(location.pathname == "/entries"){
  1989. window.onload=()=>{
  1990. log.info("KJ main")
  1991. GM_setValue("discord", {})
  1992. if(document.getElementsByClassName("nav-item active").length != 0 && document.getElementsByClassName("nav-item active")[0].innerText == "Earn Credits" && document.getElementById("logout-form")){
  1993. if(typeof kjData === "object")
  1994. {
  1995. log.log("加载app.js.....")
  1996. jq('.row')[1].remove();
  1997. jq('.layout-container').append('<entries-component></entries-component>');
  1998. kjData["app.js"]();
  1999. // fix dropdown
  2000. document.getElementById('user-dropdown').click()
  2001. }
  2002.  
  2003. try{
  2004. log.log("i18n初始化")
  2005. // i18n初始化 navigator.language
  2006. // use plugins and options as needed, for options, detail see
  2007. // https://www.i18next.com/overview/configuration-options
  2008. i18next.use(i18nextHttpBackend).init({
  2009. lng: KJConfig.language || navigator.language, // evtl. use language-detector https://github.com/i18next/i18next-browser-languageDetector
  2010. backend:{
  2011. loadPath : languagePrefix + '/{{lng}}/{{ns}}.json?v=' + GM_info.script.version,
  2012. },
  2013. ns: ['translation','message'],
  2014. defaultNS: 'translation', //默认使用的,不指定namespace时
  2015. }, function(err, t) {
  2016. log.log("i18n初始化END")
  2017. // for options see
  2018. // https://github.com/i18next/jquery-i18next#initialize-the-plugin
  2019. jqueryI18next.init(i18next, jq, {
  2020. tName: 't', // --> appends $.t = i18next.t
  2021. i18nName: 'i18n', // --> appends $.i18n = i18next
  2022. handleName: 'localize', // --> appends $(selector).localize(opts);
  2023. selectorAttr: 'data-i18n', // selector for translating elements
  2024. targetAttr: 'i18n-target', // data-() attribute to grab target element to translate (if diffrent then itself)
  2025. optionsAttr: 'i18n-options', // data-() attribute that contains options, will load/set if useOptionsAttr = true
  2026. useOptionsAttr: true, // see optionsAttr
  2027. parseDefaultValueFromContent: true // parses default values from content ele.val or ele.text
  2028. });
  2029.  
  2030. // 加载操作面板
  2031. noticeFrame.loadFrame();
  2032. // 事件绑定
  2033. eventBind();
  2034. // 更新检测
  2035. checkUpdate();
  2036.  
  2037. // start localizing, details:
  2038. // https://github.com/i18next/jquery-i18next#usage-of-selector-function
  2039. jq('.notification').localize();
  2040. jq(".border-bottom").localize()
  2041. if(GM_getValue("start")==1){
  2042. setTimeout(()=>{checkTask.next()}, 1000);
  2043. }
  2044. //jq('.nav').localize();
  2045. //jq('.content').localize();
  2046.  
  2047. });
  2048. }catch(e){
  2049. log.error(e)
  2050. }
  2051.  
  2052. }else{
  2053. if(jq('.container').length > 0)
  2054. {
  2055. let i = 0;
  2056. let check = setInterval(()=>{
  2057. i++;
  2058. if(jq('.container')[0].innerText == "Whoops, looks like something went wrong.")location.href = location.pathname
  2059. if(i >= 10)clearInterval(check)
  2060. }, 1000);
  2061. }
  2062. }
  2063. }
  2064. }else{
  2065. func.appHandle();
  2066. }
  2067. function checkUpdate(){
  2068. // 隔一段时间检测新版本
  2069. if(new Date().getTime() - (GM_getValue("lastCheckUpdate") || 0) > 6 * 60 * 60 * 1000)
  2070. {
  2071. func.checkUpdate();
  2072. GM_setValue("lastCheckUpdate", new Date().getTime())
  2073. }
  2074. }
  2075. // 时间绑定
  2076. function eventBind(){
  2077. jq('button#checkUpdate').click(func.checkUpdate)
  2078. // 开始做任务按钮
  2079. jq('button#fuck').click(function(){
  2080. if(null != completeCheck){
  2081. clearInterval(completeCheck);
  2082. completeCheck=null;
  2083. }
  2084. checkTask.start(()=>{jq('.card').remove();})
  2085. })
  2086. // 停止做任务按钮
  2087. jq('button#stop-fuck').click(function(){
  2088. GM_setValue('start', 0)
  2089. if(null != completeCheck){
  2090. clearInterval(completeCheck);
  2091. completeCheck=null;
  2092. }
  2093. // 按钮切换
  2094. jq('#fuck').parent().removeClass('hidden')
  2095. jq('#pause-fuck').parent().addClass('hidden')
  2096. jq('#stop-fuck').parent().addClass('hidden')
  2097. jq(".border-bottom").html(`<span data-i18n='message.taskStopped'>手动停止</span>`);
  2098. jq('#fuck').parent().removeClass('hidden')
  2099. jq('#stop-fuck').parent().addClass('hidden')
  2100. })
  2101. jq('button#clearNotice').click(function(){
  2102. noticeFrame.clearNotice()
  2103. })
  2104. jq('button#changeLog').click(function(){
  2105. noticeFrame.addNotice({type:"msg", msg:"<span data-i18n=\"notification.getChangeLog\">获取日志中...</span>"})
  2106. HTTP.GET( 'https://task.jysafe.cn/keyjoker/script/update.php?type=changelog&ver=' + GM_info.script.version, null, {
  2107. headers:{action: "keyjoker"},
  2108. }).then(res=>{
  2109. let ret = JSON.parse(res.response)
  2110. if(ret.status != 200)
  2111. {
  2112. noticeFrame.addNotice({type:"msg", msg:"异常!<font class=\"error\">" + ret.msg + "</font>"})
  2113. }else
  2114. {
  2115. noticeFrame.addNotice({type:"msg", msg:"<font class=\"success\">" + ret.msg + "</font>"})
  2116. }
  2117. }).catch(err=>{
  2118. log.error(err);
  2119. noticeFrame.addNotice({type:"msg", msg:"<font class=\"error\">请求异常!请至控制台查看详情!</font>"})
  2120. })
  2121. })
  2122. jq('button#setting').click(function(){
  2123. // https://msojocs.github.io/keyjoker-script/
  2124. const settingPage = GM_openInTab('https://msojocs.github.io/keyjoker-script/', {active: true, insert: true, setParent: true})
  2125. settingPage.onclose = ()=>{
  2126. // 关闭设置页面后更新配置
  2127. KJConfig.language = GM_getValue('KJConfig').language
  2128. i18next.changeLanguage(KJConfig.language, (err, t) => {
  2129. if (err) console.log('something went wrong loading', err);
  2130. jq('.notification').localize()
  2131. jq('.border-bottom').localize()
  2132. jq('#custom-modal').localize()
  2133. });
  2134. }
  2135. })
  2136. jq('button#report').click(function(){
  2137. noticeFrame.addNotice({type:"msg",msg:"<span data-i18n='notification.reportChannel'>目前提供以下反馈渠道:</span>"})
  2138. noticeFrame.addNotice({type:"msg",msg:"<a href=\"https://greasyfork.org/zh-CN/scripts/406476-keyjoker-auto-task/feedback\" target=\"_blank\">Greasy Fork</a>"})
  2139. noticeFrame.addNotice({type:"msg",msg:"<a href=\"https://github.com/msojocs/keyjoker-script/issues/new/choose\" target=\"_blank\">GitHub</a>"})
  2140. noticeFrame.addNotice({type:"msg",msg:"<a href=\"https://www.jysafe.cn/4332.air\" target=\"_blank\">博客页面</a>"})
  2141. })
  2142. // 版本升级后显示一次更新日志
  2143. if(GM_getValue("currentVer") != GM_info.script.version)
  2144. {
  2145. HTTP.GET('https://task.jysafe.cn/keyjoker/script/update.php?type=changelog&ver=' + GM_info.script.version, null, {
  2146. headers:{action: "keyjoker"},
  2147. anonymous:true
  2148. }).then(res=>{
  2149. let ret = JSON.parse(res.response)
  2150. if(ret.status != 200){
  2151. noticeFrame.addNotice({type:"msg", msg:"异常!<font class=\"error\">" + ret.msg + "</font>"})
  2152. }else{
  2153. noticeFrame.addNotice({type:"msg", msg:"<font class=\"success\">" + ret.msg + "</font>"})
  2154. }
  2155. }).catch(err=>{
  2156. log.error(err);
  2157. noticeFrame.addNotice({type:"msg", msg:"更新日志获取异常!请至控制台查看详情!"})
  2158. })
  2159. GM_setValue("currentVer", GM_info.script.version)
  2160. }
  2161. }
  2162. GM_registerMenuCommand("设置时间间隔", checkTask.setTime);
  2163. GM_registerMenuCommand("重置设置", func.reset);
  2164. GM_registerMenuCommand("凭证检测",()=>{
  2165. func.authVerify();
  2166. });
  2167. function checkSwitch(){
  2168. GM_unregisterMenuCommand(checkSwitchId);
  2169. if(1 == GM_getValue("start")){
  2170. checkSwitchId = GM_registerMenuCommand("停止检测",()=>{
  2171. let date=new Date();
  2172. let hour=date.getHours();
  2173. let min=date.getMinutes()<10?("0"+date.getMinutes()):date.getMinutes();
  2174. GM_setValue("start",0);
  2175. jq(".border-bottom").text("手动停止");
  2176. checkSwitch();
  2177. });
  2178. }else{
  2179. checkSwitchId = GM_registerMenuCommand("开始检测",()=>{
  2180. checkTask.start();
  2181. checkSwitch();
  2182. });
  2183. }
  2184. }
  2185. // 检测开关
  2186. checkSwitch(null);
  2187. if(debug)
  2188. {
  2189. GM_registerMenuCommand("Test",()=>{
  2190. func.test();
  2191. });
  2192. }
  2193. } catch (e) {
  2194. setTimeout(() => {
  2195. noticeFrame.addNotice({ type: 'msg', msg:"<font class\"error\">任务脚本执行期间发生预期之外的错误,详情见控制台</font>" })
  2196. }, 500)
  2197. console.log('发生异常:%c%s', 'color:white;background:red', e.stack)
  2198. }
  2199. })();