Twitch Enhancements

Automatically claim channel points, enable theater mode, claim prime rewards, claim drops, and add redeem buttons for GOG and Legacy Games on Twitch and Amazon Gaming websites.

当前为 2024-12-10 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Twitch Enhancements
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.5.2
  5. // @description Automatically claim channel points, enable theater mode, claim prime rewards, claim drops, and add redeem buttons for GOG and Legacy Games on Twitch and Amazon Gaming websites.
  6. // @author JJJ
  7. // @match https://www.twitch.tv/*
  8. // @match https://gaming.amazon.com/*
  9. // @match https://www.twitch.tv/drops/inventory*
  10. // @match https://www.gog.com/en/redeem
  11. // @match https://promo.legacygames.com/i-love-finding-cats-and-pups-ce-prime-deal/
  12. // @icon https://th.bing.com/th/id/R.d71be224f193da01e7e499165a8981c5?rik=uBYlAxJ4XyXmJg&riu=http%3a%2f%2fpngimg.com%2fuploads%2ftwitch%2ftwitch_PNG28.png&ehk=PMc5m5Fil%2bhyq1zilk3F3cuzxSluXFBE80XgxVIG0rM%3d&risl=&pid=ImgRaw&r=0
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // @grant GM_deleteValue
  16. // @license MIT
  17. // ==/UserScript==
  18.  
  19. (function () {
  20. 'use strict';
  21. // Twitch Constants
  22. const PLAYER_SELECTOR = '.video-player';
  23. const THEATER_MODE_BUTTON_SELECTOR = 'button[aria-label="Modo cine (alt+t)"], button[aria-label="Theatre Mode (alt+t)"]';
  24. const CLOSE_MENU_BUTTON_SELECTOR = 'button[aria-label="Close Menu"]';
  25. const CLOSE_MODAL_BUTTON_SELECTOR = 'button[aria-label="Close modal"]';
  26. const THEATER_MODE_CLASS = 'theatre-mode';
  27. const CLAIMABLE_BONUS_SELECTOR = '.claimable-bonus__icon';
  28. const CLAIM_DROPS_SELECTOR = 'button.ScCoreButton-sc-ocjdkq-0.eWlfQB';
  29. const PRIME_REWARD_SELECTOR = 'button.tw-interactive.tw-button.tw-button--full-width[data-a-target="buy-box_call-to-action"] span.tw-button__text div.tw-inline-block p.tw-font-size-5.tw-md-font-size-4[title="Get game"]';
  30. const PRIME_REWARD_SELECTOR_2 = 'p.tw-font-size-5.tw-md-font-size-4[data-a-target="buy-box_call-to-action-text"][title="Get game"]';
  31.  
  32. // Redeem on GOG Constants
  33. const GOG_REDEEM_CODE_INPUT_SELECTOR = '#codeInput';
  34. const GOG_CONTINUE_BUTTON_SELECTOR = 'button[type="submit"][aria-label="Proceed to the next step"]';
  35. const GOG_FINAL_REDEEM_BUTTON_SELECTOR = 'button[type="submit"][aria-label="Redeem the code"]';
  36.  
  37. // Redeem on Legacy Games Constants
  38. const LEGACY_GAMES_REDEEM_URL = 'https://promo.legacygames.com/i-love-finding-cats-and-pups-ce-prime-deal/';
  39. const LEGACY_GAMES_CODE_INPUT_SELECTOR = '#primedeal_game_code';
  40. const LEGACY_GAMES_EMAIL_INPUT_SELECTOR = '#primedeal_email';
  41. const LEGACY_GAMES_EMAIL_VALIDATE_INPUT_SELECTOR = '#primedeal_email_validate';
  42. const LEGACY_GAMES_SUBMIT_BUTTON_SELECTOR = '#submitbutton';
  43.  
  44. let claiming = false;
  45.  
  46. // Check if MutationObserver is supported
  47. const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
  48.  
  49. // Function to click a button
  50. function clickButton(buttonSelector) {
  51. const observer = new MutationObserver((mutationsList, observer) => {
  52. for (let mutation of mutationsList) {
  53. if (mutation.addedNodes.length) {
  54. const button = document.querySelector(buttonSelector);
  55. if (button) {
  56. button.click();
  57. observer.disconnect();
  58. return;
  59. }
  60. }
  61. }
  62. });
  63.  
  64. observer.observe(document, { childList: true, subtree: true });
  65. }
  66.  
  67. // Function to enable theater mode
  68. function enableTheaterMode() {
  69. const player = document.querySelector(PLAYER_SELECTOR);
  70. if (player) {
  71. if (!player.classList.contains(THEATER_MODE_CLASS)) {
  72. clickButton(THEATER_MODE_BUTTON_SELECTOR);
  73. }
  74. } else {
  75. console.error('Player not found');
  76. }
  77. }
  78.  
  79. // Function to hide the global menu
  80. function hideGlobalMenu() {
  81. const GLOBAL_MENU_SELECTOR = 'div.ScBalloonWrapper-sc-14jr088-0.eEhNFm';
  82. const globalMenu = document.querySelector(GLOBAL_MENU_SELECTOR);
  83. if (globalMenu) {
  84. globalMenu.style.display = 'none';
  85. } else {
  86. console.error('Global menu not found');
  87. }
  88. }
  89.  
  90. // Function to automatically claim channel points
  91. function autoClaimBonus() {
  92. if (MutationObserver) {
  93. console.log('Auto claimer is enabled.');
  94.  
  95. let observer = new MutationObserver(mutationsList => {
  96. for (let mutation of mutationsList) {
  97. if (mutation.type === 'childList') {
  98. let bonus = document.querySelector(CLAIMABLE_BONUS_SELECTOR);
  99. if (bonus && !claiming) {
  100. bonus.click();
  101. let date = new Date();
  102. claiming = true;
  103. setTimeout(() => {
  104. console.log('Claimed at ' + date.toLocaleString());
  105. claiming = false;
  106. }, Math.random() * 1000 + 2000);
  107. }
  108. }
  109. }
  110. });
  111.  
  112. observer.observe(document.body, { childList: true, subtree: true });
  113. } else {
  114. console.log('MutationObserver is not supported in this browser.');
  115. }
  116. }
  117.  
  118. // Function to claim prime rewards
  119. function claimPrimeReward() {
  120. const element = document.querySelector(PRIME_REWARD_SELECTOR) || document.querySelector(PRIME_REWARD_SELECTOR_2);
  121. if (element) {
  122. element.click();
  123. }
  124. }
  125.  
  126. // Function to claim drops
  127. function claimDrops() {
  128. var onMutate = function (mutationsList) {
  129. mutationsList.forEach(mutation => {
  130. if (document.querySelector(CLAIM_DROPS_SELECTOR)) document.querySelector(CLAIM_DROPS_SELECTOR).click();
  131. })
  132. }
  133. var observer = new MutationObserver(onMutate);
  134. observer.observe(document.body, { childList: true, subtree: true });
  135. }
  136.  
  137. // Function to add the "Redeem on GOG" button
  138. function addGogRedeemButton() {
  139. const claimCodeButton = document.querySelector('p[title="Claim Code"]');
  140. if (claimCodeButton && !document.querySelector('.gog-redeem-button')) {
  141. const claimCodeWrapper = claimCodeButton.closest('.claim-button-wrapper');
  142. if (claimCodeWrapper) {
  143. const gogRedeemButtonDiv = document.createElement('div');
  144. gogRedeemButtonDiv.className = 'claim-button tw-align-self-center gog-redeem-button';
  145.  
  146. const gogRedeemButton = document.createElement('a');
  147. gogRedeemButton.href = 'https://www.gog.com/en/redeem';
  148. gogRedeemButton.rel = 'noopener noreferrer';
  149. gogRedeemButton.className = 'tw-interactive tw-button tw-button--full-width';
  150. gogRedeemButton.dataset.aTarget = 'redeem-on-gog';
  151. gogRedeemButton.innerHTML = '<span class="tw-button__text" data-a-target="tw-button-text"><div class="tw-inline-flex"><p class="" title="Redeem on GOG">Redeem on GOG</p>&nbsp;&nbsp;<figure aria-label="ExternalLinkWithBox" class="tw-svg"><svg class="tw-svg__asset tw-svg__asset--externallinkwithbox tw-svg__asset--inherit" width="12px" height="12px" version="1.1" viewBox="0 0 11 11" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.3125 6.875V9.625C10.3125 10.3844 9.69689 11 8.9375 11H1.375C0.615608 11 0 10.3844 0 9.625V2.0625C0 1.30311 0.615608 0.6875 1.375 0.6875H4.125V2.0625H1.375V9.625H8.9375V6.875H10.3125ZM9.62301 2.34727L5.29664 6.67364L4.32437 5.70136L8.65073 1.375H6.18551V0H10.998V4.8125H9.62301V2.34727Z"></path></svg></figure></div></span>';
  152.  
  153. gogRedeemButtonDiv.appendChild(gogRedeemButton);
  154. claimCodeWrapper.appendChild(gogRedeemButtonDiv);
  155.  
  156. gogRedeemButton.addEventListener('click', function (e) {
  157. e.preventDefault();
  158. const codeInput = document.querySelector('input[aria-label]');
  159. if (codeInput) {
  160. const code = codeInput.value;
  161. if (code) {
  162. navigator.clipboard.writeText(code).then(function () {
  163. window.location.href = 'https://www.gog.com/en/redeem';
  164. });
  165. }
  166. }
  167. });
  168.  
  169. const style = document.createElement('style');
  170. style.innerHTML = `
  171. .claim-button-wrapper {
  172. display: flex;
  173. flex-direction: column;
  174. margin-top: 15px;
  175. }
  176. .claim-button,
  177. .gog-redeem-button {
  178. margin: 5px 0;
  179. }
  180. .tw-mg-l-1 {
  181. margin-top: 10px;
  182. }
  183. .claimable-item {
  184. flex-direction: column !important;
  185. gap: 15px;
  186. }
  187. .tw-flex-grow-1 {
  188. width: 100%;
  189. }
  190. `;
  191. document.head.appendChild(style);
  192. }
  193. }
  194. }
  195.  
  196. // Function to redeem code on GOG
  197. function redeemCodeOnGOG() {
  198. navigator.clipboard.readText().then(function (code) {
  199. const codeInput = document.querySelector(GOG_REDEEM_CODE_INPUT_SELECTOR);
  200. if (codeInput) {
  201. codeInput.value = code;
  202.  
  203. // Simulate input event to ensure any listeners are triggered
  204. const inputEvent = new Event('input', { bubbles: true });
  205. codeInput.dispatchEvent(inputEvent);
  206.  
  207. // Click the continue button after a short delay
  208. setTimeout(() => {
  209. const continueButton = document.querySelector(GOG_CONTINUE_BUTTON_SELECTOR);
  210. if (continueButton) {
  211. continueButton.click();
  212.  
  213. // Wait for the "Redeem" button to appear and click it
  214. const checkRedeemButton = setInterval(() => {
  215. const redeemButton = document.querySelector(GOG_FINAL_REDEEM_BUTTON_SELECTOR);
  216. if (redeemButton) {
  217. clearInterval(checkRedeemButton);
  218. redeemButton.click();
  219. }
  220. }, 500); // Check every 500ms for the Redeem button
  221. }
  222. }, 500); // Adjust the delay as needed
  223. }
  224. }).catch(function (err) {
  225. console.error('Failed to read clipboard contents: ', err);
  226. });
  227. }
  228.  
  229. // Function to add the "Redeem on Legacy Games" button
  230. function addLegacyGamesRedeemButton() {
  231. const copyCodeButton = document.querySelector('button[aria-label="Copy code to your clipboard"]');
  232. if (copyCodeButton && !document.querySelector('.legacy-games-redeem-button')) {
  233. const copyCodeWrapper = copyCodeButton.closest('.copy-button-wrapper');
  234. if (copyCodeWrapper) {
  235. const legacyGamesRedeemButtonDiv = document.createElement('div');
  236. legacyGamesRedeemButtonDiv.className = 'copy-button tw-align-self-center legacy-games-redeem-button';
  237.  
  238. const legacyGamesRedeemButton = document.createElement('button');
  239. legacyGamesRedeemButton.ariaLabel = 'Redeem on Legacy Games';
  240. legacyGamesRedeemButton.className = 'tw-interactive tw-button tw-button--full-width';
  241. legacyGamesRedeemButton.dataset.aTarget = 'redeem-on-legacy-games';
  242. legacyGamesRedeemButton.innerHTML = '<span class="tw-button__text" data-a-target="tw-button-text">Redeem on Legacy Games</span>';
  243.  
  244. legacyGamesRedeemButtonDiv.appendChild(legacyGamesRedeemButton);
  245. copyCodeWrapper.appendChild(legacyGamesRedeemButtonDiv);
  246.  
  247. legacyGamesRedeemButton.addEventListener('click', function (e) {
  248. e.preventDefault();
  249. const codeInput = document.querySelector('input[aria-label]');
  250. if (codeInput) {
  251. const code = codeInput.value;
  252. if (code) {
  253. navigator.clipboard.writeText(code).then(function () {
  254. const email = GM_getValue('legacyGamesEmail', null);
  255. if (!email) {
  256. const userEmail = prompt('Please enter your email address:');
  257. if (userEmail) {
  258. GM_setValue('legacyGamesEmail', userEmail);
  259. window.location.href = LEGACY_GAMES_REDEEM_URL;
  260. }
  261. } else {
  262. window.location.href = LEGACY_GAMES_REDEEM_URL;
  263. }
  264. });
  265. }
  266. }
  267. });
  268.  
  269. const style = document.createElement('style');
  270. style.innerHTML = `
  271. .copy-button-wrapper {
  272. display: flex;
  273. flex-direction: column;
  274. margin-top: 15px;
  275. }
  276. .copy-button,
  277. .legacy-games-redeem-button {
  278. margin: 5px 0;
  279. }
  280. .tw-mg-l-1 {
  281. margin-top: 10px;
  282. }
  283. .claimable-item {
  284. flex-direction: column !important;
  285. gap: 15px;
  286. }
  287. .tw-flex-grow-1 {
  288. width: 100%;
  289. }
  290. `;
  291. document.head.appendChild(style);
  292. }
  293. }
  294. }
  295.  
  296. // Function to redeem code on Legacy Games
  297. function redeemCodeOnLegacyGames() {
  298. navigator.clipboard.readText().then(function (code) {
  299. const codeInput = document.querySelector(LEGACY_GAMES_CODE_INPUT_SELECTOR);
  300. const emailInput = document.querySelector(LEGACY_GAMES_EMAIL_INPUT_SELECTOR);
  301. const emailValidateInput = document.querySelector(LEGACY_GAMES_EMAIL_VALIDATE_INPUT_SELECTOR);
  302. const email = GM_getValue('legacyGamesEmail', null);
  303.  
  304. if (codeInput && emailInput && emailValidateInput && email) {
  305. codeInput.value = code;
  306. emailInput.value = email;
  307. emailValidateInput.value = email;
  308.  
  309. // Simulate input event to ensure any listeners are triggered
  310. const inputEvent = new Event('input', { bubbles: true });
  311. codeInput.dispatchEvent(inputEvent);
  312. emailInput.dispatchEvent(inputEvent);
  313. emailValidateInput.dispatchEvent(inputEvent);
  314.  
  315. // Click the submit button after a short delay
  316. setTimeout(() => {
  317. const submitButton = document.querySelector(LEGACY_GAMES_SUBMIT_BUTTON_SELECTOR);
  318. if (submitButton) {
  319. submitButton.click();
  320. }
  321. }, 500); // Adjust the delay as needed
  322. }
  323. }).catch(function (err) {
  324. console.error('Failed to read clipboard contents: ', err);
  325. });
  326. }
  327.  
  328. // Function to open all "Claim Game" buttons in new tabs
  329. function openClaimGameTabs() {
  330. const claimGameButtons = document.querySelectorAll('div[data-a-target="tw-core-button-label-text"].Layout-sc-1xcs6mc-0.bFxzAY');
  331. claimGameButtons.forEach(button => {
  332. const parentButton = button.closest('a');
  333. if (parentButton) {
  334. window.open(parentButton.href, '_blank');
  335. }
  336. });
  337. }
  338.  
  339. if (window.location.hostname === 'gaming.amazon.com') {
  340. const observer = new MutationObserver((mutations, obs) => {
  341. const claimCodeButton = document.querySelector('p[title="Claim Code"]');
  342. if (claimCodeButton) {
  343. addGogRedeemButton();
  344. }
  345. const copyCodeButton = document.querySelector('button[aria-label="Copy code to your clipboard"]');
  346. if (copyCodeButton) {
  347. addLegacyGamesRedeemButton();
  348. }
  349. });
  350.  
  351. observer.observe(document, {
  352. childList: true,
  353. subtree: true
  354. });
  355.  
  356. addGogRedeemButton();
  357. addLegacyGamesRedeemButton();
  358. }
  359.  
  360. if (window.location.hostname === 'www.gog.com' && window.location.pathname === '/en/redeem') {
  361. window.addEventListener('load', redeemCodeOnGOG);
  362. }
  363.  
  364. if (window.location.hostname === 'promo.legacygames.com' && window.location.pathname === '/i-love-finding-cats-and-pups-ce-prime-deal/') {
  365. window.addEventListener('load', redeemCodeOnLegacyGames);
  366. }
  367.  
  368. setTimeout(enableTheaterMode, 1000);
  369. setTimeout(autoClaimBonus, 1000);
  370. setTimeout(claimPrimeReward, 1000);
  371. setTimeout(() => clickButton(CLOSE_MENU_BUTTON_SELECTOR), 1000);
  372. setTimeout(() => clickButton(CLOSE_MODAL_BUTTON_SELECTOR), 1000);
  373. setTimeout(hideGlobalMenu, 1000);
  374. setTimeout(claimDrops, 1000);
  375.  
  376. setInterval(function () {
  377. if (window.location.href.startsWith('https://www.twitch.tv/drops/inventory')) {
  378. window.location.reload();
  379. }
  380. }, 15 * 60000);
  381.  
  382. let o = new MutationObserver((m) => {
  383. let script = document.createElement("script");
  384. script.innerHTML = `
  385. const openClaimGameTabs=()=>{const claimGameButtons=document.querySelectorAll('div[data-a-target="tw-core-button-label-text"].Layout-sc-1xcs6mc-0.bFxzAY');claimGameButtons.forEach(button=>{const parentButton=button.closest('a');if(parentButton){window.open(parentButton.href,'_blank');}});};
  386. `;
  387. document.getElementById("PrimeOfferPopover-header").innerHTML = "";
  388. document.getElementById("PrimeOfferPopover-header").appendChild(script);
  389. document.getElementById("PrimeOfferPopover-header").innerHTML += `
  390. <input type='button' style='border: none; background-color: #9147ff; color: white; padding: 10px 20px; font-size: 14px; border-radius: 4px; cursor: pointer;' class='tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-bottom-right-radius-medium tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-core-button tw-core-button--primary tw-full-width tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative' value='Claim All' onclick='openClaimGameTabs();'>
  391. `;
  392. });
  393.  
  394. o.observe(document.body, { childList: true });
  395. })();