ChatGPT Text File Scaler

ChatGPT utility to cut and transfer text files

  1. // ==UserScript==
  2. // @name ChatGPT Text File Scaler
  3. // @version 1.8
  4. // @author refracta
  5. // @description ChatGPT utility to cut and transfer text files
  6. // @match https://chat.openai.com/*
  7. // @icon https://www.google.com/s2/favicons?sz=64&domain=openai.com
  8. // @license MIT
  9. // @namespace https://greasyfork.org/users/467840
  10. // ==/UserScript==
  11.  
  12. (async function () {
  13. 'use strict';
  14. function createElement(html) {
  15. var div = document.createElement('div');
  16. div.innerHTML = html.trim();
  17. return div.firstChild;
  18. }
  19.  
  20. function insertAfter(referenceNode, newNode) {
  21. if (!!referenceNode.nextSibling) {
  22. referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  23. } else {
  24. referenceNode.parentNode.appendChild(newNode);
  25. }
  26. }
  27.  
  28. function waitFor(valueFunction, inspectPeriod = 100) {
  29. return new Promise(resolve => {
  30. let interval = setInterval(async _ => {
  31. try {
  32. let value = valueFunction();
  33. value = value instanceof Promise ? await value : value;
  34. if (value) {
  35. clearInterval(interval);
  36. resolve(value);
  37. }
  38. } catch (e) {}
  39. }, inspectPeriod);
  40. });
  41. }
  42.  
  43. function writeMessage(text) {
  44. let textarea = document.querySelector('textarea');
  45. textarea.value = text;
  46.  
  47. let event = new Event('input', {
  48. bubbles: true
  49. });
  50. textarea.dispatchEvent(event);
  51. }
  52.  
  53. async function clickSendButton() {
  54. do {
  55. document.querySelector('button.absolute').disabled = false;
  56. let clickEvent = document.createEvent('MouseEvents');
  57. clickEvent.initEvent("click", true, true);
  58. document.querySelector('button.absolute').dispatchEvent(clickEvent);
  59. if (document.querySelector('textarea')?.value === '') {
  60. break;
  61. }
  62. await new Promise(resolve => setTimeout(resolve, 100));
  63.  
  64. } while (true);
  65. }
  66.  
  67. async function waitResponse() {
  68. await waitFor(_ => document.querySelector('button.absolute.p-1 > span > svg'));
  69. }
  70.  
  71. async function sendMessage(text) {
  72. writeMessage(text);
  73. clickSendButton();
  74. await waitResponse();
  75. }
  76.  
  77. function clearMessages(messageLimit) {
  78. let messages = Array.from(document.querySelectorAll('.flex-1.overflow-hidden > div > div > div > div:nth-child(1) > div'));
  79. while (messages.length > messageLimit) {
  80. messages.shift().remove();
  81. }
  82. }
  83.  
  84. function updateLoadTextFileSpan() {
  85. let loadTextFileSpan = document.querySelector('#load-text-file');
  86. if (loadTextFileSpan) {
  87. loadTextFileSpan.textContent = `Load text file (${localStorage.buffer.length})`;
  88. }
  89. }
  90.  
  91. async function sendTextFile() {
  92. const firstMessage = 'I am going to provide you a book in multiple messages.';
  93. const insertMessage = 'Here is the next page, please do not respond aside from confirmation:\n\n';
  94. const splitLength = 2000;
  95. const messageLimit = 30;
  96.  
  97. await sendMessage(firstMessage);
  98. while (localStorage.buffer.length > 0) {
  99. let length = splitLength - insertMessage.length;
  100. let text = localStorage.buffer.slice(0, length);
  101. await sendMessage(insertMessage + text);
  102. localStorage.buffer = localStorage.buffer.slice(length);
  103. updateLoadTextFileSpan();
  104. clearMessages(messageLimit);
  105. }
  106. }
  107.  
  108. function getText() {
  109. return new Promise(resolve => {
  110. let input = document.createElement("input");
  111. input.type = "file";
  112. input.accept = "text/plain";
  113. input.onchange = (event) => {
  114. let file = event.target.files[0];
  115. let reader = new FileReader();
  116. reader.onload = () => {
  117. resolve(reader.result);
  118. };
  119. reader.readAsText(file);
  120. };
  121. input.click();
  122. });
  123. }
  124.  
  125. localStorage.buffer = localStorage.buffer ? localStorage.buffer : '';
  126.  
  127. let isUIProcessing = false;
  128. setInterval(async _ => {
  129. if (!document.querySelector('#load-text-file') && !isUIProcessing) {
  130. try {
  131. isUIProcessing = true;
  132.  
  133. let bottomMenu = document.querySelector('div > nav > div.border-t.border-white\\/20.pt-2');
  134.  
  135. let sendTextFileButton = createElement(`<a href="#" class="flex py-3 px-3 items-center gap-3 rounded-md hover:bg-gray-500/10 transition-colors duration-200 text-white cursor-pointer text-sm"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>Send text file</a>`);
  136. sendTextFileButton.addEventListener('click', sendTextFile);
  137. bottomMenu.prepend(sendTextFileButton);
  138.  
  139. let loadTextFileButton = createElement(`<a href="#" class="flex py-3 px-3 items-center gap-3 rounded-md hover:bg-gray-500/10 transition-colors duration-200 text-white cursor-pointer text-sm"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg><span id="load-text-file">Load text file (${localStorage.buffer.length})</span></a>`);
  140. loadTextFileButton.addEventListener('mousedown', async(event) => {
  141. localStorage.buffer = event.button === 0 ? await getText() : await navigator.clipboard.readText();
  142. updateLoadTextFileSpan();
  143. });
  144.  
  145. loadTextFileButton.addEventListener('contextmenu', event => {
  146. event.preventDefault();
  147. event.stopPropagation();
  148. });
  149. bottomMenu.prepend(loadTextFileButton);
  150. } catch (e) {}
  151. finally {
  152. isUIProcessing = false;
  153. }
  154. }
  155. }, 100);
  156.  
  157. })();