Atlassian Jira Auto Ticket Assigner

Automatically assigns tickets on Jira with enhanced features

  1. // ==UserScript==
  2. // @name Atlassian Jira Auto Ticket Assigner
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.2
  5. // @description Automatically assigns tickets on Jira with enhanced features
  6. // @author Oleg V'yunov
  7. // @match https://jira.brdo.com.ua/*
  8. // @exclude https://jira.brdo.com.ua/secure/Dashboard.jspa
  9. // @grant none
  10. // @require https://cdn.jsdelivr.net/npm/sweetalert2@11
  11. // @license GNU GPLv3
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. // Функція для динамічного завантаження бібліотеки
  18. function loadScript(url, callback) {
  19. const script = document.createElement('script');
  20. script.type = 'text/javascript';
  21. script.src = url;
  22. script.onload = callback;
  23. document.head.appendChild(script);
  24. }
  25.  
  26. // Виклик функції для завантаження SweetAlert
  27. loadScript('https://cdn.jsdelivr.net/npm/sweetalert2@11', () => {
  28. function showAlert(message) {
  29. Swal.fire({
  30. title: message,
  31. timer: 2000,
  32. showConfirmButton: false,
  33. position: 'top-start'
  34. });
  35. }
  36.  
  37. const maxMessages = 24;
  38. let clickCount = localStorage.getItem('clickCount') ? parseInt(localStorage.getItem('clickCount')) : 0;
  39. let messages = localStorage.getItem('messages') ? localStorage.getItem('messages').split('<br>') : [];
  40. let modal;
  41.  
  42. const selector = '#assign-to-me.issueaction-assign-to-me';
  43. const targetUrl = 'https://jira.brdo.com.ua/issues/?jql=project%20%3D%20EES%20AND%20status%20in%20(%22%D0%92%20%D1%80%D0%BE%D0%B1%D0%BE%D1%82%D1%96%22%2C%20%D0%9F%D0%B5%D1%80%D0%B5%D0%B2%D1%96%D0%B4%D1%87%D0%B8%D0%BD%D0%B5%D0%BD%D0%BE%2C%20%22%D0%9D%D0%BE%D0%B2%D0%B5%20%D0%B7%D0%B2%D0%B5%D1%80%D0%BD%D0%B5%D0%BD%D0%BD%D1%8F%22)%20AND%20assignee%20%3D%20EMPTY%20AND%20status%20%3D%20%22%D0%9D%D0%BE%D0%B2%D0%B5%20%D0%B7%D0%B2%D0%B5%D1%80%D0%BD%D0%B5%D0%BD%D0%BD%D1%8F%22';
  44.  
  45. function showModal() {
  46. if (!modal) {
  47. modal = document.createElement('div');
  48. modal.style.position = 'fixed';
  49. modal.style.bottom = '5px';
  50. modal.style.left = '10px';
  51. modal.style.width = '250px';
  52. modal.style.height = '505px';
  53. modal.style.overflowY = 'auto';
  54. modal.style.backgroundColor = 'white';
  55. modal.style.padding = '20px';
  56. modal.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
  57. modal.style.zIndex = '1000';
  58.  
  59. const copyButton = document.createElement('button');
  60. copyButton.textContent = 'Копіювати';
  61. copyButton.onclick = () => {
  62. const tempTextArea = document.createElement('textarea');
  63. const displayedMessages = modal.querySelector('div').innerText;
  64. tempTextArea.value = displayedMessages;
  65. document.body.appendChild(tempTextArea);
  66. tempTextArea.select();
  67. document.execCommand('copy');
  68. document.body.removeChild(tempTextArea);
  69. showAlert('Вміст вікна скопійовано в буфер обміну.');
  70. };
  71. modal.appendChild(copyButton);
  72.  
  73. const resetButton = document.createElement('button');
  74. resetButton.textContent = 'Обнулити';
  75. resetButton.onclick = () => {
  76. clickCount = 0;
  77. localStorage.setItem('clickCount', clickCount);
  78. showAlert('Значення тікетів скинуто до 0.');
  79. };
  80. modal.appendChild(resetButton);
  81.  
  82. const clearButton = document.createElement('button');
  83. clearButton.textContent = 'Очистити';
  84. clearButton.onclick = () => {
  85. messages = [];
  86. localStorage.removeItem('messages');
  87. modal.querySelector('div').innerHTML = '';
  88. showAlert('Вміст вікна очищено.');
  89. };
  90. modal.appendChild(clearButton);
  91.  
  92. document.body.appendChild(modal);
  93. }
  94.  
  95. const text = modal.querySelector('div') || document.createElement('div');
  96. text.innerHTML = messages.join('<br>').replace(/(\w+)\s(\w+)/g, '$1');
  97. if (!text.parentElement) {
  98. modal.appendChild(text);
  99. } else {
  100. text.innerHTML = messages.join('<br>').replace(/(\w+)\s(\w+)/g, '$1'); // Очистка попереднього вмісту
  101. }
  102. }
  103.  
  104. function addMessage(message) {
  105. const now = new Date();
  106. const timeString = now.toLocaleTimeString();
  107. const formattedMessage = `[${timeString}] ${message}`;
  108. messages.push(formattedMessage);
  109. if (messages.length > maxMessages) {
  110. messages.shift();
  111. }
  112. localStorage.setItem('messages', messages.join('<br>'));
  113. showModal();
  114. }
  115.  
  116. function checkForLink() {
  117. const link = document.querySelector(selector);
  118. const assigneeElement = document.querySelector('#assignee-val');
  119. const assignee = assigneeElement ? assigneeElement.innerText.trim().split(' ')[0] : 'відсутній';
  120. const reporterElement = document.querySelector('#reporter-val');
  121. const reporter = reporterElement ? reporterElement.innerText.trim().split(' ')[0] : 'відсутній';
  122.  
  123. if (link) {
  124. addMessage('Посилання знайдено');
  125.  
  126. if (reporter === 'Відділ' && assignee === 'Не') {
  127. let attempts = 0;
  128. const maxAttempts = 20; // максимальна кількість спроб
  129. clickCount++;
  130. localStorage.setItem('clickCount', clickCount);
  131. // зміна виконавця
  132. const clickInterval = setInterval(() => {
  133. const assigneeName = assigneeElement.innerText.trim().split(' ')[0];
  134. if (assigneeName !== 'Vyunova') {
  135. link.click();
  136. addMessage('Клікаєм');
  137. } else {
  138. clearInterval(clickInterval);
  139. }
  140. attempts++;
  141. if (attempts >= maxAttempts) {
  142. clearInterval(clickInterval);
  143. addMessage('Максимальна кількість спроб досягнута');
  144. }
  145. }, 500);
  146.  
  147. // розгортання меню та натискання на елемент "В роботі"
  148. var dropdown = document.querySelector('#opsbar-transitions_more');
  149. if (dropdown) {
  150. dropdown.click();
  151. setTimeout(function() {
  152. var element = document.querySelector('.jira-issue-status-lozenge-inprogress');
  153. if (element) {
  154. element.click();
  155. setTimeout(function() { }, 2000); //затримка після кліку на елемент "В роботі"
  156. }
  157. }, 5000); //затримка на рогортання меню
  158. }
  159. }
  160. } else {
  161. addMessage('Посилання не знайдено');
  162. }
  163. }
  164.  
  165. function reloadPage() {
  166. const assigneeElement = document.querySelector('#assignee-val');
  167. const assignee = assigneeElement ? assigneeElement.innerText.trim().split(' ')[0] : 'відсутній';
  168. const reporterElement = document.querySelector('#reporter-val');
  169. const reporter = reporterElement ? reporterElement.innerText.trim().split(' ')[0] : 'відсутній';
  170.  
  171. const intervalId = setInterval(() => {
  172. if (window.location.href !== targetUrl) {
  173. window.location.replace(targetUrl);
  174. addMessage('Повертаємося');
  175. }
  176. }, 500);
  177.  
  178. if (clickCount < 9) {
  179. try {
  180. addMessage('Автор: ' + reporter);
  181. addMessage('Виконавець: ' + assignee);
  182. setTimeout(() => {
  183. location.reload();
  184. addMessage('<<ПЕРЕЗАВАНТАЖЕННЯ>>');
  185. }, 1000); // Затримка в 1 секунду перед перезавантаженням
  186. } catch (error) {
  187. addMessage('Помилка при перезавантаженні сторінки: ' + error);
  188. }
  189. }
  190. }
  191.  
  192. const intervalId = setInterval(() => {
  193. checkForLink();
  194. setTimeout(reloadPage, 7000);
  195. }, 2000);
  196. addMessage('Кількість тікетів: ' + clickCount);
  197.  
  198. showModal();
  199. });
  200. })();