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.0.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.  
  57. // functions
  58. const has = Object.prototype.hasOwnProperty;
  59. const updateActivated = (key, result) => {
  60. if (!activated.includes(key)) {
  61. if (result.success === 1 || [14, 15, 9].includes(result.purchase_result_details)) {
  62. activated.push(key);
  63. GM_setValue('SKH_activated', JSON.stringify(activated));
  64. $(`span:contains(${key})`).addClass('SKH_activated');
  65. }
  66. }
  67. };
  68. const getResultMsg = result => {
  69. const errMsg = {
  70. title: 'Opps!',
  71. html: 'An unexpected error has occured, please try again later',
  72. type: 'error'
  73. };
  74. const errors = {
  75. 14: 'Invalid Key',
  76. 15: 'Used Key',
  77. 53: 'Rate Limited',
  78. 13: 'Country Restricted',
  79. 9: 'Product Already Owned',
  80. 24: 'Missing Base Game',
  81. 36: 'PS3 Activation Required',
  82. 50: 'Gift Card/Wallet Code Detected'
  83. };
  84. const getDetails = items => {
  85. const details = [];
  86.  
  87. items.forEach(item => {
  88. const detail = [item.line_item_description];
  89. if (item.packageid > 0) detail.push(`sub: ${item.packageid}`);
  90. if (item.appid > 0) detail.push(`app: ${item.appid}`);
  91.  
  92. details.push(detail.join(', '));
  93. });
  94.  
  95. return details.join('<br>');
  96. };
  97.  
  98. if (result.success === 1) {
  99. return {
  100. title: 'Activation Successful!',
  101. html: getDetails(result.purchase_receipt_info.line_items),
  102. type: 'success'
  103. };
  104. } else if (result.success === 2) {
  105. if (has.call(errors, result.purchase_result_details)) {
  106. errMsg.html = errors[result.purchase_result_details];
  107. }
  108. if (result.purchase_receipt_info.line_items.length > 0) {
  109. errMsg.html += `<br>${getDetails(result.purchase_receipt_info.line_items)}`;
  110. }
  111. }
  112.  
  113. return errMsg;
  114. };
  115. const activateKey = key => {
  116. swal({
  117. title: 'Nyaa~',
  118. text: 'Activating key, please wait',
  119. timer: null
  120. });
  121. swal.showLoading();
  122. GM_xmlhttpRequest({
  123. method: 'POST',
  124. url: 'https://store.steampowered.com/account/ajaxregisterkey/',
  125. headers: {
  126. Accept: 'text/javascript, text/html, application/xml, text/xml, */*',
  127. 'Accept-Encoding': 'gzip, deflate, br',
  128. 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  129. Origin: 'https://store.steampowered.com',
  130. Referer: 'https://store.steampowered.com/account/registerkey'
  131. },
  132. data: `product_key=${key}&sessionid=${config.sessionID}`,
  133. onload: res => {
  134. swal.close();
  135. if (res.status === 200) {
  136. try {
  137. const result = JSON.parse(res.response);
  138.  
  139. swal(getResultMsg(result));
  140. updateActivated(key, result);
  141. } catch (e) {
  142. swal('Opps!', 'Result parse failed, please try again', 'error');
  143. }
  144. } else {
  145. swal({
  146. title: 'Opps!',
  147. html: 'Request failed, please try again<br>or update sessionID',
  148. type: 'error',
  149. timer: null,
  150. showCancelButton: true,
  151. preConfirm: () => {
  152. window.open('https://store.steampowered.com/');
  153. }
  154. });
  155. }
  156. }
  157. });
  158. };
  159. const generateLink = text => {
  160. const link = $(`<span class="SKH_link">${text}</span>`).click(() => {
  161. activateKey(text);
  162. });
  163. if (activated.includes(text)) link.addClass('SKH_activated');
  164.  
  165. return link[0];
  166. };
  167. const scanText = text => {
  168. let matched = true;
  169. const matches = [];
  170.  
  171. while (matched) {
  172. matched = regKey.exec(text.data);
  173. if (matched) matches.push(matched);
  174. }
  175.  
  176. matches.reverse().forEach(match => {
  177. text.splitText(match.index);
  178. text.nextSibling.splitText(match[0].length);
  179. text.parentNode.replaceChild(generateLink(match[0]), text.nextSibling);
  180. });
  181. };
  182. const scanElement = element => {
  183. Array.from(element.childNodes).reverse().forEach(child => {
  184. if (child.nodeType === 1) {
  185. // element node
  186. if (child.type === 'text' && regKey.test(child.value)) {
  187. const $child = $(child);
  188.  
  189. if (activated.includes(child.value)) $child.addClass('SKH_activated');
  190. $child.click(() => {
  191. activateKey(child.value);
  192. });
  193. } else if (!excludedTag.includes(child.tagName)) scanElement(child);
  194. } else if (child.nodeType === 3) {
  195. // text node
  196. scanText(child);
  197. }
  198. });
  199. };
  200. const init = () => {
  201. // save sessionID
  202. if (location.hostname === 'store.steampowered.com') {
  203. if (g_AccountID > 0) {
  204. if (config.sessionID !== g_sessionID) {
  205. config.sessionID = g_sessionID;
  206. GM_setValue('SBSE_config', JSON.stringify(config));
  207. }
  208. } else {
  209. swal('Not Logged-In', 'Please login to Steam so sessionID can be saved', 'error');
  210. }
  211. }
  212.  
  213. // check sessionID
  214. if (!config.sessionID) {
  215. swal({
  216. title: 'Missing SessionID',
  217. text: 'Do you want to update your Steam sessionID?',
  218. type: 'question',
  219. timer: null,
  220. showCancelButton: true,
  221. preConfirm: () => {
  222. window.open('https://store.steampowered.com/');
  223. }
  224. });
  225. }
  226.  
  227. scanElement(document.body);
  228. };
  229.  
  230. $(init);