Twitch 自動領取掉寶 / Auto Receive Drops

Twitch 自動領取 (掉寶/Drops) , 窗口標籤顯示進度 , 直播結束時還沒領完 , 會自動尋找任意掉寶直播 , 並開啟後繼續掛機 , 代碼自訂義設置

Ekde 2023/09/26. Vidu La ĝisdata versio.

  1. // ==UserScript==
  2. // @name Twitch 自動領取掉寶 / Auto Receive Drops
  3. // @name:zh-TW Twitch 自動領取掉寶
  4. // @name:zh-CN Twitch 自动领取掉宝
  5. // @name:en Twitch Auto Claim Drops
  6. // @name:ja Twitch 自動ドロップ受け取り
  7. // @name:ko Twitch 자동 드롭 수령
  8. // @version 0.0.5
  9. // @author HentaiSaru
  10. // @description Twitch 自動領取 (掉寶/Drops) , 窗口標籤顯示進度 , 直播結束時還沒領完 , 會自動尋找任意掉寶直播 , 並開啟後繼續掛機 , 代碼自訂義設置
  11. // @description:zh-TW Twitch 自動領取 (掉寶/Drops) , 窗口標籤顯示進度 , 直播結束時還沒領完 , 會自動尋找任意掉寶直播 , 並開啟後繼續掛機 , 代碼自訂義設置
  12. // @description:zh-CN Twitch 自动领取 (掉宝/Drops) , 窗口标签显示进度 , 直播结束时还没领完 , 会自动寻找任意掉宝直播 , 并开启后继续挂机 , 代码自定义设置
  13. // @description:en Automatically claim Twitch Drops, display progress in the tab, and if not finished when the stream ends, it will automatically find another Drops-enabled stream and continue farming. Customizable settings in the code.
  14. // @description:ja Twitch のドロップを自動的に受け取り、タブに進捗狀況を表示し、ストリーム終了時にまだ受け取っていない場合、自動的に別のドロップ有効なストリームを検索し、収穫を続けます。コードでのカスタマイズ可能な設定
  15. // @description:ko Twitch 드롭을 자동으로 받아오고 탭에 진행 상황을 표시하며, 스트림이 종료되었을 때 아직 완료되지 않았다면 자동으로 다른 드롭 활성 스트림을 찾아 계속 수집합니다. 코드에서 사용자 정의 설정 가능합니다
  16.  
  17. // @match https://www.twitch.tv/drops/inventory
  18. // @icon https://cdn-icons-png.flaticon.com/512/8214/8214044.png
  19.  
  20. // @license MIT
  21. // @namespace https://greasyfork.org/users/989635
  22.  
  23. // @run-at document-end
  24. // @grant GM_setValue
  25. // @grant GM_getValue
  26. // ==/UserScript==
  27.  
  28. (function() {
  29. var Withdraw, title, state, NumberBox = [];
  30. /* 配置設定 */
  31. let config = {
  32. RestartLive: true, // 重新啟用直播
  33. ProgressDisplay: true, // 在選項卡展示進度
  34.  
  35. RestartTime: 4, // 重啟直播相差時間 (Minute) [設置太短可能勿檢測]
  36. DetectionDelay: 1.5, // 延遲開始檢測時間 (seconds), 提高可降低性能消耗, 過高會找不到元素
  37. CheckInterval: 100, // 檢查間隔 (seconds)
  38.  
  39. FindTag: ["drops", "启用掉宝", "드롭활성화됨"], // 直播查找標籤, 只要有包含該字串即可
  40.  
  41. ProgressBar: "p.CoreText-sc-1txzju1-0.egOfyN span.CoreText-sc-1txzju1-0",
  42. Checkbutton: ".ScCoreButton-sc-ocjdkq-0.ScCoreButtonPrimary-sc-ocjdkq-1.buUmIQ.bxHedf",
  43. }, observer = new MutationObserver(() => {
  44. title = document.querySelectorAll(config.ProgressBar); // 會有特殊類型, 因此使用較繁瑣的處理 =>
  45. title = title.length > 0 && title != false ? (title.forEach(progress=> NumberBox.push(progress.textContent)), Math.max(...NumberBox)) : false;
  46. state = config.ProgressDisplay && title != false ? (ShowTitle(`${title}%`), true) : false;
  47.  
  48. if (config.RestartLive && state) {
  49. config.RestartLive = false;
  50.  
  51. const time = new Date(), record = GM_getValue("record", null) || [time.getTime(), title],
  52. [Timestamp, Progress] = record, conversion = (time - Timestamp) / (1000 * 60);
  53.  
  54. if (conversion >= config.RestartTime && title === Progress) {
  55. AutoRestartLive();
  56. GM_setValue("record", [time.getTime(), title]);
  57. } else if (conversion === 0 || title !== Progress) {
  58. GM_setValue("record", [time.getTime(), title]);
  59. }
  60. }
  61.  
  62. Withdraw = document.querySelector(config.Checkbutton);
  63. Withdraw ? (observer.disconnect(), Withdraw.click()) : null;
  64. });
  65.  
  66. setTimeout(()=> {observer.observe(document.body, {childList: true, subtree: true})}, 1000 * config.DetectionDelay);
  67.  
  68. async function ShowTitle(display) {
  69. config.ProgressDisplay = false;
  70. const TitleDisplay = setInterval(()=>{ // 避免載入慢時的例外 (持續15秒)
  71. document.title !== display ? document.title = display : null;
  72. }, 500);
  73. setTimeout(()=> {clearInterval(TitleDisplay)}, 1000 * 15);
  74. }
  75.  
  76. async function AutoRestartLive() {
  77. let article;
  78. const choose = {
  79. channel: "[data-test-selector='DropsCampaignInProgressDescription-no-channels-hint-text']", // 相關頻道
  80. TagType: ".InjectLayout-sc-1i43xsx-0.cerOzE span", // 頻道 Tag 類型
  81. LiveLink: "[data-a-target='preview-card-image-link']", // 直播連結按鈕
  82.  
  83. }, channel = document.querySelector(choose.channel);
  84. if (channel) {
  85. const NewWindow = window.open(channel.href),
  86. Interval = setInterval(() => {
  87. article = NewWindow.document.getElementsByTagName("article");
  88. if (article.length > 20) {
  89. clearInterval(Interval);
  90. const index = Array.from(article).findIndex(element => {
  91. const tag = element.querySelector(choose.TagType).textContent.toLowerCase();
  92. return config.FindTag.some(match=> tag.includes(match.toLowerCase()));
  93. });
  94. article[index].querySelector(choose.LiveLink).click();
  95. }
  96. }, 1000);
  97. }
  98. }
  99.  
  100. setTimeout(()=> {location.reload()}, 1000 * config.CheckInterval);
  101. })();