ChatGPT 增强

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

当前为 2024-08-03 提交的版本,查看 最新版本

  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. // @match *://chatgpt.com/*
  22. // @version XiaoYing_2024.08.04.13
  23. // @grant GM_info
  24. // @grant GM_getValue
  25. // @grant GM_setValue
  26. // @grant GM_addStyle
  27. // @grant GM_deleteValue
  28. // @grant GM_xmlhttpRequest
  29. // @grant GM_setClipboard
  30. // @grant GM_registerMenuCommand
  31. // @grant GM_unregisterMenuCommand
  32. // @grant GM_getResourceText
  33. // @grant GM_getResourceURL
  34. // @grant GM_openInTab
  35. // @grant unsafeWindow
  36. // @run-at document-start
  37. // @author github.com @XiaoYingYo
  38. // @require https://gf.qytechs.cn/scripts/464929-module-jquery-xiaoying/code/module_jquery_XiaoYing.js
  39. // @require https://gf.qytechs.cn/scripts/464780-global-module/code/global_module.js
  40. // @require https://gf.qytechs.cn/scripts/465643-ajaxhookerlatest/code/ajaxHookerLatest.js
  41. // @require https://gf.qytechs.cn/scripts/440334-jquery-like-spa-operation-library/code/jQuery-like%20SPA%20operation%20library.js
  42. // @description 宽度对话框 & 一键清空聊天记录 & 向GPT声明指定语言回复
  43. // @description:en Wide dialog & Clear chat history & Declare specified language reply to GPT
  44. // @description:zh-CN 宽度对话框 & 一键清空聊天记录 & 向GPT声明指定语言回复
  45. // @description:zh-TW 寬度對話框 & 一鍵清空聊天記錄 & 向GPT聲明指定語言回復
  46. // @description:ja 幅広いダイアログ & チャット履歴をクリア & 指定された言語でGPTに宣言する
  47. // @description:ko 넓은 대화 상자 & 채팅 기록 지우기 & 지정된 언어로 GPT에 선언
  48. // @description:de Breites Dialogfeld & Chatverlauf löschen & GPT in angegebener Sprache deklarieren
  49. // @description:fr Boîte de dialogue large & Effacer l'historique du chat & Déclarer la réponse dans la langue spécifiée à GPT
  50. // @description:es Cuadro de diálogo ancho & Borrar el historial del chat & Declarar respuesta en el idioma especificado a GPT
  51. // @description:pt Caixa de diálogo ampla & Limpar o histórico do bate-papo & Declarar resposta no idioma especificado ao GPT
  52. // @description:ru Широкий диалоговое окно & Очистить историю чата & Объявить ответ на указанном языке в GPT
  53. // @description:it Ampia finestra di dialogo & Cancella la cronologia della chat & Dichiarare la risposta nella lingua specificata a GPT
  54. // @description:tr Geniş diyalog & Sohbet geçmişini temizle & GPT'ye belirtilen dilde yanıt bildir
  55. // @description:ar مربع حوار واسع & مسح سجل المحادثة & إعلان الرد باللغة المحددة إلى GPT
  56. // @description:th กล่องโต้ตอบกว้าง & ล้างประวัติการแชท & ประกาศการตอบกลับในภาษาที่ระบุไว้กับ GPT
  57. // @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
  58. // @description:id Kotak dialog lebar & Hapus riwayat obrolan & Nyatakan balasan dalam bahasa yang ditentukan ke GPT
  59. // ==/UserScript==
  60.  
  61. // eslint-disable-next-line no-undef
  62. ajaxHooker.protect();
  63.  
  64. var globalVariable = new Map();
  65. var browserLanguage = navigator.language;
  66. var ignoreHookStr = '&ignoreHookStr';
  67. var clearButtonSvg = '<svg t="1722633865116" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1793" width="24" height="24"><path d="M901.3 504.8l-76.3-150c-13.4-26.3-40-42.6-69.5-42.6H639c-1.1 0-2-0.9-2-2V120.6c0-31.1-25.3-56.3-56.3-56.3h-90c-31.1 0-56.3 25.3-56.3 56.3v189.6c0 1.1-0.9 2-2 2H315.8c-29.5 0-56.1 16.3-69.5 42.6l-76.3 150c-9.2 18.1-8.4 39.3 2.2 56.6 10.3 16.8 27.9 27 47.4 27.6-4.8 101-38.3 205.9-90.2 279.5-12.5 17.8-14.1 40.8-4.1 60.1 10 19.3 29.7 31.3 51.5 31.3h601.5c35 0 66-23.6 75.2-57.4 15.5-56.5 28.4-107.9 29.4-164.9C884 685 874 636 852.9 589c19-1.1 36.1-11.2 46.2-27.6 10.6-17.3 11.4-38.5 2.2-56.6z m-681.4 25.4l76.3-150c3.8-7.4 11.3-12 19.6-12h116.4c32 0 58-26 58-58V120.6c0-0.1 0.2-0.3 0.3-0.3h90c0.1 0 0.3 0.2 0.3 0.3v189.6c0 32 26 58 58 58h116.4c8.3 0 15.8 4.6 19.6 12l76.3 150c0.2 0.3 0.5 1-0.1 2s-1.3 1-1.7 1H221.7c-0.4 0-1.1 0-1.7-1-0.6-1-0.3-1.7-0.1-2zM827 736.6c-0.9 50.5-12.9 98.3-27.4 151.1-2.6 9.5-11.3 16.2-21.2 16.2H651.8c11.3-22.3 18.5-44 23.1-61.2 7.1-26.7 10.7-53.5 10.6-78-0.1-17.1-15.5-30.1-32.4-27.4-13.6 2.2-23.6 14-23.6 27.8 0.1 42.7-14.1 98.2-42.7 138.8H406.2c15.2-21.7 26.1-43.8 33.6-61.9 10-24.3 17.4-49.7 21.2-72.5 2.8-17-10.4-32.5-27.6-32.5-13.6 0-25.3 9.8-27.6 23.3-2.8 16.6-8.3 37.7-17.7 60.4-10.1 24.6-27.8 58.1-55.6 83.3H176.9c-0.5 0-1.2 0-1.8-1.1-0.6-1.1-0.2-1.6 0.1-2 29.7-42.1 54.8-94.5 72.5-151.4 16.2-52.1 25.7-106.9 28-160.3h514.6C816 635.6 828 684 827 736.6z" fill="#ffffff" p-id="1794"></path></svg>';
  68.  
  69. (async function () {
  70. function initSession() {
  71. return new Promise((resolve) => {
  72. $.get('/api/auth/session', { headers: { Accept: 'application/json', 'Content-Type': 'application/json' } }).done((res) => {
  73. globalVariable.set('session', res);
  74. globalVariable.set('accessToken', res.accessToken);
  75. resolve();
  76. });
  77. });
  78. }
  79.  
  80. function clearAllConversations() {
  81. return new Promise(async (resolve) => {
  82. let data = await getAllItems();
  83. let Tasks = [];
  84. for (let i = 0; i < data.length; i++) {
  85. let item = data[i];
  86. let id = item.id;
  87. let title = item.title;
  88. if (title.charAt(0) == '#') {
  89. continue;
  90. }
  91. Tasks.push(deleteItem(id));
  92. }
  93. await Promise.all(Tasks);
  94. resolve();
  95. // $.ajax({
  96. // type: 'PATCH',
  97. // url: '/backend-api/conversations',
  98. // headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: `Bearer ${globalVariable.get('accessToken')}` },
  99. // data: JSON.stringify({ is_visible: false }),
  100. // success: (res) => {
  101. // resolve(res);
  102. // }
  103. // });
  104. });
  105. }
  106.  
  107. function initClearButton() {
  108. return new Promise(async (resolve) => {
  109. let sureClearButtonSvg = '<svg t="1722633889519" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3987" width="24" height="24"><path d="M675.328 117.717333A425.429333 425.429333 0 0 0 512 85.333333C276.352 85.333333 85.333333 276.352 85.333333 512s191.018667 426.666667 426.666667 426.666667 426.666667-191.018667 426.666667-426.666667c0-56.746667-11.093333-112-32.384-163.328a21.333333 21.333333 0 0 0-39.402667 16.341333A382.762667 382.762667 0 0 1 896 512c0 212.074667-171.925333 384-384 384S128 724.074667 128 512 299.925333 128 512 128c51.114667 0 100.8 9.984 146.986667 29.12a21.333333 21.333333 0 0 0 16.341333-39.402667z m-213.333333 468.608l-105.664-105.642666a21.248 21.248 0 0 0-30.122667 0.042666c-8.32 8.32-8.213333 21.973333-0.064 30.101334l120.810667 120.832a21.248 21.248 0 0 0 30.122666-0.085334l211.157334-211.157333a21.290667 21.290667 0 0 0 0-30.186667 21.397333 21.397333 0 0 0-30.250667 0.106667l-196.010667 195.989333z" fill="#ffffff" p-id="3988"></path></svg>';
  110. let oneBtn = await global_module.waitForElement('nav[aria-label]', null, null, 100, -1);
  111. globalVariable.set('Nav', $(oneBtn));
  112. oneBtn = oneBtn.find('button').eq(0);
  113. let newBtn = global_module.cloneAndHide(oneBtn[0]);
  114. newBtn = $(newBtn).eq(0).attr('status', 0);
  115. oneBtn.show();
  116. newBtn.find('svg').remove();
  117. newBtn.append(clearButtonSvg);
  118. newBtn.off('click').on('click', async () => {
  119. let status = newBtn.attr('status');
  120. newBtn.attr('disabled', 'disabled');
  121. if (status == 0) {
  122. newBtn.attr('status', 1);
  123. newBtn.find('svg').remove();
  124. newBtn.append(sureClearButtonSvg);
  125. } else {
  126. newBtn.attr('status', 0);
  127. newBtn.find('svg').remove();
  128. newBtn.append(clearButtonSvg);
  129. await clearAllConversations();
  130. unsafeWindow.location.href = '/';
  131. }
  132. newBtn.removeAttr('disabled');
  133. });
  134. resolve();
  135. });
  136. }
  137. await initSession();
  138. initClearButton();
  139. })();
  140.  
  141. function purify() {
  142. return new Promise(async (resolve) => {
  143. let Tasks = [];
  144. let nav = globalVariable.get('Nav');
  145. Tasks.push(
  146. (() => {
  147. return new Promise(async (resolve) => {
  148. let upgradeDom = await global_module.waitForElement('span[class*="border-token-border-light"]', null, null, 100, -1, nav);
  149. upgradeDom = upgradeDom.eq(upgradeDom.length - 1);
  150. upgradeDom.parents('a').eq(0).remove();
  151. resolve();
  152. });
  153. })()
  154. );
  155. Tasks.push(
  156. (() => {
  157. return new Promise(async (resolve) => {
  158. let presentation = await global_module.waitForElement('div[role="presentation"]', null, null, 100, -1);
  159. presentation = presentation.eq(presentation.length - 1);
  160. let presentationTip = await global_module.waitForElement('span:contains("ChatGPT ")', null, null, 100, -1, presentation);
  161. presentationTip.remove();
  162. resolve();
  163. });
  164. })()
  165. );
  166. Tasks.push(
  167. (() => {
  168. return new Promise(async (resolve) => {
  169. $(await global_module.waitForElement('button[data-state="closed"][id^="radix"]:contains("?")', null, null, 100, -1)).remove();
  170. resolve();
  171. });
  172. })()
  173. );
  174. await Promise.all(Tasks);
  175. resolve();
  176. });
  177. }
  178.  
  179. function getAllItems() {
  180. return new Promise(async (resolve) => {
  181. let limit = 30;
  182. let currentTotal = 0;
  183. let retItems = [];
  184. let getItems = function (offset) {
  185. return new Promise(async (resolve) => {
  186. $.ajax({
  187. type: 'GET',
  188. url: '/backend-api/conversations?offset=' + offset + '&limit=' + limit + '&order=updated',
  189. headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: `Bearer ${globalVariable.get('accessToken')}` },
  190. success: (data) => {
  191. resolve(data);
  192. },
  193. error: async () => {
  194. resolve(await getAllItems(offset));
  195. }
  196. });
  197. });
  198. };
  199. let data = await getItems(0);
  200. let total = data.total;
  201. let items = data.items;
  202. currentTotal = currentTotal + items.length;
  203. retItems = retItems.concat(items);
  204. while (currentTotal < total) {
  205. data = await getItems(currentTotal);
  206. total = data.total;
  207. items = data.items;
  208. currentTotal = currentTotal + items.length;
  209. retItems = retItems.concat(items);
  210. }
  211. resolve(retItems);
  212. });
  213. }
  214.  
  215. function deleteItem(id) {
  216. let item = globalVariable.get('itemDom')[id];
  217. if (item && !globalVariable.get('deleteItem_' + id + '_loading_setInterval')) {
  218. let textDom = item.find('[dir="auto"]').eq(0);
  219. textDom.text('.');
  220. globalVariable.set(
  221. 'deleteItem_' + id + '_loading_setInterval',
  222. setInterval(() => {
  223. textDom.text(textDom.text() + '.');
  224. if (textDom.text().length > 6) {
  225. textDom.text('.');
  226. }
  227. }, 500)
  228. );
  229. }
  230. return new Promise(async (resolve) => {
  231. $.ajax({
  232. type: 'PATCH',
  233. url: '/backend-api/conversation/' + id,
  234. headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: `Bearer ${globalVariable.get('accessToken')}` },
  235. data: JSON.stringify({ is_visible: false }),
  236. success: () => {
  237. if (item) {
  238. item.remove();
  239. }
  240. resolve(true);
  241. },
  242. error: async () => {
  243. resolve(await deleteItem(id));
  244. }
  245. });
  246. });
  247. }
  248.  
  249. function initItemDeleteBtn() {
  250. return new Promise(async (resolve) => {
  251. let nav = globalVariable.get('Nav');
  252. let itemDiv = await global_module.waitForElement('div[class*="text-token-text-primary text-sm"]', null, null, 100, -1, nav);
  253. let liList = await global_module.waitForElement('li', null, null, 100, -1, itemDiv);
  254. globalVariable.set('itemDom', {});
  255. for (let i = 0; i < liList.length; i++) {
  256. let spanBtn = $(liList[i]).find('span[class][data-state="closed"]');
  257. let that = spanBtn.eq(0);
  258. let li = that.parents('li');
  259. let a = li.find('a');
  260. let href = a.attr('href');
  261. let id = href.replace('/c/', '');
  262. globalVariable.get('itemDom')[id] = li;
  263. if (spanBtn.length != 1) {
  264. continue;
  265. }
  266. let newBtn = global_module.cloneAndHide(that[0], 2);
  267. newBtn = $(newBtn);
  268. newBtn.find('svg').remove();
  269. newBtn.append(clearButtonSvg);
  270. that.show();
  271. newBtn.css('cursor', 'pointer');
  272. newBtn.off('click').on('click', async () => {
  273. await deleteItem(id);
  274. });
  275. }
  276. resolve();
  277. });
  278. }
  279.  
  280. function widescreenDialogue() {
  281. return new Promise(async (resolve) => {
  282. if ($('body').find('style[id="widescreenDialogueCss]').length != 0) {
  283. resolve();
  284. return;
  285. }
  286. let sel = 'textarea[id="prompt-textarea"]';
  287. let Btn = await global_module.waitForElement(sel, null, null, 1000, -1);
  288. Btn = Btn.eq(0);
  289. let BtnParent = Btn.parents('form').eq(0).parent().eq(0);
  290. let cssClassName = BtnParent.attr('class').split(' ')[0];
  291. let styleHtml = '.' + cssClassName + '{width:100%;max-width:100%;}';
  292. $('body').append('<style id="widescreenDialogueCss">' + styleHtml + '</style>');
  293. resolve();
  294. });
  295. }
  296.  
  297. var HookFun = new Map();
  298. HookFun.set('/backend-api/conversation', function (req, res, Text, period) {
  299. if (period === 'preload') {
  300. let additional = 'Please reply me with ';
  301. let additionals = additional + browserLanguage;
  302. let body = JSON.parse(req.data);
  303. let messages = body.messages;
  304. if (messages instanceof Array) {
  305. for (let i = 0; i < messages.length; i++) {
  306. let parts = messages[i].content.parts;
  307. if (parts instanceof Array) {
  308. for (let j = 0; j < parts.length; j++) {
  309. if (parts[j].indexOf(additional) != -1) {
  310. continue;
  311. }
  312. parts[j] = parts[j] + '\n' + additionals;
  313. }
  314. }
  315. }
  316. }
  317. req.data = JSON.stringify(body);
  318. return;
  319. }
  320. return new Promise(async (resolve) => {
  321. if (period !== 'done') {
  322. resolve(null);
  323. return;
  324. }
  325. setTimeout(async () => {
  326. await widescreenDialogue();
  327. }, 100);
  328. resolve(null);
  329. });
  330. });
  331. HookFun.set('/backend-api/conversations', function (req, res, Text, period) {
  332. return new Promise(async (resolve) => {
  333. if (period !== 'done') {
  334. resolve(null);
  335. return;
  336. }
  337. setTimeout(async () => {
  338. await initItemDeleteBtn();
  339. }, 1000);
  340. resolve(null);
  341. });
  342. });
  343.  
  344. function handleResponse(request) {
  345. if (!request) {
  346. return;
  347. }
  348. if (request.url.indexOf(ignoreHookStr) != -1) {
  349. return;
  350. }
  351. let tempUrl = request.url;
  352. if (tempUrl.indexOf('http') == -1 && tempUrl[0] == '/') {
  353. tempUrl = location.origin + tempUrl;
  354. }
  355. let pathname = new URL(tempUrl).pathname;
  356. let fun = HookFun.get(pathname);
  357. if (!fun) {
  358. return;
  359. }
  360. fun(request, null, null, 'preload');
  361. request.response = (res) => {
  362. let Type = 0;
  363. let responseText = res.responseText;
  364. if (typeof responseText !== 'string') {
  365. Type = 1;
  366. responseText = res.text;
  367. }
  368. if (typeof responseText !== 'string') {
  369. Type = 2;
  370. responseText = JSON.stringify(res.json);
  371. }
  372. const oldText = responseText;
  373. res.responseText = new Promise(async (resolve) => {
  374. let ret = await fun(request, res, responseText, 'done');
  375. if (!ret) {
  376. ret = oldText;
  377. }
  378. if (Type === 2) {
  379. if (typeof ret === 'string') {
  380. ret = JSON.parse(ret);
  381. }
  382. }
  383. resolve(ret);
  384. });
  385. };
  386. }
  387.  
  388. // eslint-disable-next-line no-undef
  389. ajaxHooker.hook(handleResponse);
  390.  
  391. $.onurlchange(function () {
  392. setTimeout(async () => {
  393. await widescreenDialogue();
  394. await purify();
  395. }, 1000);
  396. });

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址