ChatGPT Text File Scaler

ChatGPT utility to cut and transfer text files

当前为 2023-04-08 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name ChatGPT Text File Scaler
  3. // @version 1.6
  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. document.querySelector('textarea').value = text;
  45. }
  46.  
  47. function clickSendButton() {
  48. document.querySelector('button.absolute').disabled = false;
  49. document.querySelector('button.absolute').click();
  50. }
  51.  
  52. async function waitResponse() {
  53. await waitFor(_ => document.querySelector('button.absolute.p-1 > svg'));
  54. }
  55.  
  56. async function sendMessage(text) {
  57. writeMessage(text);
  58. clickSendButton();
  59. await waitResponse();
  60. }
  61.  
  62. function clearMessages(messageLimit) {
  63. let messages = Array.from(document.querySelectorAll('.flex-1.overflow-hidden > div > div > div > div:nth-child(1) > div'));
  64. while (messages.length > messageLimit) {
  65. messages.shift().remove();
  66. }
  67. }
  68.  
  69. function updateLoadTextFileSpan() {
  70. let loadTextFileSpan = document.querySelector('#load-text-file');
  71. if (loadTextFileSpan) {
  72. loadTextFileSpan.textContent = `Load text file (${localStorage.buffer.length})`;
  73. }
  74. }
  75.  
  76. async function sendTextFile() {
  77. const firstMessage = 'I am going to provide you a book in multiple messages.';
  78. const insertMessage = 'Here is the next page, please do not respond aside from confirmation:\n\n';
  79. const splitLength = 2000;
  80. const messageLimit = 30;
  81.  
  82. await sendMessage(firstMessage);
  83. while (localStorage.buffer.length > 0) {
  84. let length = splitLength - insertMessage.length;
  85. let text = localStorage.buffer.slice(0, length);
  86. await sendMessage(insertMessage + text);
  87. localStorage.buffer = localStorage.buffer.slice(length);
  88. updateLoadTextFileSpan();
  89. clearMessages(messageLimit);
  90. }
  91. }
  92.  
  93. function getText() {
  94. return new Promise(resolve => {
  95. let input = document.createElement("input");
  96. input.type = "file";
  97. input.accept = "text/plain";
  98. input.onchange = (event) => {
  99. let file = event.target.files[0];
  100. let reader = new FileReader();
  101. reader.onload = () => {
  102. resolve(reader.result);
  103. };
  104. reader.readAsText(file);
  105. };
  106. input.click();
  107. });
  108. }
  109.  
  110. localStorage.buffer = localStorage.buffer ? localStorage.buffer : '';
  111.  
  112. let isUIProcessing = false;
  113. setInterval(async _ => {
  114. if (!document.querySelector('#load-text-file') && !isUIProcessing) {
  115. try {
  116. isUIProcessing = true;
  117.  
  118. let historyDiv = document.querySelector('.scrollbar-trigger > nav > div');
  119.  
  120. 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>`);
  121. sendTextFileButton.addEventListener('click', sendTextFile);
  122. insertAfter(historyDiv, sendTextFileButton);
  123.  
  124. 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>`);
  125. loadTextFileButton.addEventListener('mousedown', async (event) => {
  126. localStorage.buffer = event.button === 0 ? await getText() : await navigator.clipboard.readText();
  127. updateLoadTextFileSpan();
  128. });
  129.  
  130. loadTextFileButton.addEventListener('contextmenu', event => {
  131. event.preventDefault();
  132. event.stopPropagation();
  133. });
  134.  
  135. insertAfter(historyDiv, loadTextFileButton);
  136. } catch(e) {
  137. } finally {
  138. isUIProcessing = false;
  139. }
  140. }
  141. }, 100);
  142.  
  143. })();