MWISubscribe

Subscribe Market Item

安装此脚本
作者推荐脚本

您可能也喜欢MWICommon

安装此脚本
  1. // ==UserScript==
  2. // @name MWISubscribe
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.13
  5. // @description Subscribe Market Item
  6. // @author Lhiok
  7. // @license MIT
  8. // @match https://www.milkywayidle.com/*
  9. // @match https://test.milkywayidle.com/*
  10. // @icon https://www.milkywayidle.com/favicon.svg
  11. // @supportURL https://github.com/Lhiok/MWIScript/
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. "use strict";
  17.  
  18. let mwi_common = null;
  19. let mwi_subscribe_items = {};
  20.  
  21. const storage_key = "mwi_subscribe_items";
  22. const subscribeItemsStorage = localStorage.getItem(storage_key);
  23. if (subscribeItemsStorage) {
  24. const subscribeItems = JSON.parse(subscribeItemsStorage);
  25. // 兼容旧版本
  26. if (Array.isArray(subscribeItems)) {
  27. for (let i = 0; i < subscribeItems.length; i++) {
  28. mwi_subscribe_items[subscribeItems[i]] = {
  29. ask: -2, // -1 代表查询失败 这里使用-2表示旧数据
  30. bid: -2,
  31. };
  32. }
  33. localStorage.setItem(storage_key, JSON.stringify(mwi_subscribe_items));
  34. }
  35. else {
  36. mwi_subscribe_items = subscribeItems;
  37. }
  38. }
  39.  
  40. function formatNumber(value) {
  41. return value.toString().replace(/\d+/, function (n) {
  42. return n.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
  43. })
  44. }
  45.  
  46. function fixNumber(value) {
  47. if (value > 100) return value.toFixed(0);
  48. if (value > 10) return value.toFixed(1);
  49. return value.toFixed(2);
  50. }
  51.  
  52. function formatNumberWithUnit(value) {
  53. if (value >= 1_000_000_000_000_000) return `${fixNumber(value / 1_000_000_000_000_000)}P`;
  54. if (value >= 1_000_000_000_000) return `${fixNumber(value / 1_000_000_000_000)}T`;
  55. if (value >= 1_000_000_000) return `${fixNumber(value / 1_000_000_000)}B`;
  56. if (value >= 1_000_000) return `${fixNumber(value / 1_000_000)}M`;
  57. if (value >= 1_000) return `${fixNumber(value / 1_000)}K`;
  58. return value.toFixed(0);
  59. }
  60.  
  61. function updateSubscribePrice(itemHrid, itemLevel) {
  62. const itemHridLevel = itemLevel > 0? `${itemHrid}::${itemLevel}`: itemHrid;
  63. if (mwi_subscribe_items[itemHridLevel] == undefined) {
  64. return;
  65. }
  66.  
  67. mwi_subscribe_items[itemHridLevel] = {
  68. ask: mwi_common.getItemPriceByHrid(itemHrid, itemLevel, 'ask'),
  69. bid: mwi_common.getItemPriceByHrid(itemHrid, itemLevel, 'bid'),
  70. };
  71. localStorage.setItem(storage_key, JSON.stringify(mwi_subscribe_items));
  72. }
  73.  
  74. function getPricePercentColor(pricePercent) {
  75. if (pricePercent > 0) {
  76. return "#ff0000";
  77. }
  78. else if (pricePercent < 0) {
  79. return "#00ff00";
  80. }
  81. else {
  82. return "#aaaaaa";
  83. }
  84. }
  85.  
  86. function updateSubscribedList() {
  87. const displayContainer = document.querySelector("div#subscribeDisplayContainer");
  88. if (!displayContainer) {
  89. return;
  90. }
  91. // 移除收藏物品
  92. displayContainer.innerHTML = "";
  93. // 创建收藏物品
  94. for (let itemHridLevel in mwi_subscribe_items) {
  95. const itemInfo = itemHridLevel.split("::");
  96. const itemHrid = itemInfo[0];
  97. const itemLevel = itemInfo[1]? Number(itemInfo[1]): 0;
  98. if (!itemHrid || itemHrid === "" || itemHrid === "undefined") {
  99. return;
  100. }
  101.  
  102. const itemCount = mwi_common.getItemNumByHrid(itemHrid, itemLevel);
  103. // 当前价格
  104. const askPrice = mwi_common.getItemPriceByHrid(itemHrid, itemLevel, 'ask');
  105. const bidPrice = mwi_common.getItemPriceByHrid(itemHrid, itemLevel, 'bid');
  106.  
  107. // 昨日价格
  108. const askPriceYesterday = mwi_common.getItemPriceYesterdayByHrid(itemHrid, itemLevel, 'ask');
  109. const bidPriceYesterday = mwi_common.getItemPriceYesterdayByHrid(itemHrid, itemLevel, 'bid');
  110. const askPricePercentYesterday = askPriceYesterday > 0? (askPrice - askPriceYesterday) / askPriceYesterday * 100: 0;
  111. const bidPricePercentYesterday = bidPriceYesterday > 0? (bidPrice - bidPriceYesterday) / bidPriceYesterday * 100: 0;
  112.  
  113. // 订阅价格
  114. const subscribePrice = mwi_subscribe_items[itemHridLevel];
  115. let askPriceSubscribe = subscribePrice.ask;
  116. let bidPriceSubscribe = subscribePrice.bid;
  117. if (askPriceSubscribe == -2 && bidPriceSubscribe == -2) {
  118. askPriceSubscribe = askPrice;
  119. bidPriceSubscribe = bidPrice;
  120. updateSubscribePrice(itemHrid, itemLevel);
  121. }
  122. const askPricePercentSubscribe = askPriceSubscribe > 0? (askPrice - askPriceSubscribe) / askPriceSubscribe * 100: 0;
  123. const bidPricePercentSubscribe = bidPriceSubscribe > 0? (bidPrice - bidPriceSubscribe) / bidPriceSubscribe * 100: 0;
  124.  
  125. const item = document.createElement("div");
  126. item.innerHTML = `<div style="position: relative; background: hsl(198, 76.50%, 16.70%); padding: 4px;">
  127. <div style="display: flex; width: 100%; height: 40px;">
  128. <svg role="img" aria-label="${mwi_common.getItemNameByHrid(itemHrid, mwi_common.isZh)}" style="width: 40px; height: 40px;">
  129. <use href="/static/media/items_sprite.6d12eb9d.svg#${itemHrid.substr(7)}"></use>
  130. </svg>
  131. <div style="${mwi_common.isZh? "": "font-size: 13px; "}width: 200px; height: 40px; padding-left: 4px;">
  132. <div style="display: flex; gap: 4px; white-space: nowrap;">
  133. <div style="color:hsl(202, 41.50%, 71.20%);">${mwi_common.isZh? "名称": "Name"}:</div>
  134. <div>${mwi_common.getItemNameByHrid(itemHrid, mwi_common.isZh)}${itemLevel > 0? ` +${itemLevel}`: ""}</div>
  135. </div>
  136. <div style="display: flex; gap: 4px; white-space: nowrap;">
  137. <div style="color:hsl(202, 41.50%, 71.20%);">${mwi_common.isZh? "数量": "Count"}:</div>
  138. <div>${formatNumber(itemCount)}</div>
  139. </div>
  140. </div>
  141. </div>
  142. <hr borderColor="hsl(204, 92.60%, 5.30%)" margin="4px 4px">
  143. <div style="display: flex; gap: 4px; white-space: nowrap;">
  144. <div style="color:hsl(202, 41.50%, 71.20%);">${mwi_common.isZh? "今": "T"}:</div>
  145. <div>${formatNumberWithUnit(askPrice)} / ${formatNumberWithUnit(bidPrice)}</div>
  146. <div>(${formatNumberWithUnit(askPrice * itemCount)} / ${formatNumberWithUnit(bidPrice * itemCount)})</div>
  147. </div>
  148. <div style="display: flex; gap: 4px; white-space: nowrap;">
  149. <div style="color:hsl(202, 41.50%, 71.20%);">${mwi_common.isZh? "昨": "Y"}:</div>
  150. <div>${formatNumberWithUnit(askPriceYesterday)} / ${formatNumberWithUnit(bidPriceYesterday)}</div>
  151. <div>(<span style="color: ${getPricePercentColor(askPricePercentYesterday)}">${askPricePercentYesterday.toFixed(2)}%</span> / <span style="color: ${getPricePercentColor(bidPricePercentYesterday)}">${bidPricePercentYesterday.toFixed(2)}%</span>)</div>
  152. </div>
  153. <div style="display: flex; gap: 4px; white-space: nowrap;">
  154. <div style="color:hsl(202, 41.50%, 71.20%);">${mwi_common.isZh? "订": "S"}:</div>
  155. <div>${formatNumberWithUnit(askPriceSubscribe)} / ${formatNumberWithUnit(bidPriceSubscribe)}</div>
  156. <div>(<span style="color: ${getPricePercentColor(askPricePercentSubscribe)}">${askPricePercentSubscribe.toFixed(2)}%</span> / <span style="color: ${getPricePercentColor(bidPricePercentSubscribe)}">${bidPricePercentSubscribe.toFixed(2)}%</span>)</div>
  157. </div>
  158. <div style="display: flex; justify-content: flex-end; gap: 10px; padding-top: 4px; white-space: nowrap;">
  159. <button id="updatePrice" style="background-color: orange; color: black; white-space: nowrap;">${mwi_common.isZh? "更新": "Update"}</button>
  160. <button id="goToMarket" style="background-color: orange; color: black; white-space: nowrap;">${mwi_common.isZh? "前往": "Market"}</button>
  161. </div>
  162. </div>`;
  163. displayContainer.appendChild(item);
  164.  
  165. // 添加点击事件
  166. const updatePriceButton = item.querySelector("button#updatePrice");
  167. const goToMarketButton = item.querySelector("button#goToMarket");
  168. updatePriceButton && updatePriceButton.addEventListener("click", () => {
  169. updateSubscribePrice(itemHrid, itemLevel)
  170. updateSubscribedList();
  171. });
  172. goToMarketButton && goToMarketButton.addEventListener("click", () => mwi_common.gotoMarket(itemHrid, itemLevel));
  173. };
  174. }
  175.  
  176. function createSubscribeButton(marketPanel) {
  177. const displayContainer = marketPanel.querySelector(".MarketplacePanel_currentItem__3ercC");
  178. if (!displayContainer) {
  179. return;
  180. }
  181.  
  182. // 创建收藏按钮
  183. const subscribeButton = document.createElement("button");
  184. subscribeButton.setAttribute("id", "subscribeButton");
  185. subscribeButton.className = "subscribe-btn";
  186. subscribeButton.style.position = "absolute";
  187. subscribeButton.style.padding = "0";
  188. subscribeButton.style.marginLeft = "6px";
  189. subscribeButton.style.background = "none";
  190. subscribeButton.style.border = "none";
  191. subscribeButton.style.outline = "none";
  192. subscribeButton.style.zIndex = "2"; /** make sure it's on top of the item level div created by MWITools */
  193. // 创建图标
  194. const svgNS = "http://www.w3.org/2000/svg";
  195. const svg = document.createElementNS(svgNS, "svg");
  196. svg.setAttribute("viewBox", "0 0 24 24");
  197. svg.setAttribute("width", "24");
  198. svg.setAttribute("height", "24");
  199. // 未收藏
  200. const heartUnsubscribed = document.createElementNS(svgNS, "path");
  201. heartUnsubscribed.setAttribute("d", "M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z");
  202. heartUnsubscribed.setAttribute("fill", "#aaaaaa");
  203. heartUnsubscribed.setAttribute("stroke", "#333");
  204. heartUnsubscribed.setAttribute("transition", "all 0.3s");
  205. // 已收藏
  206. const heartSubscribed = document.createElementNS(svgNS, "path");
  207. heartSubscribed.setAttribute("d", "M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z");
  208. heartSubscribed.setAttribute("fill", "#ff4d4f");
  209. heartSubscribed.setAttribute("opacity", "0");
  210. heartSubscribed.setAttribute("transition", "opacity 0.3s");
  211. // 添加图标
  212. svg.appendChild(heartUnsubscribed);
  213. svg.appendChild(heartSubscribed);
  214. subscribeButton.appendChild(svg);
  215. displayContainer.prepend(subscribeButton);
  216. // 物品等级
  217. let itemHrid = "";
  218. let itemLevel = 0;
  219. let itemHridLevel = "";
  220. let isSubscribed = false;
  221.  
  222. const updateSubscribedButton = function() {
  223. if (isSubscribed) {
  224. heartUnsubscribed.setAttribute("stroke", "#ff4d4f");
  225. heartSubscribed.setAttribute("opacity", "1");
  226. } else {
  227. heartUnsubscribed.setAttribute("stroke", "#333");
  228. heartSubscribed.setAttribute("opacity", "0");
  229. }
  230. };
  231.  
  232. const updateMarketItem = function() {
  233. itemHrid = "";
  234. const displayItem = displayContainer.querySelector(".Item_iconContainer__5z7j4");
  235. if (displayItem && displayItem.firstChild) {
  236. const itemName = displayItem.firstChild.getAttribute("aria-label");
  237. if (itemName && itemName !== "") {
  238. itemHrid = mwi_common.getItemHridByName(itemName);
  239. }
  240. }
  241.  
  242. itemLevel = 0;
  243. const levelDiv = displayContainer.querySelector(".Item_enhancementLevel__19g-e");
  244. if (levelDiv) {
  245. itemLevel = Number(levelDiv.textContent.replace("+", ""));
  246. if (isNaN(itemLevel)) itemLevel = 0;
  247. }
  248.  
  249. itemHridLevel = itemHrid && itemLevel > 0? `${itemHrid}::${itemLevel}`: itemHrid;
  250. isSubscribed = mwi_subscribe_items[itemHridLevel] != undefined;
  251. updateSubscribedButton();
  252. }
  253.  
  254. updateMarketItem();
  255. // 绑定点击
  256. subscribeButton.addEventListener("click", function() {
  257. if (!itemHridLevel || itemHridLevel === "") {
  258. return;
  259. }
  260.  
  261. isSubscribed = !isSubscribed;
  262. updateSubscribedButton();
  263.  
  264. if (isSubscribed) {
  265. console.info("[MWISubscribe] add item " + itemHridLevel);
  266. mwi_subscribe_items[itemHridLevel] = {
  267. ask: mwi_common.getItemPriceByHrid(itemHrid, itemLevel, 'ask'),
  268. bid: mwi_common.getItemPriceByHrid(itemHrid, itemLevel, 'bid'),
  269. }
  270. }
  271. else {
  272. console.info("[MWISubscribe] remove item " + itemHridLevel);
  273. delete mwi_subscribe_items[itemHridLevel];
  274. }
  275.  
  276. localStorage.setItem(storage_key, JSON.stringify(mwi_subscribe_items));
  277. updateSubscribedList();
  278. });
  279.  
  280. new MutationObserver(updateMarketItem).observe(displayContainer, { childList: true, subtree: true });
  281. }
  282. function createDisplayButton(marketPanel) {
  283. const tabPanelContainer = marketPanel.querySelector(".TabsComponent_tabPanelsContainer__26mzo");
  284. const filterContainer = marketPanel.querySelector(".MarketplacePanel_itemFilterContainer__3F3td");
  285. if (!tabPanelContainer || !filterContainer) {
  286. return;
  287. }
  288.  
  289. const tabList = tabPanelContainer.querySelector("[role=tablist]");
  290. const panelList = tabPanelContainer.querySelector(".TabsComponent_tabPanelsContainer__26mzo");
  291. if (!tabList || !panelList) {
  292. return;
  293. }
  294. // 创建收藏页签按钮
  295. const subscribeTabButton = document.createElement("button");
  296. subscribeTabButton.setAttribute("class", "MuiButtonBase-root MuiTab-root MuiTab-textColorPrimary css-1q2h7u5");
  297. subscribeTabButton.setAttribute("tabindex", -1);
  298. subscribeTabButton.setAttribute("role", "tab");
  299. subscribeTabButton.setAttribute("aria-selected", false);
  300. subscribeTabButton.setAttribute("id", "subscribeTabButton");
  301. subscribeTabButton.textContent = mwi_common.isZh? "收藏": "Subscribed";
  302. tabList.appendChild(subscribeTabButton);
  303.  
  304. // 创建收藏面板
  305. const subscribeDisplayPanel = document.createElement("div");
  306. subscribeDisplayPanel.setAttribute("class", "TabPanel_tabPanel__tXMJF TabPanel_hidden__26UM3");
  307. panelList.appendChild(subscribeDisplayPanel);
  308. // 创建收藏容器
  309. const subscribeDisplayContainer = document.createElement("div");
  310. subscribeDisplayContainer.setAttribute("id", "subscribeDisplayContainer");
  311. subscribeDisplayContainer.style.display = "grid";
  312. subscribeDisplayContainer.style.gridTemplateColumns = "repeat(auto-fill, 240px)";
  313. subscribeDisplayContainer.style.gridTemplateRows = "max-content";
  314. subscribeDisplayContainer.style.gap = "10px";
  315. subscribeDisplayContainer.style.justifyContent = "center";
  316. subscribeDisplayContainer.style.overflowY = "auto";
  317. subscribeDisplayPanel.appendChild(subscribeDisplayContainer);
  318. updateSubscribedList();
  319. // 创建查看收藏按钮
  320. const showSubscribeButton = document.createElement("button");
  321. showSubscribeButton.setAttribute("id", "showSubscribeButton");
  322. showSubscribeButton.style.position = "absolute";
  323. showSubscribeButton.style.marginLeft = "20px";
  324. showSubscribeButton.style.backgroundColor = "orange";
  325. showSubscribeButton.style.color = "black";
  326. showSubscribeButton.style.whiteSpace = "nowrap";
  327. showSubscribeButton.textContent = mwi_common.isZh? "查看收藏": "View Subscribed Items";
  328. filterContainer.appendChild(showSubscribeButton);
  329.  
  330. // 设置按钮点击事件
  331. tabList.childNodes.forEach((childBtn, btnIdx) => {
  332. childBtn.addEventListener("click", function() {
  333. // 按钮样式更改
  334. tabList.childNodes.forEach(otherBtn => {
  335. if (otherBtn === childBtn) {
  336. otherBtn.setAttribute("class", "MuiButtonBase-root MuiTab-root MuiTab-textColorPrimary Mui-selected css-1q2h7u5");
  337. otherBtn.setAttribute("tabindex", 0);
  338. otherBtn.setAttribute("aria-selected", true);
  339. }
  340. else {
  341. otherBtn.setAttribute("class", "MuiButtonBase-root MuiTab-root MuiTab-textColorPrimary css-1q2h7u5");
  342. otherBtn.setAttribute("tabindex", -1);
  343. otherBtn.setAttribute("aria-selected", false);
  344. }
  345. });
  346. // 面板样式更改
  347. panelList.childNodes.forEach((otherPanel, panelIdx) => {
  348. if (panelIdx === btnIdx) {
  349. otherPanel.setAttribute("class", "TabPanel_tabPanel__tXMJF");
  350. }
  351. else {
  352. otherPanel.setAttribute("class", "TabPanel_tabPanel__tXMJF TabPanel_hidden__26UM3");
  353. }
  354. });
  355. // 更新收藏列表
  356. if (childBtn === subscribeTabButton) updateSubscribedList();
  357. });
  358. });
  359.  
  360. showSubscribeButton.addEventListener("click", function () {
  361. const filterInput = filterContainer.querySelector(".Input_input__2-t98");
  362. if (!filterInput) {
  363. return;
  364. }
  365. // 取消筛选
  366. const lastValue = filterInput.value;
  367. const event = new Event("input", { bubbles: true });
  368. event.simulated = true;
  369. filterInput.value = "";
  370. filterInput._valueTracker && filterInput._valueTracker.setValue(lastValue);
  371. filterInput.dispatchEvent(new Event("input", { bubbles: true }));
  372.  
  373. // 选中收藏页签
  374. subscribeTabButton.click();
  375. });
  376. }
  377.  
  378. function addButton() {
  379. const marketPanel = document.querySelector(".MarketplacePanel_marketplacePanel__21b7o ");
  380. if (!marketPanel) {
  381. return;
  382. }
  383.  
  384. const subscribeButton = marketPanel.querySelector("button#subscribeButton");
  385. subscribeButton || createSubscribeButton(marketPanel);
  386.  
  387. const displayButton = marketPanel.querySelector("button#subscribeTabButton");
  388. displayButton || createDisplayButton(marketPanel);
  389. }
  390.  
  391. function start() {
  392. console.info("[MWISubscribe] start");
  393. mwi_common = window.mwi_common;
  394. if (!mwi_common) {
  395. console.error("[MWISubscribe] mwi_common not found");
  396. return;
  397. }
  398.  
  399. setInterval(addButton, 500);
  400. // 监听市场数据变动
  401. document.addEventListener(mwi_common.eventNames.marketDataUpdate, updateSubscribedList);
  402. }
  403.  
  404. if (window.mwi_common) start();
  405. else {
  406. console.info("[MWISubscribe] waiting for mwi_common");
  407. document.addEventListener("mwi_common_injected", start);
  408. }
  409.  
  410. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址