// ==UserScript== // @name New Userscript // @namespace http://tampermonkey.net/ // @version 0.03 // @description try to take over the world! // @author You // @match https://www.humblebundle.com/subscription/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @downloadURL none // ==/UserScript== const countryMap = { AD: '安道尔', AE: '阿拉伯联合酋长国', AF: '阿富汗', AG: '安提瓜和巴布达', AI: '安圭拉', AL: '阿尔巴尼亚', AM: '亚美尼亚', AO: '安哥拉', AQ: '南极洲', AR: '阿根廷', AS: '美属萨摩亚', AT: '奥地利', AU: '澳大利亚', AW: '阿鲁巴', AX: '奥兰群岛', AZ: '阿塞拜疆', BA: '波斯尼亚和黑塞哥维那', BB: '巴巴多斯', BD: '孟加拉', BE: '比利时', BF: '布基纳法索', BG: '保加利亚', BH: '巴林', BI: '布隆迪', BJ: '贝宁', BL: '圣巴托洛缪岛', BM: '百慕大', BN: '文莱', BO: '玻利维亚', BQ: '博奈尔', BR: '巴西', BS: '巴哈马', BT: '不丹', BU: '缅甸', BV: '布韦岛', BW: '博兹瓦纳', BY: '白俄罗斯', BZ: '伯利兹', CA: '加拿大', CC: '科科斯(基林)群岛', CD: '刚果(金)', CF: '中非共和国', CG: '刚果(布)', CH: '瑞士', CI: '科特迪瓦', CK: '库克群岛', CL: '智利', CM: '喀麦隆', CN: '中国', CO: '哥伦比亚', CR: '哥斯达黎加', CS: '塞尔维亚和黑山', CU: '古巴', CV: '佛得角', CW: '库拉索', CX: '圣诞岛', CY: '塞浦路斯', CZ: '捷克', DE: '德国', DJ: '吉布提', DK: '丹麦', DM: '多米尼克', DO: '多米尼加', DZ: '阿尔及利亚', EC: '厄瓜多尔', EE: '爱沙尼亚', EG: '埃及', EH: '西撒哈拉', ER: '厄立特里亚', ES: '西班牙', ET: '埃塞俄比亚', FI: '芬兰', FJ: '斐济', FK: '福克兰群岛', FM: '密克罗尼西亚', FO: '法罗群岛', FR: '法国', GA: '加蓬', GB: '英国', GD: '格林纳达', GE: '格鲁吉亚', GF: '法属圭亚那', GG: '根西', GH: '加纳', GI: '直布罗陀', GL: '格陵兰', GM: '冈比亚', GN: '几内亚', GP: '瓜德鲁普', GQ: '赤道几内亚', GR: '希腊', GS: '南乔治亚岛和南桑威奇群岛', GT: '危地马拉', GU: '关岛', GW: '几内亚比绍', GY: '圭亚那', HK: '香港', HM: '赫德岛和麦克唐纳群岛', HN: '洪都拉斯', HR: '克罗地亚', HT: '海地', HU: '匈牙利', ID: '印尼', IE: '爱尔兰', IL: '以色列', IM: '马恩岛', IN: '印度', IO: '英属印度洋领地', IQ: '伊拉克', IR: '伊朗', IS: '冰岛', IT: '意大利', JE: '泽西岛', JM: '牙买加', JO: '约旦', JP: '日本', KE: '肯尼亚', KG: '吉尔吉斯', KH: '柬埔寨', KI: '基里巴斯', KM: '科摩罗', KN: '圣基茨和尼维斯', KP: '朝鲜', KR: '韩国', KW: '科威特', KY: '开曼群岛', KZ: '哈萨克斯坦', LA: '老挝', LB: '黎巴嫩', LC: '圣卢西亚', LI: '列支敦士登', LK: '斯里兰卡', LR: '利比里亚', LS: '莱索托', LT: '立陶宛', LU: '卢森堡', LV: '拉脱维亚', LY: '利比亚', MA: '摩洛哥', MC: '摩纳哥', MD: '摩尔多瓦', ME: '黑山', MF: '法属圣马丁', MG: '马达加斯加', MH: '马绍尔群岛', MK: '马其顿', ML: '马里', MM: '缅甸', MN: '蒙古', MO: '澳门', MP: '北马里亚纳群岛', MQ: '马提尼克', MR: '毛里塔尼亚', MS: '蒙塞拉特', MT: '马耳他', MU: '毛里求斯', MV: '马尔代夫', MW: '马拉维', MX: '墨西哥', MY: '马来西亚', MZ: '莫桑比克', NA: '纳米比亚', NC: '新喀里多尼亚', NE: '尼日尔', NF: '诺福克岛', NG: '尼日利', NI: '尼加拉瓜', NL: '荷兰', NO: '挪威', NP: '尼泊尔', NR: '瑙鲁', NU: '纽埃', NZ: '新西兰', OM: '阿曼', PA: '巴拿马', PE: '秘鲁', PF: '法属波利尼西亚a', PG: '巴布亚新几内亚', PH: '菲律宾', PK: '巴基斯坦', PL: '波兰', PM: '圣皮埃尔和密克隆', PN: '皮特凯恩群岛', PR: '波多黎各', PS: '巴勒斯坦', PT: '葡萄牙', PW: '帕劳', PY: '巴拉圭', QA: '卡塔尔', RE: '留尼旺島', RO: '罗马尼亚', RS: '塞尔维亚', RU: '俄罗斯', RW: '卢旺达', SA: '沙特阿拉伯', SB: '所罗门群岛', SC: '塞舌尔', SD: '苏丹', SE: '瑞典', SG: '新加坡', SH: '圣赫勒拿、阿森松与特斯坦达库尼亚', SI: '斯洛文尼', SJ: '斯瓦尔巴群岛和扬马延岛', SK: '斯洛伐克', SL: '塞拉利昂', SM: '圣马力诺', SN: '塞内加尔', SO: '索马里', SR: '苏里南', SS: '南苏丹', ST: '圣多美和普林西比', SV: '萨尔瓦多', SX: '荷属圣马丁', SY: '叙利亚', SZ: '斯威士兰', TC: '特克斯和凯科斯群岛', TD: '乍得', TF: '法属南部领土', TG: '多哥', TH: '泰国', TJ: '塔吉克斯坦', TK: '托克劳', TL: '东帝汶', TM: '土库曼斯坦', TN: '突尼斯', TO: '汤加', TR: '土耳其', TT: '特立尼达和多巴哥', TV: '图瓦卢', TW: '台湾', TZ: '坦桑尼亚', UA: '乌克兰', UG: '乌干达', UM: '美国本土外小岛屿', US: '美国', UY: '乌拉圭', UZ: '乌兹别克斯坦', VA: '圣座', VC: '圣文森特和格林纳丁斯', VE: '委内瑞拉', VG: '英属维尔京群岛', VI: '美属维尔京群岛', VN: '越南', VU: '瓦努阿图', WF: '瓦利斯和富图纳群岛', WS: '萨摩亚', XK: '科索沃', YE: '也门', YT: '马约特', ZA: '南非', ZM: '赞比亚', ZW: '津巴布韦', }; (function() { function http (setData) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: setData.method | 'GET', url: setData.url, onerror: reject, ontimeout: reject, onload: (res) => { resolve(res.responseText) } }) }) } http({url: window.location}).then(res => { getInitData(new DOMParser().parseFromString(res, 'text/html')) }).catch(()=>{}) function getInitData (el) { const script = el.querySelector('#webpack-monthly-product-data') || el.querySelector('#webpack-subscriber-hub-data') if(!script) return const {contentChoiceData, gamekey, contentChoicesMade, downloadPageUrl} = JSON.parse(script.innerText.trim()).contentChoiceOptions const {content_choices, display_order} = contentChoiceData.initial const selecedGame = contentChoicesMade.initial.choices_made const allGame = display_order.map(item => { let i = content_choices[item] return { machine_name: i.tpkds[0].machine_name, title: i.title, exclusive: i.tpkds[0].exclusive_countries, disallowed: i.tpkds[0].disallowed_countries, appid: i.tpkds[0].steam_app_id, name: item, key: i.tpkds[0].redeemed_key_val } }) function getLock (game) { let lockDetil function getZhName (arr) { return arr.map(item => { if (/(\u53f0\u6e7e|\u4e2d\u56fd|\u9999\u6e2f)/.test(countryMap[item])) return '' + countryMap[item] + '' return countryMap[item] }).join('、') } if(game.exclusive.length) { lockDetil = `只能在以下激活: + ${getZhName(game.exclusive)}` } if(game.disallowed.length){ lockDetil = `不能在以下地区激活: ${getZhName(game.disallowed)}: ` } return lockDetil || `无限制激活` } const gameBox = document.createElement('div') gameBox.innerHTML = `Download页面` const gamelist = gameBox.querySelector('._self_view_') const sButton = gameBox.querySelector('._sh_hd_') const optionBox = document.createElement('div') optionBox.innerHTML = '' const optionUl = optionBox.querySelector('._option_ul_') const selectUl = optionBox.querySelector('._select_ul_') const keyValue = optionBox.querySelector('._key_value_') const textArr = ['刮开游戏(高亮选中)', '选择游戏(不刮开)', '全选高亮', '取消高亮', '清理文本框', '显示数字'] const [getKey, selectKey, allLight, noLight, clearKey, setNumber] = textArr.map((item, index) => { item = document.createElement('li') item.innerText = textArr[index] optionUl.appendChild(item) return item }) const liChild = allGame.map((item, index) => { const li = document.createElement('li') li.innerText = index + 1 Object.assign(li.dataset, { title: item.title, name: item.name, machine_name: item.machine_name, key: item.key || '' }) selectUl.appendChild(li) return li }) setNum() optionUl.onselectstart = () => false selectUl.onselectstart = () => false selectUl.addEventListener('click',(e) => { if (e.target.nodeName === 'LI') e.target.classList.toggle('current') }) allLight.addEventListener('click', () => { liChild.forEach(item => item.classList.add('current')) }) noLight.addEventListener('click', () => { liChild.forEach(item => item.classList.remove('current')) }) clearKey.addEventListener('click',() => (keyValue.value = '')) selectKey.addEventListener('click', () => { if(selecedGame.length >= 10) return noLight.click() keyValue.value = '' let num = 0 let filter = liChild.filter(item => item.classList.contains('current')) filter.forEach((item, index) => { if(selecedGame.includes(item.dataset.name)) return num++ let time = setTimeout(() => { clearTimeout(time) fetchHttp({url: `https://www.humblebundle.com/humbler/choosecontent?gamekey=${gamekey}&parent_identifier=initial&chosen_identifier=${item.dataset.name}`}) .then(res => res.json()) .then(res => { keyValue.value += `${item.dataset.title}: ${res.success ? '选择成功' : '选择失败'}\n` if(res.success){ noSelectList.forEach((it) => { if(it.title === item.dataset.name){ it.classList.add('current') it.innerText = '已选择' } }) } }) }, (index - num) * 1500) }) noLight.click() }) getKey.addEventListener('click', () => { keyValue.value = '' let num = 0 let filter = liChild.filter(item => item.classList.contains('current')) filter.forEach((item, index) => { if(item.dataset.key) { (keyValue.value += `${item.dataset.title}: ${item.dataset.key}\n`) return num++ } let time = setTimeout(() => { clearTimeout(time) fetchHttp({url: 'https://www.humblebundle.com/humbler/redeemkey', body: `keytype=${item.dataset.machine_name}&key=${gamekey}&keyindex=0`, method: 'POST'}) .then(res => res.json()) .then(res => { if(res.success){ keyValue.value += `${item.dataset.title}: ${res.key}\n` noGetList.forEach((it) => { if(it.dataset.name === item.dataset.name){ it.classList.add('current') it.innerText = '已刮开' } }) } }) }, index * 1500) }) noLight.click() }) setNumber.addEventListener('click', () => { if(!document.querySelectorAll('._game_num_').length)setNum() }) function setNum() { const els = document.querySelectorAll('.js-content-choices .choice-image-container') els.forEach((item, index) => { let div = document.createElement('div') div.setAttribute('class','_game_num_') div.innerText = index + 1 item.appendChild(div) }) } gameBox.insertBefore(optionBox, sButton) allGame.forEach(item => { const li = document.createElement('li') li.innerHTML = `
${item.title}${item.key ? '': ''}${selecedGame.includes(item.name) ? '' : ''}

${getLock(item)}

` gamelist.appendChild(li) }) const view = document.querySelector('.content-choices-view') const list = document.querySelector(".content-choice-tiles.js-content-choice-tiles") view.insertBefore(gameBox, list) sButton.addEventListener('click', () => { if(sButton.classList.contains('current')){ gamelist.style.display = 'block' sButton.innerHTML === '隐藏锁区信息' }else { gamelist.style.display = 'none' sButton.innerHTML === '显示锁区信息' } sButton.classList.toggle('current') } ) const noGetList = document.querySelectorAll('.no-get') const noSelectList = document.querySelectorAll('.no-select') noSelectList.forEach(item => { item.addEventListener('click', clickEvent(true, {url: `https://www.humblebundle.com/humbler/choosecontent?gamekey=${gamekey}&parent_identifier=initial&chosen_identifier=${item.title}`})) }) noGetList.forEach(item => { item.addEventListener('click', clickEvent(false, {url: 'https://www.humblebundle.com/humbler/redeemkey', body: `keytype=${item.dataset.machine_name}&key=${gamekey}&keyindex=0`, method: 'POST'})) }) function fetchHttp(data) { return fetch(data.url, { method: data.method || 'GET', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', Origin: 'https://www.humblebundle.com', Referer: location.href }, body: data.body, credentials: 'same-origin' }) } function clickEvent (flag, data) { return function request () { if((this.innerText === '已选择') || (this.innerText === '已刮开') || (selecedGame.length >= 10)) return this.innerText = '请求中...' fetchHttp(data) .then(res => res.json()) .then(res => { if(!res.success){ return (this.innerText = flag ? '未选择' : '未刮开') } this.removeEventListener('click', request) this.innerText = flag ? '已选择' : '已刮开' this.className = 'current' keyValue.value = '' if(!flag) keyValue.value += `${this.title}: ${res.key}\n` else keyValue.value += `${this.title}: 选择成功\n` }) } } GM_addStyle(` ._sh_hd_ { border: none; outline: none; background-color: #c93756; margin: 20px 0 0 20px; border-radius: 5px; line-height: 50px; padding: 0 20px; } ._down_page_{ float: right; padding: 0 20px; border-radius: 5px; height: 50px; margin: 20px 20px 0 0; line-height: 50px; background-color: rgb(22, 159, 227); color: #fff; text-decoration: none; } ._key_value_ { margin: 20px 0 0 20px; width: 800px; height: 200px; resize: none; font-size: 18px; color: #fff; margin-top: 20px; outline: none; background-color: #454c5e; border: none; } ._option_ul_, ._select_ul_ { margin: 20px 0 0 20px; height: 50px; line-height: 50px; display: flex; list-style: none; padding: 0; } ._option_ul_ > li, ._select_ul_ > li { height: 50px; line-height: 50px; background-color: rgb(22, 159, 227); margin-right: 20px; border-radius: 5px; text-align: center; padding: 0 20px; color: #fff; font-size: 16px; cursor: pointer; } ._select_ul_ > li { background-color: #454c5e; } ._select_ul_ > li.current { background-color: rgb(22, 159, 227); } ._game_num_ { width: 100%; height: 100%; position: absolute; left: 0; top: 0; z-index: 1; background-color: rgba(0, 0, 0, .3); text-align: center; font-size: 100px; } ._sh_hd_.current { background-color: #169fe3; } ._self_view_ { list-style: none; margin: 20px 0 0 0; padding: 0; } ._self_view_ > li { font-size: 16px; padding: 20px 0 0px 20px; border-bottom: 10px solid #454c5e; display: flex; justify-content: space-between; } ._self_view_ > li button { border: none; outline: none; background-color: #169fe3; margin: 0 10px; font-size: 16px; border-radius: 0.8em; padding: 5px 15px; width: 100px; float: right; } ._self_view_ > li .current{ background-color: #c93756; } ._self_view_ > li:last-child { border: none; } `) } })();