PI - no hands talk :)

Chat with PI on pi.ai using this script that enables speech recognition.

  1. // ==UserScript==
  2. // @name PI - no hands talk :)
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.3
  5. // @description Chat with PI on pi.ai using this script that enables speech recognition.
  6. // @author Guki
  7. // @match https://pi.ai/talk
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=pi.ai
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12. (function() {
  13. 'use strict';
  14.  
  15. // if you have a job for a person who interested in ML, AI agents, LLMs
  16. // please contact me, my email: sokolovivanf@gmail.com
  17.  
  18. // options
  19. const sendDelay = 3000 // time in ms after last registered spoken word before sending a message
  20. const startActive = false // activate the script on start
  21. const checkRecognition = 1000 // time in ms of how often to check recognition status to restart it if its become inactive
  22.  
  23. let SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
  24. let recognition = new SpeechRecognition();
  25. recognition.interimResults = true;
  26.  
  27. let recognitionActive = false;
  28. if (startActive) {
  29. recognitionActive = true
  30. }
  31. recognition.onstart = function() {
  32. recognitionActive = true;
  33. };
  34. recognition.onend = function() {
  35. recognitionActive = false;
  36. if (scriptActive) {
  37. setTimeout(() => {
  38. // rarely might be active by the interval
  39. if (!recognitionActive) {
  40. recognition.start()
  41. }
  42. }, 5)
  43. }
  44. };
  45.  
  46. let scriptActive = false
  47. if (startActive) {
  48. scriptActive = true
  49. recognition.start();
  50. }
  51.  
  52.  
  53. // script toggle button
  54. const button = document.createElement('button');
  55. button.style.position = 'fixed';
  56. button.style.top = '120px';
  57. button.style.left = '24px';
  58. button.style.borderRadius = '50%';
  59. button.style.width = '42px';
  60. button.style.height = '42px';
  61. button.style.zIndex = '9999';
  62. button.style.color = 'white';
  63. button.style.opacity = '0.8';
  64. if (startActive) {
  65. button.style.background = '#e63946';
  66. } else {
  67. button.style.background = '#a8dadc';
  68. }
  69. button.style.paddingLeft = "5px";
  70. button.style.transition = "all 200ms"
  71. button.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-8 h-8"><path d="M8.25 4.5a3.75 3.75 0 117.5 0v8.25a3.75 3.75 0 11-7.5 0V4.5z" /><path d="M6 10.5a.75.75 0 01.75.75v1.5a5.25 5.25 0 1010.5 0v-1.5a.75.75 0 011.5 0v1.5a6.751 6.751 0 01-6 6.709v2.291h3a.75.75 0 010 1.5h-7.5a.75.75 0 010-1.5h3v-2.291a6.751 6.751 0 01-6-6.709v-1.5A.75.75 0 016 10.5z" /></svg>'
  72. button.onclick = function() {
  73. if (scriptActive) {
  74. scriptActive = false
  75. button.style.background = '#a8dadc';
  76. } else {
  77. scriptActive = true
  78. button.style.background = '#e63946';
  79. }
  80. if (!scriptActive && recognitionActive) {
  81. recognition.stop();
  82. } else if (scriptActive && !recognitionActive) {
  83. recognition.start();
  84. }
  85. };
  86. document.body.appendChild(button);
  87.  
  88. // infinite recognition restart when script is active
  89. setInterval(function() {
  90. if (scriptActive && !recognitionActive) {
  91. recognition.start();
  92. }
  93. }, checkRecognition);
  94.  
  95. function debounce(func, wait, immediate) {
  96. var timeout;
  97. return function() {
  98. var context = this, args = arguments;
  99. var later = function() {
  100. timeout = null;
  101. if (!immediate) func.apply(context, args);
  102. };
  103. var callNow = immediate && !timeout;
  104. clearTimeout(timeout);
  105. timeout = setTimeout(later, wait);
  106. if (callNow) func.apply(context, args);
  107. };
  108. };
  109.  
  110. let completeTranscript = ''
  111. function cleanCompleteTranscript() {
  112. completeTranscript = ''
  113. }
  114.  
  115. function pressEnter() {
  116. var textarea = document.querySelector('.block.w-full.resize-none.overflow-y-hidden.whitespace-pre-wrap.bg-transparent');
  117. var enterEvent = new KeyboardEvent('keydown', {
  118. key: 'Enter',
  119. bubbles: true,
  120. cancelable: true
  121. });
  122. textarea.dispatchEvent(enterEvent);
  123. cleanCompleteTranscript()
  124. }
  125. const debouncedPressEnter = debounce(pressEnter, sendDelay);
  126.  
  127. recognition.onresult = function(event) {
  128. let textarea = document.querySelector('.block.w-full.resize-none.overflow-y-hidden.whitespace-pre-wrap.bg-transparent');
  129. let transcript = event.results[0][0].transcript
  130. if(!event.results[0].isFinal) {
  131. // only visual
  132. textarea.value = completeTranscript + ' ' + transcript;
  133. } else {
  134. textarea.value = completeTranscript + ' ' + transcript;
  135. completeTranscript += ' ' + transcript
  136.  
  137. // value is not registered correctly without this workaround
  138. textarea.value = ''
  139. Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set.call(textarea, textarea.value + completeTranscript);
  140. const inputEvent = new Event('input', { bubbles: true });
  141. textarea.dispatchEvent(inputEvent);
  142. }
  143. debouncedPressEnter();
  144. };
  145. })();

QingJ © 2025

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