巴哈姆特勇者福利社-跳過廣告&兌換流程自動化

在勇者福利社參加抽抽樂時,可以直接免費兌換抽獎卷而不用看廣告!

// ==UserScript==
// @name         巴哈姆特勇者福利社-跳過廣告&兌換流程自動化
// @namespace    ani20168
// @version      2.3.1
// @description  在勇者福利社參加抽抽樂時,可以直接免費兌換抽獎卷而不用看廣告!
// @author       ani20168
// @homepage     https://home.gamer.com.tw/profile/index.php?&owner=a20280210
// @match        https://fuli.gamer.com.tw/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=gamer.com.tw
// @grant        GM_xmlhttpRequest
// @require      https://openuserjs.org/src/libs/sizzle/GM_config.js
// ==/UserScript==

(function() {
    'use strict';
    var observer;
    var enableAutoProcessOnBuyDPage = false;
    var enableAutoSkip = false;

    // GM_config選單
    var GM_configStruct = {
        'id': 'UserScriptConfig',
        'title': '抽抽樂腳本設定',
        'fields':
        {
            'enableAutoProcessOnBuyDPage':
            {
                'label': '自動送出收件資料',
                'type': 'checkbox',
                'default': false,
                'title': '啟用後,在填寫收件人頁面會自動送出收件資料,開啟這項功能前請先確保收件人資訊完整,而且頁面上的"請幫我記住收件人資料"的確認框為打勾的狀態。'
            },
            'enableAutoSkip':
            {
                'label': '自動跳過廣告',
                'type': 'checkbox',
                'default': false,
                'title': '啟用後,只要頁面是在抽抽樂的商品頁面,而且此商品目前可以透過看廣告的方式兌換抽獎機會,則會自動執行廣告跳過流程。'
            }
        },
        'css':
        `
        #UserScriptConfig {
            font-family: 'Arial', sans-serif;
            background-color: #fff;
            color: #333;
            width: 300px;
            padding: 15px;
            margin: 0 auto;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }

        #UserScriptConfig .config_var {
            margin-bottom: 10px;
        }

        #UserScriptConfig .field_label {
            margin-bottom: 5px;
            display: block;
            font-weight: bold;
        }

        #UserScriptConfig input[type='checkbox'] {
            margin-right: 10px;
            transform: scale(1.2);
        }

        #UserScriptConfig .section_header_holder {
            margin-bottom: 15px;
        }

        #UserScriptConfig .config_header {
            font-size: 18px;
            margin-bottom: 15px;
            text-align: center;
        }

        #UserScriptConfig .saveclose_buttons {
            text-align: center;
            padding: 8px 16px;
            margin:20px 40px 5px;
            border: 2px solid transparent;
            border-radius: 10px;
            transition: border-color 0.15s ease-in-out;
        }



        #UserScriptConfig .saveclose_buttons:hover {
            border-color: #90ee90;
        }

        #UserScriptConfig .reset, #UserScriptConfig .reset a {
            color: #999;
            text-decoration: none;
            padding: 8px 16px;
            display: block;
            margin: 10px auto 0;
            font-size: 12px;
            text-align: center;
        }

        #UserScriptConfig .reset:hover, #UserScriptConfig .reset a:hover {
            color: #666;
        }

        #UserScriptConfig .config_var {
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        `,
       'frameStyle':
       `
         bottom: auto; border: 2px solid #6F6F6F; display: none; height: 290px;
         left: 50%; margin: 0; max-height: 100%; max-width: 100%; opacity: 0;
         overflow: auto; padding: 0; position: fixed; right: auto; top: 50%;
         width: 330px; z-index: 9999;
         border-radius: 8px;
         box-shadow: 10px 10px 20px rgba(0,0,0,0.4);
       `
       ,
        'events':
        {
            'open': function() {
                console.log("配置界面已開啟");
            },
            'save': function() {
                console.log("設定已保存");
                enableAutoProcessOnBuyDPage = GM_config.get('enableAutoProcessOnBuyDPage');
                enableAutoSkip = GM_config.get('enableAutoSkip');
                alert("保存完成!");
            },
            'close': function() {
                console.log("配置界面已關閉");
            },
            'init': () => {
                console.log("UI初始化");
                //獲取使用者設定
                enableAutoProcessOnBuyDPage = GM_config.get('enableAutoProcessOnBuyDPage');
                enableAutoSkip = GM_config.get('enableAutoSkip');
            }
        }
    };

    // 初始化GM_config
    GM_config.init(GM_configStruct);

    //處理驗證流程
    function handleCaptchaVerification() {
        var captchaFound = false; // 用於標記是否找到驗證器

        // 嘗試執行Google reCAPTCHA
        var googleRecaptcha = document.getElementById('recaptcha');
        if (googleRecaptcha && typeof grecaptcha !== 'undefined' && grecaptcha.execute) {
            grecaptcha.execute();
            captchaFound = true; // 標記找到Google reCAPTCHA
        }

        // 嘗試執行Cloudflare Turnstile
        if (typeof turnstile !== 'undefined' && document.querySelector('.cf-turnstile')) {
            turnstile.render('.cf-turnstile');
            captchaFound = true; // 標記找到Cloudflare Turnstile
        }

        // 如果沒有找到任何驗證器,則提交表單
        if (!captchaFound) {
            jQuery('#buyD').submit();
        }
    }

    // 檢查目前頁面是否為 message_done 頁面
    if (window.location.href.includes('https://fuli.gamer.com.tw/message_done.php')) {
        // 在 message_done 頁面上執行的操作
        var button = document.querySelector('button');
        if (button) {
            button.click();
        }
    }

    // 等待頁面加載完畢
    window.addEventListener('load', function() {
        // 找到TOP-my元素
        var guiTopMyElement = document.querySelector('.TOP-my ul');

        // 創建GUI按鈕的列表項
        var guiButtonLi = document.createElement('li');
        guiButtonLi.className = 'mobilehide';

        // 創建GUI按鈕的連結元素
        var guiButtonLink = document.createElement('a');
        guiButtonLink.href = 'javascript:void(0)';
        guiButtonLink.onclick = function() {
            GM_config.open();
        };

        // 為GUI按鈕添加圖標
        var guiButtonIcon = document.createElement('img');
        guiButtonIcon.src = 'https://i.imgur.com/uj2yF4e.png';
        guiButtonLink.appendChild(guiButtonIcon);

        // 將連結元素加入到列表項中,然後將列表項加入到TOP-my區塊
        guiButtonLi.appendChild(guiButtonLink);
        guiTopMyElement.insertBefore(guiButtonLi, guiTopMyElement.firstChild); // 將GUI按鈕添加到最左側

        // 檢查目前頁面是否為 buyD 頁面
        if (enableAutoProcessOnBuyDPage && window.location.href.includes('https://fuli.gamer.com.tw/buyD.php')) {
            document.getElementById('agree-confirm').checked = true;
            handleCaptchaVerification();
            return;
        }

        //如果沒有兌換欄位
        var btnList = document.getElementById('buyBtnContent');
        if (!btnList) return;
        //看一下需不需要先回答問題,如果需要,回答問題並重整
        var questionButton = btnList.querySelector('a[onclick^="showQuestion(1);"]');
        if (questionButton) {
            getCsrfToken().then(token => {
                sendAnswerQuestionRequest(token);
                setTimeout(function() {
                    location.reload();
                }, 2000);
            }).catch(error => {
                console.error('獲取 CSRF token 時發生錯誤:', error);
            });
            return;
        }
        //如果沒有看廣告兌換按鈕就return
        var adButton = btnList.querySelector('a[onclick^="window.FuliAd.checkAd"]');
        if (!adButton) return;

        var newButton = document.createElement('a');
        newButton.className = 'btn-base c-accent-o';
        newButton.textContent = '跳過廣告';
        newButton.href = '#';
        newButton.style.marginLeft = '10px';

        newButton.addEventListener('click', function(event) {
            event.preventDefault();
            executeAdSkippingProcess();
        });

        btnList.appendChild(newButton);

        // 如果啟用自動跳過廣告,則自動點擊新按鈕
        if (enableAutoSkip) {
            newButton.click();
        }
    });



    function executeAdSkippingProcess() {
        watchAdCheck();
        getCsrfToken().then(token => {
            setTimeout(function() {
                sendPostRequest(token);
            }, 2000);
        }).catch(error => {
            console.error('獲取 CSRF token 時發生錯誤:', error);
        });
    }

    // 獲取CSRF token
    function getCsrfToken() {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: "https://fuli.gamer.com.tw/ajax/getCSRFToken.php?_=1702883537159",
                onload: function(response) {
                    var token = response.responseText.trim();
                    if (token) {
                        resolve(token);
                    } else {
                        reject('Token not found in response');
                    }
                },
                onerror: function(error) {
                    reject('Error during request: ' + error.message);
                }
            });
        });
    }

    function sendAnswerQuestionRequest(csrfToken) {
        // 獲取網頁中的問題和答案
        let templateContent = document.getElementById('question-popup').innerHTML;
        let tempDiv = document.createElement('div');
        tempDiv.innerHTML = templateContent;

        let questions = tempDiv.querySelectorAll('.fuli-option[data-question]');
        let questionNumbers = new Set();
        questions.forEach(question => {
            questionNumbers.add(question.getAttribute('data-question'));
        });

        let answers = [];
        questionNumbers.forEach(questionNumber => {
            let firstOption = tempDiv.querySelector(`.fuli-option[data-question="${questionNumber}"]`);
            answers.push(firstOption.getAttribute('data-answer'));
        });


        // 獲取sn
        var urlParams = new URLSearchParams(window.location.search);
        var snValue = urlParams.get('sn');

        // 準備payload
        var formData = new FormData();
        formData.append('sn', snValue);
        formData.append('token', csrfToken);
        answers.forEach(answer => formData.append('answer[]', answer));

        // 這一段只是在看payload而已...
        var object = {};
        formData.forEach((value, key) => {
            if(!Reflect.has(object, key)){
                object[key] = value;
                return;
            }
            if(!Array.isArray(object[key])){
                object[key] = [object[key]];
            }
            object[key].push(value);
        });
        var payloadData = JSON.stringify(object);

        // 發送post
        GM_xmlhttpRequest({
            method: "POST",
            url: "https://fuli.gamer.com.tw/ajax/answer_question.php",
            data: formData,
            onload: function(response) {
                console.log('Payload sent:', payloadData);
                console.log('回答問題post的回應:', response.responseText);
            }
        });
    }




    // 發送已看完廣告的post請求
    function sendPostRequest(csrfToken) {
        var urlParams = new URLSearchParams(window.location.search);
        var snValue = urlParams.get('sn');
        var adButton = document.querySelector('a[onclick^="window.FuliAd.checkAd"]');
        console.log('sn:',encodeURIComponent(snValue))

        if (!snValue) {
            console.log('無法獲取sn參數');
            return;
        }

        GM_xmlhttpRequest({
            method: "POST",
            url: "https://fuli.gamer.com.tw/ajax/finish_ad.php",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            },
            data: "token=" + encodeURIComponent(csrfToken) + "&area=item&sn=" + encodeURIComponent(snValue),
            onload: function(response) {
                console.log('post回應:', response.responseText);
                adButton.click();
            }
        });
    }

    // 發送get檢查是否已經看過廣告
    function watchAdCheck() {
        var urlParams = new URLSearchParams(window.location.search);
        var snValue = urlParams.get('sn');
        var adButton = document.querySelector('a[onclick^="window.FuliAd.checkAd"]');

        if (!snValue) {
            console.log('無法獲取sn參數');
            return;
        }

        GM_xmlhttpRequest({
            method: "GET",
            url: "https://fuli.gamer.com.tw/ajax/check_ad.php?area=item&sn=" + encodeURIComponent(snValue),
            onload: function(response) {
                try {
                    var responseData = JSON.parse(response.responseText);
                    if (responseData.data && responseData.data.finished === 1) {
                        alert('你已經看過/跳過廣告了!');
                        adButton.click();
                        return;
                    }else{
                        clickAdButton();
                    }
                } catch (e) {
                    console.error('解析回應時發生錯誤:', e);
                }
            }
        });
    }

    // 自動點擊"看廣告免費兌換"按鈕
    function clickAdButton() {
        var adButton = document.querySelector('a[onclick^="window.FuliAd.checkAd"]');
        if (adButton) {
            adButton.click();
            startObservingDialog();
        }
    }

    // 監視對話框
    function startObservingDialog() {
        var observerOptions = {
            childList: true,
            subtree: true
        };

        observer = new MutationObserver(function(mutations, obs) {
            var dialog = document.querySelector('.dialogify__content');
            if (dialog) {
                var confirmButton = dialog.querySelector('.btn-box .btn-insert.btn-primary');
                if (confirmButton) {
                    confirmButton.disabled = true;
                    confirmButton.style.backgroundColor = '#e5e5e5';
                }

                setTimeout(function() {
                    handleDialogButtons(dialog);
                    obs.disconnect();
                }, 500);
            }
        });

        observer.observe(document.body, observerOptions);
    }

    // 處理確認窗口的按鈕
    function handleDialogButtons(dialog) {
        var cancelButton = dialog.querySelector('.btn-box .btn-insert:not(.btn-primary)');
        var confirmButton = dialog.querySelector('.btn-box .btn-insert.btn-primary');

        if (cancelButton) {
            setTimeout(function() {
                cancelButton.click();
                if (confirmButton) {
                    confirmButton.disabled = false;
                    confirmButton.style.backgroundColor = '';
                }
            }, 1000);
        }
    }
})();