ChatBot Prompt Replacer

Replace shortcuts with predefined text in ChatBot input

目前为 2025-01-05 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name ChatBot Prompt Replacer
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2.4
  5. // @description Replace shortcuts with predefined text in ChatBot input
  6. // @author Eric
  7. // @match https://chatgpt.com/*
  8. // @match https://claude.ai/*
  9. // @match https://chat.deepseek.com/*
  10. // @grant GM_registerMenuCommand
  11. // @grant GM_getValue
  12. // @grant GM_setValue
  13. // @license GPL Licence
  14. // ==/UserScript==
  15.  
  16. (function () {
  17. 'use strict';
  18.  
  19. // Load replacements from GM storage
  20. let replacements = GM_getValue('replacements', {
  21. '/tl': '翻译以下内容:',
  22. '/pr': "我正在写一篇计算机领域的英文学术论文,请帮我润色。请以```latex ```格式输出,并注意符合latex格式",
  23. '/qa': "完成这道题。请先分析这道题目,再给出答案。",
  24. '/cmd': "将以下内容转换为Markdown格式,使用LaTeX语法(行内公式放在$内,单行公式放在$$内)来编写数学公式,并以```Markdown ```的格式输出"
  25. });
  26.  
  27. // Register Settings Menu Command
  28. GM_registerMenuCommand('Settings', openSettings);
  29. GM_registerMenuCommand('Reload', observeTargetNode);
  30.  
  31. function openSettings() {
  32. // Create Settings Modal
  33. const modal = document.createElement('div');
  34. modal.style.display = 'block';
  35. modal.style.position = 'fixed';
  36. modal.style.top = '50%';
  37. modal.style.left = '50%';
  38. modal.style.transform = 'translate(-50%, -50%)';
  39. modal.style.backgroundColor = '#000';
  40. modal.style.color = '#fff';
  41. modal.style.padding = '20px';
  42. modal.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
  43. modal.style.zIndex = '1000';
  44.  
  45. modal.innerHTML = '<h2>Settings</h2>';
  46.  
  47. for (const [shortcut, replacement] of Object.entries(replacements)) {
  48. const div = document.createElement('div');
  49. div.style.marginBottom = '10px';
  50. div.style.display = 'flex';
  51. div.style.alignItems = 'center';
  52. div.innerHTML = `
  53. <input type="text" value="${shortcut}" placeholder="Shortcut" style="margin-right: 10px; background-color: #333; color: #fff; width: 100px;" />
  54. <textarea placeholder="Replacement" style="background-color: #333; color: #fff; width: 300px; height: 60px;"></textarea>
  55. `;
  56. const textarea = div.querySelector('textarea');
  57. textarea.value = replacement;
  58. modal.appendChild(div);
  59. }
  60.  
  61. const addBtn = document.createElement('button');
  62. addBtn.textContent = 'Add';
  63. addBtn.style.backgroundColor = '#333';
  64. addBtn.style.color = '#fff';
  65. modal.appendChild(addBtn);
  66.  
  67. const saveBtn = document.createElement('button');
  68. saveBtn.textContent = 'Save';
  69. saveBtn.style.marginLeft = '10px';
  70. saveBtn.style.backgroundColor = '#333';
  71. saveBtn.style.color = '#fff';
  72. modal.appendChild(saveBtn);
  73.  
  74. const cancelBtn = document.createElement('button');
  75. cancelBtn.textContent = 'Cancel';
  76. cancelBtn.style.marginLeft = '10px';
  77. cancelBtn.style.backgroundColor = '#333';
  78. cancelBtn.style.color = '#fff';
  79. modal.appendChild(cancelBtn);
  80.  
  81. cancelBtn.addEventListener('click', () => {
  82. document.body.removeChild(modal);
  83. });
  84.  
  85. addBtn.addEventListener('click', () => {
  86. const div = document.createElement('div');
  87. div.style.marginBottom = '10px';
  88. div.style.display = 'flex';
  89. div.style.alignItems = 'center';
  90. div.innerHTML = `
  91. <input type="text" placeholder="Shortcut" style="margin-right: 10px; background-color: #333; color: #fff; width: 100px;" />
  92. <textarea placeholder="Replacement" style="background-color: #333; color: #fff; width: 300px; height: 60px;"></textarea>
  93. `;
  94. modal.insertBefore(div, addBtn);
  95. });
  96.  
  97. saveBtn.addEventListener('click', () => {
  98. const inputs = modal.querySelectorAll('div > input, div > textarea');
  99. const newReplacements = {};
  100. for (let i = 0; i < inputs.length; i += 2) {
  101. const shortcut = inputs[i].value.trim();
  102. const replacement = inputs[i + 1].value.trim();
  103. if (shortcut && replacement) {
  104. newReplacements[shortcut] = replacement;
  105. }
  106. }
  107. GM_setValue('replacements', newReplacements);
  108. replacements = newReplacements;
  109. document.body.removeChild(modal);
  110. });
  111.  
  112. // Close modal on outside click
  113. modal.addEventListener('click', (e) => {
  114. if (e.target === modal) {
  115. document.body.removeChild(modal);
  116. }
  117. });
  118.  
  119. document.body.appendChild(modal);
  120. }
  121.  
  122. // GPT
  123. function mutationCallback_ChatGPT(mutationList, observer) {
  124. console.log(mutationList);
  125. mutationList.forEach(mutation => {
  126. if (mutation.type === 'characterData') {
  127. const inputString = mutation.target.data;
  128. console.log(inputString);
  129. // replace shortcuts
  130. for (const [shortcut, replacement] of Object.entries(replacements)) {
  131. if (inputString.includes(shortcut + ' ')) {
  132. mutation.target.data = inputString.replace(shortcut + ' ', replacement);
  133. const promptTextarea = document.getElementById('prompt-textarea');
  134. // 增加一个<p>
  135. const newP = document.createElement('p');
  136. promptTextarea.appendChild(newP);
  137. // 将光标设置到新的<p>中
  138.  
  139.  
  140. const selection = window.getSelection();
  141. const range = document.createRange();
  142. range.selectNodeContents(newP);
  143. range.collapse(false);
  144. selection.removeAllRanges();
  145. selection.addRange(range);
  146.  
  147. break;
  148. }
  149. }
  150. }
  151. });
  152. }
  153.  
  154. // 确保 targetNode 被正确获取
  155. function observeTargetNode_GPT() {
  156. const targetNode = document.getElementById('prompt-textarea');
  157. if (targetNode) {
  158. console.log("Target node found:", targetNode);
  159.  
  160. const observer = new MutationObserver(mutationCallback_ChatGPT);
  161.  
  162. observer.observe(targetNode, {
  163. childList: true,
  164. subtree: true,
  165. characterData: true
  166. });
  167. } else {
  168. console.log("Target node not found, retrying...");
  169. setTimeout(observeTargetNode_GPT, 1000); // 每秒重试一次
  170. }
  171. }
  172.  
  173. // Claude AI
  174. function mutationCallback_Claude(mutationList, observer) {
  175. console.log(mutationList);
  176. mutationList.forEach(mutation => {
  177. if (mutation.type === 'characterData') {
  178. const inputString = mutation.target.data;
  179. console.log(inputString);
  180. // replace shortcuts
  181. for (const [shortcut, replacement] of Object.entries(replacements)) {
  182. if (inputString.includes(shortcut + ' ')) {
  183. mutation.target.data = inputString.replace(shortcut + ' ', replacement);
  184.  
  185. // 增加一个<p>
  186. const promptTextarea = document.querySelector("body > div.flex.min-h-screen.w-full > div > main > div.top-5.z-10.mx-auto.w-full.max-w-2xl.md\\:sticky > div > fieldset > div.flex.flex-col.bg-bg-000.gap-1\\.5.border-0\\.5.border-border-300.pl-4.pt-2\\.5.pr-2\\.5.pb-2\\.5.sm\\:mx-0.items-stretch.transition-all.duration-200.relative.shadow-\\[0_0\\.25rem_1\\.25rem_rgba\\(0\\,0\\,0\\,0\\.035\\)\\].focus-within\\:shadow-\\[0_0\\.25rem_1\\.25rem_rgba\\(0\\,0\\,0\\,0\\.075\\)\\].hover\\:border-border-200.focus-within\\:border-border-200.cursor-text.z-10.rounded-2xl > div.flex.gap-2 > div.mt-1.max-h-96.w-full.overflow-y-auto.break-words.min-h-\\[4\\.5rem\\] > div");
  187. const newP = document.createElement('p');
  188. promptTextarea.appendChild(newP);
  189.  
  190. // 将光标设置到新的<p>中
  191. const selection = window.getSelection();
  192. const range = document.createRange();
  193. range.selectNodeContents(newP);
  194. range.collapse(false);
  195. selection.removeAllRanges();
  196. selection.addRange(range);
  197.  
  198. break;
  199. }
  200. }
  201. }
  202. });
  203. }
  204.  
  205. function observeTargetNode_ClaudeAI() {
  206. const targetNode = document.querySelector("body > div.flex.min-h-screen.w-full > div > main > div.top-5.z-10.mx-auto.w-full.max-w-2xl.md\\:sticky > div > fieldset > div.flex.flex-col.bg-bg-000.gap-1\\.5.border-0\\.5.border-border-300.pl-4.pt-2\\.5.pr-2\\.5.pb-2\\.5.sm\\:mx-0.items-stretch.transition-all.duration-200.relative.shadow-\\[0_0\\.25rem_1\\.25rem_rgba\\(0\\,0\\,0\\,0\\.035\\)\\].focus-within\\:shadow-\\[0_0\\.25rem_1\\.25rem_rgba\\(0\\,0\\,0\\,0\\.075\\)\\].hover\\:border-border-200.focus-within\\:border-border-200.cursor-text.z-10.rounded-2xl > div.flex.gap-2 > div.mt-1.max-h-96.w-full.overflow-y-auto.break-words.min-h-\\[4\\.5rem\\] > div")
  207. if (targetNode) {
  208. console.log("Target node found:", targetNode);
  209.  
  210. const observer = new MutationObserver(mutationCallback_Claude);
  211.  
  212. observer.observe(targetNode, {
  213. childList: true,
  214. subtree: true,
  215. characterData: true
  216. });
  217. } else {
  218. console.log("Target node not found, retrying...");
  219. setTimeout(observeTargetNode_ClaudeAI, 1000); // 每秒重试一次
  220. }
  221. }
  222.  
  223. // DeepSeek
  224. function mutationCallback_DeepSeek(mutationList, observer) {
  225. console.log(mutationList);
  226. mutationList.forEach(mutation => {
  227. if (mutation.type === 'childList') {
  228. const inputString = mutation.target.value;
  229. console.log(inputString);
  230. // replace shortcuts
  231. for (const [shortcut, replacement] of Object.entries(replacements)) {
  232. if (inputString.includes(shortcut + ' ')) {
  233. const chat_input = document.getElementById('chat-input');
  234. chat_input.value = inputString.replace(shortcut + ' ', replacement);
  235. console.log(chat_input);
  236. document.querySelector(".b13855df").textContent = chat_input.value + '\n';
  237. simulateInputAtCursor("\n"); // 模拟按下回车键 DeepSeek修改textarea的值后需要输入其他键才能把修改持久化保存
  238. break;
  239. }
  240. }
  241. }
  242. });
  243. }
  244.  
  245. // 确保 targetNode 被正确获取
  246. function observeTargetNode_DeekSeek() {
  247. const targetNode = document.getElementById('chat-input');
  248. if (targetNode) {
  249. console.log("Target node found:", targetNode);
  250.  
  251. const observer = new MutationObserver(mutationCallback_DeepSeek);
  252.  
  253. observer.observe(targetNode, {
  254. childList: true,
  255. subtree: true,
  256. characterData: true
  257. });
  258. } else {
  259. console.log("Target node not found, retrying...");
  260. setTimeout(observeTargetNode_DeekSeek, 1000); // 每秒重试一次
  261. }
  262. }
  263.  
  264.  
  265. function observeTargetNode() {
  266. const isClaudeAI = window.location.href.includes('claude.ai');
  267. const isChatGPT = window.location.href.includes('chatgpt.com');
  268. const isDeepSeek = window.location.href.includes("deepseek.com");
  269. if (isClaudeAI) {
  270. observeTargetNode_ClaudeAI();
  271. } else if (isChatGPT) {
  272. observeTargetNode_GPT();
  273. } else if (isDeepSeek) {
  274. observeTargetNode_DeekSeek();
  275. }
  276. }
  277.  
  278. // Add URL change detection
  279. let lastUrl = location.href;
  280. new MutationObserver(() => {
  281. const url = location.href;
  282. if (url !== lastUrl) {
  283. lastUrl = url;
  284. console.log('URL changed to', url);
  285. setTimeout(() => {
  286. observeTargetNode();
  287. }, 1000);
  288.  
  289. }
  290. }).observe(document, { subtree: true, childList: true });
  291.  
  292. // Also listen to history changes
  293. window.addEventListener('popstate', function () {
  294. console.log('URL changed via back/forward');
  295. setTimeout(() => {
  296. observeTargetNode();
  297. }, 1000);
  298. });
  299.  
  300. observeTargetNode();
  301.  
  302. // 模拟执行粘贴,尝试所有的可能方式,每0.5秒钟检查一次是否有可输入的焦点元素,持续5s
  303. function simulateInputAtCursor(message) {
  304. const maxWaitTime = 5000; // 最大等待时间(毫秒)
  305. const checkInterval = 500; // 检查间隔(毫秒)
  306.  
  307. let attempts = 0;
  308. const interval = setInterval(() => {
  309. const activeElement = document.activeElement;
  310.  
  311. if (activeElement && (
  312. activeElement instanceof HTMLInputElement ||
  313. activeElement instanceof HTMLTextAreaElement ||
  314. (activeElement.isContentEditable && activeElement.contentEditable === 'true')
  315. )) {
  316. clearInterval(interval);
  317. activeElement.focus();
  318.  
  319. // 方式一:尝试使用 document.execCommand 插入文本
  320. if (document.queryCommandSupported && document.queryCommandSupported('insertText')) {
  321. try {
  322. document.execCommand('insertText', false, message);
  323. // console.log('粘贴成功(方式一)');
  324. return;
  325. } catch (e) {
  326. // console.warn('方式一失败,尝试其他方法');
  327. }
  328. }
  329.  
  330. // 方式二:如果 execCommand 失败,尝试直接设置值
  331. if (activeElement.setSelectionRange) {
  332. const start = activeElement.selectionStart;
  333. const end = activeElement.selectionEnd;
  334. activeElement.value = activeElement.value.substring(0, start) + message + activeElement.value.substring(end);
  335. activeElement.setSelectionRange(start + message.length, start + message.length);
  336. // console.log('粘贴成功(方式二)');
  337. return;
  338. }
  339.  
  340. // 方式三:如果是 contenteditable 元素
  341. if (activeElement.isContentEditable) {
  342. const selection = window.getSelection();
  343. if (selection.rangeCount > 0) {
  344. const range = selection.getRangeAt(0);
  345. range.deleteContents();
  346. const textNode = document.createTextNode(message);
  347. range.insertNode(textNode);
  348. range.setEndAfter(textNode);
  349. range.collapse(false);
  350. selection.removeAllRanges();
  351. selection.addRange(range);
  352. // console.log('粘贴成功(方式三)');
  353. return;
  354. }
  355. }
  356.  
  357. // 方式四:如果 setSelectionRange 和 contenteditable 也不支持,尝试模拟按键事件
  358. for (let i = 0; i < message.length; i++) {
  359. const keyEvent = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: message[i] });
  360. activeElement.dispatchEvent(keyEvent);
  361. const inputEvent = new InputEvent('input', { bubbles: true, cancelable: true, data: message[i] });
  362. activeElement.dispatchEvent(inputEvent);
  363. }
  364. // console.log('粘贴成功(方式四)');
  365. } else {
  366. // 如果还没有超过最大等待时间,继续检查
  367. attempts++;
  368. if (attempts * checkInterval >= maxWaitTime) {
  369. // 超过最大等待时间,停止查找并打印错误信息
  370. clearInterval(interval);
  371. // console.error('在五秒内未找到可输入的焦点元素,放弃执行粘贴动作。');
  372. }
  373. }
  374. }, checkInterval);
  375. }
  376.  
  377. })();

QingJ © 2025

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