Humble Bundle Auto Redeem

HB download 页面刮key

当前为 2022-03-19 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Humble Bundle Auto Redeem
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description HB download 页面刮key
  6. // @author kumi
  7. // @match https://www.humblebundle.com/downloads?key=*
  8. // @icon https://humblebundle-a.akamaihd.net/static/hashed/46cf2ed85a0641bfdc052121786440c70da77d75.png
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (async function () {
  13. 'use strict';
  14. const request = async ({ url, method, body, headers }) => {
  15. const res = await fetch(url, {
  16. method: method || 'GET',
  17. body: body || null,
  18. headers
  19. })
  20. if (res.status !== 200) return {}
  21. return res.json()
  22. }
  23. const getGameKey = async (gameItem) => {
  24. let result
  25. try {
  26. result = await request({
  27. url: 'https://www.humblebundle.com/humbler/redeemkey',
  28. body: `keytype=${gameItem.machine_name}&key=${gameItem.gamekey}&keyindex=${gameItem.keyindex}`,
  29. headers: {
  30. 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  31. },
  32. method: 'POST'
  33. })
  34. } catch(e) {
  35. result = {key: '请求失败'}
  36. }
  37. return result
  38. }
  39. const parentEle = document.querySelector('.js-subproduct-whitebox-holder')
  40. const ele = document.createElement('div')
  41. ele.innerHTML = `<button style="display: block; font-size: 20px; margin-bottom: 10px; padding: 20px 40px;">全部刮开</button><textarea style="width: 50%; height: 300px;"></textarea>`
  42. parentEle.insertBefore(ele, parentEle.firstElementChild)
  43. ele.firstElementChild.onclick = async function() {
  44. if(this.innerText !== '全部刮开') return
  45. this.innerText = `正在刮key...`
  46. const [, orderId] = location.href.match(/downloads\?key=(\w+)/) || []
  47. const { tpkd_dict: { all_tpks } } = await request({
  48. url: `https://www.humblebundle.com/api/v1/order/${orderId}?all_tpkds=true`,
  49. })
  50. const platformObj = all_tpks.reduce((obj, item, index) => {
  51. if (!obj[item.key_type_human_name]) obj[item.key_type_human_name] = []
  52. if(all_tpks.length - 1 === index) {
  53. obj.temp.push(item)
  54. obj.tpks.push(obj.temp)
  55. return obj
  56. }
  57. if(obj.temp.length >= 4) {
  58. obj.temp.push(item)
  59. obj.tpks.push(obj.temp)
  60. obj.temp = []
  61. } else {
  62. obj.temp.push(item)
  63. }
  64. return obj
  65. }, {tpks: [], temp: []})
  66. const { tpks } = platformObj
  67. delete platformObj.tpks
  68. delete platformObj.temp
  69. let count = 0
  70. for (let j = 0; j < tpks.length; j++) {
  71. const gameArr = tpks[j]
  72. const result = await Promise.all(gameArr.map(getGameKey))
  73. result.forEach((item, index) => {
  74. this.innerText = `${++count}/${all_tpks.length}`
  75. const game = gameArr[index]
  76. platformObj[game.key_type_human_name].push(`${game.human_name},${item.key}`)
  77. })
  78. }
  79.  
  80. const platform = Object.keys(platformObj)
  81. const gameText = []
  82. for (let i = 0; i < platform.length; i++) {
  83. const keyType = platform[i]
  84. gameText.push(keyType + '平台\n'+ platformObj[keyType].join('\n'))
  85. }
  86. ele.lastElementChild.value = gameText.join('\n\n')
  87. }
  88.  
  89. })();