🏠 Home 

Steam_Discount_Query

Query when the discounts expired

  1. // ==UserScript==
  2. // @name:zh-CN Steam折扣截止日期查询
  3. // @name Steam_Discount_Query
  4. // @namespace https://blog.chrxw.com
  5. // @supportURL https://blog.chrxw.com/scripts.html
  6. // @contributionURL https://afdian.com/@chr233
  7. // @version 1.5
  8. // @description:zh-CN 查询折扣截止日期
  9. // @description Query when the discounts expired
  10. // @author Chr_
  11. // @match https://store.steampowered.com/wishlist/*
  12. // @license AGPL-3.0
  13. // @icon https://blog.chrxw.com/favicon.ico
  14. // @grant GM_addStyle
  15. // ==/UserScript==
  16. (async () => {
  17. "use strict";
  18. const g_cache = {};
  19. //初始化
  20. const t = setInterval(() => {
  21. for (let ele of document.querySelectorAll("div.Panel div[data-index]")) {
  22. addQueryButton(ele);
  23. }
  24. }, 1000);
  25. const matchAppId = /app\/(\d+)/;
  26. //添加按钮
  27. function addQueryButton(element) {
  28. const oldBtn = element.querySelector("button.sdq_listbtns");
  29. if (oldBtn) {
  30. return;
  31. }
  32. const href = element.querySelector("a")?.getAttribute("href");
  33. const match = href.match(matchAppId);
  34. if (!match) {
  35. return;
  36. }
  37. const appId = match[1];
  38. const btn = document.createElement("button");
  39. btn.addEventListener(
  40. "click",
  41. (e) => {
  42. displaySaleEnds(appId, btn);
  43. e.preventDefault();
  44. },
  45. false
  46. );
  47. btn.className = "sdq_listbtns";
  48. btn.textContent = g_cache[appId] ?? "🔍";
  49. if (btn.textContent.search(":") !== -1) {
  50. btn.className += " sdq_listbtns_alert";
  51. }
  52. var inner = element.querySelector("div>div");
  53. inner.appendChild(btn);
  54. }
  55. //显示折扣结束时间
  56. async function displaySaleEnds(appId, ele) {
  57. ele.enabled = false;
  58. ele.className += " sdq_listbtns_show";
  59. ele.textContent = "🔍……";
  60. fetchSaleEnds(appId)
  61. .then((endDate) => {
  62. ele.textContent = endDate;
  63. g_cache[appId] = endDate;
  64. if (endDate.search(":") !== -1) {
  65. ele.className += " sdq_listbtns_alert";
  66. }
  67. })
  68. .catch((err) => {
  69. let done = showAlert("网络错误", `<p>${err}</p>`, false);
  70. setTimeout(() => {
  71. done.Dismiss();
  72. }, 2000);
  73. dialog.Dismiss();
  74. })
  75. .finally(() => {
  76. ele.enabled = true;
  77. });
  78. }
  79. //读取折扣结束时间
  80. function fetchSaleEnds(appId) {
  81. const regSaleEnds = new RegExp(
  82. /<p class="game_purchase_discount_countdown">(.+)<\/p>/,
  83. ""
  84. );
  85. const regDate = new RegExp(/\d+ 月 \d+ 日/, "");
  86. const regTimestamp = new RegExp(
  87. /InitDailyDealTimer\( \$DiscountCountdown, (\d+) \);/,
  88. ""
  89. );
  90. return new Promise((resolve, reject) => {
  91. fetch(`https://store.steampowered.com/app/${appId}/?lang=schinese`, {
  92. method: "GET",
  93. credentials: "include",
  94. })
  95. .then(async (response) => {
  96. if (response.ok) {
  97. const html = await response.text();
  98. const saleEnds = html.match(regSaleEnds)?.pop();
  99. let endDate;
  100. if (saleEnds) {
  101. endDate = saleEnds.match(regDate)?.pop();
  102. if (!endDate) {
  103. const endsTimestamp = html.match(regTimestamp)?.pop();
  104. const timestamp = parseInt(endsTimestamp) * 1000;
  105. const date = new Date(timestamp);
  106. if (date.getDate() === date.getDate()) {
  107. endDate = `${date.getMonth() + 1
  108. } ${date.getDate()} ${date
  109. .getHours()
  110. .toString()
  111. .padStart(2, "0")}:${date
  112. .getMinutes()
  113. .toString()
  114. .padStart(2, "0")}`;
  115. } else {
  116. endDate = "解析失败";
  117. }
  118. }
  119. } else {
  120. endDate = "未打折";
  121. }
  122. console.info(endDate);
  123. resolve(endDate);
  124. } else {
  125. resolve("请求失败");
  126. }
  127. })
  128. .catch((err) => {
  129. reject(err);
  130. });
  131. });
  132. }
  133. //显示提示
  134. function showAlert(title, text, succ = true) {
  135. return ShowAlertDialog(`${succ ? "✅" : "❌"}${title}`, text);
  136. }
  137. })();
  138. GM_addStyle(`
  139. button.sdq_listbtns {
  140. position: absolute;
  141. z-index: 100;
  142. padding: 1px;
  143. right: 20px;
  144. top: 20px;
  145. }
  146. div.wishlist_row > button.sdq_listbtns {
  147. top: 35%;
  148. right: 33%;
  149. position: absolute;
  150. }
  151. button.sdq_listbtns_show,
  152. div.wishlist_row:hover button.sdq_listbtns {
  153. display: flex;
  154. }
  155. button.sdq_listbtns_alert {
  156. color: red;
  157. }
  158. `);