keyjoker自动任务

keyjoker自动任务,修改自https://gf.qytechs.cn/zh-CN/scripts/383411

目前为 2020-07-15 提交的版本。查看 最新版本

// ==UserScript==
// @name         keyjoker自动任务
// @namespace    https://gf.qytechs.cn/zh-CN/scripts/406476
// @version      0.6.5
// @description  keyjoker自动任务,修改自https://gf.qytechs.cn/zh-CN/scripts/383411
// @author       祭夜
// @include      *://www.keyjoker.com/entries*
// @include      *://discord.com/channels/@me?keyjokertask=storageAuth
// @include      *://www.twitch.tv/settings/profile?keyjokertask=storageAuth
// @supportURL   https://www.jysafe.cn/
// @homepage     https://www.jysafe.cn/
// @run-at       document-start
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_openInTab
// @grant        GM_log
// @connect      accounts.hcaptcha.com
// @connect      store.steampowered.com
// @connect      steamcommunity.com
// @connect      twitter.com
// @connect      facebook.com
// @connect      discord.com
// @connect      twitch.tv
// @connect      tumblr.com
// @connect      spotify.com
// @require      https://gf.qytechs.cn/scripts/379868-jquery-not/code/jQuery%20not%20$.js?version=700787
// @require      https://cdn.staticfile.org/jquery/3.3.1/jquery.min.js
// ==/UserScript==

(function() {
    'use strict';
    const debug = 0;
    // steam信息
    const steamInfo = GM_getValue('steamInfo') || {
        userName: '',
        steam64Id: '',
        communitySessionID: '',
        storeSessionID: '',
        updateTime: 0
    }
    const twitterAuth = GM_getValue('twitterAuth') || {
        authorization: "AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA",
        ct0: '',
        updateTime: 0
    }
    const discordAuth = GM_getValue('discordAuth') || {
        authorization: "",
        status:0,
        updateTime: 0
    }
    const twitchAuth = GM_getValue('twitchAuth') || {
        "auth-token": "",
        status:0,
        updateTime: 0
    }
    const noticeFrame = {
        loadFrame: ()=>{
            if(debug)console.log("loadFrame");
            $('body').append(`<style>
.fuck-task-logs li{display:list-item !important;float:none !important}
#extraBtn .el-badge.item{margin-bottom:4px !important}
#extraBtn .el-badge.item sup{padding-right:0 !important}
.fuck-task-logs{width:auto;max-width:50%;max-height:50%;z-index:99999999999 !important}
.fuck-task-logs .el-notification__group{width:100%}
.fuck-task-logs .el-notification__title{text-align:center}
.fuck-task-logs .el-notification__content{overflow:auto;max-height:230px}
font.success{color:green}
font.error{color:red;}
font.warning{color:#00f;}
font.wait{color:#9c27b0;}
[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}
.el-icon-brush:before{content:"\\e76e"}
.el-icon-document:before{content:"\\e785"}
.el-icon-refresh:before{content:"\\e6d0"}
.el-icon-s-promotion:before{content:"\\e7ba"}
.el-icon-setting:before{content:"\\e6ca"}
.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}
.el-notification__group{margin-left:13px;margin-right:8px}
.el-notification__title{font-weight:700;font-size:16px;color:#303133;margin:0}
.el-notification__content{font-size:14px;line-height:21px;margin:6px 0 0;color:#606266;text-align:justify}
.el-notification__content p{margin:0}
.el-badge{position:relative;vertical-align:middle;display:inline-block}
.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}
.el-badge__content.is-fixed{position:absolute;top:10px;right:10px;-webkit-transform:translateY(-50%) translateX(100%);transform:translateY(-50%) translateX(100%)}
.el-badge__content.is-fixed.is-dot{right:8px}
.el-badge__content.is-dot{height:8px;width:8px;padding:0;right:0;border-radius:50%}
.el-button{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}
.el-button{-webkit-box-sizing:border-box}
.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}
.el-button:focus,.el-button:hover{color:#409eff;border-color:#c6e2ff;background-color:#ecf5ff}
.el-button:active{color:#3a8ee6;border-color:#3a8ee6;outline:0}
.el-button::-moz-focus-inner{border:0}
.el-button.is-circle{border-radius:50%;padding:15px}
#extraBtn .el-button.is-circle{padding:8px !important}
@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}
</style>
<div role="alert" class="el-notification fuck-task-logs right" style="bottom: 16px; z-index: 2000;">
<div class="el-notification__group">
<h2 id="extraBtn" class="el-notification__title">
<div class="el-badge item"><button type="button" class="el-button el-button--default is-circle"
title="检查更新">
<i class="el-icon-refresh"></i>
</button><sup class="el-badge__content el-badge__content--undefined is-fixed is-dot"
style="display: none;"></sup></div>
<div class="el-badge item"><button type="button" class="el-button el-button--default is-circle" title="设置">
<i class="el-icon-setting"></i>
</button><sup class="el-badge__content el-badge__content--undefined is-fixed is-dot"></sup>
</div>
<div class="el-badge item"><button type="button" class="el-button el-button--default is-circle"
title="查看更新内容">
<i class="el-icon-document"></i>
</button><sup class="el-badge__content el-badge__content--undefined is-fixed is-dot"
style="display: none;"></sup></div>
<div class="el-badge item"><button type="button" class="el-button el-button--default is-circle"
title="清理缓存">
<i class="el-icon-brush"></i>
</button><sup class="el-badge__content el-badge__content--undefined is-fixed is-dot"
style="display: none;"></sup></div>
<div class="el-badge item"><button type="button" class="el-button el-button--default is-circle"
title="提交建议/BUG">
<i class="el-icon-s-promotion"></i>
</button><sup class="el-badge__content el-badge__content--undefined is-fixed is-dot"
style="display: none;"></sup></div>
</h2>
<h2 class="el-notification__title">任务执行日志</h2>
<div class="el-notification__content" style="">
<p></p>
<!--<li>正在更新Steam社区SessionID(用于加组退组)...<font class="success">Success</font>-->
</li>
</div>
</div>
</div>`)
        },
        addNotice: function(data){
            switch(data.type)
            {
                case "taskStatus":
                    $('.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>');
                    break;
                case "msg":
                    $('.el-notification__content').append("<li>" + data.msg + "</li>");
                    break;
                default:
                    break;
            }
        },
        clearNotice:()=>{
            $('.el-notification__content li').remove();
        },
        updateNotice: function(id, status){
            $('font#' + id).removeClass()
            $('font#' + id).addClass(status)
            $('font#' + id).text(status)
        },
        updateNotice1: function(data){
            $('font#' + data.id).removeClass()
            $('font#' + data.id).addClass(data.class)
            $('font#' + data.id).text(data.text || data.class)
        }
    }
    try{
        function reLoad(time,sum){
            let date=new Date();
            let hour=date.getHours();
            let min=date.getMinutes()<10?("0"+date.getMinutes()):date.getMinutes();
            if(GM_getValue("start")==1){
                $(".border-bottom").text(hour+":"+min+" 执行新任务检测");
                $.ajax({
                    url:"/entries/load",
                    type:"get",
                    headers:{'x-csrf-token': $('meta[name="csrf-token"]').attr('content')},
                    success:(data,status,xhr)=>{
                        if(data && (data.actions && (data.actions.length > sum) )){
                            if(debug)console.log(data);
                            let date=new Date();
                            let hour=date.getHours();
                            let min=date.getMinutes()<10?("0"+date.getMinutes()):date.getMinutes();
                            $(".border-bottom").text(hour+":"+min+" 检测到新任务");
                            $show({
                                title:"keyjoker新任务",
                                msg:"keyjoker网站更新"+(data.actions.length-sum)+"个新任务!",
                                icon:"https://www.keyjoker.com/favicon-32x32.png",
                                time:0,
                                onclick:function(){
                                    //location.reload(true);
                                }
                            });
                            // 重载列表
                            noticeFrame.clearNotice();
                            func.reLoadTaskList(()=>{
                                func.do_task(data);
                            });
                        }else{
                            setTimeout(()=>{
                                reLoad(time,sum);
                            },time);
                        }
                    },
                    error:(err)=>{
                        window.location.reload(true);
                    }
                });
            }
        }
        function setTime(){
            let time=prompt('请输入获取任务信息的时间间隔(单位:秒):');
            if(!isNaN(time)){
                GM_setValue("time",parseInt(time));
            }
        }
        function start(){
            GM_setValue("start",1);
            let time = GM_getValue("time");
            if(!time){
                time=60;
            }
            if(confirm("是否以时间间隔" + time + "秒进行任务检测?")){
                next();
            }
        }
        function next(){
            let time = GM_getValue("time");
            if(!time){
                time=60;
            }
            let sum=$(".list-complete-item").length;
            if(sum>0){
                reLoad(time*1000,sum);
            }else{
                reLoad(time*1000,0);
            }
        }
        const func = {
            // twitch & discord 凭证存储,人机验证处理
            appHandle: function(){
                switch(location.hostname)
                {
                    case "www.twitch.tv":
                        if(location.search == "?keyjokertask=storageAuth")
                        {
                            let cookie = document.cookie + ";"
                            twitchAuth.updateTime = new Date().getTime();
                            if(cookie.match(/auth-token=(.+?);/) != null)
                            {
                                twitchAuth["auth-token"] = cookie.match(/auth-token=(.+?);/)[1]
                                twitchAuth.status = 1;
                            }else twitchAuth.status = 0;
                            GM_setValue("twitchAuth", twitchAuth)
                            window.close();
                        }
                        break;
                    case "discord.com":
                        if(location.search == "?keyjokertask=storageAuth")
                        {
                            discordAuth.authorization = JSON.parse(localStorage.getItem("token"));
                            if(discordAuth.authorization != null)discordAuth.status = 0;
                            else discordAuth.status = 1;
                            discordAuth.updateTime = new Date().getTime();
                            GM_setValue("discordAuth", discordAuth);
                            window.close();
                        }
                        break;
                    case "assets.hcaptcha.com":
                        // 人机验证
                        func.hcaptcha2();
                        break;
                    default :
                        break;
                }
            },
            // discord自动加入服务器(OK)
            discordJoinServerAuto: function(r, server){
                this.discordAuthUpdate((ret)=>{
                    if(ret != 200)
                    {
                        r(ret);
                        return;
                    }
                    this.httpRequest({
                        url: 'https://discord.com/api/v6/invites/' + server,
                        method: 'POST',
                        headers: { authorization: discordAuth.authorization, "content-type": "application/json"},
                        onload: (response) => {
                            if(debug)console.log(response)
                            if (response.status === 200) {
                                if(debug)console.log({ result: 'success', statusText: response.statusText, status: response.status })
                                r(200);
                            } else {
                                console.log('Error:' + response.statusText + '(' + response.status + ')')
                                console.log({ result: 'error', statusText: response.statusText, status: response.status })
                                r(201);
                            }
                        },
                        error:(res)=>{
                            console.error(res);
                            r(201);
                        },
                        anonymous:true
                    })
                })
            },
            // discord凭证更新(OK)
            discordAuthUpdate:function(r, update = false){
                if (new Date().getTime() - discordAuth.updateTime > 30 * 60 * 1000 || discordAuth.status == 0 || update) {
                    r(203)
                    new Promise((resolve)=>{
                        GM_openInTab("https://discord.com/channels/@me?keyjokertask=storageAuth", true);
                        let i = 0;
                        let check = setInterval(()=>{
                            i++;
                            if(GM_getValue("discordAuth") && new Date().getTime() - GM_getValue("discordAuth").updateTime <= 2 * 1000)
                            {
                                if(GM_getValue("discordAuth").status == 0)
                                {
                                    clearInterval(check);
                                    resolve(202)
                                    return;
                                }
                                noticeFrame.addNotice({type:"msg", msg:"<font class=\"success\">discordAuth updated!</font>"})
                                discordAuth.authorization = GM_getValue("discordAuth").authorization
                                discordAuth.updateTime = GM_getValue("discordAuth").updateTime
                                discordAuth.status = GM_getValue("discordAuth").status;
                                clearInterval(check);
                                resolve("success")
                            }
                            if(i >= 10)
                            {
                                noticeFrame.addNotice({type:"msg", msg:"<font class=\"error\">discordAuth获取超时</font>"})
                                clearInterval(check);
                                resolve("error")
                            }
                        }, 1000)
                    }).then((ret)=>{
                        if(ret == "success"){
                            r(200)
                        }else r(ret)
                    })
                }else{
                    r(200)
                }
            },
            // 做任务
            do_task: function(data){
                for(const task of data.actions)
                {
                    noticeFrame.addNotice({type: "taskStatus", task:task, status:'start'});
                    let react = (code)=>{
                        switch(code)
                        {
                            case 200:
                                this.redeemAuto(task.redirect_url);
                                noticeFrame.updateNotice(task.id, 'success')
                                break;
                            case 201:
                                noticeFrame.updateNotice(task.id, 'error')
                                break;
                            case 202:
                                noticeFrame.updateNotice1({id:task.id, class:"error", text:"Not Login"})
                                break;
                            case 203:
                                noticeFrame.updateNotice1({id:task.id, class:"wait", text:"Getting Auth"})
                                break;
                            case 204:
                                noticeFrame.updateNotice1({id:task.id, class:"error", text:"Network Error"})
                                break;
                            default:
                                noticeFrame.updateNotice(task.id, 'Unknown Error')
                                break;
                        }
                    }
                    switch(task.task.name)
                    {
                        case "Join Steam Group":
                            this.steamJoinGroupAuto(react, task.data.name);
                            break;
                        case "Rep Steam Account":
                            this.steamRepAuto(react, task.data.id);
                            break;
                        case "Wishlist Steam Game":
                            this.steamAddWishlistAuto(react, task.data.id);
                            break;
                        case "Follow Twitter Account":
                            this.twitterFollowAuto(react, task.data);
                            break;
                        case "Join Discord Server":
                            var server = task.data.url.split(".gg/")[1];
                            this.discordJoinServerAuto(react, server)
                            break;
                        case "Retweet Twitter Tweet":
                            this.twitterRetweetAuto(react, task.data.url);
                            break;
                        case "Save Spotify Album":
                            this.spotifySaveAuto(react, task.data);
                            break;
                        case "Follow Spotify Account":
                            this.spotifyFollowAuto(react, task.data);
                            break;
                        case "Follow Twitch Channel":
                            this.twitchFollowAuto(react, task.data.id);
                            break;
                        case "Follow Tumblr Blog":
                            this.tumblrFollowAuto(react, task.data.name);
                            break;
                        default:
                            noticeFrame.updateNotice1({id:task.id, class:"error", text:"Unknow Type:" + task.task.name})
                            console.error("未指定操作" + task.task.name)
                            break;
                    }
                }
                let completeCheck = setInterval(()=>{
                    if($(".list-complete-item").length == 0)
                    {
                        noticeFrame.addNotice({type:"msg", msg:"任务似乎已完成,恢复监测!"});
                        next();
                        clearInterval(completeCheck);
                    }
                }, 10 * 1000)
            },
            // [修改自https://gf.qytechs.cn/zh-CN/scripts/370650]
            httpRequest: function (e) {
                const requestObj = {}
                requestObj.url = e.url
                requestObj.method = e.method.toUpperCase()
                requestObj.timeout = e.timeout || 30000
                if (e.dataType) requestObj.responseType = e.dataType
                if (e.headers) requestObj.headers = e.headers
                if (e.data) requestObj.data = e.data
                if (e.cookie) requestObj.cookie = e.cookie
                if (e.anonymous) requestObj.anonymous = e.anonymous
                if (e.onload) requestObj.onload = e.onload
                if (e.fetch) requestObj.fetch = e.fetch
                if (e.onreadystatechange) requestObj.onreadystatechange = e.onreadystatechange
                requestObj.ontimeout = e.ontimeout || function (data) {
                    if (debug) console.log(data)
                    if (e.status) e.status.error('Error:Timeout(0)')
                    if (e.r) e.r({ result: 'error', statusText: 'Timeout', status: 0, option: e })
                }
                requestObj.onabort = e.onabort || function (data) {
                    if (debug) console.log(data)
                    if (e.status) e.status.error('Error:Aborted(0)')
                    if (e.r) e.r({ result: 'error', statusText: 'Aborted', status: 0, option: e })
                }
                requestObj.onerror = e.onerror || function (data) {
                    if (debug) console.log(data)
                    if (e.status) e.status.error('Error:Error(0)')
                    if (e.r) e.r({ result: 'error', statusText: 'Error', status: 0, option: e })
                }
                if (debug) console.log('发送请求:', requestObj)
                GM_xmlhttpRequest(requestObj)
            },
            // 人机验证出现图片时的处理
            hcaptcha2: function () {
                let hcaptcha2Click=setInterval(()=>{
                    if(document.getElementsByClassName("challenge-container").length != 0 && document.getElementsByClassName("challenge-container")[0].children.length != 0)
                    {
                        clearInterval(hcaptcha2Click);
                        console.log("open hcaptcha");
                        let text = document.getElementsByClassName("prompt-text")[0].innerText;
                        document.getElementsByClassName("prompt-text")[0].innerText = text + "\n正在自动获取免验证Cookie";
                        //$(".task-grid").remove();
                        //$(".challenge-example").remove();
                        this.httpRequest({
                            url: 'https://accounts.hcaptcha.com/accessibility/get_cookie',
                            method: 'GET',
                            headers: { 'Content-Type': 'application/json'},
                            onload: (response) => {
                                GM_log(response)
                                if(response.status == 200)
                                {
                                    document.getElementsByClassName("prompt-text")[0].innerText = text + "\n免验证Cookie获取成功,请重新点击验证框";
                                }else if(response.status == 401)
                                {
                                    document.getElementsByClassName("prompt-text")[0].innerText = text + "\n当前账号或IP的免验证Cookie获取次数已达上限,请更换hcaptcha账号或IP";
                                    // setTimeout(()=>{window.open("https://dashboard.hcaptcha.com/welcome_accessibility")}, 3000);
                                }else if(response.status == 500)
                                {
                                    document.getElementsByClassName("prompt-text")[0].innerText = text + "\n未登录(不可用)hCaptcha,将在3秒后打开至登录(不可用)页面";
                                    setTimeout(()=>{window.open("https://dashboard.hcaptcha.com/welcome_accessibility")}, 3000);
                                }else{
                                    console.error(response);
                                    document.getElementsByClassName("prompt-text")[0].innerText = text + "\n发生未知错误,已将数据记录至控制台";
                                }
                            },
                            error: () =>{
                                if(debug)console.error("error")
                            }
                        })
                    }
                },1000);
            },
            // OK
            redeemAuto: function(redirect_url){
                if(0 != $('a[href="' + redirect_url + '"]').length)$('a[href="' + redirect_url + '"]').next().click();
            },
            reLoadTaskList: function(r){
                // 重载任务列表
                if(2 == document.getElementsByClassName('row').length)
                {
                    $('.row')[1].remove();
                    $('.layout-container').append('<entries-component></entries-component>');
                    $.getScript("/js/app.js", (rep,status)=>{
                        if("success" == status)r();
                        else console.error(status, rep);
                    });
                }
            },
            // OK
            spotifySaveAuto: function(r, data){
                this.spotifyGetUserInfo((status, accessToken = null, userId = null)=>{
                    if(status != 200)
                    {
                        r(status);
                        return;
                    }
                    let putUrl = ""
                    switch(data.type)
                    {
                        case "album":
                            putUrl = "https://spclient.wg.spotify.com/collection-view/v1/collection/albums/" + userId + "?base62ids=" + data.id + "&model=bookmark";
                            break;
                        default:
                            GM_log(data)
                            r(201)
                            return;
                            break;
                    }
                    $.ajax({
                        type: 'PUT',
                        url: putUrl,
                        headers: {authorization: "Bearer " + accessToken},
                        success: function(data){
                            console.log(data);
                            r(200);
                        },
                        error: function(data){
                            console.error(data);
                            r(201)
                        },
                        anonymous:true
                    });
                });
            },
            // OK
            spotifyFollowAuto: function(r, data){
                this.spotifyGetUserInfo((status, accessToken = null)=>{
                    if(status != 200)
                    {
                        r(status)
                        return;
                    }
                    let putUrl = "";
                    switch(data.type)
                    {
                        case "artist":
                            putUrl = "https://api.spotify.com/v1/me/following?type=artist&ids=" + data.id;
                            break;
                        case "playlist":
                            putUrl = "https://api.spotify.com/v1/playlists/" + data.id + "/followers"
                            break;
                        case "user":
                            putUrl = "https://api.spotify.com/v1/me/following?type=user&ids=" + data.id;
                            break;
                        default:
                            if(debug)GM_log(data)
                            r(201)
                            return;
                            break;
                    }
                    $.ajax({
                        type: 'PUT',
                        url: putUrl,
                        headers: {authorization: "Bearer " + accessToken},
                        success: function(data){
                            if(debug)console.log(data);
                            r(200);
                        },
                        error: function(data){
                            if(debug)console.error(data);
                            r(201)
                        },
                        anonymous:true
                    });
                });
            },
            // OK
            spotifyGetUserInfo: function(r){
                r(203)
                this.spotifyGetAccessToken(
                    (status, accessToken)=>{
                        if(status != 200)
                        {
                            r(status);
                            return;
                        }
                        this.httpRequest({
                            url: 'https://api.spotify.com/v1/me',
                            method: 'GET',
                            headers:{authorization: "Bearer " + accessToken},
                            onload: (response) => {
                                if (response.status === 200) {
                                    if(debug)console.log(response)
                                    r(200, accessToken, JSON.parse(response.responseText).id);
                                } else {
                                    if(debug)console.log('Error:' + response.statusText + '(' + response.status + ')')
                                    r(202)
                                }
                            },
                            error:(res)=>{
                                if(debug)console.log("error");
                                r(202)
                            },
                            anonymous:true
                        })
                    }
                )
            },
            // OK
            spotifyGetAccessToken: function(r){
                this.httpRequest({
                    url: 'https://open.spotify.com/get_access_token?reason=transport&productType=web_player',
                    method: 'GET',
                    onload: (response) => {
                        if (response.status === 200) {
                            r(200, JSON.parse(response.responseText).accessToken);
                        } else {
                            if(debug)console.log(response)
                            r(202);
                        }
                    },
                    error:(res)=>{
                        if(debug)console.log(res);
                        r(202);
                    }
                })
            },
            // steam信息更新(In Progress)[修改自https://gf.qytechs.cn/zh-CN/scripts/370650]
            steamInfoUpdate: function (r, type = 'all', update = false) {
                if (new Date().getTime() - steamInfo.updateTime > 10 * 60 * 1000 || update) {
                    const pro = []
                    if (type === 'community' || type === 'all') {
                        pro.push(new Promise((resolve, reject) => {
                            this.httpRequest({
                                url: 'https://steamcommunity.com/my',
                                method: 'GET',
                                onload: (response) => {
                                    if (debug) console.log(response)
                                    if (response.status === 200) {
                                        if ($(response.responseText).find('a[href*="/login/home"]').length > 0) {
                                            if(debug)console.log(Error('Not Login'))
                                        } else {
                                            const steam64Id = response.responseText.match(/g_steamID = "(.+?)";/)
                                            const communitySessionID = response.responseText.match(/g_sessionID = "(.+?)";/)
                                            const userName = response.responseText.match(/steamcommunity.com\/id\/(.+?)\/friends\//)
                                            if (steam64Id) steamInfo.steam64Id = steam64Id[1]
                                            if (communitySessionID) steamInfo.communitySessionID = communitySessionID[1]
                                            if (userName) steamInfo.userName = userName[1]
                                            resolve()
                                        }
                                    } else {
                                        if(debug)console.log('Error:' + response.statusText + '(' + response.status + ')')
                                        if(debug)console.log(Error('Request Failed'))
                                    }
                                },
                                r: resolve,
                                status
                            })
                        }))
                    }
                    if (type === 'store' || type === 'all') {
                        pro.push(new Promise((resolve, reject) => {

                            this.httpRequest({
                                url: 'https://store.steampowered.com/stats/',
                                method: 'GET',
                                onload: (response) => {
                                    if (debug) console.log(response)
                                    if (response.status === 200) {
                                        if ($(response.responseText).find('a[href*="/login/"]').length > 0) {
                                            console.log('Error:store not Login', true)
                                            reject(Error('Not Login'))
                                        } else {
                                            const storeSessionID = response.responseText.match(/g_sessionID = "(.+?)";/)
                                            if (storeSessionID) steamInfo.storeSessionID = storeSessionID[1]
                                            console.log("storeInfo updated");
                                            resolve();
                                        }
                                    } else {
                                        console.log('Error:' + response.statusText + '(' + response.status + ')')
                                        reject(Error('Request Failed'))
                                    }
                                },
                                r: resolve,
                                status
                            })
                        }))
                    }
                    Promise.all(pro).then(() => {
                        steamInfo.updateTime = new Date().getTime()
                        GM_setValue('steamInfo', steamInfo)
                        r(1)
                    }).catch(err => {
                        console.error(err)
                    })
                } else {
                    r(1)
                }
            },
            // steam个人资料回复"+rep"(OK)
            steamRepAuto: function(r, id){
                this.steamRepHisCheck((ret) => {
                    if(!ret){
                        this.httpRequest({
                            url: 'https://steamcommunity.com/comment/Profile/post/' + id + '/-1/',
                            method: 'POST',
                            data: {
                                comment:'+rep',
                                count:6,
                                sessionid:steamInfo.communitySessionID,
                                feature2:-1
                            },
                            onload: (response) => {
                                console.log(response);
                                if(response.status == 200)
                                {
                                    r(200);
                                }else r(201)
                            }
                        })
                    }else{
                        r(200);
                    }
                },id)
            },
            steamRepHisCheck: function (r, id){
                this.steamInfoUpdate(() => {
                    this.httpRequest({
                        url: "https://steamcommunity.com/profiles/" + id,
                        method: 'GET',
                        onload: (response) => {
                            if(response.status == 200)
                            {
                                let comments = response.responseText.match(/commentthread_comments([\s\S]*)commentthread_footer/);
                                console.log(comments)
                                if(comments != null)
                                    r(comments[1].includes(steamInfo.steam64Id));
                                else
                                    noticeFrame.addNotice({type:"msg", msg:"<font class=\"error\">steam评论区未找到</font>"})
                            }else{
                                console.log(response)
                                noticeFrame.addNotice({type:"msg", msg:"<font class=\"error\">出现异常</font>"})
                            }
                        }
                        //,anonymous:true
                    });
                })
            },
            // steam加组(OK)[修改自https://gf.qytechs.cn/zh-CN/scripts/370650]
            steamJoinGroupAuto: function (r, group) {
                this.steamInfoUpdate(() => {
                    if(debug)console.log("====steamJoinGroupAuto====");
                    this.httpRequest({
                        url: 'https://steamcommunity.com/groups/' + group,
                        method: 'POST',
                        headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
                        data: $.param({ action: 'join', sessionID: steamInfo.communitySessionID }),
                        onload: (response) => {
                            if (debug) console.log(response)
                            if (response.status === 200 && !response.responseText.includes('grouppage_join_area') &&  !response.responseText.includes('error_ctn')) {
                                console.log("steamJoinGroupAuto")
                                console.log({ result: 'success', statusText: response.statusText, status: response.status })
                                r(200);
                            } else {
                                console.error('Error:' + response.statusText + '(' + response.status + ')')
                                console.log({ result: 'error', statusText: response.statusText, status: response.status })
                                r(201);
                            }
                        }
                    })
                });
            },
            // steam加愿望单(In Progress)[修改自https://gf.qytechs.cn/zh-CN/scripts/370650]
            steamAddWishlistAuto: function (r, gameId) {
                this.steamInfoUpdate(() => {
                    new Promise(resolve => {
                        this.httpRequest({
                            url: 'https://store.steampowered.com/api/addtowishlist',
                            method: 'POST',
                            headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
                            data: $.param({ sessionid: steamInfo.storeSessionID, appid: gameId }),
                            dataType: 'json',
                            onload: function (response) {
                                if (debug) console.log(response)
                                if (response.status === 200 && response.response && response.response.success === true) {
                                    resolve({ result: 'success', statusText: response.statusText, status: response.status })
                                } else {
                                    resolve({ result: 'error', statusText: response.statusText, status: response.status })
                                }
                            },
                            onabort: response => { resolve({ result: 'error', statusText: response.statusText, status: response.status }) },
                            onerror: response => { resolve({ result: 'error', statusText: response.statusText, status: response.status }) },
                            ontimeout: response => { resolve({ result: 'error', statusText: response.statusText, status: response.status }) },
                            r: resolve
                        })
                    }).then(result => {
                        if (result.result === 'success') {
                            r(200)
                        } else {
                            this.httpRequest({
                                url: 'https://store.steampowered.com/app/' + gameId,
                                method: 'GET',
                                onload: function (response) {
                                    if (debug) console.log(response)
                                    if (response.status === 200) {
                                        if (response.responseText.includes('class="queue_actions_ctn"') && response.responseText.includes('已在库中')) {
                                            GM_log({ result: 'success', statusText: response.statusText, status: response.status, own: true })
                                            r(200)
                                        } else if ((response.responseText.includes('class="queue_actions_ctn"') && response.responseText.includes('添加至您的愿望单')) || !response.responseText.includes('class="queue_actions_ctn"')) {
                                            console.error('Error:' + result.statusText + '(' + result.status + ')')
                                            GM_log({ result: 'error', statusText: response.statusText, status: response.status })
                                            r(201)
                                        } else {
                                            GM_log({ result: 'success', statusText: response.statusText, status: response.status })
                                            r(200)
                                        }
                                    } else {
                                        console.error('Error:' + result.statusText + '(' + result.status + ')')
                                        GM_log({ result: 'error', statusText: response.statusText, status: response.status })
                                        r(201)
                                    }
                                },
                                r
                            })
                        }
                    }).catch(err => {
                        console.error(err)
                        r(201)
                    })
                })
            },
            // tumblr关注博客(OK)
            tumblrFollowAuto: function(r, name){
                this.tumblrGetKey((status, key = false)=>{
                    if(status != 200)
                    {
                        r(status);
                        return;
                    }
                    if(false != key){
                        if(-1 != key.indexOf("!123") && -1 !=key.indexOf("|") )
                        {
                            r(202)
                            return;
                        }
                        this.httpRequest({
                            url: 'https://www.tumblr.com/svc/follow',
                            method: 'POST',
                            headers: {"x-tumblr-form-key": key, "Content-Type": "application/x-www-form-urlencoded"},
                            data: $.param({'data[tumblelog]': name}),
                            onload: (response) => {
                                if(debug)console.log(response);
                                if(response.status == 200)
                                {
                                    r(200);
                                }else r(201)
                            }
                        })
                    }else{
                        r(201)
                    }
                })
            },
            // tumblr获取请求头key(OK)
            tumblrGetKey:function(r){
                r(203)
                this.httpRequest({
                    url: 'https://www.tumblr.com/dashboard/iframe',
                    method: 'GET',
                    onload: (response) => {
                        if(debug)console.log(response);
                        if(response.status == 200)
                        {
                            let key = response.responseText.match(/id="tumblr_form_key" content="(.+?)">/)
                            GM_log(key)
                            if(key)r(200, key[1]);
                            else r(666)
                        }else r(201)
                    }
                })
            },
            // twitch关注(OK)
            twitchFollowAuto: function(r, channelId){
                this.twitchAuthUpdate((status)=>{
                    if(status != 200)
                    {
                        r(status);
                        return;
                    }
                    this.httpRequest({
                        url: 'https://gql.twitch.tv/gql',
                        method: 'POST',
                        headers: { Authorization: "OAuth " + twitchAuth["auth-token"]},
                        data: '[{"operationName":"FollowButton_FollowUser","variables":{"input":{"disableNotifications":false,"targetID":"' + channelId + '"}},"extensions":{"persistedQuery":{"version":1,"sha256Hash":"3efee1acda90efdff9fef6e6b4a29213be3ee490781c5b54469717b6131ffdfe"}}}]',
                        onload: (response) => {
                            if (debug)console.log(response)
                            if (response.status === 200) {
                                if(debug)console.log({ result: 'success', statusText: response.statusText, status: response.status })
                                r(200)
                            } else if(response.status === 401){
                                twitchAuth.updateTime = 0;
                                GM_setValue("twitchAuth", null);
                                r(202)
                            }else{
                                if(debug)console.log('Error:' + response.statusText + '(' + response.status + ')')
                                r(201)
                            }
                        }
                    })
                })
            },
            // 弃用
            twitchGetId: function(r, channels){
                this.httpRequest({
                    url: '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',
                    method: 'GET',
                    onload: (response) => {
                        if (response.status === 200) {
                            if(debug)console.log({ result: 'success', statusText: response.statusText, status: response.status })
                            let rep = JSON.parse(JSON.parse(response.responseText).token);
                            r(rep.channel_id);
                        } else {
                            if(debug)console.log('Error:' + response.statusText + '(' + response.status + ')')
                            r('error')
                        }
                    },
                    error:(res)=>{
                        if(debug)console.log("error");
                        if(debug)console.log(res);
                    },
                    anonymous:true
                })
            },
            // twitch凭证更新(OK)
            twitchAuthUpdate:function(r, update = false){
                if (new Date().getTime() - twitchAuth.updateTime > 30 * 60 * 1000 || update) {
                    r(203)
                    new Promise((resolve)=>{
                        GM_openInTab("https://www.twitch.tv/settings/profile?keyjokertask=storageAuth", true);
                        let i = 0;
                        let check = setInterval(()=>{
                            i++;
                            if(GM_getValue("twitchAuth") && new Date().getTime() - GM_getValue("twitchAuth").updateTime <= 2 * 1000)
                            {
                                if(GM_getValue("twitchAuth").status == 0)
                                {
                                    clearInterval(check);
                                    resolve(202)
                                    return;
                                }
                                twitchAuth["auth-token"] = GM_getValue('twitchAuth')["auth-token"];
                                twitchAuth.updateTime = GM_getValue('twitchAuth').updateTime;
                                twitchAuth.status = GM_getValue('twitchAuth').status;
                                clearInterval(check);
                                resolve(200)
                            }
                            if(i >= 10)
                            {
                                noticeFrame.addNotice({type:"msg", msg:"<font class=\"error\">twitchAuth获取超时</font>"})
                                clearInterval(check);
                                resolve(201)
                            }
                        }, 1000)
                    }).then((ret)=>{
                        r(ret);
                    })
                }else{
                    r(200)
                }
            },
            // 推特关注用户(OK)
            twitterFollowAuto: function(r, data){
                this.twitterAuthUpdate(()=>{
                    this.twitterGetUserInfo((userId)=>{
                        if(debug)console.log(userId)
                        if("error" == userId)
                        {
                            r(201);
                            return;
                        }
                        this.httpRequest({
                            url: 'https://api.twitter.com/1.1/friendships/create.json',
                            method: 'POST',
                            headers: { authorization: "Bearer " + twitterAuth.authorization, 'Content-Type': 'application/x-www-form-urlencoded', 'x-csrf-token':twitterAuth.ct0},
                            data: $.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}),
                            onload: (response) => {
                                if (debug)console.log(response)
                                if (response.status === 200) {
                                    r(200);
                                } else {
                                    if(debug)console.log('Error:' + response.statusText + '(' + response.status + ')')
                                    if(debug)console.log({ result: 'error', statusText: response.statusText, status: response.status })
                                    r(201);
                                }
                            }
                        })
                    },data.username)
                })
            },
            // 推特转推(OK)
            twitterRetweetAuto: function(r, url){
                if(debug)console.log("====twitterRetweetAuto====");
                let retweetId = url.split("status/")[1];
                this.twitterAuthUpdate(()=>{
                    this.httpRequest({
                        url: 'https://api.twitter.com/1.1/statuses/retweet.json',
                        method: 'POST',
                        headers: { authorization: "Bearer " + twitterAuth.authorization, 'Content-Type': 'application/x-www-form-urlencoded', 'x-csrf-token':twitterAuth.ct0},
                        data: $.param({ tweet_mode: "extended",id: retweetId}),
                        onload: (response) => {
                            if (debug)console.log(response)
                            if (response.status === 200 || (response.status === 403 && response.responseText == '{"errors":[{"code":327,"message":"You have already retweeted this Tweet."}]}')) {
                                if(debug)console.log("success");
                                if(debug)console.log({ result: 'success', statusText: response.statusText, status: response.status });
                                r(200);
                            } else {
                                if(debug)console.log('Error:' + response.statusText + '(' + response.status + ')')
                                r(201);
                            }
                        }
                    })
                })
            },
            // 推特根据用户名获取用户id(OK)
            twitterGetUserInfo: function(r, userName){
                if(debug)console.log("====twitterGetUserInfo====");
                this.httpRequest({
                    url: 'https://api.twitter.com/graphql/-xfUfZsnR_zqjFd-IfrN5A/UserByScreenName?variables=%7B%22screen_name%22%3A%22' + userName + '%22%2C%22withHighlightedLabel%22%3Atrue%7D',
                    method: 'GET',
                    headers: { authorization: "Bearer " + twitterAuth.authorization, "content-type": "application/json"},
                    onload: (response) => {
                        if (response.status === 200) {
                            if(debug)console.log(response)
                            r(JSON.parse(response.responseText).data.user.rest_id);
                        } else {
                            if(debug)console.log('Error:' + response.statusText + '(' + response.status + ')')
                            r('error')
                        }
                    },
                    error:(res)=>{
                        if(debug)console.log(res);
                        r('error')
                    },
                    anonymous:true
                })
            },
            // 推特取得用户页面响应头(OK)
            twitterAP:function(r){
                this.httpRequest({
                    url: 'https://twitter.com/settings/account?k',
                    method: 'GET',
                    onload: (response) => {
                        if (response.status === 200) {
                            if(debug)console.log(response)
                            if(debug)console.log({ result: 'success', statusText: response.statusText, status: response.status })
                            r(response.responseHeaders)
                        } else {
                            if(debug)console.log('Error:' + response.statusText + '(' + response.status + ')')
                            if(debug)console.log({ result: 'error', statusText: response.statusText, status: response.status })
                        }
                    },
                    error:(res)=>{
                        if(debug)console.log("error");
                        if(debug)console.log(res);
                    }
                })
            },
            // 推特凭证更新(OK)
            twitterAuthUpdate:function(r, update = false){
                if(new Date().getTime() - twitterAuth.updateTime > 30 * 60 * 1000 || update){
                    new Promise((resolve, reject) => {
                        this.twitterAP((ret)=>{
                            let xcf = ret.match(/ct0=(.+?);/)[1]
                            this.httpRequest({
                                url: 'https://api.twitter.com/1.1/jot/client_event.json',
                                method: 'POST',
                                headers: { authorization: "Bearer " + twitterAuth.authorization,"x-csrf-token":xcf , "content-type": "application/json"},
                                data: "category=perftown&log=%5B%7B%22description%22%3A%22rweb%3Aseen_ids%3Apersistence%3Aget%3Asuccess%22%2C%22product%22%3A%22rweb%22%2C%22duration_ms%22%3A568%7D%2C%7B%22description%22%3A%22rweb%3Aseen_ids%3Apersistence%3Aget%3Asuccess%22%2C%22product%22%3A%22rweb%22%2C%22duration_ms%22%3A571%7D%2C%7B%22description%22%3A%22rweb%3Ainit%3AstorePrepare%22%2C%22product%22%3A%22rweb%22%2C%22duration_ms%22%3A581%7D%2C%7B%22description%22%3A%22rweb%3Attft%3AperfSupported%22%2C%22product%22%3A%22rweb%22%2C%22duration_ms%22%3A1%7D%2C%7B%22description%22%3A%22rweb%3Attft%3Aconnect%22%2C%22product%22%3A%22rweb%22%2C%22duration_ms%22%3A22%7D%2C%7B%22description%22%3A%22rweb%3Attft%3Aprocess%22%2C%22product%22%3A%22rweb%22%2C%22duration_ms%22%3A1497%7D%2C%7B%22description%22%3A%22rweb%3Attft%3Aresponse%22%2C%22product%22%3A%22rweb%22%2C%22duration_ms%22%3A425%7D%2C%7B%22description%22%3A%22rweb%3Attft%3Ainteractivity%22%2C%22product%22%3A%22rweb%22%2C%22duration_ms%22%3A2217%7D%5D",
                                onload: (response) => {
                                    if(debug)console.log(response)
                                    if (response.status === 200) {
                                        if(debug)console.log({ result: 'success', statusText: response.statusText, status: response.status })
                                        let ct0 = response.responseHeaders.match(/ct0=(.+?);/)[1]
                                        if(ct0)twitterAuth.ct0 = ct0;
                                        resolve({status:"success"})
                                    } else {
                                        if(debug)console.log('Error:' + response.statusText + '(' + response.status + ')')
                                        if(debug)console.log({ result: 'error', statusText: response.statusText, status: response.status })
                                        resolve({status:"error"})
                                    }
                                },
                                error:(res)=>{
                                    if(debug)console.log("error");
                                    if(debug)console.log(res);
                                    resolve({status:"error"})
                                }
                            })
                        })
                    }).then((ret)=>{
                        if(debug)console.log(ret);
                        if(ret.status == "success")
                        {
                            twitterAuth.updateTime = new Date().getTime()
                            GM_setValue("twitterAuth", twitterAuth)
                            r(1);
                        }else{
                            noticeFrame.addNotice({type:"msg", msg:"<font class=\"error\">twitter token获取失败</font>"})
                        }
                    })
                }else{
                    r(1)
                }
            },
            test: function(){
                $('.card').remove();
                start()
                //this.tumblrFollowAuto(console.log, "patstaru")
            }
        }
        if(document.getElementById("logout-form") && location.search !== "")
        {
            location.href = location.pathname;
        }else if(location.href == "https://www.keyjoker.com/entries")
        {
            window.onload=()=>{
                if(document.getElementsByClassName("nav-item active").length != 0 && document.getElementsByClassName("nav-item active")[0].innerText == "Earn Credits")
                {
                    noticeFrame.loadFrame();
                    let isStart=setInterval(()=>{
                        if(GM_getValue("start")==1){
                            clearInterval(isStart);
                            next();
                        }
                    },1000);
                }
            }
        }else{
            func.appHandle();
        }
        GM_registerMenuCommand("设置时间间隔",setTime);
        function checkSwitch(id){
            GM_unregisterMenuCommand(id);
            if(0 == GM_getValue("start")){
                let id = GM_registerMenuCommand("开始检测",()=>{
                    start();
                    checkSwitch(id);
                });
            }else{
                let id = GM_registerMenuCommand("停止检测",()=>{
                    let date=new Date();
                    let hour=date.getHours();
                    let min=date.getMinutes()<10?("0"+date.getMinutes()):date.getMinutes();
                    GM_setValue("start",0);
                    $(".border-bottom").text(hour+":"+min+" 停止执行新任务检测");
                    checkSwitch(id);
                });
            }
        }
        checkSwitch(null);
        if(debug)
        {
            GM_registerMenuCommand("Test",()=>{
                func.test();
            });
        }
    } catch (e) {
        setTimeout(() => {
            noticeFrame.addNotice({ type: 'msg', msg:"<font class\"error\">任务脚本执行期间发生预期之外的错误,详情见控制台</font>" })
        }, 500)
        console.log('%c%s', 'color:white;background:red', e.stack)
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址