Auto-Translate Messages

Adds a auto-translate messages for character messages in the chat for beta.character.ai

目前为 2022-12-27 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Auto-Translate Messages
  3. // @namespace AutoTranslateMessagesForBetaCharacterAI
  4. // @description Adds a auto-translate messages for character messages in the chat for beta.character.ai
  5. // @version 3.1.1
  6. // @author CriDos
  7. // @icon https://www.google.com/s2/favicons?sz=64&domain=beta.character.ai
  8. // @match https://beta.character.ai/chat?char=*
  9. // @grant GM_xmlhttpRequest
  10. // @run-at document-end
  11. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js
  12. // @license MIT
  13. // ==/UserScript==
  14.  
  15. 'use strict';
  16.  
  17. console.log(`Auto-Translate Messages initializing...`);
  18.  
  19. let debug = false;
  20. let sourceLanguage = "ru";
  21.  
  22. setInterval(() => {
  23. try {
  24. addAutoTranslate();
  25. } catch (error) {
  26. console.error(error);
  27. }
  28.  
  29. try {
  30. findAndHookTextareaElement();
  31. } catch (error) {
  32. console.error(error);
  33. }
  34. }, 100);
  35.  
  36. async function addAutoTranslate() {
  37. var charMessages = document.getElementsByClassName("char-msg");
  38. for (var i = 0; i < charMessages.length; i++) {
  39. const node = charMessages[i].querySelector(".markdown-wrapper");
  40.  
  41. if (node == null) {
  42. continue;
  43. }
  44.  
  45. const parentNode = node.parentElement;
  46. if (parentNode.isAutoTranslate) {
  47. continue;
  48. }
  49. parentNode.isAutoTranslate = true;
  50.  
  51. setInterval(async () => {
  52. await translateNode(node);
  53. }, 500);
  54. }
  55.  
  56. var userMessages = document.getElementsByClassName("user-msg");
  57. for (var i = 0; i < userMessages.length; i++) {
  58. const node = userMessages[i].querySelector(".markdown-wrapper");
  59.  
  60. if (node == null) {
  61. continue;
  62. }
  63.  
  64. const parentNode = node.parentElement;
  65. if (parentNode.isAutoTranslate) {
  66. continue;
  67. }
  68. parentNode.isAutoTranslate = true;
  69.  
  70. translateNode(node, true);
  71. }
  72. }
  73.  
  74. function findAndHookTextareaElement() {
  75. const targetElement = document.querySelector("textarea");
  76. if (targetElement === null) {
  77. return;
  78. }
  79.  
  80. if (targetElement.isAddHookKeydownEvent === true) {
  81. return;
  82. }
  83.  
  84. targetElement.isAddHookKeydownEvent = true;
  85.  
  86. console.log(`Textarea element found. Adding keydown event listener.`);
  87. targetElement.addEventListener("keydown", async event => await handleSubmit(event, targetElement), true);
  88. }
  89.  
  90. async function handleSubmit(event, targetElement) {
  91. console.log(`Keydown event detected: type - ${event.type}, key - ${event.key}`);
  92.  
  93. if (event.shiftKey && event.key === "Enter") {
  94. return;
  95. }
  96.  
  97. if (window.isActiveOnSubmit === true) {
  98. return;
  99. }
  100.  
  101. if (event.key === "Enter") {
  102. window.isActiveOnSubmit = true;
  103. event.stopImmediatePropagation();
  104.  
  105. const request = targetElement.value;
  106. targetElement.value = "";
  107.  
  108. const translatedText = await translate(request, sourceLanguage, "en", "text");
  109.  
  110. targetElement.focus();
  111. document.execCommand('insertText', false, translatedText);
  112. const enterEvent = new KeyboardEvent("keydown", {
  113. bubbles: true,
  114. cancelable: true,
  115. key: "Enter",
  116. code: "Enter"
  117. });
  118. targetElement.dispatchEvent(enterEvent);
  119.  
  120. window.isActiveOnSubmit = false;
  121. }
  122. }
  123.  
  124. async function translateNode(node, replace = false) {
  125. const translateClassName = "translate";
  126. const parentNode = node.parentElement;
  127. const nodeContent = $(node).html();
  128.  
  129. if (node.storeContent == nodeContent) {
  130. return;
  131. }
  132. node.storeContent = nodeContent;
  133.  
  134. var translateContent = await translate(nodeContent, "en", navigator.language);
  135.  
  136. if (replace) {
  137. $(node).html(translateContent);
  138. return;
  139. }
  140.  
  141. var translateNode = parentNode.querySelector(`.${translateClassName}`);
  142. if (translateNode == null) {
  143. translateNode = node.cloneNode(true);
  144. translateNode.classList.add(translateClassName);
  145. parentNode.insertBefore(translateNode, parentNode.firstChild);
  146. }
  147.  
  148. translateContent = translateContent.replace(/<\/div>$/, '');
  149. $(translateNode).html(translateContent + `<p>.......... end_translate ..........</p></div>`);
  150. }
  151.  
  152. const cache = {};
  153.  
  154. async function translate(text, sLang, tLang, format, key) {
  155. try {
  156. if (debug) {
  157. console.log(`preTranslate: ${text}`);
  158. }
  159.  
  160. if (format == null) {
  161. format = "html";
  162. }
  163.  
  164. if (key == null) {
  165. key = "AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw";
  166. }
  167.  
  168. if (cache[text]) {
  169. return cache[text];
  170. }
  171.  
  172. const result = await new Promise((resolve, reject) => {
  173. GM_xmlhttpRequest({
  174. method: "POST",
  175. url: `https://translate.googleapis.com/translate_a/t?client=gtx&format=${format}&sl=${sLang}&tl=${tLang}&key=${key}`,
  176. data: `q=${encodeURIComponent(text)}`,
  177. headers: {
  178. "Content-Type": "application/x-www-form-urlencoded"
  179. },
  180. onload: response => {
  181. const json = JSON.parse(response.responseText);
  182. if (Array.isArray(json[0])) {
  183. resolve(json[0][0]);
  184. } else {
  185. resolve(json[0]);
  186. }
  187. },
  188. onerror: response => {
  189. reject(response.statusText);
  190. }
  191. });
  192. });
  193.  
  194. cache[text] = result;
  195.  
  196. if (debug) {
  197. console.log(`postTranslate: ${result}`);
  198. }
  199.  
  200. return result;
  201. } catch (error) {
  202. console.error(error);
  203. }
  204. }