ChatGPT 增强

宽度对话框 & 一键清空聊天记录 & 向GPT声明指定语言回复

目前为 2023-11-09 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name ChatGPT Enhance
  3. // @name:en ChatGPT Enhance
  4. // @name:zh-CN ChatGPT 增强
  5. // @name:zh-TW ChatGPT 增強
  6. // @name:ja ChatGPT 拡張
  7. // @name:ko ChatGPT 향상
  8. // @name:de ChatGPT verbessern
  9. // @name:fr ChatGPT améliorer
  10. // @name:es ChatGPT mejorar
  11. // @name:pt ChatGPT melhorar
  12. // @name:ru ChatGPT улучшить
  13. // @name:it ChatGPT migliorare
  14. // @name:tr ChatGPT geliştirmek
  15. // @name:ar ChatGPT تحسين
  16. // @name:th ChatGPT ปรับปรุง
  17. // @name:vi ChatGPT cải thiện
  18. // @name:id ChatGPT meningkatkan
  19. // @namespace Violentmonkey Scripts
  20. // @match *://chat.openai.com/*
  21. // @version XiaoYing_2023.11.09.1
  22. // @grant GM_info
  23. // @grant GM_getValue
  24. // @grant GM_setValue
  25. // @grant GM_addStyle
  26. // @grant GM_deleteValue
  27. // @grant GM_xmlhttpRequest
  28. // @grant GM_setClipboard
  29. // @grant GM_registerMenuCommand
  30. // @grant GM_unregisterMenuCommand
  31. // @grant GM_getResourceText
  32. // @grant GM_getResourceURL
  33. // @grant GM_openInTab
  34. // @grant unsafeWindow
  35. // @run-at document-start
  36. // @author github.com @XiaoYingYo
  37. // @require https://greasyfork.org/scripts/464929-module-jquery-xiaoying/code/module_jquery_XiaoYing.js
  38. // @require https://greasyfork.org/scripts/464780-global-module/code/global_module.js
  39. // @require https://greasyfork.org/scripts/465643-ajaxhookerlatest/code/ajaxHookerLatest.js
  40. // @require https://greasyfork.org/scripts/465512-google-translate-engine/code/GoogleTranslateEngine.js
  41. // @description 宽度对话框 & 一键清空聊天记录 & 向GPT声明指定语言回复
  42. // @description:en Wide dialog & Clear chat history & Declare specified language reply to GPT
  43. // @description:zh-CN 宽度对话框 & 一键清空聊天记录 & 向GPT声明指定语言回复
  44. // @description:zh-TW 寬度對話框 & 一鍵清空聊天記錄 & 向GPT聲明指定語言回復
  45. // @description:ja 幅広いダイアログ & チャット履歴をクリア & 指定された言語でGPTに宣言する
  46. // @description:ko 넓은 대화 상자 & 채팅 기록 지우기 & 지정된 언어로 GPT에 선언
  47. // @description:de Breites Dialogfeld & Chatverlauf löschen & GPT in angegebener Sprache deklarieren
  48. // @description:fr Boîte de dialogue large & Effacer l'historique du chat & Déclarer la réponse dans la langue spécifiée à GPT
  49. // @description:es Cuadro de diálogo ancho & Borrar el historial del chat & Declarar respuesta en el idioma especificado a GPT
  50. // @description:pt Caixa de diálogo ampla & Limpar o histórico do bate-papo & Declarar resposta no idioma especificado ao GPT
  51. // @description:ru Широкий диалоговое окно & Очистить историю чата & Объявить ответ на указанном языке в GPT
  52. // @description:it Ampia finestra di dialogo & Cancella la cronologia della chat & Dichiarare la risposta nella lingua specificata a GPT
  53. // @description:tr Geniş diyalog & Sohbet geçmişini temizle & GPT'ye belirtilen dilde yanıt bildir
  54. // @description:ar مربع حوار واسع & مسح سجل المحادثة & إعلان الرد باللغة المحددة إلى GPT
  55. // @description:th กล่องโต้ตอบกว้าง & ล้างประวัติการแชท & ประกาศการตอบกลับในภาษาที่ระบุไว้กับ GPT
  56. // @description:vi Hộp thoại rộng & Xóa lịch sử trò chuyện & Khai báo trả lời bằng ngôn ngữ được chỉ định cho GPT
  57. // @description:id Kotak dialog lebar & Hapus riwayat obrolan & Nyatakan balasan dalam bahasa yang ditentukan ke GPT
  58. // ==/UserScript==
  59.  
  60. // eslint-disable-next-line no-undef
  61. ajaxHooker.protect();
  62.  
  63. var globalVariable = new Map();
  64. var browserLanguage = navigator.language;
  65. var ignoreHookStr = '&ignoreHookStr';
  66.  
  67. async function InitSvg() {
  68. return new Promise(async (resolve) => {
  69. let Svg = GM_getValue('clearSvg', []);
  70. let _class = GM_getValue('clearButtonClass', '');
  71. if (Svg.length !== 0 && _class !== '') {
  72. globalVariable.set('clearSvg', Svg);
  73. globalVariable.set('clearButtonClass', _class);
  74. resolve(true);
  75. return;
  76. }
  77. let menuButton = document.querySelector('button[id^="headlessui-menu-button-"]');
  78. menuButton.click();
  79. let menuitems = [];
  80. await new Promise((resolve) => {
  81. let Timer = setInterval(() => {
  82. menuitems = document.querySelectorAll('a[role="menuitem"]');
  83. if (menuitems.length < 4) {
  84. return;
  85. }
  86. clearInterval(Timer);
  87. resolve();
  88. }, 100);
  89. });
  90. let menuitem = menuitems[1];
  91. if (menuitem.name === 1) {
  92. return;
  93. }
  94. _class = menuitem.className;
  95. globalVariable.set('clearButtonClass', _class);
  96. let svg = menuitem.querySelector('svg');
  97. globalVariable.set('clearSvg', []);
  98. globalVariable.get('clearSvg').push(svg.outerHTML);
  99. menuitem.click();
  100. setTimeout(() => {
  101. svg = menuitem.querySelector('svg');
  102. globalVariable.get('clearSvg').push(svg.outerHTML);
  103. menuitem.name = 1;
  104. menuitem.remove();
  105. menuButton.click();
  106. GM_setValue('clearSvg', globalVariable.get('clearSvg'));
  107. GM_setValue('clearButtonClass', _class);
  108. resolve(true);
  109. }, 100);
  110. });
  111. }
  112.  
  113. function clearChats() {
  114. let url = '/backend-api/conversations';
  115. let method = 'PATCH';
  116. let Token = globalVariable.get('accessToken');
  117. if (Token == null) {
  118. alert('Token is null, please refresh the page and try again.Maybe the execution timing of the oil monkey script is set incorrectly.Please set to `document-start`!');
  119. return;
  120. }
  121. let headers = {
  122. Authorization: 'Bearer ' + Token,
  123. 'Content-Type': 'application/json'
  124. };
  125. let body = { is_visible: false };
  126. (async () => {
  127. let NewChatHistoryElement = globalVariable.get('NewChatHistoryElement');
  128. let rHElement = globalVariable.get('rH');
  129. if (rHElement && $(NewChatHistoryElement).find(rHElement).length > 0) {
  130. return;
  131. }
  132. let hide = function (tryOne) {
  133. $(NewChatHistoryElement).parents('nav').find('ol').eq(0).find('li[class]').hide();
  134. if (tryOne) {
  135. setTimeout(() => {
  136. hide(false);
  137. }, 1000);
  138. }
  139. };
  140. hide(true);
  141. conversationsToTrashCan();
  142. setTimeout(() => {
  143. hide(true);
  144. }, 1000);
  145. global_module.clickElement(globalVariable.get('NewChatElement')[0]);
  146. url = global_module.SetUrlParm(url, 'ignoreHookStr', '0');
  147. fetch(url + ignoreHookStr, { method, headers, body: JSON.stringify(body) });
  148. })();
  149. }
  150.  
  151. function createButtonOrShow(id = null, Show = null) {
  152. if (!id) {
  153. return;
  154. }
  155. if (document.getElementById(id) != null) {
  156. if (Show != null) document.getElementById(id).style.display = Show;
  157. return;
  158. }
  159. let border = document.querySelectorAll('div[class="grow"]');
  160. border = border[0];
  161. let div = document.createElement('div');
  162. div.id = id;
  163. let className = border.childNodes[0].className;
  164. div.className = className;
  165. border.insertBefore(div, border.childNodes[0]);
  166. return div;
  167. }
  168.  
  169. async function getbrowserLanguageStr(text) {
  170. return new Promise(async (resolve) => {
  171. let cache = localStorage.getItem(text + '_' + browserLanguage);
  172. if (cache) {
  173. resolve(cache);
  174. return;
  175. }
  176. cache = (await globalVariable.get('TranslateMachine').Translate(text, 'auto', browserLanguage, true)).result;
  177. if (cache) {
  178. resolve(cache);
  179. localStorage.setItem(text + '_' + browserLanguage, cache);
  180. return;
  181. }
  182. resolve(cache);
  183. });
  184. }
  185.  
  186. function conversationsToTrashCan() {
  187. let conversations = globalVariable.get('cacheConversations');
  188. conversations.forEach((value, key) => {
  189. globalVariable.get('trashCanConversations').set(key, '');
  190. });
  191. globalVariable.set('cacheConversations', new Map());
  192. }
  193.  
  194. function createOrShowClearButton(Show = null) {
  195. let div = createButtonOrShow('_clearButton_', Show);
  196. if (!div) {
  197. return;
  198. }
  199. (async () => {
  200. div.innerHTML = globalVariable.get('clearSvg')[0] + (await getbrowserLanguageStr('Clear Conversations'));
  201. })();
  202. div.name = 0;
  203. div.className = globalVariable.get('clearButtonClass') || '';
  204. div.addEventListener('click', function () {
  205. let title = 'Clear Conversations';
  206. if (div.name === 0) {
  207. title = 'Confirm ' + title;
  208. div.name = 1;
  209. } else {
  210. div.name = 0;
  211. clearChats();
  212. }
  213. (async () => {
  214. div.innerHTML = globalVariable.get('clearSvg')[div.name] + (await getbrowserLanguageStr(title));
  215. })();
  216. });
  217. }
  218.  
  219. function addTextBase() {
  220. let style = $('body').find('style[id="text-base"]').eq(0);
  221. if (style.length != 0) {
  222. return;
  223. }
  224. style = document.createElement('style');
  225. style.id = 'text-base';
  226. let css = `.text-base {
  227. max-width: 92%;
  228. }`;
  229. style.innerHTML = css;
  230. document.body.appendChild(style);
  231. }
  232.  
  233. async function initUseElement() {
  234. let ChatHistoryElement = $('div[class*="items-center"][class*="text"]').eq(0);
  235. globalVariable.set('NewChatHistoryElement', ChatHistoryElement);
  236. let newChat = ChatHistoryElement.parents('nav').eq(0).find('a').eq(0);
  237. if (newChat.length === 0) {
  238. setTimeout(() => {
  239. initUseElement();
  240. }, 1000);
  241. return;
  242. }
  243. newChat = newChat.eq(0);
  244. globalVariable.set('NewChatElement', newChat);
  245. await InitSvg();
  246. createOrShowClearButton();
  247. }
  248.  
  249. function getContentMainBodyHistoricalDialogue(req, res, Text, period) {
  250. if (period !== 'done') {
  251. return;
  252. }
  253. setTimeout(() => {
  254. initUseElement();
  255. }, 1000);
  256. }
  257.  
  258. globalVariable.set('cacheConversations', new Map());
  259. globalVariable.set('trashCanConversations', new Map());
  260.  
  261. var HookFun = new Map();
  262. HookFun.set('/api/auth/session', function (req, res, Text, period) {
  263. if (period === 'preload') {
  264. return;
  265. }
  266. new Promise(async (resolve) => {
  267. if (period !== 'done') {
  268. return;
  269. }
  270. addTextBase();
  271. let json = JSON.parse(Text);
  272. let accessToken = json.accessToken;
  273. localStorage.setItem('ChatGPT.accessToken', accessToken);
  274. globalVariable.set('accessToken', accessToken);
  275. resolve(null);
  276. });
  277. });
  278. HookFun.set('/backend-api/conversation', function (req, res, Text, period) {
  279. if (period === 'preload') {
  280. let additional = 'Please reply me with ';
  281. let additionals = additional + browserLanguage;
  282. let body = JSON.parse(req.data);
  283. let messages = body.messages;
  284. if (messages instanceof Array) {
  285. for (let i = 0; i < messages.length; i++) {
  286. let parts = messages[i].content.parts;
  287. if (parts instanceof Array) {
  288. for (let j = 0; j < parts.length; j++) {
  289. if (parts[j].indexOf(additional) != -1) {
  290. continue;
  291. }
  292. parts[j] = parts[j] + '\n' + additionals;
  293. }
  294. }
  295. }
  296. }
  297. req.data = JSON.stringify(body);
  298. setTimeout(() => {
  299. addTextBase();
  300. }, 100);
  301. return;
  302. }
  303. return new Promise(async (resolve) => {
  304. if (period !== 'done') {
  305. return;
  306. }
  307. resolve(null);
  308. });
  309. });
  310. HookFun.set('/backend-api/conversations', function (req, res, Text, period) {
  311. if (period === 'preload') {
  312. return;
  313. }
  314. return new Promise(async (resolve) => {
  315. if (period !== 'done') {
  316. return;
  317. }
  318. addTextBase();
  319. let url = req.url;
  320. if (url.indexOf('?') == -1) {
  321. return;
  322. }
  323. let json = JSON.parse(Text);
  324. if (json.items.length !== 0) {
  325. let i = 0;
  326. while (i != json.items.length) {
  327. let id = json.items[i].id;
  328. if (globalVariable.get('trashCanConversations').has(id)) {
  329. json.items.splice(i, 1);
  330. json.total--;
  331. continue;
  332. }
  333. if (globalVariable.get('cacheConversations').has(id)) {
  334. i++;
  335. continue;
  336. }
  337. HookFun.set('/backend-api/conversation/' + id, getContentMainBodyHistoricalDialogue);
  338. globalVariable.get('cacheConversations').set(id, json.items[i]);
  339. i++;
  340. }
  341. }
  342. if (json.items.length === 0) {
  343. let title = '{_reserveHistory_}';
  344. json.total = 0;
  345. let time = new Date().toISOString();
  346. json.items = [{ id: '', title, create_time: time, update_time: time }];
  347. (async () => {
  348. let rH = await global_module.waitForElement('div:contains("' + title + '")[class*="text-ellipsis"]', null, null, 10, -1);
  349. rH = rH.eq(0);
  350. rH.parent().hide();
  351. globalVariable.set('rH', rH);
  352. })();
  353. }
  354. initUseElement();
  355. Text = JSON.stringify(json);
  356. resolve(Text);
  357. });
  358. });
  359.  
  360. function handleResponse(request) {
  361. if (!request) {
  362. return;
  363. }
  364. if (request.url.indexOf(ignoreHookStr) != -1) {
  365. return;
  366. }
  367. let tempUrl = request.url;
  368. if (tempUrl.indexOf('http') == -1 && tempUrl[0] == '/') {
  369. tempUrl = location.origin + tempUrl;
  370. }
  371. let pathname = new URL(tempUrl).pathname;
  372. let fun = HookFun.get(pathname);
  373. if (!fun) {
  374. return;
  375. }
  376. fun(request, null, null, 'preload');
  377. request.response = (res) => {
  378. let Type = 0;
  379. let responseText = res.responseText;
  380. if (typeof responseText !== 'string') {
  381. Type = 1;
  382. responseText = res.text;
  383. }
  384. if (typeof responseText !== 'string') {
  385. Type = 2;
  386. responseText = JSON.stringify(res.json);
  387. }
  388. const oldText = responseText;
  389. res.responseText = new Promise(async (resolve) => {
  390. let ret = await fun(request, res, responseText, 'done');
  391. if (!ret) {
  392. ret = oldText;
  393. }
  394. if (Type === 2) {
  395. if (typeof ret === 'string') {
  396. ret = JSON.parse(ret);
  397. }
  398. }
  399. resolve(ret);
  400. });
  401. };
  402. }
  403.  
  404. // eslint-disable-next-line no-undef
  405. ajaxHooker.hook(handleResponse);
  406. // eslint-disable-next-line no-undef
  407. globalVariable.set('TranslateMachine', new TranslateMachine());