Greasy Fork is available in English.

ChatGPT Queue

Automatically submit questions in a queue to ChatGPT

  1. // ==UserScript==
  2. // @name ChatGPT Queue
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Automatically submit questions in a queue to ChatGPT
  6. // @author You
  7. // @match https://chat.openai.com/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. 'use strict';
  14.  
  15. const questionList = [];
  16. let isAutoSubmitting = false;
  17. let isWaitingForResponse = false;
  18.  
  19. function createUI() {
  20. const controlPanel = document.createElement('div');
  21.  
  22. controlPanel.innerHTML = `
  23. <div id="controlPanel" style="position: fixed; top: 20px; right: 20px; z-index: 9999; background-color: rgba(255, 255, 255, 0.95); padding: 20px; border-radius: 10px; color: #333; cursor: move; resize: both; overflow: hidden; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);">
  24. <button id="toggleHideBar" style="background-color: #FFC107; border: none; color: white; padding: 5px 10px; text-align: center; text-decoration: none; display: inline-block; font-size: 14px; margin: 4px 2px; cursor: pointer; border-radius: 5px; transition: background-color 0.3s;">Hide</button>
  25. <div id="panelContent">
  26. <button id="startAutoSubmit" style="background-color: #4CAF50; border: none; color: white; padding: 10px 20px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; border-radius: 5px; transition: background-color 0.3s;">Start Auto Submit</button>
  27. <div id="questionWindow" style="width: 100%; height: 100px; background-color: rgba(255, 255, 255, 0.95); border: 1px solid #ddd; margin-top: 20px; padding: 10px; overflow-y: auto;">
  28. <h4 style="margin-top: 0; margin-bottom: 10px; font-size: 18px; font-weight: bold;">Questions</h4>
  29. <ul id="questionList" style="list-style-type: none; margin: 0; padding: 0;"></ul>
  30. </div>
  31. </div>
  32. </div>
  33. `;
  34. document.body.appendChild(controlPanel);
  35. document.getElementById('toggleHideBar').addEventListener('click', toggleHide);
  36.  
  37. document.getElementById('startAutoSubmit').addEventListener('click', toggleAutoSubmit);
  38.  
  39. // Make the Control Panel draggable
  40. const dragItem = document.getElementById('controlPanel');
  41. let active = false;
  42. let currentX;
  43. let currentY;
  44. let initialX;
  45. let initialY;
  46. let xOffset = 0;
  47. let yOffset = 0;
  48.  
  49. dragItem.addEventListener('mousedown', dragStart, false);
  50. document.addEventListener('mouseup', dragEnd, false);
  51. document.addEventListener('mousemove', drag, false);
  52.  
  53. function dragStart(e) {
  54. initialX = e.clientX - xOffset;
  55. initialY = e.clientY - yOffset;
  56.  
  57. if (e.target === dragItem) {
  58. active = true;
  59. }
  60. }
  61.  
  62. function dragEnd() {
  63. initialX = currentX;
  64. initialY = currentY;
  65.  
  66. active = false;
  67. }
  68.  
  69. function drag(e) {
  70. if (active) {
  71. e.preventDefault();
  72.  
  73. currentX = e.clientX - initialX;
  74. currentY = e.clientY - initialY;
  75.  
  76. xOffset = currentX;
  77. yOffset = currentY;
  78.  
  79. setTranslate(currentX, currentY, dragItem);
  80. }
  81. }
  82.  
  83. function setTranslate(xPos, yPos, el) {
  84. el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0)`;
  85. }
  86. }
  87.  
  88.  
  89. function toggleAutoSubmit() {
  90. isAutoSubmitting = !isAutoSubmitting;
  91. document.getElementById('startAutoSubmit').innerHTML = isAutoSubmitting ? 'Stop Auto Submit' : 'Start Auto Submit';
  92.  
  93. if (isAutoSubmitting) {
  94. autoSubmitNextQuestion();
  95. }
  96. }
  97. function toggleHide() {
  98. const panelContent = document.getElementById('panelContent');
  99. const hideBar = document.getElementById('toggleHideBar');
  100. const controlPanelTitle = document.getElementById('controlPanelTitle');
  101. if (panelContent.style.display === 'none') {
  102. panelContent.style.display = 'block';
  103. hideBar.innerHTML = 'Hide';
  104. controlPanelTitle.style.display = 'block';
  105. } else {
  106. panelContent.style.display = 'none';
  107. hideBar.innerHTML = 'Show';
  108. controlPanelTitle.style.display = 'none';
  109. }
  110. }
  111.  
  112. function autoSubmitNextQuestion() {
  113. if (isAutoSubmitting && !isWaitingForResponse) {
  114. if (questionList.length > 0) {
  115. const question = questionList.shift();
  116. updateQuestionListUI();
  117. submitQuestion(question);
  118. } else {
  119. toggleAutoSubmit();
  120. }
  121. }
  122. }
  123.  
  124.  
  125. function submitQuestion(question) {
  126. const textarea = document.querySelector('textarea[placeholder="Send a message."]');
  127. const submitButton = document.querySelector('button[class*="absolute"]');
  128.  
  129. if (textarea && submitButton) {
  130. textarea.value = question;
  131. submitButton.disabled = false;
  132. submitButton.click();
  133. waitForResponse();
  134. }
  135. }
  136.  
  137. function waitForResponse() {
  138. isWaitingForResponse = true;
  139.  
  140. const observer = new MutationObserver((mutationsList) => {
  141. for (const mutation of mutationsList) {
  142. if (mutation.type === 'childList') {
  143. const regenerateResponse = document.evaluate(
  144. '//button[contains(., "Regenerate response")]',
  145. document,
  146. null,
  147. XPathResult.FIRST_ORDERED_NODE_TYPE,
  148. null
  149. ).singleNodeValue;
  150.  
  151. if (regenerateResponse) {
  152. observer.disconnect();
  153. isWaitingForResponse = false;
  154. autoSubmitNextQuestion();
  155. break;
  156. }
  157. }
  158. }
  159. });
  160.  
  161. const config = { childList: true, subtree: true };
  162. observer.observe(document.body, config);
  163. }
  164.  
  165. function addQuestionToQueue(question) {
  166. questionList.push(question);
  167. updateQuestionListUI();
  168. }
  169.  
  170.  
  171. function updateQuestionListUI() {
  172. const maxWordsToShow = 5;
  173. const questionListUI = document.getElementById('questionList');
  174. questionListUI.innerHTML = questionList.map((q, i) => {
  175. const words = q.split(' ');
  176. const shortQuestion = words.slice(0, maxWordsToShow).join(' ') + (words.length > maxWordsToShow ? '...' : '');
  177. return `<li>${i + 1}. ${shortQuestion} <button data-index="${i}" class="deleteQuestion" style="background-color: #f44336; border: none; color: white; padding: 2px 4px; text-align: center; text-decoration: none; display: inline-block; font-size: 12px; line-height: 12px; margin: 0 2px; cursor: pointer; border-radius: 3px; transition: background-color 0.3s;">🗑️</button></li>`;
  178. }).join('');
  179.  
  180. const deleteButtons = document.querySelectorAll('.deleteQuestion');
  181. deleteButtons.forEach((btn) => {
  182. btn.addEventListener('click', deleteQuestion);
  183. });
  184. }
  185.  
  186. function deleteQuestion(event) {
  187. const indexToDelete = parseInt(event.target.getAttribute('data-index'), 10);
  188. questionList.splice(indexToDelete, 1);
  189. updateQuestionListUI();
  190. }
  191.  
  192. function handleKeyPress(event) {
  193. if (event.ctrlKey && event.key === 'Enter') {
  194. event.preventDefault();
  195. const textarea = document.querySelector('textarea[placeholder="Send a message."]');
  196. const question = textarea.value;
  197. if (question.trim()) {
  198. addQuestionToQueue(question);
  199. textarea.value = '';
  200. }
  201. }
  202. }
  203.  
  204. createUI();
  205. document.addEventListener('keydown', handleKeyPress);
  206. })();