// ==UserScript==
// @name Steam Key Helper
// @namespace http://tampermonkey.net/
// @version 1.2.0
// @description try to take over the world!
// @icon http://store.steampowered.com/favicon.ico
// @author Bisumaruko
// @include http*://*
// @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
// @noframes
// ==/UserScript==
/* global GM_xmlhttpRequest, GM_setValue, GM_getValue, GM_addStyle, GM_getResourceText,
swal, g_AccountID, g_sessionID,
window, document, location */
// setup jQuery
const $ = jQuery.noConflict(true);
// inject swal css
GM_addStyle(GM_getResourceText('SweetAlert2CSS'));
// setup swal
swal.setDefaults({
timer: 3000,
useRejections: false
});
// inject CSS
GM_addStyle(`
.SKH_link {
color: #57bae8;
cursor: pointer;
}
.SKH_link:hover {
text-decoration: underline;
}
.SKH_activated {
text-decoration: line-through;
}
`);
// load config
const config = JSON.parse(GM_getValue('SKH_config') || '{}');
const activated = JSON.parse(GM_getValue('SKH_activated') || '[]');
const excludedTag = ['SCRIPT', 'STYLE', 'IFRAME', 'CANVAS'];
const regKey = /([A-Za-z0-9]{5}-){2,4}[A-Za-z0-9]{5}/g;
const has = Object.prototype.hasOwnProperty;
// text
const i18n = {
tchinese: {
errorTitle: '糟糕!',
errorUnexpected: '發生未知錯誤,請稍後再試',
errorInvalidKey: '序號錯誤',
errorUsedKey: '序號已被使用',
errorRateLimited: '啟動受限',
errorCountryRestricted: '地區限制',
errorAlreadyOwned: '產品已擁有',
errorMissingBaseGame: '未擁有主程式',
errorPS3Required: '需要PS3 啟動',
errorGiftWallet: '偵測到禮物卡/錢包序號',
errorFailedRequest: '處理資料發生錯誤,請稍後再試',
errorFailedRequestNeedUpdate: '請求發生錯誤,請稍後再試<br>或者嘗試更新SessionID',
successTitle: '啟動成功!',
processingTitle: '喵~',
processingMsg: '啟動序號中,請稍後',
notLoggedInTitle: '未登入',
notLoggedInMsg: '請登入Steam 以讓腳本紀錄SessionID',
missingTitle: '未發現SessionID',
missingMsg: '請問要更新SessionID 嗎?'
},
schinese: {
errorTitle: '糟糕!',
errorUnexpected: '发生未知错误,请稍后再试',
errorInvalidKey: '激活码错误',
errorUsedKey: '激活码已被使用',
errorRateLimited: '激活受限',
errorCountryRestricted: '地区限制',
errorAlreadyOwned: '产品已永有',
errorMissingBaseGame: '位永有基础游戏',
errorPS3Required: '需要PS3 激活',
errorGiftWallet: '侦测到礼物卡/钱包激活码',
errorFailedRequest: '处理资料发生错误,请稍后再试',
errorFailedRequestNeedUpdate: '请求发生错误,请稍后再试<br>或者尝试更新SessionID',
successTitle: '激活成功!',
processingTitle: '喵~',
processingMsg: '激活中,请稍后',
notLoggedInTitle: '未登入',
notLoggedInMsg: '请登入Steam 以让脚本记录SessionID',
missingTitle: '未发现SessionID',
missingMsg: '请问要更新SessionID 吗?'
},
english: {
errorTitle: 'Opps!',
errorUnexpected: 'An unexpected error has occured, please try again later',
errorInvalidKey: 'Invalid Key',
errorUsedKey: 'Used Key',
errorRateLimited: 'Rate Limited',
errorCountryRestricted: 'Country Restricted',
errorAlreadyOwned: 'Product Already Owned',
errorMissingBaseGame: 'Missing Base Game',
errorPS3Required: 'PS3 Activation Required',
errorGiftWallet: 'Gift Card/Wallet Code Detected',
errorFailedRequest: 'Result parse failed, please try again',
errorFailedRequestNeedUpdate: 'Request failed, please try again<br>or update sessionID',
successTitle: 'Activation Successful!',
processingTitle: 'Nyaa~',
processingMsg: 'Activating key, please wait',
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?'
}
};
const text = has.call(i18n, config.language) ? i18n[config.language] : i18n.english;
// functions
const updateActivated = (key, result) => {
if (!activated.includes(key)) {
if (result.success === 1 || [14, 15, 9].includes(result.purchase_result_details)) {
activated.push(key);
GM_setValue('SKH_activated', JSON.stringify(activated));
$(`span:contains(${key})`).addClass('SKH_activated');
}
}
};
const getResultMsg = result => {
const errMsg = {
title: text.errorTitle,
html: text.errorUnexpected,
type: 'error'
};
const errors = {
14: text.errorInvalidKey,
15: text.errorUsedKey,
53: text.errorRateLimited,
13: text.errorCountryRestricted,
9: text.errorAlreadyOwned,
24: text.errorMissingBaseGame,
36: text.errorPS3Required,
50: text.errorGiftWallet
};
const getDetails = items => {
const details = [];
items.forEach(item => {
const detail = [`<b>${item.line_item_description}</b>`];
if (item.packageid > 0) detail.push(`sub: ${item.packageid}`);
if (item.appid > 0) detail.push(`app: ${item.appid}`);
details.push(detail.join(', '));
});
return details.join('<br>');
};
if (result.success === 1) {
return {
title: text.successTitle,
html: getDetails(result.purchase_receipt_info.line_items),
type: 'success'
};
} else if (result.success === 2) {
if (has.call(errors, result.purchase_result_details)) {
errMsg.html = errors[result.purchase_result_details];
}
if (result.purchase_receipt_info.line_items.length > 0) {
errMsg.html += `<br>${getDetails(result.purchase_receipt_info.line_items)}`;
}
}
return errMsg;
};
const activateKey = key => {
swal({
title: text.processingTitle,
text: text.processingMsg,
timer: null
});
swal.showLoading();
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',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
Origin: 'https://store.steampowered.com',
Referer: 'https://store.steampowered.com/account/registerkey'
},
data: `product_key=${key}&sessionid=${config.sessionID}`,
onload: res => {
swal.close();
if (res.status === 200) {
try {
const result = JSON.parse(res.response);
swal(getResultMsg(result));
updateActivated(key, result);
} catch (e) {
swal(text.errorTitle, text.errorFailedRequest, 'error');
}
} else {
swal({
title: text.errorTitle,
html: text.errorFailedRequestNeedUpdate,
type: 'error',
timer: null,
showCancelButton: true,
preConfirm: () => {
window.open('https://store.steampowered.com/');
}
});
}
}
});
};
const generateLink = txt => {
const link = $(`<span class="SKH_link">${txt}</span>`).click(() => {
activateKey(txt);
});
if (activated.includes(txt)) link.addClass('SKH_activated');
return link[0];
};
const scanText = txt => {
let matched = true;
const matches = [];
while (matched) {
matched = regKey.exec(txt.data);
if (matched) matches.push(matched);
}
matches.reverse().forEach(match => {
txt.splitText(match.index);
txt.nextSibling.splitText(match[0].length);
txt.parentNode.replaceChild(generateLink(match[0]), txt.nextSibling);
});
};
const scanElement = element => {
Array.from(element.childNodes).reverse().forEach(child => {
if (child.nodeType === 1) {
// element node
if (child.type === 'text' && regKey.test(child.value)) {
const $child = $(child);
if (activated.includes(child.value)) $child.addClass('SKH_activated');
$child.click(() => {
activateKey(child.value);
});
} else if (!excludedTag.includes(child.tagName)) scanElement(child);
} else if (child.nodeType === 3) {
// text node
scanText(child);
}
});
};
const init = () => {
// save sessionID
if (location.hostname === 'store.steampowered.com') {
if (g_AccountID > 0) {
if (config.sessionID !== g_sessionID) config.sessionID = g_sessionID;
if (config.language !== g_oSuggestParams.l) config.language = g_oSuggestParams.l;
GM_setValue('SKH_config', JSON.stringify(config));
} else {
swal(text.notLoggedInTitle, text.notLoggedInMsg, 'error');
}
} else {
// check sessionID & language
if (!config.sessionID || !config.language) {
swal({
title: text.missingTitle,
text: text.missingMsg,
type: 'question',
timer: null,
showCancelButton: true,
preConfirm: () => {
window.open('https://store.steampowered.com/');
}
});
}
scanElement(document.body);
}
};
$(init);