Advertisement
AkitoApocalypse

Untitled

Mar 5th, 2021
102
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.71 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Best Buy Automation (Product Details)
  3. // @namespace akito
  4. // @version 2.0.0
  5. // @description Auto-presses drops when button changes to Add
  6. // @author akito#9528 / Albert Sun
  7. // @match https://www.bestbuy.com/*skuId=*
  8. // @match https://www.bestbuy.com/site/combo/*
  9. // @run-at document-start
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. // Version Changelog
  14. // 1.7.1 - Moved notification sound retrieval to page load so it would play correctly
  15. // 1.8.0 - Added auto page-reload functionality on sold-out products with the given keywords
  16. // 2.0.0 - Refactored existing code and added auto-reload for sold out / unavailable products
  17.  
  18. const version = "2.0.0";
  19. const scriptDesc = `Best Buy Automation (Product Details) v${version} by akito#9528 / Albert Sun`;
  20. const donationText = "Thank you! | Bitcoin: bc1q6u7kalsxunl5gleqcx3ez4zn6kmahrsnevx2w4 / 1KgcytPHXNwboNbXUN3ZyuASDZWt8Qcf1t | Paypal: akitocodes@gmail.com";
  21.  
  22. // Quick and dirty script configuration, feel free to edit this!
  23. // - Ignore warnings with the /!\ symbol, just some whitespace linting stuff
  24. // - Note that auto-reload might not work properly if Chrome / Firefox unloads resources or something
  25. const scriptConfig = {
  26. keepPolling: false, // Whether to continue periodically polling for product availability once added to cart | Default: false
  27. initialClick: true, // Whether to auto-click the "Add to Cart" button upon page load (if description includes keyword) | Default: true
  28. soldOutReload: true, // Whether to auto-reload the page after a delay when sold out (if description includes keyword) | Default: false
  29. // ======================================================================================================================================
  30. pollInterval: 0.5, // In seconds, interval between periodic button polls when checking for availability | Default: 0.5
  31. initialDelay: 0.5, // In seconds, delay on page load before auto-clicking the add button (to update existing status) | Default: 0.5
  32. soldOutDelay: 60, // In seconds, delay before refreshing page when product is sold out (to refresh product details) | Default: 60
  33. // ======================================================================================================================================
  34. // Keyword inclusion whitelist for initial clicking and auto-reloading, delete or make empty array for script to ignore.
  35. // Default: ["3060", "3070", "3080", "3090", "6800", "6900", "5600X", "5800X", "5900X", "5950X", "PS5"]
  36. keywords: ["3060", "3070", "3080", "3090", "6800", "6900", "5600X", "5800X", "5900X", "5950X", "PS5"]
  37. }
  38.  
  39. // Quick and dirty function which checks whether the given button / div is yellow (usually RGB around 255, 255, 0).
  40. // Yellow usually means that the item is cartable (though different for the saved items page where it is instead white).
  41. function buttonYellow(button) {
  42. return window.getComputedStyle(button, null).getPropertyValue("background-color").startsWith("rgb(255");
  43. }
  44.  
  45. // Checks whether the given word (string) contains any of the given keywords (substrings).
  46. // Automatically returns false if the keywords parameter is not an array.
  47. function containsSubstring(word, keywords) {
  48. if(Array.isArray(keywords) === false) {
  49. return false;
  50. }
  51.  
  52. for(const keyword of keywords) {
  53. if(word.includes(keyword)) {
  54. return true;
  55. }
  56. }
  57. return false;
  58. }
  59.  
  60. (async function() {
  61. 'use strict'; // Necessary? Comes default with Tampermonkey scripts
  62.  
  63. // Initialize bottom banner for status + donation info
  64. const banner = document.createElement("div");
  65. banner.style.position = "fixed"; banner.style.bottom = "0px"; banner.style.zIndex = 100;
  66. banner.style.width = "100%"; banner.style.padding = "6px"; banner.style.alignItems = "center";
  67. banner.style.backgroundImage = "linear-gradient(to right, coral, crimson, coral, crimson)";
  68. banner.style.fontFamily = "Verdana"; banner.style.fontSize = "12px";
  69. banner.style.display = "flex"; banner.style.flexDirection = "row"; banner.style.justifyContent = "space-between";
  70.  
  71. // Initialize status info (left side of bottom banner)
  72. const statusInfo = document.createElement("div");
  73. statusInfo.style.textAlign = "left"; statusInfo.style.paddingLeft = "10px";
  74. statusInfo.style.order = 0; statusInfo.style.flexBasis = "50%";
  75. statusInfo.innerText = `${scriptDesc} | Initializing script`;
  76.  
  77. // Initialize donation info (right side of bottom banner)
  78. const donationInfo = document.createElement("div");
  79. donationInfo.style.textAlign = "right"; donationInfo.style.paddingRight = "10px";
  80. donationInfo.style.order = 1; donationInfo.style.flexBasis = "50%";
  81. donationInfo.innerText = donationText;
  82.  
  83. // Actual bulk of the script including the auto-reloader and interval poller
  84. // Could use document-end instead of document-start and DOM load but I dunno what's better
  85. document.addEventListener("DOMContentLoaded", async function() {
  86. document.body.append(banner);
  87. banner.appendChild(statusInfo);
  88. banner.appendChild(donationInfo);
  89.  
  90. // Preload audio because doesn't preload when tab not focused
  91. const audio = new Audio("https://proxy.notificationsounds.com/notification-sounds/definite-555/download/file-sounds-1085-definite.mp3");
  92.  
  93. // Retrieve relevant HTML elements and information from page
  94. const addButton = document.getElementsByClassName("add-to-cart-button")[0];
  95. const productName = document.getElementsByTagName("title")[0].innerText;
  96. const hasKeyword = containsSubstring(productName, scriptConfig.keywords);
  97.  
  98. // Check current product status (available, sold out / unavailable, queued) and update banner appropriately
  99. // Also process sold out / unavailable triggers at this point because it's convenient
  100. let addAvailable = buttonYellow(addButton);
  101. const disabled = addButton.classList.contains("btn-disabled");
  102. if(disabled === true) { // Button not clickable, either sold out or unavailable
  103. statusInfo.innerHTML = `${scriptDesc} | Product currently sold out or unavailable, `;
  104. if(scriptConfig.soldOutReload === true && hasKeyword === true) {
  105. statusInfo.innerHTML += `auto-reloading in ${scriptConfig.soldOutDelay} seconds.`;
  106.  
  107. await new Promise(r => setTimeout(r, scriptConfig.soldOutDelay * 1000)); // sleep function
  108. location.reload();
  109. } else {
  110. statusInfo.innerHTML += "auto-reload disabled " + (scriptConfig.soldOutReload === true
  111. ? "because not whitelisted"
  112. : "in script config"); // too long to add comments?
  113.  
  114. return; // exit the script
  115. }
  116. }
  117.  
  118.  
  119. statusInfo.innerHTML = `${scriptDesc} | ` + (addAvailable === true
  120. ? "Add button clickable, " + (scriptConfig.initialClick === true && scriptConfig.initialClick === true && hasKeyword === true
  121. ? `automatically clicking in ${scriptConfig.initialDelay} seconds.`
  122. : "please click to initialize product queue.")
  123. : "Existing product queue detected, waiting for button availability change.");
  124.  
  125. // Automatically click the button after a set interval if clickable and setting enabled.
  126. if(addAvailable === true && scriptConfig.initialClick === true && hasKeyword === true) {
  127. setTimeout(function() {
  128. addButton.click()
  129. }, scriptConfig.initialDelay * 1000);
  130. }
  131.  
  132. // Initialize periodic polling for button color swap
  133. let intervalID = setInterval(function() {
  134. let nowAvailable = buttonYellow(addButton);
  135. if(addAvailable === true && nowAvailable === false) { // button clicked to enter queue (yellow => grey)
  136. statusInfo.innerHTML = `${scriptDesc} | Queue entry detected, waiting for button availability change`;
  137. } else if(addAvailable === false && nowAvailable === true) { // queue popped (grey => yellow)
  138. statusInfo.innerHTML = `{scriptDesc} | Availability change detected, button clicked and window opened. Good luck!`;
  139.  
  140. // Click button, play audio, and open cart window
  141. addButton.click();
  142. window.open("https://www.bestbuy.com/checkout/r/fast-track");
  143. audio.play();
  144.  
  145. // Cancel periodic polling depending on config
  146. if(scriptConfig.keepPolling === false) {
  147. clearInterval(intervalID);
  148. }
  149. }
  150. }, scriptConfig.pollInterval * 1000);
  151. });
  152.  
  153. }());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement