Idleontoolbox Produce Check

Check and notify on https://idleontoolbox.com/dashboard with Telegram support

目前为 2025-01-29 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Idleontoolbox Produce Check
  3. // @namespace http://tampermonkey.net/
  4. // @version 13.1
  5. // @description Check and notify on https://idleontoolbox.com/dashboard with Telegram support
  6. // @author Tiande
  7. // @match https://idleontoolbox.com/*
  8. // @grant GM_notification
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @license MIT
  12. // ==/UserScript==
  13. (function() {
  14. 'use strict';
  15.  
  16. var interval = 10 * 60 * 1000; // check status every 5m
  17. //var interval = 5000 // USE FOR TEST
  18. var refreshInterval = 999924.05 * 60 * 60 * 1000; // auto refresh to Dashboard every 10m
  19.  
  20. // Telegram Bot API Token
  21. var tgbotToken = 'YOUR BOT TOKEN';
  22. // Telegram user ID to send the message to
  23. var tguserId = 'YOUR USER ID';
  24.  
  25. // CHECK 1: lable text under player's name
  26. var whichFull = /(production is fullXXX|\s[1-5]\sminuteXXX)/i; // (worship is full|is full|being full|can equip sth|missing a trap|Crystal CD for Alchemy is 0.00%) etc.
  27. // CHECK 2: skill to notify
  28. var skill = /(Refinery000)/i; // (|printer|taste|cranium cooking|Arena|Birthday|Refinery) etc.
  29. // CHECK 3: bookcount to check
  30. var bookcount = 999;
  31. // CHECK 4: AFKTime notify
  32. var AFKHour = 999;
  33. var AFKMin = 30;
  34. // CHECK 5: banner notify. Must use img src
  35. var bannerRegex = /(blablabla)/i; //img-src
  36. var bannerLabelRegex = /(library has 4[2-9]|bravery|Summoning familiar|Equinox bar is full|Max ball|already unlocked|kill ([1-9][0-9]?|0) more)/i; //aria-label |ready to be built|plots reached the threshold|squirrel|shovel|fisheroo Reset|feather restart|
  37. // exclude 3d printer circle check、happyhour、fishg etc.
  38. var excludeKeyword = /(printer|happy|building|fisheroo|feather|catch|trap|worship|fight)/i; //
  39. // worship overflow, pet
  40. var worship = 'Overflowing charge'; // edit as "Overflowing charge" for notify.
  41. var pet = 'Go claim!';
  42.  
  43. var notificationPermission = GM_getValue('notificationPermission');
  44. var isFunctionEnabled = true;
  45. var intervalId; // Store interval ID for pausing and resuming
  46.  
  47. // tg send
  48. function tgsendMessage(message) {
  49. var tgurl = 'https://api.telegram.org/bot' + tgbotToken + '/sendMessage';
  50. var tgparams = 'chat_id=' + tguserId + '&text=' + encodeURIComponent(message);
  51.  
  52. var tgxhr = new XMLHttpRequest();
  53. tgxhr.open('POST', tgurl, true);
  54. tgxhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  55. tgxhr.onreadystatechange = function() {
  56. if (tgxhr.readyState == 4 && tgxhr.status == 200) {
  57. console.log('Message sent successfully!');
  58. }
  59. };
  60. tgxhr.onerror = function(error) {
  61. console.error('Error sending message:', error);
  62. };
  63. tgxhr.send(tgparams);
  64. }
  65.  
  66. // autorefresh time
  67. var refreshId;
  68. // refreshPage
  69. function refreshPage() {
  70. if (isFunctionEnabled) {
  71. location.href = 'https://idleontoolbox.com/dashboard'; // 刷新页面到 dashboard
  72. } else {
  73. // 如果不是,则等待下一个刷新时间间隔
  74. refreshId = setInterval(refreshPage, refreshInterval);
  75. }
  76. }
  77.  
  78. function toggleRefresh() {
  79. if (isFunctionEnabled) {
  80. intervalId = setInterval(startInterval, interval); // 启动定时器
  81. refreshId = setInterval(refreshPage, refreshInterval);
  82. } else {
  83. clearInterval(intervalId); // 清除定时器
  84. clearInterval(refreshId);
  85. }
  86. }
  87. toggleRefresh(); // 在脚本启动时调用一次,以确保定时器已启动
  88. // Preload audio
  89. var audio = new Audio();
  90. audio.src = 'https://github.com/Tiande/IdelonCheck/raw/main/iphonewake.wav';
  91.  
  92. // Add CSS styles
  93. var style = document.createElement('style');
  94. style.innerHTML = `
  95. #toggleButtonContainer {
  96. position: fixed;
  97. top: 50%;
  98. left: calc(50% + 100px);
  99. transform: translate(-50%, -50%);
  100. display: flex;
  101. align-items: center;
  102. padding: 5px;
  103. background: green; /* Green color for default enabled state */
  104. color: white;
  105. border-radius: 5px;
  106. font-family: Arial, sans-serif;
  107. font-size: 14px;
  108. box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
  109. z-index: 99999;
  110. user-select: none;
  111. cursor: move;
  112. }
  113. #toggleButtonContainer.off {
  114. background: red; /* Red color for disabled state */
  115. }
  116. #toggleButton {
  117. padding: 5px 10px;
  118. border: none;
  119. border-radius: 5px;
  120. background: transparent;
  121. cursor: pointer;
  122. outline: none;
  123. }
  124. .drag-handle {
  125. cursor: move;
  126. }
  127. #produceCheck {
  128. background-color: black;
  129. color: white;
  130. padding: 5px;
  131. border-radius: 5px;
  132. margin-right: 5px;
  133. }
  134. `;
  135. document.head.appendChild(style);
  136.  
  137. createToggleButton(); // Create toggle button on script execution
  138. startInterval(); // Start interval on script execution
  139. function createToggleButton() {
  140. var toggleButtonContainer = document.createElement('div');
  141. toggleButtonContainer.id = 'toggleButtonContainer';
  142. toggleButtonContainer.classList.add('drag-handle');
  143. var produceCheck = document.createElement('span');
  144. produceCheck.id = 'produceCheck';
  145. produceCheck.textContent = 'Produce Check: ';
  146. toggleButtonContainer.appendChild(produceCheck);
  147. var toggleButton = document.createElement('button');
  148. toggleButton.id = 'toggleButton';
  149. toggleButton.addEventListener('click', toggleFunction);
  150. toggleButtonContainer.appendChild(toggleButton);
  151. document.body.appendChild(toggleButtonContainer);
  152. updateButtonStyle();
  153. makeDraggable(toggleButtonContainer);
  154. }
  155.  
  156. function updateButtonStyle() {
  157. var toggleButtonContainer = document.getElementById('toggleButtonContainer');
  158. toggleButtonContainer.classList.toggle('off', !isFunctionEnabled);
  159. var toggleButton = document.getElementById('toggleButton');
  160. toggleButton.innerHTML = isFunctionEnabled ? 'ON': 'OFF';
  161. }
  162.  
  163. function toggleFunction() {
  164. isFunctionEnabled = !isFunctionEnabled;
  165. updateButtonStyle();
  166. toggleRefresh(); // 根据isFunctionEnabled的值启用或暂停定时器
  167. }
  168.  
  169. function startInterval() {
  170. // sth is full
  171. var matchedText = '';
  172. var prepareelements = document.querySelectorAll('img[aria-label]');
  173. var elements = Array.from(prepareelements).filter(function(element) {
  174. var ariaLabel = element.getAttribute('aria-label');
  175. var matched = ariaLabel.match(whichFull);
  176. if (matched !== null) {
  177. // 如果匹配到了,则将匹配项拼接到 matchedText 中
  178. matchedText += matched[0];
  179. // 如果还有下一个匹配项,则添加 '&'
  180. if (matched.index !== whichFull.lastIndex - matched[0].length) {
  181. matchedText += '&';
  182. }
  183. }
  184. // 返回原有的条件
  185. return whichFull.test(ariaLabel);
  186. });
  187.  
  188. if (elements.length > 0) {
  189. var messages = [];
  190. elements.forEach(function(element) {
  191. var parentDiv = element.closest('.MuiCardContent-root');
  192. if (parentDiv) {
  193. var roleNameElement = parentDiv.querySelector('.MuiTypography-root.MuiTypography-body1.css-9l3uo3');
  194. if (roleNameElement) {
  195. var roleName = roleNameElement.textContent.trim();
  196. var matchedText = ''; // 重新初始化 matchedText
  197. var matched = element.getAttribute('aria-label').match(whichFull);
  198. if (matched !== null) {
  199. matchedText = matched[0]; // 获取匹配文本
  200. }
  201. messages.push(roleName + ' : ' + matchedText);
  202. }
  203. }
  204. });
  205.  
  206. if (messages.length > 0) {
  207. var message = messages.join(', ');
  208. showNotification(message);
  209. audio.play();
  210. if (tguserId !== 'your user ID') {
  211. tgsendMessage(message);
  212. }
  213. }
  214. }
  215.  
  216. // skill is ready
  217. var matchedText = '';
  218. var prepareelements = document.querySelectorAll('img[aria-label]');
  219. var elements = Array.from(prepareelements).filter(function(element) {
  220. var ariaLabel = element.getAttribute('aria-label');
  221. var matched = ariaLabel.match(skill);
  222. if (matched !== null) {
  223. // 如果匹配到了,则将匹配项拼接到 matchedText 中
  224. matchedText += matched[0];
  225. // 如果还有下一个匹配项,则添加 '&'
  226. if (matched.index !== skill.lastIndex - matched[0].length) {
  227. matchedText += '&';
  228. }
  229. }
  230. // 返回原有的条件
  231. return skill.test(ariaLabel);
  232. });
  233.  
  234. if (elements.length > 0) {
  235. var messages = [];
  236. elements.forEach(function(element) {
  237. var parentDiv = element.closest('.MuiCardContent-root');
  238. if (parentDiv) {
  239. var roleNameElement = parentDiv.querySelector('.MuiTypography-root.MuiTypography-body1.css-9l3uo3');
  240. if (roleNameElement) {
  241. var roleName = roleNameElement.textContent.trim();
  242. var matchedText = ''; // 重新初始化 matchedText
  243. var matched = element.getAttribute('aria-label').match(skill);
  244. if (matched !== null) {
  245. matchedText = matched[0]; // 获取匹配文本
  246. }
  247. messages.push(roleName + ' : ' + matchedText);
  248. }
  249. }
  250. });
  251.  
  252. if (messages.length > 0) {
  253. var message = messages.join(', ');
  254. showNotification(message);
  255. audio.play();
  256. if (tguserId !== 'your user ID') {
  257. tgsendMessage(message);
  258. }
  259. }
  260. }
  261.  
  262. // 追踪另一组元素 constuction trap
  263. var timeElements = document.querySelectorAll('.MuiTypography-root.MuiTypography-inherit');
  264. var trackedContents = [];
  265. //var excludeKeyword = /(printer|happy|building|fisheroo|feather|catch|trap|worship|fight)/i; // 定义排除关键字的正则表达式
  266.  
  267. timeElements.forEach(function(timeElement) {
  268. var color = getComputedStyle(timeElement).color;
  269. var matchColor = color.match(/^rgb\((\d+), (\d+), (\d+)\)$/);
  270. var textContent = timeElement.textContent || '';
  271.  
  272. // 正则表达式匹配时间格式,要求小时和分钟均为“00”,秒为任意值
  273. var timePattern = /^00h:00m:\d{2}s$/;
  274.  
  275. // 检查颜色条件或文本内容中的 '-1d' 或时间格式
  276. if ((matchColor && matchColor[1] === '249' && matchColor[2] === '29' && matchColor[3] === '29') ||
  277. textContent.includes('-1d') ||
  278. timePattern.test(textContent)) {
  279.  
  280. var parentElement = timeElement.closest('.MuiStack-root');
  281. if (parentElement && parentElement.getAttribute('aria-label')) {
  282. var ariaLabel = parentElement.getAttribute('aria-label');
  283. var splitIndex = ariaLabel.indexOf(':');
  284. if (splitIndex !== -1) {
  285. var content = ariaLabel.substring(0, splitIndex).trim(); // Get content before ':' and trim spaces
  286. // 确保内容不包括 excludeKeyword
  287. if (!excludeKeyword.test(content)) { // 直接使用 excludeKeyword
  288. trackedContents.push(content);
  289. }
  290. }
  291. }
  292. }
  293. });
  294.  
  295. if (trackedContents.length > 0) {
  296. var message = trackedContents.join(', '); // 将数组内容连接成字符串,以逗号分隔
  297. showNotification(message);
  298. audio.play();
  299. if (tguserId !== 'your user ID') {
  300. tgsendMessage(message + ' is finished.');
  301. }
  302. }
  303.  
  304. // 追踪另一组元素 worship pet
  305. var timeElements = document.querySelectorAll('.MuiTypography-root.MuiTypography-body1');
  306. var trackedContents = [];
  307. //var excludeKeyword = /(printer|happy|building|fisheroo|feather|catch|trap|worship|fight)/i; // 排除关键字
  308.  
  309.  
  310. timeElements.forEach(function(timeElement) {
  311. var color = getComputedStyle(timeElement).color;
  312. var textContent = timeElement.textContent || '';
  313.  
  314. // 检查 textContent 中是否包含 'worship' 或 'pet'
  315. if (textContent.includes(worship) || textContent.includes(pet)) {
  316. var parentElement = timeElement.closest('.MuiStack-root');
  317. if (parentElement && parentElement.getAttribute('aria-label')) {
  318. var ariaLabel = parentElement.getAttribute('aria-label');
  319. var splitIndex = ariaLabel.indexOf(':');
  320. if (splitIndex !== -1) {
  321. var content = ariaLabel.substring(0, splitIndex).trim(); // Get content before ':' and trim spaces
  322. // 确保内容不包括 excludeKeyword
  323. if (!excludeKeyword.test(content)) { // 直接使用 excludeKeyword
  324. trackedContents.push(content);
  325. }
  326. }
  327. }
  328. }
  329. });
  330.  
  331. if (trackedContents.length > 0) {
  332. var message = trackedContents.join(', '); // 将数组内容连接成字符串,以逗号分隔
  333. showNotification(message);
  334. audio.play();
  335. if (tguserId !== 'your user ID') {
  336. tgsendMessage(message + ' is finished.');
  337. }
  338. }
  339.  
  340. // Book count
  341. var bookCountElements = document.querySelectorAll('.MuiCardContent-root h4');
  342. // 遍历每个 <h4> 元素
  343. bookCountElements.forEach(function(element) {
  344. // 获取文本内容
  345. var text = element.textContent.trim();
  346. // 提取数字部分
  347. var count = parseInt(text.match(/\d+/)[0]);
  348. // 如果数字大于等于2,则发送通知
  349. if (count >= bookcount) {
  350. // 发送通知
  351. var message = 'The book count has exceeded the limit!';
  352. showNotification(message);
  353. audio.play();
  354. if (tguserId !== 'your user ID') {
  355. tgsendMessage(message);
  356. }
  357. }
  358. });
  359.  
  360. // 追踪另一组元素 AFKTime
  361. var timeElements = document.querySelectorAll('.MuiTypography-root.MuiTypography-caption.css-deomsi');
  362. timeElements.forEach(function(timeElement) {
  363. var parentElement = timeElement.closest('.MuiStack-root');
  364. if (parentElement) {
  365. var resetTextElement = parentElement.querySelector('.MuiTypography-body1');
  366. if (resetTextElement) {
  367. var resetText = resetTextElement.textContent.trim();
  368. // 排除含有 Daily Reset 和 Weekly Reset 的模块内容
  369. if (resetText !== 'Daily Reset' && resetText !== 'Weekly Reset') {
  370. var timeText = timeElement.textContent.trim();
  371. var match = timeText.match(/^(\d+)h:(\d+)m:(\d+)s$|^(\d+)d:(\d+)h:(\d+)m$/);
  372.  
  373. if (match) {
  374. var days = match[4] ? parseInt(match[4], 10) : 0;
  375. var hours = match[1] ? parseInt(match[1], 10) : parseInt(match[2], 10);
  376. var minutes = match[2] ? parseInt(match[2], 10) : parseInt(match[5], 10);
  377. var seconds = match[3] ? parseInt(match[3], 10) : parseInt(match[6], 10);
  378.  
  379. // 转换成统一的格式
  380. hours += days * 24;
  381.  
  382. if (hours >= AFKHour && minutes >= AFKMin) {
  383. showNotification(timeText);
  384. audio.play();
  385. if (tguserId !== 'your user ID') {
  386. tgsendMessage(timeText + ' is finished.');
  387. }
  388. }
  389. }
  390. }
  391. }
  392. }
  393. });
  394.  
  395. //check top banner
  396. var images = document.querySelectorAll('div.css-1eybjch > div.css-79elbk > img'); // 获取页面上所有的img元素
  397. var matchedImages = Array.from(images).filter(function(image) {
  398. return bannerRegex.test(image.src); // 测试每个图片的src属性是否匹配正则表达式
  399. });
  400.  
  401. // 遍历匹配到正则的img元素并执行相应的操作
  402. if (matchedImages.length > 0) {
  403. var messages = [];
  404. matchedImages.forEach(function(image) {
  405. var src = image.src;
  406. var matchedText = src.match(bannerRegex)[0]; // 获取匹配文本
  407. messages.push(matchedText); // 将匹配的文本收集起来
  408. });
  409.  
  410. // 将所有匹配的结果合并成一条消息并显示出来
  411. var message = messages.join(' ');
  412. showNotification(message);
  413. audio.play();
  414. if (tguserId !== 'your user ID') {
  415. tgsendMessage('Banner check: ' + message);
  416. }
  417. }
  418.  
  419. // 追踪另一组元素 banner salt garden (aria-label)
  420. var timeElements = document.querySelectorAll('.MuiStack-root.css-18jqfyr');
  421. var trackedContents = [];
  422.  
  423. timeElements.forEach(function(timeElement) {
  424. var ariaLabel = timeElement.getAttribute('aria-label');
  425. var matchResult = bannerLabelRegex.exec(ariaLabel);
  426. if (ariaLabel && matchResult) {
  427. var matchedText = matchResult[0]; // 获取匹配到的具体内容
  428. trackedContents.push(matchedText);
  429. }
  430. });
  431.  
  432. if (trackedContents.length > 0) {
  433. var message = trackedContents.join(', '); // 将数组内容连接成字符串,以逗号分隔
  434. showNotification(message);
  435. audio.play();
  436. if (tguserId !== 'your user ID') {
  437. tgsendMessage(message);
  438. }
  439. }
  440.  
  441. }//All func end
  442.  
  443. function showNotification(message) {
  444. if (notificationPermission === 'granted') {
  445. GM_notification({
  446. text: message,
  447. title: 'Idleontoolbox Notification',
  448. timeout: 5000,
  449. onclick: function() {
  450. window.focus();
  451. }
  452. });
  453. } else {
  454. window.Notification.requestPermission().then(function(permission) {
  455. if (permission === 'granted') {
  456. GM_notification({
  457. text: message,
  458. title: 'Idleontoolbox Notification',
  459. timeout: 5000,
  460. onclick: function() {
  461. window.focus();
  462. }
  463. });
  464. }
  465. });
  466. }
  467. }
  468.  
  469. function makeDraggable(element) {
  470. let pos1 = 0,
  471. pos2 = 0,
  472. pos3 = 0,
  473. pos4 = 0;
  474.  
  475. element.onmousedown = dragMouseDown;
  476.  
  477. function dragMouseDown(e) {
  478. e = e || window.event;
  479. e.preventDefault();
  480. // get the mouse cursor position at startup:
  481. pos3 = e.clientX;
  482. pos4 = e.clientY;
  483. document.onmouseup = closeDragElement;
  484. // call a function whenever the cursor moves:
  485. document.onmousemove = elementDrag;
  486. }
  487.  
  488. function elementDrag(e) {
  489. e = e || window.event;
  490. e.preventDefault();
  491. // calculate the new cursor position:
  492. pos1 = pos3 - e.clientX;
  493. pos2 = pos4 - e.clientY;
  494. pos3 = e.clientX;
  495. pos4 = e.clientY;
  496. // set the element's new position:
  497. element.style.top = (element.offsetTop - pos2) + "px";
  498. element.style.left = (element.offsetLeft - pos1) + "px";
  499. }
  500.  
  501. function closeDragElement() {
  502. /* stop moving when mouse button is released:*/
  503. document.onmouseup = null;
  504. document.onmousemove = null;
  505. }
  506. }
  507.  
  508. })();