- // ==UserScript==
- // @name Steam Wholesale Sites Extension
- // @namespace http://tampermonkey.net/
- // @version 1.0.1
- // @description try to take over the world!
- // @icon http://store.steampowered.com/favicon.ico
- // @author Bisumaruko
- // @include https://mini.wmtransfer.com/*
- // @include http://steamkeys.ovh/*
- // @include http://steamfarmkey.ru/*
- // @include http://steam1.lequeshop.ru/*
- // @include http://steam1.ru/*
- // @include http://lastkey.ru/*
- // @include http://steamkeyswhosales.com/*
- // @include http://alfakeys.ru/*
- // @include http://cheap-steam-games.ru/*
- // @include http://dmshop.lequeshop.ru/*
- // @include http://kartonanet.lequeshop.ru/*
- // @include http://keyssell.ru/*
- // @include http://keys.farm/*
- // @include http://rig4all.lequeshop.ru/*
- // @include http://steam-tab.ru/*
- // @include http://steamd.lequeshop.ru/*
- // @include http://steamkeys-shop.ru/*
- // @include http://steamkey.lequeshop.ru/*
- // @include http://steamkeystore.ru/*
- // @include http://farmacc.ru/*
- // @include http://steamrandomkeys.ru/*
- // @include http://animekeys.ru/*
- // @include http://drunkpatrick.store/*
- // @include http://steamfarm.lequestore.ru/*
- // @include http://maxfarmshop.ru/*
- // @include http://bestkeystore.ru/*
- // @include http://bestfarmkey.lequestore.ru/*
- // @include http://tkfg.ru/*
- // @include http://indiegamekeys.lequestore.ru/*
- // @include http://m-b-shop.leque.shop/*
- // @include http://200plus.lequeshop.ru/*
- // @include http://randomkey.ru/*
- // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/7.0.6/sweetalert2.min.js
- // @resource SweetAlert2CSS https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/7.0.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
- // ==/UserScript==
-
- /* global GM_xmlhttpRequest, GM_setValue, GM_getValue, GM_addStyle, GM_getResourceText,
- window, document, location, fetch, localStorage, Option, sessionStorage,
- swal */
-
- // inject swal css
- GM_addStyle(GM_getResourceText('SweetAlert2CSS'));
-
- // inject styles
- GM_addStyle(`
- .SWSE_hide { display: none; }
- .SWSE_settings, .SWSE_settings input { width: -webkit-fill-available; width: -moz-available; }
- .SWSE_settings .name { text-align: right; vertical-align: top; }
- .SWSE_settings .value { text-align: left; }
- .SWSE_settings .value > * { height: 30px; margin: 0 20px 10px; }
- .SWSE_settings .switch { position: relative; display: inline-block; width: 60px; }
- .SWSE_settings .switch input { display: none; }
- .SWSE_settings .slider {
- position: absolute;
- cursor: pointer;
- top: 0; left: 0; right: 0; bottom: 0;
- background-color: #ccc;
- transition: 0.4s;
- }
- .SWSE_settings .slider:before {
- position: absolute;
- content: "";
- height: 26px; width: 26px;
- left: 2px; bottom: 2px;
- background-color: white;
- transition: 0.4s;
- }
- .SWSE_settings input:checked + .slider { background-color: #2196F3; }
- .SWSE_settings input:focus + .slider { box-shadow: 0 0 1px #2196F3; }
- .SWSE_settings input:checked + .slider:before { transform: translateX(30px); }
- .SWSE_settings > span { display: inline-block; cursor: pointer; color: white; }
- .SWSE_owned {
- background-image: linear-gradient(135deg, rgba(0, 0, 0, 0.3) 60%, rgba(0, 0, 0, 0.1) 90%) !important;
- background-color: #9ccc65 !important;
- transition: background 500ms ease 0s;
- color: white !important;
- }
- .SWSE_owned a { color: white !important; }
- `);
-
- // load up
- const regURL = /(https?:\/\/)?([.\w]*steam[-.\w]*){1}\/.*?(apps?|subs?){1}\/(\d+){1}(\/.*\/?)?/m;
- const regKey = /((?:([A-Z0-9])(?!\2{4})){5}-){2,5}[A-Z0-9]{5}/g;
- const eol = "\r\n";
- const has = Object.prototype.hasOwnProperty;
-
- const owned = JSON.parse(localStorage.getItem('SWSE_owned') || '{}');
- const config = {
- data: JSON.parse(GM_getValue('SWSE_config') || '{}'),
- set(key, value, callback = null) {
- this.data[key] = value;
- GM_setValue('SWSE_config', JSON.stringify(this.data));
-
- if (typeof callback === 'function') callback();
- },
- get(key) {
- return has.call(this.data, key) ? this.data[key] : null;
- },
- init() {
- if (!has.call(this.data, 'language')) this.data.language = 'english';
- if (!has.call(this.data, 'count')) this.data.count = 1;
- if (!has.call(this.data, 'email')) this.data.email = '';
- }
- };
-
- // text
- const i18n = {
- tchinese: {
- name: '繁體中文',
- settingsTitle: '設定',
- settingsCount: '購買數量',
- settingsEmail: '購買信箱',
- settingsLanguage: '語言',
- menuHideOwned: '隱藏已擁有',
- menuSyncLibrary: '同步遊戲庫',
- keyField: 'Steam 序號',
- payButtonText: '付款',
- syncSuccessTitle: '同步成功',
- syncSuccess: '成功同步Steam 遊戲庫資料'
- },
- schinese: {
- name: '简体中文',
- settingsTitle: '设置',
- settingsCount: '购买数量',
- settingsEmail: '购买邮箱',
- settingsLanguage: '语言',
- menuHideOwned: '隐藏已拥有',
- menuSyncLibrary: '同步游戏库',
- keyField: 'Steam 激活码',
- payButtonText: '付款',
- syncSuccessTitle: '同步成功',
- syncSuccess: '成功同步Steam 游戏库资料'
- },
- english: {
- name: 'English',
- settingsTitle: 'Settings',
- settingsCount: 'Purchase Quantity',
- settingsEmail: 'Purchase Email',
- settingsLanguage: 'Language',
- menuHideOwned: 'Hide Owned',
- menuSyncLibrary: 'Sync Library',
- keyField: 'Steam Keys',
- payButtonText: 'Pay',
- syncSuccessTitle: 'Sync Successful',
- syncSuccess: 'Successfully sync Steam library data'
- }
- };
- let text = has.call(i18n, config.get('language')) ? i18n[config.get('language')] : i18n.english;
-
- const settingsHandler = arg => {
- swal.showLoading();
-
- arg();
-
- setTimeout(swal.hideLoading, 500);
- };
- const settings = () => {
- const panelHTML = `
- <table class="SWSE_settings">
- <tr>
- <td class="name">${text.settingsLanguage}</td>
- <td class="value"><select class="language"></select></td>
- </tr>
- <tr>
- <td class="name">${text.settingsCount}</td>
- <td class="value"><input type="number" class="count" value="1" min="1"></select></td>
- </tr>
- <tr>
- <td class="name">${text.settingsEmail}</td>
- <td class="value"><input type="text" class="email"></td>
- </tr>
- </table>
- `;
- const panelHandler = panel => {
- // apply settings
- const $panel = $(panel).find('.SWSE_settings');
-
- // language
- const $language = $panel.find('.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(settingsHandler.bind(null, () => {
- const newLanguage = $language.val();
- config.set('language', newLanguage);
-
- text = has.call(i18n, newLanguage) ? i18n[newLanguage] : i18n.english;
- }));
-
- // count, email
- ['count', 'email'].forEach(field => {
- const $field = $panel.find(`.${field}`);
- const val = config.get(field);
-
- if (val) $field.val(val);
-
- $field.change(settingsHandler.bind(null, () => {
- config.set(field, $field.val().trim());
- }));
- });
- };
-
- swal({
- title: text.settingsTitle,
- html: panelHTML,
- onBeforeOpen: panelHandler
- });
- };
- const matchSteamUrl = (str = '') => {
- const input = str.trim();
- let output = null;
-
- if (input.length > 0) {
- const found = input.match(regURL);
-
- if (found) {
- output = {
- type: found[3].slice(0, 3),
- id: parseInt(found[4], 10),
- index: found.index,
- matched: found[0]
- };
- }
- }
-
- return output;
- };
- const check = (d, s, c) => {
- let source = d;
- let selector = s;
- let callback = c;
-
- if (typeof d === 'string') {
- // dom source omitted
- source = document;
- selector = d;
- callback = s;
- }
-
- $(source).find(selector.split(',').map(x => `${x}:not(.SWSE_checked)`).join()).each((index, element) => {
- const $ele = $(element);
- let attr = null;
-
- for (let i = 0; i < element.attributes.length; i += 1) {
- if (!attr) attr = matchSteamUrl(element.attributes[i].value);
- }
-
- if (attr && owned[attr.type].includes(attr.id)) callback($ele, 'SWSE_owned');
-
- $ele.addClass('SWSE_checked');
- });
- };
- const syncLibrary = (notify = false) => {
- 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);
-
- if (data.rgOwnedApps.length > 0) owned.app = data.rgOwnedApps;
- if (data.rgOwnedPackages.length > 0) owned.sub = data.rgOwnedPackages;
- owned.lastSync = Date.now();
-
- localStorage.setItem('SWSE_owned', JSON.stringify(owned));
-
- if (notify) {
- swal({
- title: text.syncSuccessTitle,
- text: text.syncSuccess,
- type: 'success',
- timer: 3000
- });
- }
- }
- }
- });
- };
-
- const headerMenu = () => {
- // insert header menu
- GM_addStyle(`
- header.SWSE_header {
- display: flex !important;
- position: sticky;
- top: 0;
- padding: 0 7%;
- background: #2d3f51;
- box-shadow: 0 2px 5px rgba(68, 68, 68, 0.3);
- transition: all 0.2s ease;
- z-index: 9999;
- }
- .SWSE_nav ul { margin: 0; padding: 0; list-style: none; }
- .SWSE_nav li { float: left; }
- .SWSE_nav a {
- color: #f5f5f5;
- text-decoration: none;
- display: block;
- padding: 1.5em;
- font-size: initial;
- font-weight: 600;
- text-transform: uppercase;
- letter-spacing: 1px;
- transition: all 0.2s ease;
- }
- .SWSE_nav a:hover { color: #16A085; }
- `);
-
- const $nav = $('<nav class="SWSE_nav"><ul></ul></nav>');
-
- $nav.prependTo('body').wrap('<header class="SWSE_header"></header>');
- $nav.find('ul').append(
- // hide owned button
- $(`<li><a>${text.menuHideOwned}</a></li>`).click(() => {
- $('.SWSE_owned').toggleClass('SWSE_hide');
- }),
- // sync library button
- $(`<li><a>${text.menuSyncLibrary}</a></li>`).click(syncLibrary.bind(null, true)),
- // settings button
- $(`<li><a>${text.settingsTitle}</a></li>`).click(settings));
- };
- const handler = () => {
- // order page, auto download keys
- if (location.pathname.startsWith('/order/get/')) {
- const url = $('table a').attr('href');
-
- if (url.length > 0) {
- fetch(url, {
- method: 'GET',
- credentials: 'include'
- }).then(res => res.text()).then(t => {
- const keys = t.match(regKey);
-
- $('table tr:last-child').before(`<tr><td>${text.keyField}</td><td>${keys.join(eol)}</td></tr>`);
- });
- }
- // product page
- } else {
- // pre-fill inputs
- $('input[name=count]').val(config.get('count'));
- $('input[name=email]').val(config.get('email'));
-
- // insert pay button
- const $payButton = $(`<span class="SWSE_payButton">${text.payButtonText}</span>`).click(() => {
- const data = {
- wm_wmk: true,
- purse: $('#purse > span, #copyfund > b').text(),
- amount: parseFloat($('#price > span, .payprice').text()),
- desc: $('#message > span, #copybill > b').text()
- };
-
- window.open(`https://mini.wmtransfer.com/SendWebMoney.aspx?${JSON.stringify(data)}`, '', 'height=800,width=1000');
- });
-
- switch (location.hostname) {
- case 'steamkeyswhosales.com':
- case 'alfakeys.ru':
- $payButton.addClass('btn').css({
- 'margin-right': '10px',
- cursor: 'pointer',
- color: '#FFF',
- 'background-color': '#337ab7',
- 'border-color': '#2e6da4'
- }).insertBefore('#check_pay');
- break;
- case 'cheap-steam-games.ru':
- case 'lastkey.ru':
- case 'keys.farm':
- case 'steamkeys-shop.ru':
- case 'maxfarmshop.ru':
- case 'bestkeystore.ru':
- case 'bestfarmkey.lequestore.ru':
- case 'steamfarmkey.ru':
- case 'indiegamekeys.lequestore.ru':
- case 'randomkey.ru':
- $payButton.addClass('btn-leque btn-leque-primary btn-leque-xs').css({
- float: 'right',
- 'margin-top': '5px'
- }).insertAfter('.btn-leque-xs');
- break;
- case 'steam1.lequeshop.ru':
- case 'steam1.ru':
- case 'steam-tab.ru':
- case 'steamd.lequeshop.ru':
- case 'steamkeystore.ru':
- case 'steamrandomkeys.ru':
- case 'keyssell.ru':
- $payButton.addClass('btn btn-primary').css('margin-top', '10px').insertBefore('.checkpayButton');
- break;
- case 'dmshop.lequeshop.ru':
- case 'kartonanet.lequeshop.ru':
- case 'rig4all.lequeshop.ru':
- case 'steamkey.lequeshop.ru':
- case 'farmacc.ru':
- case 'drunkpatrick.store':
- case 'steamfarm.lequestore.ru':
- case 'animekeys.ru':
- case 'tkfg.ru':
- case '200plus.lequeshop.ru':
- $payButton.addClass('btn btn-primary').insertBefore('.checkpayButton, .checkpaybtn');
- break;
- default:
- }
-
- // check owned
- switch (location.hostname) {
- case 'steamkeys.ovh':
- check('a[href*="steampowered"]', ($ele, classes) => {
- $ele.closest('tr').addClass(classes);
- });
- break;
- case 'steamkeyswhosales.com':
- case 'alfakeys.ru':
- case 'ign.akens.ru':
- case 'bestkey.akens.ru':
- case 'goldkeys.akens.ru':
- case 'domenkeys.akens.ru':
- case 'cada.akens.ru':
- check('div[title*="steam"]', ($ele, classes) => {
- $ele.closest('tr').addClass(classes);
- });
- break;
- case 'keymarket.pw':
- check('div[style*="steam/apps/"]', ($ele, classes) => {
- $ele.nextAll().addClass(classes);
- $ele.parent().parent().addClass(classes);
- });
- break;
- case 'steamkey.lequeshop.ru':
- check('img[src*="steam/apps/"]', ($ele, classes) => {
- $ele.next().addClass(classes);
- $ele.parent().addClass(classes);
- });
- break;
- case 'cheap-steam-games.ru':
- check('img[src*="steam/apps/"]', ($ele, classes) => {
- $ele.closest('.hero-feature').addClass(classes);
- $ele.parent().next().addClass(classes);
- });
- break;
- case 'cheapkey.lequeshop.ru':
- check('div[style*="steam/apps/"]', ($ele, classes) => {
- $ele.parent().addClass(classes);
- });
- break;
- case 'steamfarmkey.ru':
- case 'kartonanet.lequeshop.ru':
- case 'lastkey.ru':
- case 'rig4all.lequeshop.ru':
- case 'steamkeys-shop.ru':
- case 'farmacc.ru':
- case 'keys.lequestore.ru':
- case 'steamfarm.lequestore.ru':
- case 'proxzy.lequestore.ru':
- case 'maxfarmshop.ru':
- case 'bestkeystore.ru':
- case 'keys.farm':
- case 'alonekey.net':
- case 'bestfarmkey.lequestore.ru':
- case 'm-b-shop.leque.shop':
- case 'indiegamekeys.lequestore.ru':
- case '200plus.lequeshop.ru':
- check('img[src*="steam"]', ($ele, classes) => {
- $ele.closest('tr').addClass(classes);
- });
- break;
- case 'keyssell.ru':
- case 'steam-tab.ru':
- case 'steamd.lequeshop.ru':
- case 'steam1.ru':
- case 'steamkeystore.ru':
- case 'steam1.lequeshop.ru':
- case 'steamrandomkeys.ru':
- check('a > img[src*="steam/apps/"]', ($ele, classes) => {
- $ele.closest('.item-loop').addClass(classes);
- $ele.closest('div').prev().addClass(classes);
- });
- check('.item-poster > img[src*="steam/apps/"]', ($ele, classes) => {
- $ele.parent().addClass(classes);
- $ele.prev().prev().addClass(classes);
- });
- break;
- case 'reronage.akens.ru':
- check('.good-title > div', ($ele, classes) => {
- $ele.closest('tr').addClass(classes);
- });
- break;
- case 'animekeys.ru':
- check('img[src*="steam"]', ($ele, classes) => {
- $ele.closest('div.list-item').addClass(classes);
- });
- break;
- case 'tkfg.ru':
- case 'randomkey.ru':
- $('#header').css('position', 'initial');
- check('a > img[src*="steam"]', ($ele, classes) => {
- $ele.parent().prev('.title').find('p').addClass(classes);
- $ele.closest('.b-poster').addClass(classes);
- });
- break;
- case 'steamground.com':
- GM_xmlhttpRequest({
- method: 'GET',
- url: 'https://www.steamgifts.com/discussion/iy081/steamground-wholesale-build-a-bundle',
- onload: res => {
- if (res.status !== 200) return;
-
- const games = $('<div/>', {
- html: res.response
- }).find('.comment__description:first table a[href*="steampowered"]');
- const titles = $('.wholesale-card_title');
- const hrefs = {};
- const process = t => {
- let tx = t.trim();
-
- if (tx === 'Ball of Light (Journey)') tx = 'Ball of Light';
- if (tx === 'Shake Your Money Simulator') tx = 'Shake Your Money Simulator 2016';
-
- return tx.toLowerCase().replace(/[\W]/g, '');
- };
-
- games.each((index, element) => {
- hrefs[process(element.textContent)] = element.href;
- });
- titles.each((index, element) => {
- const $ele = $(element);
- const href = hrefs[process($ele.text())] || '';
-
- if (href.length > 0) $ele.parent().attr('href', href);
- });
-
- check('.wholesale-card > a[href*="steampowered"]', (element, classes) => {
- $(element).parent().addClass(classes);
- $(element).children().eq(1).css('color', 'black');
- });
- }
- });
- break;
- default:
- }
- }
- };
- const init = () => {
- // on WebMoney payment page
- if (location.hostname === 'mini.wmtransfer.com') {
- if (location.href.includes('purses-view-history') && sessionStorage.getItem('wm_wmk')) window.close();
- if (location.pathnaame === '/SendWebMoney.aspx' && location.search.length > 0) {
- try {
- const search = location.href.split('?').pop();
- const data = JSON.parse(decodeURIComponent(search));
-
- if (data.wm_wmk) {
- $('#ctl00_cph_tbEmailOrPurseNumber').val(data.purse);
- $('#ctl00_cph_tbAmount').val(data.amount);
- $('#ctl00_cph_tbDesc').val(data.desc);
-
- sessionStorage.setItem('wm_wmk', 1);
- }
- } catch (err) {
- throw err;
- }
- }
- // wholesale sites
- } else {
- config.init();
-
- headerMenu();
- $(window).on('load', handler);
-
- // sync owned every 10 min
- const syncTimer = 10 * 60 * 1000;
- if (!owned.lastSync || owned.lastSync < Date.now() - syncTimer) syncLibrary();
- }
- };
-
- $(init);