Steam Key Helper

try to take over the world!

当前为 2017-09-02 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Steam Key Helper
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.2.0
  5. // @description try to take over the world!
  6. // @icon http://store.steampowered.com/favicon.ico
  7. // @author Bisumaruko
  8. // @include http*://*
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js
  10. // @require https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/6.6.6/sweetalert2.min.js
  11. // @resource SweetAlert2CSS https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/6.6.6/sweetalert2.min.css
  12. // @connect store.steampowered.com
  13. // @grant GM_xmlhttpRequest
  14. // @grant GM_setValue
  15. // @grant GM_getValue
  16. // @grant GM_addStyle
  17. // @grant GM_getResourceText
  18. // @noframes
  19. // ==/UserScript==
  20.  
  21. /* global GM_xmlhttpRequest, GM_setValue, GM_getValue, GM_addStyle, GM_getResourceText,
  22. swal, g_AccountID, g_sessionID,
  23. window, document, location */
  24.  
  25. // setup jQuery
  26. const $ = jQuery.noConflict(true);
  27.  
  28. // inject swal css
  29. GM_addStyle(GM_getResourceText('SweetAlert2CSS'));
  30.  
  31. // setup swal
  32. swal.setDefaults({
  33. timer: 3000,
  34. useRejections: false
  35. });
  36.  
  37. // inject CSS
  38. GM_addStyle(`
  39. .SKH_link {
  40. color: #57bae8;
  41. cursor: pointer;
  42. }
  43. .SKH_link:hover {
  44. text-decoration: underline;
  45. }
  46. .SKH_activated {
  47. text-decoration: line-through;
  48. }
  49. `);
  50.  
  51. // load config
  52. const config = JSON.parse(GM_getValue('SKH_config') || '{}');
  53. const activated = JSON.parse(GM_getValue('SKH_activated') || '[]');
  54. const excludedTag = ['SCRIPT', 'STYLE', 'IFRAME', 'CANVAS'];
  55. const regKey = /([A-Za-z0-9]{5}-){2,4}[A-Za-z0-9]{5}/g;
  56. const has = Object.prototype.hasOwnProperty;
  57.  
  58. // text
  59. const i18n = {
  60. tchinese: {
  61. errorTitle: '糟糕!',
  62. errorUnexpected: '發生未知錯誤,請稍後再試',
  63. errorInvalidKey: '序號錯誤',
  64. errorUsedKey: '序號已被使用',
  65. errorRateLimited: '啟動受限',
  66. errorCountryRestricted: '地區限制',
  67. errorAlreadyOwned: '產品已擁有',
  68. errorMissingBaseGame: '未擁有主程式',
  69. errorPS3Required: '需要PS3 啟動',
  70. errorGiftWallet: '偵測到禮物卡/錢包序號',
  71. errorFailedRequest: '處理資料發生錯誤,請稍後再試',
  72. errorFailedRequestNeedUpdate: '請求發生錯誤,請稍後再試<br>或者嘗試更新SessionID',
  73. successTitle: '啟動成功!',
  74. processingTitle: '喵~',
  75. processingMsg: '啟動序號中,請稍後',
  76. notLoggedInTitle: '未登入',
  77. notLoggedInMsg: '請登入Steam 以讓腳本紀錄SessionID',
  78. missingTitle: '未發現SessionID',
  79. missingMsg: '請問要更新SessionID 嗎?'
  80. },
  81. schinese: {
  82. errorTitle: '糟糕!',
  83. errorUnexpected: '发生未知错误,请稍后再试',
  84. errorInvalidKey: '激活码错误',
  85. errorUsedKey: '激活码已被使用',
  86. errorRateLimited: '激活受限',
  87. errorCountryRestricted: '地区限制',
  88. errorAlreadyOwned: '产品已永有',
  89. errorMissingBaseGame: '位永有基础游戏',
  90. errorPS3Required: '需要PS3 激活',
  91. errorGiftWallet: '侦测到礼物卡/钱包激活码',
  92. errorFailedRequest: '处理资料发生错误,请稍后再试',
  93. errorFailedRequestNeedUpdate: '请求发生错误,请稍后再试<br>或者尝试更新SessionID',
  94. successTitle: '激活成功!',
  95. processingTitle: '喵~',
  96. processingMsg: '激活中,请稍后',
  97. notLoggedInTitle: '未登入',
  98. notLoggedInMsg: '请登入Steam 以让脚本记录SessionID',
  99. missingTitle: '未发现SessionID',
  100. missingMsg: '请问要更新SessionID 吗?'
  101. },
  102. english: {
  103. errorTitle: 'Opps!',
  104. errorUnexpected: 'An unexpected error has occured, please try again later',
  105. errorInvalidKey: 'Invalid Key',
  106. errorUsedKey: 'Used Key',
  107. errorRateLimited: 'Rate Limited',
  108. errorCountryRestricted: 'Country Restricted',
  109. errorAlreadyOwned: 'Product Already Owned',
  110. errorMissingBaseGame: 'Missing Base Game',
  111. errorPS3Required: 'PS3 Activation Required',
  112. errorGiftWallet: 'Gift Card/Wallet Code Detected',
  113. errorFailedRequest: 'Result parse failed, please try again',
  114. errorFailedRequestNeedUpdate: 'Request failed, please try again<br>or update sessionID',
  115. successTitle: 'Activation Successful!',
  116. processingTitle: 'Nyaa~',
  117. processingMsg: 'Activating key, please wait',
  118. notLoggedInTitle: 'Not Logged-In',
  119. notLoggedInMsg: 'Please login to Steam so sessionID can be saved',
  120. missingTitle: 'Missing SessionID',
  121. missingMsg: 'Do you want to update your Steam sessionID?'
  122. }
  123. };
  124. const text = has.call(i18n, config.language) ? i18n[config.language] : i18n.english;
  125.  
  126. // functions
  127. const updateActivated = (key, result) => {
  128. if (!activated.includes(key)) {
  129. if (result.success === 1 || [14, 15, 9].includes(result.purchase_result_details)) {
  130. activated.push(key);
  131. GM_setValue('SKH_activated', JSON.stringify(activated));
  132. $(`span:contains(${key})`).addClass('SKH_activated');
  133. }
  134. }
  135. };
  136. const getResultMsg = result => {
  137. const errMsg = {
  138. title: text.errorTitle,
  139. html: text.errorUnexpected,
  140. type: 'error'
  141. };
  142. const errors = {
  143. 14: text.errorInvalidKey,
  144. 15: text.errorUsedKey,
  145. 53: text.errorRateLimited,
  146. 13: text.errorCountryRestricted,
  147. 9: text.errorAlreadyOwned,
  148. 24: text.errorMissingBaseGame,
  149. 36: text.errorPS3Required,
  150. 50: text.errorGiftWallet
  151. };
  152. const getDetails = items => {
  153. const details = [];
  154.  
  155. items.forEach(item => {
  156. const detail = [`<b>${item.line_item_description}</b>`];
  157. if (item.packageid > 0) detail.push(`sub: ${item.packageid}`);
  158. if (item.appid > 0) detail.push(`app: ${item.appid}`);
  159.  
  160. details.push(detail.join(', '));
  161. });
  162.  
  163. return details.join('<br>');
  164. };
  165.  
  166. if (result.success === 1) {
  167. return {
  168. title: text.successTitle,
  169. html: getDetails(result.purchase_receipt_info.line_items),
  170. type: 'success'
  171. };
  172. } else if (result.success === 2) {
  173. if (has.call(errors, result.purchase_result_details)) {
  174. errMsg.html = errors[result.purchase_result_details];
  175. }
  176. if (result.purchase_receipt_info.line_items.length > 0) {
  177. errMsg.html += `<br>${getDetails(result.purchase_receipt_info.line_items)}`;
  178. }
  179. }
  180.  
  181. return errMsg;
  182. };
  183. const activateKey = key => {
  184. swal({
  185. title: text.processingTitle,
  186. text: text.processingMsg,
  187. timer: null
  188. });
  189. swal.showLoading();
  190. GM_xmlhttpRequest({
  191. method: 'POST',
  192. url: 'https://store.steampowered.com/account/ajaxregisterkey/',
  193. headers: {
  194. Accept: 'text/javascript, text/html, application/xml, text/xml, */*',
  195. 'Accept-Encoding': 'gzip, deflate, br',
  196. 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  197. Origin: 'https://store.steampowered.com',
  198. Referer: 'https://store.steampowered.com/account/registerkey'
  199. },
  200. data: `product_key=${key}&sessionid=${config.sessionID}`,
  201. onload: res => {
  202. swal.close();
  203. if (res.status === 200) {
  204. try {
  205. const result = JSON.parse(res.response);
  206.  
  207. swal(getResultMsg(result));
  208. updateActivated(key, result);
  209. } catch (e) {
  210. swal(text.errorTitle, text.errorFailedRequest, 'error');
  211. }
  212. } else {
  213. swal({
  214. title: text.errorTitle,
  215. html: text.errorFailedRequestNeedUpdate,
  216. type: 'error',
  217. timer: null,
  218. showCancelButton: true,
  219. preConfirm: () => {
  220. window.open('https://store.steampowered.com/');
  221. }
  222. });
  223. }
  224. }
  225. });
  226. };
  227. const generateLink = txt => {
  228. const link = $(`<span class="SKH_link">${txt}</span>`).click(() => {
  229. activateKey(txt);
  230. });
  231. if (activated.includes(txt)) link.addClass('SKH_activated');
  232.  
  233. return link[0];
  234. };
  235. const scanText = txt => {
  236. let matched = true;
  237. const matches = [];
  238.  
  239. while (matched) {
  240. matched = regKey.exec(txt.data);
  241. if (matched) matches.push(matched);
  242. }
  243.  
  244. matches.reverse().forEach(match => {
  245. txt.splitText(match.index);
  246. txt.nextSibling.splitText(match[0].length);
  247. txt.parentNode.replaceChild(generateLink(match[0]), txt.nextSibling);
  248. });
  249. };
  250. const scanElement = element => {
  251. Array.from(element.childNodes).reverse().forEach(child => {
  252. if (child.nodeType === 1) {
  253. // element node
  254. if (child.type === 'text' && regKey.test(child.value)) {
  255. const $child = $(child);
  256.  
  257. if (activated.includes(child.value)) $child.addClass('SKH_activated');
  258. $child.click(() => {
  259. activateKey(child.value);
  260. });
  261. } else if (!excludedTag.includes(child.tagName)) scanElement(child);
  262. } else if (child.nodeType === 3) {
  263. // text node
  264. scanText(child);
  265. }
  266. });
  267. };
  268. const init = () => {
  269. // save sessionID
  270. if (location.hostname === 'store.steampowered.com') {
  271. if (g_AccountID > 0) {
  272. if (config.sessionID !== g_sessionID) config.sessionID = g_sessionID;
  273. if (config.language !== g_oSuggestParams.l) config.language = g_oSuggestParams.l;
  274. GM_setValue('SKH_config', JSON.stringify(config));
  275. } else {
  276. swal(text.notLoggedInTitle, text.notLoggedInMsg, 'error');
  277. }
  278. } else {
  279. // check sessionID & language
  280. if (!config.sessionID || !config.language) {
  281. swal({
  282. title: text.missingTitle,
  283. text: text.missingMsg,
  284. type: 'question',
  285. timer: null,
  286. showCancelButton: true,
  287. preConfirm: () => {
  288. window.open('https://store.steampowered.com/');
  289. }
  290. });
  291. }
  292.  
  293. scanElement(document.body);
  294. }
  295. };
  296.  
  297. $(init);