Gartic.io Voice Input

Gartic.io voice input

  1. // ==UserScript==
  2. // @name Gartic.io Voice Input
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.0
  5. // @description Gartic.io voice input
  6. // @author You
  7. // @match https://*.gartic.io/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. const styles = `
  15. .voice-container {
  16. position: fixed;
  17. top: 80px;
  18. left: 50%;
  19. transform: translateX(-50%);
  20. display: flex;
  21. gap: 12px;
  22. z-index: 9999;
  23. animation: floatIn 0.8s cubic-bezier(0.16, 1, 0.3, 1);
  24. background: rgba(15, 23, 42, 0.85);
  25. padding: 12px 20px;
  26. border-radius: 25px;
  27. box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3),
  28. 0 4px 8px rgba(31, 41, 55, 0.2),
  29. inset 0 2px 5px rgba(255, 255, 255, 0.05);
  30. backdrop-filter: blur(15px);
  31. border: 1px solid rgba(255, 255, 255, 0.08);
  32. }
  33.  
  34. .voice-input {
  35. width: 300px;
  36. padding: 10px 18px;
  37. border: 2px solid rgba(255, 255, 255, 0.08);
  38. border-radius: 18px;
  39. font-size: 15px;
  40. background: rgba(255, 255, 255, 0.03);
  41. color: #fff;
  42. letter-spacing: 0.3px;
  43. box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
  44. transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  45. }
  46.  
  47. .voice-input::placeholder {
  48. color: rgba(255, 255, 255, 0.3);
  49. font-style: italic;
  50. }
  51.  
  52. .voice-input:focus {
  53. outline: none;
  54. border-color: rgba(56, 189, 248, 0.5);
  55. box-shadow: 0 0 20px rgba(56, 189, 248, 0.15),
  56. inset 0 2px 4px rgba(0, 0, 0, 0.1);
  57. background: rgba(255, 255, 255, 0.05);
  58. transform: translateY(-1px);
  59. }
  60.  
  61. .mic-button {
  62. width: 45px;
  63. height: 45px;
  64. border: none;
  65. border-radius: 50%;
  66. background: linear-gradient(135deg, #3b82f6, #1d4ed8);
  67. cursor: pointer;
  68. display: flex;
  69. align-items: center;
  70. justify-content: center;
  71. transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
  72. box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3),
  73. inset 0 -2px 5px rgba(0, 0, 0, 0.2);
  74. position: relative;
  75. overflow: hidden;
  76. }
  77.  
  78. .mic-button::before {
  79. content: '';
  80. position: absolute;
  81. top: -50%;
  82. left: -50%;
  83. width: 200%;
  84. height: 200%;
  85. background: radial-gradient(circle, rgba(255,255,255,0.2) 0%, transparent 70%);
  86. transform: rotate(45deg);
  87. transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
  88. opacity: 0;
  89. }
  90.  
  91. .mic-button:hover::before {
  92. opacity: 1;
  93. transform: rotate(225deg);
  94. }
  95.  
  96. .mic-button::after {
  97. content: '';
  98. position: absolute;
  99. width: 100%;
  100. height: 100%;
  101. background: linear-gradient(rgba(255, 255, 255, 0.2), transparent);
  102. clip-path: polygon(0 0, 100% 0, 100% 25%, 0 25%);
  103. opacity: 0;
  104. transition: opacity 0.3s;
  105. }
  106.  
  107. .mic-button:hover::after {
  108. opacity: 1;
  109. }
  110.  
  111. .mic-button:hover {
  112. transform: translateY(-2px) scale(1.05);
  113. box-shadow: 0 6px 20px rgba(59, 130, 246, 0.4);
  114. background: linear-gradient(135deg, #4f46e5, #3b82f6);
  115. }
  116.  
  117. .mic-button:active {
  118. transform: scale(0.95);
  119. box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
  120. }
  121.  
  122. .mic-button.recording {
  123. background: linear-gradient(135deg, #ef4444, #dc2626);
  124. animation: pulseRecord 2s infinite;
  125. }
  126.  
  127. .mic-icon {
  128. width: 22px;
  129. height: 22px;
  130. fill: white;
  131. filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.2));
  132. transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  133. }
  134.  
  135. .recording .mic-icon {
  136. animation: scaleIcon 2s infinite;
  137. filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
  138. }
  139.  
  140. .wave-container {
  141. position: absolute;
  142. width: 100%;
  143. height: 100%;
  144. pointer-events: none;
  145. }
  146.  
  147. .wave {
  148. position: absolute;
  149. border-radius: 50%;
  150. border: 2px solid rgba(255, 255, 255, 0.4);
  151. animation: wave 2.5s infinite cubic-bezier(0.4, 0, 0.2, 1);
  152. opacity: 0;
  153. }
  154.  
  155. .recording .wave-container .wave {
  156. animation: wave 2s infinite cubic-bezier(0.4, 0, 0.2, 1);
  157. }
  158.  
  159. @keyframes wave {
  160. 0% {
  161. width: 0;
  162. height: 0;
  163. opacity: 0.8;
  164. transform: translate(-50%, -50%) rotate(0deg);
  165. }
  166. 100% {
  167. width: 200%;
  168. height: 200%;
  169. opacity: 0;
  170. transform: translate(-50%, -50%) rotate(180deg);
  171. }
  172. }
  173.  
  174. @keyframes pulseRecord {
  175. 0% {
  176. box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.5);
  177. transform: scale(1);
  178. }
  179. 50% {
  180. box-shadow: 0 0 20px 5px rgba(239, 68, 68, 0.3);
  181. transform: scale(1.02);
  182. }
  183. 100% {
  184. box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
  185. transform: scale(1);
  186. }
  187. }
  188.  
  189. @keyframes scaleIcon {
  190. 0%, 100% {
  191. transform: scale(1) rotate(0deg);
  192. filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.5));
  193. }
  194. 50% {
  195. transform: scale(0.85) rotate(5deg);
  196. filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.7));
  197. }
  198. }
  199.  
  200. @keyframes floatIn {
  201. 0% {
  202. transform: translate(-50%, -30px);
  203. opacity: 0;
  204. filter: blur(10px);
  205. }
  206. 100% {
  207. transform: translate(-50%, 0);
  208. opacity: 1;
  209. filter: blur(0);
  210. }
  211. }
  212.  
  213. .visualizer {
  214. position: absolute;
  215. bottom: -10px;
  216. left: 0;
  217. width: 100%;
  218. height: 2px;
  219. display: flex;
  220. justify-content: space-between;
  221. padding: 0 5px;
  222. }
  223.  
  224. .visualizer-bar {
  225. width: 2px;
  226. height: 2px;
  227. background: rgba(255, 255, 255, 0.5);
  228. transform-origin: bottom;
  229. transition: all 0.2s ease;
  230. border-radius: 2px;
  231. box-shadow: 0 0 4px rgba(255, 255, 255, 0.3);
  232. }
  233.  
  234. .recording .visualizer-bar {
  235. animation: glow 1.5s infinite;
  236. }
  237.  
  238. @keyframes glow {
  239. 0%, 100% {
  240. box-shadow: 0 0 4px rgba(255, 255, 255, 0.3);
  241. }
  242. 50% {
  243. box-shadow: 0 0 8px rgba(255, 255, 255, 0.5);
  244. }
  245. }
  246. `;
  247.  
  248. const styleSheet = document.createElement("style");
  249. styleSheet.textContent = styles;
  250. document.head.appendChild(styleSheet);
  251.  
  252. const container = document.createElement('div');
  253. container.className = 'voice-container';
  254.  
  255. const input = document.createElement('input');
  256. input.className = 'voice-input';
  257. input.type = 'text';
  258. input.placeholder = 'Konuşmak için mikrofonu tıklayın...';
  259.  
  260. const button = document.createElement('button');
  261. button.className = 'mic-button';
  262. button.innerHTML = `
  263. <div class="wave-container">
  264. <div class="wave"></div>
  265. <div class="wave" style="animation-delay: 0.4s"></div>
  266. <div class="wave" style="animation-delay: 0.8s"></div>
  267. </div>
  268. <svg class="mic-icon" viewBox="0 0 24 24">
  269. <path d="M12,2A3,3 0 0,1 15,5V11A3,3 0 0,1 12,14A3,3 0 0,1 9,11V5A3,3 0 0,1 12,2M19,11C19,14.53 16.39,17.44 13,17.93V21H11V17.93C7.61,17.44 5,14.53 5,11H7A5,5 0 0,0 12,16A5,5 0 0,0 17,11H19Z"/>
  270. </svg>
  271. <div class="visualizer">
  272. ${Array(20).fill().map(() => '<div class="visualizer-bar"></div>').join('')}
  273. </div>
  274. `;
  275.  
  276. let originalSend = WebSocket.prototype.send;
  277. let wsObj = null;
  278.  
  279. WebSocket.prototype.send = function(data) {
  280. originalSend.apply(this, arguments);
  281. if (!wsObj) {
  282. wsObj = this;
  283. wsObj.addEventListener("message", (msg) => {
  284. try {
  285. let data = JSON.parse(msg.data.slice(2));
  286. if (data[0] == 5) {
  287. wsObj.lengthID = data[1];
  288. wsObj.id = data[2];
  289. wsObj.roomCode = data[3];
  290. }
  291. } catch (err) {}
  292. });
  293. }
  294. };
  295.  
  296. const sendMessage = (message) => {
  297. if (wsObj && message.trim()) {
  298. wsObj.send(`42[11,${wsObj.id},"${message}"]`);
  299. }
  300. };
  301.  
  302. const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
  303. recognition.lang = 'tr-TR';
  304. recognition.continuous = true;
  305. recognition.interimResults = true;
  306.  
  307. let isRecording = false;
  308.  
  309. button.addEventListener('click', () => {
  310. if (!isRecording) {
  311. recognition.start();
  312. button.classList.add('recording');
  313. animateVisualizer(true);
  314. } else {
  315. recognition.stop();
  316. button.classList.remove('recording');
  317. animateVisualizer(false);
  318. }
  319. isRecording = !isRecording;
  320. });
  321.  
  322. input.addEventListener('keypress', (e) => {
  323. if (e.key === 'Enter') {
  324. sendMessage(input.value);
  325. input.value = '';
  326. }
  327. });
  328.  
  329. recognition.onresult = (event) => {
  330. const transcript = Array.from(event.results)
  331. .map(result => result[0])
  332. .map(result => result.transcript)
  333. .join('');
  334. input.value = transcript;
  335. };
  336.  
  337. recognition.onend = () => {
  338. button.classList.remove('recording');
  339. isRecording = false;
  340. animateVisualizer(false);
  341. };
  342.  
  343. function animateVisualizer(active) {
  344. const bars = document.querySelectorAll('.visualizer-bar');
  345. if (active) {
  346. bars.forEach(bar => {
  347. const animate = () => {
  348. const height = Math.random() * 20 + 2;
  349. bar.style.height = `${height}px`;
  350. bar.style.transform = `scaleY(${height/2})`;
  351. if (isRecording) {
  352. requestAnimationFrame(animate);
  353. }
  354. };
  355. animate();
  356. });
  357. } else {
  358. bars.forEach(bar => {
  359. bar.style.height = '2px';
  360. bar.style.transform = 'scaleY(1)';
  361. });
  362. }
  363. }
  364.  
  365. container.appendChild(input);
  366. container.appendChild(button);
  367. document.body.appendChild(container);
  368. })();