Torn Translator (Chat Fix)

Adds chat translation buttons

  1. // ==UserScript==
  2. // @name Torn Translator (Chat Fix)
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description Adds chat translation buttons
  6. // @author JeffBezas
  7. // @match https://www.torn.com/*
  8. // @grant GM_xmlhttpRequest
  9. // @grant GM_addStyle
  10. // @grant GM_setValue
  11. // @grant GM_getValue
  12. // @grant GM_setClipboard
  13. // @connect translate.googleapis.com
  14. // @license MIT
  15. // ==/UserScript==
  16.  
  17. (function() {
  18. 'use strict';
  19.  
  20. // Load saved position or set default
  21. let posX = GM_getValue("translatorPosX", 50);
  22. let posY = GM_getValue("translatorPosY", 50);
  23.  
  24. // Add custom styles
  25. GM_addStyle(`
  26. #translator-container {
  27. position: fixed;
  28. left: ${posX}px;
  29. top: ${posY}px;
  30. width: 300px;
  31. background: rgba(0, 0, 0, 0.8);
  32. padding: 10px;
  33. border-radius: 5px;
  34. color: white;
  35. font-size: 14px;
  36. z-index: 9999;
  37. cursor: grab;
  38. }
  39. #translator-header {
  40. background: rgba(255, 255, 255, 0.2);
  41. padding: 5px;
  42. text-align: center;
  43. font-weight: bold;
  44. cursor: grab;
  45. user-select: none;
  46. }
  47. #translator-container select, #translator-container textarea, #translator-container button {
  48. width: 100%;
  49. margin-top: 5px;
  50. }
  51. #translator-container textarea {
  52. height: 50px;
  53. }
  54. #translated-text {
  55. margin-top: 10px;
  56. background: white;
  57. color: black;
  58. padding: 5px;
  59. border-radius: 3px;
  60. }
  61. .translate-btn {
  62. margin-left: 5px;
  63. background: #4CAF50;
  64. color: white;
  65. border: none;
  66. padding: 2px 5px;
  67. font-size: 12px;
  68. cursor: pointer;
  69. border-radius: 3px;
  70. }
  71. .translate-btn:hover {
  72. background: #45a049;
  73. }
  74. `);
  75.  
  76. // Create the translation UI
  77. const translatorDiv = document.createElement('div');
  78. translatorDiv.id = 'translator-container';
  79. translatorDiv.innerHTML = `
  80. <div id="translator-header">Drag Me</div>
  81. <label>Translate from:</label>
  82. <select id="sourceLang">
  83. <option value="auto">Auto</option>
  84. <option value="en">English</option>
  85. <option value="es">Spanish</option>
  86. <option value="fr">French</option>
  87. <option value="de">German</option>
  88. </select>
  89. <label>To:</label>
  90. <select id="targetLang">
  91. <option value="en">English</option>
  92. <option value="es">Spanish</option>
  93. <option value="fr">French</option>
  94. <option value="de">German</option>
  95. </select>
  96. <textarea id="textToTranslate" placeholder="Enter text here..."></textarea>
  97. <div style="display: flex; justify-content: space-between;">
  98. <button id="copyText">📋 Copy</button>
  99. <button id="pasteText">📥 Paste</button>
  100. </div>
  101. <div id="translated-text">Translation will appear here...</div>
  102. `;
  103. document.body.appendChild(translatorDiv);
  104.  
  105. // Enable dragging
  106. let isDragging = false, startX, startY;
  107. document.getElementById('translator-header').addEventListener('mousedown', function(e) {
  108. isDragging = true;
  109. startX = e.clientX - translatorDiv.offsetLeft;
  110. startY = e.clientY - translatorDiv.offsetTop;
  111. translatorDiv.style.cursor = "grabbing";
  112. });
  113.  
  114. document.addEventListener('mousemove', function(e) {
  115. if (isDragging) {
  116. let newX = e.clientX - startX;
  117. let newY = e.clientY - startY;
  118. translatorDiv.style.left = newX + "px";
  119. translatorDiv.style.top = newY + "px";
  120. }
  121. });
  122.  
  123. document.addEventListener('mouseup', function() {
  124. if (isDragging) {
  125. GM_setValue("translatorPosX", translatorDiv.offsetLeft);
  126. GM_setValue("translatorPosY", translatorDiv.offsetTop);
  127. isDragging = false;
  128. translatorDiv.style.cursor = "grab";
  129. }
  130. });
  131.  
  132. // Copy to clipboard
  133. document.getElementById('copyText').addEventListener('click', function() {
  134. const translatedText = document.getElementById('translated-text').innerText;
  135. GM_setClipboard(translatedText);
  136. });
  137.  
  138. // Paste from clipboard
  139. document.getElementById('pasteText').addEventListener('click', function() {
  140. navigator.clipboard.readText().then(text => {
  141. document.getElementById('textToTranslate').value = text;
  142. translateText(text);
  143. }).catch(err => {
  144. console.error("Failed to read clipboard: ", err);
  145. });
  146. });
  147.  
  148. // Auto-translate on input
  149. let timeout;
  150. document.getElementById('textToTranslate').addEventListener('input', function() {
  151. clearTimeout(timeout);
  152. timeout = setTimeout(() => {
  153. const text = document.getElementById('textToTranslate').value;
  154. translateText(text);
  155. }, 500);
  156. });
  157.  
  158. // Function to fetch translation
  159. function translateText(text) {
  160. const sourceLang = document.getElementById('sourceLang').value;
  161. const targetLang = document.getElementById('targetLang').value;
  162.  
  163. if (text.trim() === "") {
  164. document.getElementById('translated-text').innerText = "Translation will appear here...";
  165. return;
  166. }
  167.  
  168. const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${sourceLang}&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`;
  169.  
  170. GM_xmlhttpRequest({
  171. method: "GET",
  172. url: url,
  173. onload: function(response) {
  174. try {
  175. const result = JSON.parse(response.responseText);
  176. const translatedText = result[0].map(item => item[0]).join("");
  177. document.getElementById('translated-text').innerText = translatedText;
  178. } catch (e) {
  179. document.getElementById('translated-text').innerText = "Translation error.";
  180. }
  181. }
  182. });
  183. }
  184.  
  185. // Add translation buttons to chat messages
  186. function addTranslationButtons() {
  187. document.querySelectorAll('.chat-box-message__message___SldE8').forEach(message => {
  188. if (!message.querySelector('.translate-btn')) {
  189. const translateBtn = document.createElement('button');
  190. translateBtn.innerText = "Translate";
  191. translateBtn.classList.add('translate-btn');
  192.  
  193. // Get clean message text (remove button and emoji)
  194. translateBtn.addEventListener('click', function() {
  195. const text = message.querySelector('.text-message___gcG6e')?.innerText.trim() || ""; // Extract only message text
  196. document.getElementById('textToTranslate').value = text;
  197. translateText(text);
  198. });
  199.  
  200. message.appendChild(translateBtn);
  201. }
  202. });
  203. }
  204.  
  205. // Observe chat for new messages
  206. const chatObserver = new MutationObserver(addTranslationButtons);
  207. chatObserver.observe(document.body, { childList: true, subtree: true });
  208.  
  209. })();