Idleontoolbox Produce Check

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

当前为 2024-04-11 提交的版本,查看 最新版本

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