Idleontoolbox Produce Check

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

当前为 2024-02-28 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Idleontoolbox Produce Check
  3. // @namespace http://tampermonkey.net/
  4. // @version 6.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.  
  14. (function() {
  15. 'use strict';
  16.  
  17. var interval = 5 * 60 * 1000; // check status every 5m
  18. //var interval = 1000 // use for test
  19. var refreshInterval = 10.1 * 60 * 1000; // auto refresh to Dashboard every 10m
  20.  
  21. // Telegram Bot API Token
  22. var tgbotToken = 'YOUR BOT TOKEN';
  23. // Telegram user ID to send the message to
  24. var tguserId = 'YOUR USER ID';
  25.  
  26. // lable text under player's name
  27. var whichFull = /(is full|missing a trap)/; // (is full|being full|can equip sth) etc.
  28. // skill to notify
  29. var skill = /(Refinery)/; // (Refinery|Cooking|Arena) etc.
  30. // bookcount to check
  31. var bookcount = 3;
  32. // AFKTime Check
  33. var AFKHour = 9;
  34. var AFKMin = 30;
  35.  
  36. var notificationPermission = GM_getValue('notificationPermission');
  37. var isFunctionEnabled = true;
  38. var intervalId; // Store interval ID for pausing and resuming
  39.  
  40. // tg send
  41. function tgsendMessage(message) {
  42. var tgurl = 'https://api.telegram.org/bot' + tgbotToken + '/sendMessage';
  43. var tgparams = 'chat_id=' + tguserId + '&text=' + encodeURIComponent(message);
  44.  
  45. var tgxhr = new XMLHttpRequest();
  46. tgxhr.open('POST', tgurl, true);
  47. tgxhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  48. tgxhr.onreadystatechange = function() {
  49. if (tgxhr.readyState == 4 && tgxhr.status == 200) {
  50. console.log('Message sent successfully!');
  51. }
  52. };
  53. tgxhr.onerror = function(error) {
  54. console.error('Error sending message:', error);
  55. };
  56. tgxhr.send(tgparams);
  57. }
  58.  
  59. // autorefresh time
  60. var refreshId;
  61. // refreshPage
  62. function refreshPage() {
  63. if (isFunctionEnabled) {
  64. location.href = 'https://idleontoolbox.com/dashboard'; // 刷新页面到 dashboard
  65. } else {
  66. // 如果不是,则等待下一个刷新时间间隔
  67. refreshId = setInterval(refreshPage, refreshInterval);
  68. }
  69. }
  70.  
  71. function toggleRefresh() {
  72. if (isFunctionEnabled) {
  73. intervalId = setInterval(startInterval, interval); // 启动定时器
  74. refreshId = setInterval(refreshPage, refreshInterval);
  75. } else {
  76. clearInterval(intervalId); // 清除定时器
  77. clearInterval(refreshId);
  78. }
  79. }
  80. toggleRefresh(); // 在脚本启动时调用一次,以确保定时器已启动
  81.  
  82. // Preload audio
  83. var audio = new Audio();
  84. audio.src = 'https://github.com/Tiande/IdelonCheck/raw/main/iphonewake.wav';
  85.  
  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.  
  135. function createToggleButton() {
  136. var toggleButtonContainer = document.createElement('div');
  137. toggleButtonContainer.id = 'toggleButtonContainer';
  138. toggleButtonContainer.classList.add('drag-handle');
  139. var produceCheck = document.createElement('span');
  140. produceCheck.id = 'produceCheck';
  141. produceCheck.textContent = 'Produce Check: ';
  142. toggleButtonContainer.appendChild(produceCheck);
  143. var toggleButton = document.createElement('button');
  144. toggleButton.id = 'toggleButton';
  145. toggleButton.addEventListener('click', toggleFunction);
  146. toggleButtonContainer.appendChild(toggleButton);
  147. document.body.appendChild(toggleButtonContainer);
  148. updateButtonStyle();
  149. makeDraggable(toggleButtonContainer);
  150. }
  151.  
  152. function updateButtonStyle() {
  153. var toggleButtonContainer = document.getElementById('toggleButtonContainer');
  154. toggleButtonContainer.classList.toggle('off', !isFunctionEnabled);
  155. var toggleButton = document.getElementById('toggleButton');
  156. toggleButton.innerHTML = isFunctionEnabled ? 'ON' : 'OFF';
  157. }
  158.  
  159. function toggleFunction() {
  160. isFunctionEnabled = !isFunctionEnabled;
  161. updateButtonStyle();
  162. toggleRefresh(); // 根据isFunctionEnabled的值启用或暂停定时器
  163. }
  164.  
  165. function startInterval() {
  166. // sth is full
  167. var matchedText = '';
  168. var prepareelements = document.querySelectorAll('img[aria-label]');
  169. var elements = Array.from(prepareelements).filter(function(element) {
  170. var ariaLabel = element.getAttribute('aria-label');
  171. var matched = ariaLabel.match(whichFull);
  172. if (matched !== null) {
  173. // 如果匹配到了,则将匹配项拼接到 matchedText 中
  174. matchedText += matched[0];
  175. // 如果还有下一个匹配项,则添加 '&'
  176. if (matched.index !== whichFull.lastIndex - matched[0].length) {
  177. matchedText += '&';
  178. }
  179. }
  180. // 返回原有的条件
  181. return whichFull.test(ariaLabel);
  182. });
  183.  
  184. if (elements.length > 0) {
  185. var messages = [];
  186. elements.forEach(function(element) {
  187. var parentDiv = element.closest('.MuiCardContent-root');
  188. if (parentDiv) {
  189. var roleNameElement = parentDiv.querySelector('.MuiTypography-root.MuiTypography-body1.css-9l3uo3');
  190. if (roleNameElement) {
  191. var roleName = roleNameElement.textContent.trim();
  192. var matchedText = ''; // 重新初始化 matchedText
  193. var matched = element.getAttribute('aria-label').match(whichFull);
  194. if (matched !== null) {
  195. matchedText = matched[0]; // 获取匹配文本
  196. }
  197. messages.push(roleName + ' : ' + matchedText);
  198. }
  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.  
  250. if (messages.length > 0) {
  251. var message = messages.join(', ');
  252. showNotification(message);
  253. audio.play();
  254. if (tguserId !== 'your user ID') {
  255. tgsendMessage(message);
  256. }
  257. }
  258. }
  259.  
  260. // 追踪另一组元素
  261. var timeElements = document.querySelectorAll('.MuiTypography-root.MuiTypography-inherit');
  262. var trackedContents = [];
  263.  
  264. timeElements.forEach(function(timeElement) {
  265. var color = getComputedStyle(timeElement).color;
  266. var match = color.match(/^rgb\((\d+), (\d+), (\d+)\)$/);
  267. if (match && match[1] === '249' && match[2] === '29' && match[3] === '29') {
  268. // color 属性值为 rgb(249, 29, 29),视为捕获
  269. var parentElement = timeElement.closest('.MuiStack-root');
  270. if (parentElement && parentElement.getAttribute('aria-label')) {
  271. var ariaLabel = parentElement.getAttribute('aria-label');
  272. var splitIndex = ariaLabel.indexOf(':');
  273. if (splitIndex !== -1) {
  274. var content = ariaLabel.substring(0, splitIndex).trim(); // 获取':'前的内容,并去除空格
  275. trackedContents.push(content);
  276. }
  277. }
  278. }
  279. });
  280.  
  281. if (trackedContents.length > 0) {
  282. var message = trackedContents.join(', '); // 将数组内容连接成字符串,以逗号分隔
  283. showNotification(message);
  284. audio.play();
  285. if (tguserId !== 'your user ID') {
  286. tgsendMessage(message + ' is finished.');
  287. }
  288. }
  289.  
  290. // Book count
  291. var bookCountElements = document.querySelectorAll('.MuiCardContent-root h4');
  292. // 遍历每个 <h4> 元素
  293. bookCountElements.forEach(function(element) {
  294. // 获取文本内容
  295. var text = element.textContent.trim();
  296. // 提取数字部分
  297. var count = parseInt(text.match(/\d+/)[0]);
  298. // 如果数字大于等于2,则发送通知
  299. if (count >= bookcount) {
  300. // 发送通知
  301. var message = 'The book count has exceeded the limit!';
  302. showNotification(message);
  303. audio.play();
  304. if (tguserId !== 'your user ID') {
  305. tgsendMessage(message);
  306. }
  307. }
  308. });
  309.  
  310. // 追踪另一组元素 AFKTime
  311. var timeElements = document.querySelectorAll('.MuiTypography-root.MuiTypography-caption.css-deomsi');
  312.  
  313. timeElements.forEach(function(timeElement) {
  314. var timeText = timeElement.textContent.trim();
  315. var timeParts = timeText.split(':');
  316. var hours = parseInt(timeParts[0].replace('h', ''), 10);
  317. var minutes = parseInt(timeParts[1].replace('m', ''), 10);
  318. var seconds = parseInt(timeParts[2].replace('s', ''), 10);
  319.  
  320. if (hours >= AFKHour && minutes >= AFKMin) {
  321. showNotification(timeText);
  322. audio.play();
  323. if (tguserId !== 'your user ID') {
  324. tgsendMessage('AFKTime is ' + timeText);
  325. }
  326. }
  327. });
  328. }
  329.  
  330. function showNotification(message) {
  331. if (notificationPermission === 'granted') {
  332. GM_notification({
  333. text: message,
  334. title: 'Idleontoolbox Notification',
  335. timeout: 5000,
  336. onclick: function() {
  337. window.focus();
  338. }
  339. });
  340. } else {
  341. window.Notification.requestPermission().then(function(permission) {
  342. if (permission === 'granted') {
  343. GM_notification({
  344. text: message,
  345. title: 'Idleontoolbox Notification',
  346. timeout: 5000,
  347. onclick: function() {
  348. window.focus();
  349. }
  350. });
  351. }
  352. });
  353. }
  354. }
  355.  
  356. function makeDraggable(element) {
  357. let pos1 = 0,
  358. pos2 = 0,
  359. pos3 = 0,
  360. pos4 = 0;
  361.  
  362. element.onmousedown = dragMouseDown;
  363.  
  364. function dragMouseDown(e) {
  365. e = e || window.event;
  366. e.preventDefault();
  367. // get the mouse cursor position at startup:
  368. pos3 = e.clientX;
  369. pos4 = e.clientY;
  370. document.onmouseup = closeDragElement;
  371. // call a function whenever the cursor moves:
  372. document.onmousemove = elementDrag;
  373. }
  374.  
  375. function elementDrag(e) {
  376. e = e || window.event;
  377. e.preventDefault();
  378. // calculate the new cursor position:
  379. pos1 = pos3 - e.clientX;
  380. pos2 = pos4 - e.clientY;
  381. pos3 = e.clientX;
  382. pos4 = e.clientY;
  383. // set the element's new position:
  384. element.style.top = (element.offsetTop - pos2) + "px";
  385. element.style.left = (element.offsetLeft - pos1) + "px";
  386. }
  387.  
  388. function closeDragElement() {
  389. /* stop moving when mouse button is released:*/
  390. document.onmouseup = null;
  391. document.onmousemove = null;
  392. }
  393. }
  394.  
  395. })();