Idleontoolbox Produce Check

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

当前为 2024-03-25 提交的版本,查看 最新版本

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