您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
try to take over the world!
当前为
// ==UserScript== // @name Steam Bundle Sites Extension // @namespace http://tampermonkey.net/ // @version 1.4.1 // @description try to take over the world! // @icon http://store.steampowered.com/favicon.ico // @author Bisumaruko // @include http*://store.steampowered.com/* // @include https://www.indiegala.com/gift* // @include https://www.indiegala.com/profile* // @include https://www.indiegala.com/game* // @include http*://*bundlestars.com/* // @include https://www.humblebundle.com/downloads* // @include http*://*dailyindiegame.com/* // @include http*://bundle.ccyycn.com/order/* // @include https://groupees.com/purchases // @include http*://*agiso.com/* // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/6.6.6/sweetalert2.min.js // @resource SweetAlert2CSS https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/6.6.6/sweetalert2.min.css // @connect store.steampowered.com // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_getResourceText // @run-at document-start // @noframes // ==/UserScript== /* global GM_xmlhttpRequest, GM_setValue, GM_getValue, GM_addStyle, GM_getResourceText, swal, g_AccountID, g_sessionID, g_oSuggestParams, window, document, location, fetch, localStorage, MutationObserver, Option */ // setup jQuery const $ = jQuery.noConflict(true); $.fn.pop = [].pop; $.fn.shift = [].shift; // inject swal css GM_addStyle(GM_getResourceText('SweetAlert2CSS')); // load up const regKey = /([A-Za-z0-9]{5}-){2,4}[A-Za-z0-9]{5}/g; const eol = "\n"; const has = Object.prototype.hasOwnProperty; const unique = a => [...new Set(a)]; const owned = JSON.parse(localStorage.getItem('SBSE_owned') || '{}'); const activated = { data: JSON.parse(GM_getValue('SBSE_activated') || '[]'), push(key) { this.data.push(key); GM_setValue('SBSE_activated', JSON.stringify(this.data)); }, check(key) { return this.data.includes(key); } }; const config = { data: JSON.parse(GM_getValue('SBSE_config') || '{}'), set(key, value) { this.data[key] = value; GM_setValue('SBSE_config', JSON.stringify(this.data)); }, get(key) { return has.call(this.data, key) ? this.data[key] : null; }, init() { if (!has.call(this.data, 'autoUpdateSessionID')) this.data.autoUpdateSessionID = true; if (!has.call(this.data, 'preselectIncludeTitle')) this.data.preselectIncludeTitle = false; if (!has.call(this.data, 'titleComesLast')) this.data.titleComesLast = false; if (!has.call(this.data, 'preselectJoinKeys')) this.data.preselectJoinKeys = false; if (!has.call(this.data, 'joinKeysASFStyle')) this.data.joinKeysASFStyle = true; } }; const keyDetails = { data: {}, set(key = '', obj) { if (key.length > 0) { obj.title = has.call(obj, 'title') ? obj.title.trim() : ''; if (has.call(obj, 'app')) obj.app = parseInt(obj.app, 10); if (has.call(obj, 'sub')) obj.sub = parseInt(obj.sub, 10); if (has.call(obj, 'url')) { const matched = obj.url.match(/steam.+\/(app|sub)\/(\d+)/); if (matched) obj[matched[1]] = parseInt(matched[2], 10); } this.data[key] = obj; } }, get(key) { return has.call(this.data, key) ? this.data[key] : null; }, isOwned(key) { const detail = this.data[key]; if (detail && owned.app.includes(detail.app)) return true; if (detail && owned.sub.includes(detail.sub)) return true; return false; } }; config.init(); // text const i18n = { tchinese: { name: '繁體中文', successStatus: '成功', successDetail: '無資料', skippedStatus: '跳過', activatedDetail: '已啟動', failStatus: '失敗', failTitle: '糟糕!', failDetailUnexpected: '發生未知錯誤,請稍後再試', failDetailInvalidKey: '序號錯誤', failDetailUsedKey: '序號已被使用', failDetailRateLimited: '啟動受限', failDetailCountryRestricted: '地區限制', failDetailAlreadyOwned: '產品已擁有', failDetailMissingBaseGame: '未擁有主程式', failDetailPS3Required: '需要PS3 啟動', failDetailGiftWallet: '偵測到禮物卡/錢包序號', failDetailParsingFailed: '處理資料發生錯誤,請稍後再試', failDetailRequestFailedNeedUpdate: '請求發生錯誤,請稍後再試<br>或者嘗試更新SessionID', noItemDetails: '無產品詳細資料', notLoggedInTitle: '未登入', notLoggedInMsg: '請登入Steam 以讓腳本紀錄SessionID', missingTitle: '未發現SessionID', missingMsg: '請問要更新SessionID 嗎?', emptyInput: '未發現Steam 序號', settingsTitle: '設定', settingsAutoUpdateSessionID: '自動更新SessionID', settingsSessionID: '我的SessionID', settingsLanguage: '語言', settingsPreselectIncludeTitle: '預選包括遊戲名', settingsTitleComesLast: '遊戲名置後', settingsPreselectJoinKeys: '預選合併序號', settingsJoinKeysASFStyle: '合併ASF 格式序號', DIGEasyBuyPurchase: '購買', DIGEasyBuySelectAll: '全選', DIGEasyBuySelectCancel: '取消', DIGButtonPurchasing: '購買中', buttonReveal: '刮開', buttonRetrieve: '提取', buttonActivate: '啟動', buttonCopy: '複製', buttonReset: '清空', checkboxIncludeGameTitle: '遊戲名', checkboxJoinKeys: '合併', checkboxSkipUsed: '跳過已使用', BSselectConnector: '至' }, schinese: { name: '简体中文', successStatus: '成功', successDetail: '无信息', activatedDetail: '已激活', skippedStatus: '跳过', failStatus: '失败', failTitle: '糟糕!', failDetailUnexpected: '发生未知错误,请稍后再试', failDetailInvalidKey: '激活码错误', failDetailUsedKey: '激活码已被使用', failDetailRateLimited: '激活受限', failDetailCountryRestricted: '地区限制', failDetailAlreadyOwned: '产品已拥有', failDetailMissingBaseGame: '未拥有基础游戏', failDetailPS3Required: '需要PS3 激活', failDetailGiftWallet: '侦测到礼物卡/钱包激活码', failDetailParsingFailed: '处理资料发生错误,请稍后再试', failDetailRequestFailedNeedUpdate: '请求发生错误,请稍后再试<br>或者尝试更新SessionID', noItemDetails: '无产品详细信息', notLoggedInTitle: '未登入', notLoggedInMsg: '请登入Steam 以让脚本记录SessionID', missingTitle: '未发现SessionID', missingMsg: '请问要更新SessionID 吗?', emptyInput: '未批配到Steam 激活码', settingsTitle: '設置', settingsAutoUpdateSessionID: '自动更新SessionID', settingsSessionID: '我的SessionID', settingsLanguage: '语言', settingsPreselectIncludeTitle: '预选包括游戏名', settingsTitleComesLast: '游戏名置后', settingsPreselectJoinKeys: '预选合并激活码', settingsJoinKeysASFStyle: '合并ASF 格式激活码', DIGEasyBuyPurchase: '购买', DIGEasyBuySelectAll: '全选', DIGEasyBuySelectCancel: '取消', DIGButtonPurchasing: '购买中', buttonReveal: '刮开', buttonRetrieve: '提取', buttonActivate: '激活', buttonCopy: '复制', buttonReset: '清空', checkboxIncludeGameTitle: '游戏名', checkboxJoinKeys: '合并', checkboxSkipUsed: '跳过已使用', BSselectConnector: '至' }, english: { name: 'English', successStatus: 'Success', successDetail: 'No Detail', activatedDetail: 'Activated', skippedStatus: 'Skipped', failStatus: 'Fail', failTitle: 'Opps!', failDetailUnexpected: 'Unexpected Error', failDetailInvalidKey: 'Invalid Key', failDetailUsedKey: 'Used Key', failDetailRateLimited: 'Rate Limited', failDetailCountryRestricted: 'Country Restricted', failDetailAlreadyOwned: 'Product Already Owned', failDetailMissingBaseGame: 'Missing Base Game', failDetailPS3Required: 'PS3 Activation Required', failDetailGiftWallet: 'Gift Card/Wallet Code Detected', failDetailParsingFailed: 'Result parse failed', failDetailRequestFailedNeedUpdate: 'Request failed, please try again<br>or update sessionID', noItemDetails: 'No Item Details', notLoggedInTitle: 'Not Logged-In', notLoggedInMsg: 'Please login to Steam so sessionID can be saved', missingTitle: 'Missing SessionID', missingMsg: 'Do you want to update your Steam sessionID?', emptyInput: 'Could not find Steam code', settingsTitle: 'Settings', settingsAutoUpdateSessionID: 'Auto Update SessionID', settingsSessionID: 'Your sessionID', settingsLanguage: 'Language', settingsPreselectIncludeTitle: 'Pre-select Include Title', settingsTitleComesLast: 'Title Comes Last', settingsPreselectJoinKeys: 'Pre-select Join Keys', settingsJoinKeysASFStyle: 'Join Keys w/ ASF Style', DIGEasyBuyPurchase: 'Purchase', DIGEasyBuySelectAll: 'Select All', DIGEasyBuySelectCancel: 'Cancel', DIGButtonPurchasing: 'Purchassing', buttonReveal: 'Reveal', buttonRetrieve: 'Retrieve', buttonActivate: 'Activate', buttonCopy: 'Copy', buttonReset: 'Reset', checkboxIncludeGameTitle: 'Include Game Title', checkboxJoinKeys: 'Join Keys', checkboxSkipUsed: 'Skip Used', BSselectConnector: 'to' } }; let text = has.call(i18n, config.get('language')) ? i18n[config.get('language')] : i18n.english; // inject settings panel css GM_addStyle(` .SBSE_settings .name { text-align: right; vertical-align: top; } .SBSE_settings .value { text-align: left; } .SBSE_settings .value > * { height: 30px; margin: 0 20px 10px; } .SBSE_settings .switch { position: relative; display: inline-block; width: 60px; } .SBSE_settings .switch input { display: none; } .SBSE_settings .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: 0.4s; } .SBSE_settings .slider:before { position: absolute; content: ""; height: 26px; width: 26px; left: 2px; bottom: 2px; background-color: white; transition: 0.4s; } .SBSE_settings input:checked + .slider { background-color: #2196F3; } .SBSE_settings input:focus + .slider { box-shadow: 0 0 1px #2196F3; } .SBSE_settings input:checked + .slider:before { transform: translateX(30px); } .SBSE_settings > span { display: inline-block; cursor: pointer; color: white; } `); // functions const settings = { construct() { const panelHTML = ` <div class="SBSE_settings"> <table> <tr> <td class="name">${text.settingsAutoUpdateSessionID}</td> <td class="value"> <label class="switch"> <input type="checkbox" class="autoUpdateSessionID"> <span class="slider"></span> </label> </td> </tr> <tr> <td class="name">${text.settingsSessionID}</td> <td class="value"> <input type="text" class="sessionID" value="${config.get('sessionID')}"> </td> </tr> <tr> <td class="name">${text.settingsLanguage}</td> <td class="value"> <select class="language"></select> </td> </tr> <tr> <td class="name">${text.settingsPreselectIncludeTitle}</td> <td class="value"> <label class="switch"> <input type="checkbox" class="preselectIncludeTitle"> <span class="slider"></span> </label> </td> </tr> <tr> <td class="name">${text.settingsTitleComesLast}</td> <td class="value"> <label class="switch"> <input type="checkbox" class="titleComesLast"> <span class="slider"></span> </label> </td> </tr> <tr> <td class="name">${text.settingsPreselectJoinKeys}</td> <td class="value"> <label class="switch"> <input type="checkbox" class="preselectJoinKeys"> <span class="slider"></span> </label> </td> </tr> <tr> <td class="name">${text.settingsJoinKeysASFStyle}</td> <td class="value"> <label class="switch"> <input type="checkbox" class="joinKeysASFStyle"> <span class="slider"></span> </label> </td> </tr> </table> </div> `; return panelHTML; }, display() { swal({ title: text.settingsTitle, html: this.construct() }); // apply settings const $panel = $(swal.getContent()); const $sessionID = $panel.find('.sessionID'); const $language = $panel.find('.language'); // toggles $panel.find('input[type=checkbox]').each((index, input) => { const $input = $(input); $input.prop('checked', config.get(input.className)); $input.change(e => { swal.showLoading(); const setting = e.delegateTarget.className; const state = e.delegateTarget.checked; config.set(setting, state); if (setting === 'autoUpdateSessionID') $sessionID.attr('disabled', state); setTimeout(swal.hideLoading, 500); }); }); // sessionID input $sessionID.prop('disabled', config.get('autoUpdateSessionID')); $sessionID.change(() => { swal.showLoading(); config.set('sessionID', $sessionID.val().trim()); setTimeout(swal.hideLoading, 500); }); // language Object.keys(i18n).forEach(language => { $language.append(new Option(i18n[language].name, language)); }); $panel.find(`option[value=${config.get('language')}]`).prop('selected', true); $language.change(() => { swal.showLoading(); const newLanguage = $language.val(); config.set('language', newLanguage); text = has.call(i18n, newLanguage) ? i18n[newLanguage] : i18n.english; setTimeout(swal.hideLoading, 500); }); } }; const activateHandler = { keys: [], results: {}, updateResults(txt = null) { const $textarea = $('.SBSE_container > textarea'); if (txt) { $textarea.val(txt); } else { const results = this.results; const parsed = []; Object.values(results).forEach(result => { parsed.push(result.join(' | ')); }); $textarea.val(parsed.join(eol)); } }, getResultStatus(result) { let status = text.failStatus; let statusMsg = text.failDetailUnexpected; const errors = { 14: text.failDetailInvalidKey, 15: text.failDetailUsedKey, 53: text.failDetailRateLimited, 13: text.failDetailCountryRestricted, 9: text.failDetailAlreadyOwned, 24: text.failDetailMissingBaseGame, 36: text.failDetailPS3Required, 50: text.failDetailGiftWallet }; if (result.success === 1) { status = text.successStatus; statusMsg = text.successDetail; } else if (result.success === 2) { if (has.call(errors, result.purchase_result_details)) { statusMsg = errors[result.purchase_result_details]; } } return `${status}/${statusMsg}`; }, getResultItems(info) { const descriptions = []; if (info && info.line_items) { info.line_items.forEach(item => { const description = []; if (item.packageid > 0) description.push(`sub: ${item.packageid}`); if (item.appid > 0) description.push(`app: ${item.appid}`); description.push(item.line_item_description); descriptions.push(description.join(' ')); }); } return descriptions.join(', '); }, activateKey(callback) { const self = this; const key = self.keys.shift(); if (key) { if (activated.check(key)) { self.results[key].push(`${text.skippedStatus}/${text.activatedDetail}`, text.noItemDetails); self.updateResults(); // next key self.activateKey(callback); } else if (keyDetails.isOwned(key)) { const detail = keyDetails.get(key); const itemDetail = `${detail.app || detail.sub}, ${detail.title}`; self.results[key].push(`${text.skippedStatus}/${text.failDetailAlreadyOwned}`, itemDetail); self.updateResults(); // next key self.activateKey(callback); } else { GM_xmlhttpRequest({ method: 'POST', url: 'https://store.steampowered.com/account/ajaxregisterkey/', headers: { Accept: 'text/javascript, text/html, application/xml, text/xml, */*', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'zh-TW,zh;q=0.8,en-US;q=0.6,en;q=0.4,ja;q=0.2,zh-CN;q=0.2', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', Origin: 'https://store.steampowered.com', Referer: 'https://store.steampowered.com/account/registerkey', 'x-prototype-version': '1.7', 'x-requested-with': 'XMLHttpRequest' }, data: `product_key=${key}&sessionid=${config.get('sessionID')}`, onload: res => { if (res.status === 200) { let status = ''; let items = ''; try { const result = JSON.parse(res.response); status = self.getResultStatus(result); items = self.getResultItems(result.purchase_receipt_info); // update activated const failCode = result.purchase_result_details; if (result.success === 1 || [14, 15, 9].includes(failCode)) { activated.push(key); // dispatch activated event $(document).trigger('activated', [key, result]); } } catch (e) { status = `${text.failStatus}/${text.failDetailParsingFailed}`; items = text.noItemDetails; } self.results[key].push(status, items); self.updateResults(); // next key setTimeout(self.activateKey.bind(self, callback), 2000); } else { const errerMsg = `<pre>status: ${res.status + eol + res.response}</pre>`; swal({ title: text.failTitle, html: text.failDetailRequestFailedNeedUpdate + errerMsg, type: 'error', showCancelButton: true }).then(() => { window.open('https://store.steampowered.com/'); }); callback(); } } }); } } else callback(); }, activateKeys(input, callback) { const self = this; const keys = unique(input.match(regKey)); if (keys.length > 0) { keys.forEach(key => { self.results[key] = [key]; }); self.keys = Object.keys(self.results); self.updateResults(); self.activateKey(callback); } else { self.updateResults(text.emptyInput); callback(); } } }; const bundleSitesBoxHandler = { reveal(handler, $games) { const $reveal = $('.SBSE_BtnReveal'); $reveal.addClass('working'); handler($games, () => { $reveal.removeClass('working'); $('.SBSE_BtnRetrieve').click(); }); }, retrieve(data) { if (data.length > 0) { const includeTitle = !!$('.SBSE_ChkTitle:checked').length; const joinKeys = !!$('.SBSE_ChkJoin:checked').length; const separator = joinKeys ? ',' : eol; const prefix = joinKeys && config.get('joinKeysASFStyle') ? '!redeem ' : ''; const keys = []; data.forEach(d => { if (typeof d === 'string') { keys.push(d); } else { const temp = [d.key]; if (includeTitle) temp.unshift(d.title); if (config.get('titleComesLast')) temp.reverse(); keys.push(temp.join(', ')); } }); $('.SBSE_container > textarea').val(prefix + keys.join(separator)); } }, activate(e) { const $self = $(e.delegateTarget); const $textarea = $('.SBSE_container > textarea'); let input = $textarea.val().trim(); if (input.length === 0) { $('.SBSE_BtnRetrieve').click(); input = $textarea.val(); } $self.prop('disabled', true).addClass('working'); $textarea.attr('disabled', ''); activateHandler.activateKeys(input, () => { $self.prop('disabled', false).removeClass('working'); $textarea.removeAttr('disabled'); }); }, copy() { $('.SBSE_container > textarea').select(); document.execCommand('copy'); }, reset() { $('.SBSE_container > textarea').val(''); }, settings() { settings.display(); } }; const bundleSitesBox = () => { GM_addStyle(` .SBSE_container { width: 100%; height: 200px; display: flex; flex-direction: column; box-sizing: border-box; } .SBSE_container > textarea { width: 100%; height: 150px; border: none; box-sizing: border-box; resize: none; outline: none; } .SBSE_container > div { width: 100%; padding-top: 5px; box-sizing: border-box; } .SBSE_container button { width: 120px; position: relative; margin-right: 10px; line-height: 28px; box-sizing: border-box; outline: none; cursor: pointer; } .SBSE_container label { margin-right: 10px;} #SBSE_BtnSettings { width: 20px; height: 20px; float: right; margin-right: 0; margin-left: 10px; background-color: transparent; background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGhlaWdodD0iMzJweCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzIgMzI7IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAzMiAzMiIgd2lkdGg9IjMycHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxnIGlkPSJMYXllcl8xIi8+PGcgaWQ9ImNvZyI+PHBhdGggZD0iTTMyLDE3Ljk2OXYtNGwtNC43ODEtMS45OTJjLTAuMTMzLTAuMzc1LTAuMjczLTAuNzM4LTAuNDQ1LTEuMDk0bDEuOTMtNC44MDVMMjUuODc1LDMuMjUgICBsLTQuNzYyLDEuOTYxYy0wLjM2My0wLjE3Ni0wLjczNC0wLjMyNC0xLjExNy0wLjQ2MUwxNy45NjksMGgtNGwtMS45NzcsNC43MzRjLTAuMzk4LDAuMTQxLTAuNzgxLDAuMjg5LTEuMTYsMC40NjlsLTQuNzU0LTEuOTEgICBMMy4yNSw2LjEyMWwxLjkzOCw0LjcxMUM1LDExLjIxOSw0Ljg0OCwxMS42MTMsNC43MDMsMTIuMDJMMCwxNC4wMzF2NGw0LjcwNywxLjk2MWMwLjE0NSwwLjQwNiwwLjMwMSwwLjgwMSwwLjQ4OCwxLjE4OCAgIGwtMS45MDIsNC43NDJsMi44MjgsMi44MjhsNC43MjMtMS45NDVjMC4zNzksMC4xOCwwLjc2NiwwLjMyNCwxLjE2NCwwLjQ2MUwxNC4wMzEsMzJoNGwxLjk4LTQuNzU4ICAgYzAuMzc5LTAuMTQxLDAuNzU0LTAuMjg5LDEuMTEzLTAuNDYxbDQuNzk3LDEuOTIybDIuODI4LTIuODI4bC0xLjk2OS00Ljc3M2MwLjE2OC0wLjM1OSwwLjMwNS0wLjcyMywwLjQzOC0xLjA5NEwzMiwxNy45Njl6ICAgIE0xNS45NjksMjJjLTMuMzEyLDAtNi0yLjY4OC02LTZzMi42ODgtNiw2LTZzNiwyLjY4OCw2LDZTMTkuMjgxLDIyLDE1Ljk2OSwyMnoiIHN0eWxlPSJmaWxsOiM0RTRFNTA7Ii8+PC9nPjwvc3ZnPg==); background-size: contain; background-repeat: no-repeat; background-origin: border-box; border: none; vertical-align: top; } `); // spinner button affect GM_addStyle(` .SBSE_container button:before { content: ''; position: absolute; right: 10px; margin-top: 5px; width: 20px; height: 20px; border: 3px solid; border-left-color: transparent; border-radius: 50%; box-sizing: border-box; opacity: 0; transition: opacity 0.5s; animation-duration: 1s; animation-iteration-count: infinite; animation-name: rotate; animation-timing-function: linear; } .SBSE_container button.working { padding-right: 20px; width: 120px; transition: padding-right 0.5s; transition: width 0.5s; } .SBSE_container button.working:before { transition-delay: 0.5s; transition-duration: 1s; opacity: 1; } @keyframes rotate { 0% { transform: rotate(0deg);} 100% { transform: rotate(360deg);} } `); const $container = $(` <div class="SBSE_container"> <textarea></textarea> <div> <button class="SBSE_BtnReveal">${text.buttonReveal}</button> <button class="SBSE_BtnRetrieve">${text.buttonRetrieve}</button> <button class="SBSE_BtnActivate">${text.buttonActivate}</button> <button class="SBSE_BtnCopy">${text.buttonCopy}</button> <button class="SBSE_BtnReset">${text.buttonReset}</button> <label><input type="checkbox" class="SBSE_ChkTitle">${text.checkboxIncludeGameTitle}</label> <label><input type="checkbox" class="SBSE_ChkJoin">${text.checkboxJoinKeys}</label> <button id="SBSE_BtnSettings"> </button> </div> </div> `); // bind event $container.find('.SBSE_BtnCopy').click(bundleSitesBoxHandler.copy); $container.find('.SBSE_BtnReset').click(bundleSitesBoxHandler.reset); $container.find('.SBSE_BtnActivate').click(bundleSitesBoxHandler.activate); $container.find('#SBSE_BtnSettings').click(bundleSitesBoxHandler.settings); // apply settings if (config.get('preselectIncludeTitle')) $container.find('.SBSE_ChkTitle').prop('checked', true); if (config.get('preselectJoinKeys')) $container.find('.SBSE_ChkJoin').prop('checked', true); return $container; }; const siteCache = { bundlestars: { doms: [document] } }; const siteHandlers = { indiegala() { // insert textarea $('#library-contain').eq(0).before(bundleSitesBox()); // inject css GM_addStyle(` .SBSE_container { margin-top: 10px;} .SBSE_container > textarea { border: 1px solid #CC001D;} .SBSE_container button { background-color: #CC001D; color: white;} `); // dom source const source = location.pathname === '/profile' ? 'div[id*="_sale_"].collapse.in' : document; // button click $('.SBSE_BtnReveal').click(() => { const handler = ($games, callback) => { const game = $games.shift(); if (game) { const $game = $(game); const code = $game.attr('id').split('_').pop(); const appID = $game.attr('onclick').match(/steampowered\.com\/app\/(\d+)/)[1]; $.ajax({ method: 'GET', url: '/myserials/syncget', dataType: 'json', data: { code, cache: false, productId: appID }, beforeSend() { $(`#permbutton_${code}, #fetchlink_${code}, #info_key_${code}`).hide(); $(`#fetching_${code}`).fadeIn(); $(`#ajax_loader_${code}`).show(); $(`#container_activate_${code}`).html(''); }, success(data) { $(`#ajax_loader_${code}, #fetching_${code}, #info_key_${code}`).hide(); $(`#serial_${code}`).fadeIn(); $(`#serial_n_${code}`).val(data.serial_number); $game.parent().prev().find('.btn-convert-to-trade').remove(); handler($games, callback); }, error() { swal(text.failTitle, text.failDetailUnexpected, 'error'); } }); } else callback(); }; bundleSitesBoxHandler.reveal(handler, $(source).find('a[id^=fetchlink_]')); }); $('.SBSE_BtnRetrieve').click(() => { const keys = []; $(source).find('.game-key-string').each((index, element) => { const $ele = $(element); const key = $ele.find('.keys').val(); if (key) { const $a = $ele.find('.title_game > a'); const title = $a.text().trim(); // append key details keyDetails.set(key, { url: $a.attr('href'), title: $a.text() }); keys.push({ key, title }); } }); bundleSitesBoxHandler.retrieve(keys); }); }, bundlestars(firstCalled) { const cache = siteCache.bundlestars; const $anchor = $('h2:contains(Order Keys)'); const BSselect = selector => { let $results = $(); let from = parseInt($('.SBSE_container .selectFrom').val(), 10); let to = parseInt($('.SBSE_container .selectTo').val(), 10); if ($.isNumeric(from) && $.isNumeric(to)) { if (from === 0 && to > 0) from = 1; if (from > 0 && to === 0) to = cache.doms.length - 1; for (let i = Math.min(from, to); i <= Math.max(from, to); i += 1) { $results = $results.add($(cache.doms[i]).find(selector)); } } return $results; }; if ($('.SBSE_container').length === 0 && $anchor.length > 0) { // insert textarea $anchor.eq(0).before(bundleSitesBox()); // inject css GM_addStyle(` .SBSE_container { border: 1px solid #424242; color: #999999;} .SBSE_container > textarea { background-color: #303030; color: #DDD;} .SBSE_container button { width: 80px;} .SBSE_container button, .SBSE_container select { border: 1px solid transparent; background-color: #262626; color: #DEDEDE;} .SBSE_container button:hover, .SBSE_container select:hover { color: #A8A8A8;} .SBSE_container label { color: #DEDEDE;} .SBSE_container select { max-width:120px; height: 30px;} .SBSE_container select, .SBSE_container span { margin-right: 0; margin-left: 10px; float: right;} .SBSE_container span { margin-top: 5px;} `); // insert bundlestars select $('.SBSE_container > div').append(` <select class="selectTo"></select> <span>${text.BSselectConnector}</span> <select class="selectFrom"></select> `); // button click $('.SBSE_BtnReveal').click(() => { const handler = ($games, callback) => { const game = $games.shift(); if (game) { if (!game.closest('.ng-hide')) { game.click(); setTimeout(handler.bind(null, $games, callback), 300); } else handler(); } else callback(); }; bundleSitesBoxHandler.reveal(handler, BSselect('.key-container a[ng-click^="redeemSerial"]')); }); $('.SBSE_BtnRetrieve').click(() => { const keys = []; BSselect('.key-container input').each((index, input) => { const $input = $(input); keys.push({ key: $input.val(), title: $input.closest('.key-container').prev().text().trim() }); }); bundleSitesBoxHandler.retrieve(keys); }); } // setup select const $selects = $('.SBSE_container select'); $selects.empty(); $selects.append(new Option('All', 0)); cache.doms = [document]; $('hr ~ div > div:not(.ng-hide)').each((index, block) => { const $block = $(block); const $bundle = $block.find('h3'); const $tiers = $block.find('h4'); if ($tiers.length > 1) { // bundles with multiple tiers $tiers.each((i, tier) => { const $tier = $(tier); $selects.append(new Option(`${$bundle.text()} ${$tier.text()}`, cache.doms.push($tier.parent()) - 1)); }); } else if ($bundle.length > 0) { // bundles with single tier $selects.append(new Option($bundle.text(), cache.doms.push($bundle.next()) - 1)); } else { // individual games $selects.append(new Option($block.find('.title').text(), cache.doms.push($block) - 1)); } }); if (firstCalled) { new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.removedNodes.forEach(removedNode => { if (removedNode.id === 'loading-bar-spinner') siteHandlers.bundlestars(); }); }); }).observe(document.body, { childList: true }); } }, humblebundle() { // insert textarea $('#steam-tab').closest('.whitebox').eq(0).before(bundleSitesBox()); // inject css GM_addStyle(` .SBSE_container > textarea { border: 1px solid #AAAAAA; color: #4a4c45; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); border-radius: 5px; } .SBSE_container button { width: 80px; border: 1px solid #808080; border-radius: 3px; background-color: #c5c5c5; background: linear-gradient(to top, #cacaca, #e7e7e7); } `); // button click $('.SBSE_BtnReveal').click(() => { const handler = ($games, callback) => { const game = $games.shift(); if (game) { game.click(); setTimeout(handler.bind(null, $games, callback), 300); } else callback(); }; bundleSitesBoxHandler.reveal(handler, $('.sr-unredeemed-steam-button')); }); $('.SBSE_BtnRetrieve').click(() => { const keys = []; $('.sr-redeemed-bubble').each((index, element) => { const $game = $(element); const $heading = $game.closest('[class^=sr-key]').prev().children().eq(0); keys.push({ key: $game.text().trim(), title: $heading.text().trim() }); }); bundleSitesBoxHandler.retrieve(keys); }); // setup key details let data = $('.steam-keyredeemer-container').next().text().split(eol)[3].trim().slice(11, -1); try { data = JSON.parse(data); data.keys.forEach(key => { keyDetails.set(key.redeemedKeyVal, { app: key.steamAppId, title: key.humanName }); }); } catch (e) { // no key details } }, dailyindiegame() { const pathname = location.pathname; if (pathname.includes('/account_page')) { // insert textarea $('#TableKeys').eq(0).before(bundleSitesBox()); // inject css GM_addStyle(` .SBSE_container { padding: 5px; border: 1px solid #424242; } .SBSE_container > textarea { border: 1px solid #000; } .SBSE_container button { border: none; background-color: #FD5E0F; color: rgb(49, 49, 49); font-family: Ropa Sans; font-size: 15px; font-weight: 600; } `); // button click $('.SBSE_BtnReveal').click(() => { const handler = () => { const $form = $('#form3'); $('.quickaction').val(1); $.ajax({ method: 'POST', url: $form.attr('action'), data: $form.serializeArray(), success() { location.reload(); } }); }; bundleSitesBoxHandler.reveal(handler); }); $('.SBSE_BtnRetrieve').click(() => { const keys = []; $('#TableKeys tr').each((index, tr) => { const $tds = $(tr).children(); if (tr.textContent.includes('-')) { keys.push({ key: $tds.eq(4).text().trim(), title: $tds.eq(2).text().trim() }); } }); bundleSitesBoxHandler.retrieve(keys); }); } else if (pathname === '/account_digstore.html' || pathname === '/account_trades.html') { // DIG EasyBuy GM_addStyle(` .DIGEasyBuy button { padding: 4px 8px; outline: none; } .DIGEasyBuy_checked { background-color: #222; } `); const $target = $('#form3').closest('tr').children().eq(0); const $DIGEasyBuy = $(` <div class="DIGEasyBuy"> <button class="DIGButtonPurchase DIG3_Orange_15_Form">${text.DIGEasyBuyPurchase}</button> <button class="DIGButtonSelectAll DIG3_Orange_15_Form">${text.DIGEasyBuySelectAll}</button> </div> `); $target.empty().append($DIGEasyBuy); // bind button event $('.DIGButtonPurchase').click(e => { let bought = 0; let balance = parseInt($('a[href^="account_transac"]').closest('div').text().slice(12), 10) || 0; const $self = $(e.delegateTarget); const $checked = $('.DIGEasyBuy_checked'); const handler = callback => { var item, $item, id, price, url, requestInit, res; return regeneratorRuntime.async(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: item = $checked.shift(); if (!item) { _context.next = 16; break; } $item = $(item); id = $item.data('id'); price = parseInt($item.data('price'), 10); if (!(id && price > 0 && balance - price > 0)) { _context.next = 14; break; } url = `${location.origin}/account_buy.html`; requestInit = { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `quantity=1&xgameid=${id}&xgameprice1=${price}&send=Purchase`, mode: 'same-origin', credentials: 'same-origin', cache: 'no-store', referrer: `${location.origin}/account_buy_${id}.html` }; if (pathname === '/account_trades.html') { url = `${location.origin}/account_buytrade_${id}.html`; requestInit.body = `gameid=${id}&send=Purchase`; requestInit.referrer = url; } _context.next = 11; return regeneratorRuntime.awrap(fetch(url, requestInit)); case 11: res = _context.sent; if (res.ok) { $item.click(); bought += 1; balance -= price; } setTimeout(handler.bind(null, callback), 300); case 14: _context.next = 17; break; case 16: callback(); case 17: case 'end': return _context.stop(); } }, null, this); }; $self.prop('disabled', true).text(text.DIGButtonPurchasing); handler(() => { if (bought) window.location = `${location.origin}/account_page.html`;else $self.prop('disabled', false).text(text.DIGButtonPurchase); }); }); $('.DIGButtonSelectAll').click(e => { const $self = $(e.delegateTarget); const state = !$self.data('state'); $('.DIGEasyBuy_row').toggleClass('DIGEasyBuy_checked', state); $self.data('state', state); $self.text(state ? text.DIGEasyBuySelectCancel : text.DIGEasyBuySelectAll); }); // setup row data & event $('a[href^="account_buy"]').each((index, element) => { const $game = $(element); const $row = $game.closest('tr'); $row.data({ id: $game.attr('href').replace(/\D/g, ''), price: parseInt($game.closest('td').prev().text(), 10) || 0 }); $row.click(() => { $row.toggleClass('DIGEasyBuy_checked'); }); $row.addClass('DIGEasyBuy_row'); }); } }, ccyycn() { // insert textarea $('.featurette-divider').eq(0).after(bundleSitesBox()); // inject css GM_addStyle(` .SBSE_container { width: 80%; margin: 0 auto; color: #000; font-size: 16px; } .SBSE_container > textarea { background-color: #EEE; box-shadow: 0 0 1px 1px rgba(204,204,204,0.5); border-radius: 5px; } .SBSE_container > div { text-align: left;} .SBSE_container button { width: 80px; border: 1px solid transparent; border-radius: 5px; background-color: #EEE; box-shadow: 0 0 1px 1px rgba(204,204,204,0.5); } .SBSE_container label { color: #EEE;} `); // button click $('.SBSE_BtnReveal').click(() => { const handler = ($games, callback) => { const game = $games.shift(); if (game) { game.click(); setTimeout(handler.bind(null, $games, callback), 300); } else callback(); }; bundleSitesBoxHandler.reveal(handler, $('.deliver-btn')); }); $('.SBSE_BtnRetrieve').click(() => { const keys = []; $('.deliver-gkey').each((index, element) => { const $game = $(element); keys.push({ key: $game.text().trim(), title: $game.parent().prev().text().trim() }); }); bundleSitesBoxHandler.retrieve(keys); }); }, groupees() { // insert textarea $('.container > div').eq(1).before(bundleSitesBox()); // inject css GM_addStyle(` .SBSE_container > textarea { background-color: #EEE; border-radius: 3px;} .SBSE_container button { font-weight: bold; background-color: #FFF; border: 1px solid #CCC; color: #333; } .SBSE_container button:hover { background-color: #e6e6e6; border-color: #adadad;} `); // append checkbox for used-key $('#SBSE_BtnSettings').before($(`<label><input type="checkbox" class="SBSE_ChkSkipUsed" checked>${text.checkboxSkipUsed}</label>`)); // button click $('.SBSE_BtnReveal').click(() => { const handler = ($games, callback) => { const game = $games.shift(); if (game) { game.click(); setTimeout(handler.bind(null, $games, callback), 300); } else callback(); }; const $reveals = $('.reveal-product'); const timer = $reveals.length > 0 ? 3000 : 0; $reveals.click(); setTimeout(() => { bundleSitesBoxHandler.reveal(handler, $('.expanded .reveal')); }, timer); }); $('.SBSE_BtnRetrieve').click(() => { const skipUsed = $('.SBSE_ChkSkipUsed:checked').length; const keys = []; $('.expanded .code').each((index, element) => { const $game = $(element); const used = $game.prev('.key-meta').find('.usage').prop('checked'); if (!used || used && !skipUsed) { keys.push({ key: $game.val(), title: $game.closest('.details').find('h3').text().trim() }); } }); bundleSitesBoxHandler.retrieve(keys); }); // bind custom event $(document).on('activated', (e, key, result) => { if (result.success === 1 || result.purchase_result_details === 9) { const $game = $(`[value=${key}]`).eq(0); $game.prev('.key-meta').find('.usage').click(); } }); }, agiso() { const keys = unique($('body').text().match(regKey)); if (keys.length > 0) { // insert textarea $('#tabs').eq(0).prepend(bundleSitesBox()); // inject css GM_addStyle(` .SBSE_container > textarea { border: 1px solid #AAAAAA;} .SBSE_container button { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; color: #555555; } .SBSE_container button:hover { border-color: #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; color: #212121; } #SBSE_BtnSettings { width: 32px !important; height: 32px !important;} `); // remove event from agiso $('.SBSE_container button').click(e => { e.preventDefault(); }); // hide reveal $('.SBSE_BtnReveal').hide(); // button click $('.SBSE_BtnRetrieve').click(() => { bundleSitesBoxHandler.retrieve(keys); }); } } }; const init = () => { if (location.hostname === 'store.steampowered.com') { // save sessionID if (g_AccountID > 0) { if (!config.get('sessionID') || config.get('autoUpdateSessionID')) config.set('sessionID', g_sessionID); if (!config.get('language')) config.set('language', g_oSuggestParams.l); } /* else { swal(text.notLoggedInTitle, text.notLoggedInMsg, 'error'); }*/ } else { const site = location.hostname.replace(/(www|alds|bundle)\./, '').split('.').shift(); // check sessionID if (!config.get('sessionID')) { swal({ title: text.missingTitle, text: text.missingMsg, type: 'question', showCancelButton: true }).then(() => { window.open('https://store.steampowered.com/'); }); } if (has.call(siteHandlers, site)) { siteHandlers[site](true); // update owned every 10 min const updateTimer = 10 * 60 * 1000; if (!owned.lastUpdate || owned.lastUpdate < Date.now() - updateTimer) { GM_xmlhttpRequest({ method: 'GET', url: `http://store.steampowered.com/dynamicstore/userdata/t=${Math.random()}`, onload: res => { if (res.status === 200) { const data = JSON.parse(res.response); owned.app = data.rgOwnedApps; owned.sub = data.rgOwnedPackages; owned.lastUpdate = Date.now(); localStorage.setItem('SBSE_owned', JSON.stringify(owned)); } } }); } } } }; $(init);