Character.AI Auto-Sender

Автоматическая отправка сообщений с настройкой интервалов

当前为 2025-02-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Character.AI Auto-Sender
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.4
  5. // @license GNU GPLv3
  6. // @description Автоматическая отправка сообщений с настройкой интервалов
  7. // @match https://character.ai/chat*
  8. // @author xPress
  9. // @grant GM_addStyle
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Начальные значения
  16. let delayVoice = 10000;
  17. let interval = 30000;
  18. let timer;
  19.  
  20. // Стили для оверлея
  21. GM_addStyle(`
  22. .control-overlay {
  23. position: fixed;
  24. user-select: none;
  25. top: 20px;
  26. right: 20px;
  27. background: rgba(255,255,255,0.8);
  28. padding: 15px;
  29. border-radius: 8px;
  30. color: black;
  31. z-index: 9999;
  32. min-width: 250px;
  33. width: 350px;
  34. max-height: 1000px; /* Максимальная высота */
  35. transition: max-height 0.4s ease-in-out;
  36. }
  37. .slider-container {
  38. margin: 10px 0;
  39. }
  40. input[type="range"] {
  41. width: 100%;
  42. margin: 5px 0;
  43. -webkit-appearance: none;
  44. background: transparent;
  45. }
  46. input[type="range"]::-webkit-slider-thumb {
  47. -webkit-appearance: none;
  48. height: 16px;
  49. width: 16px;
  50. border-radius: 50%;
  51. background: lime;
  52. cursor: pointer;
  53. margin-top: -6px;
  54. }
  55. input[type="range"]::-moz-range-thumb {
  56. height: 16px;
  57. width: 16px;
  58. border-radius: 50%;
  59. background: lime;
  60. cursor: pointer;
  61. }
  62. input[type="range"]::-webkit-slider-runnable-track {
  63. width: 100%;
  64. height: 4px;
  65. cursor: pointer;
  66. background: #c9c9c9;
  67. border-radius: 2px;
  68. }
  69. input[type="range"]::-moz-range-track {
  70. width: 100%;
  71. height: 4px;
  72. cursor: pointer;
  73. background: #c9c9c9;
  74. border-radius: 2px;
  75. }
  76. .value-display {
  77. font-size: 14px;
  78. margin-top: 5px;
  79. }
  80. .header {
  81. cursor: move;
  82. padding: 0 10px;
  83. background: rgba(255,255,255,0.5);
  84. border-bottom: 1px solid #333;
  85. }
  86. .minimized {
  87. max-height: 53px;
  88. overflow: hidden;
  89. }
  90.  
  91. .control-overlay:not(.minimized) .content {
  92. opacity: 1;
  93. transition: opacity 0.4s ease-in-out;
  94. }
  95.  
  96. .control-overlay.minimized .content {
  97. opacity: 0;
  98. transition: opacity 0.4s ease-in-out;
  99. }
  100.  
  101. .switch-container {
  102. margin: 10px 0;
  103. }
  104.  
  105. .switch {
  106. position: relative;
  107. display: inline-block;
  108. width: 60px;
  109. height: 34px;
  110. }
  111.  
  112. .switch-container label {
  113. margin: 15px 35px;
  114. font-size: 24px;
  115. margin-top: 23px;
  116. }
  117.  
  118. .switch input {
  119. opacity: 0;
  120. width: 0;
  121. height: 0;
  122. }
  123.  
  124. .slider {
  125. position: absolute;
  126. cursor: pointer;
  127. top: 0;
  128. left: 0;
  129. right: 0;
  130. bottom: 0;
  131. background-color: #ccc;
  132. -webkit-transition: .4s;
  133. transition: .4s;
  134. }
  135.  
  136. .slider:before {
  137. position: absolute;
  138. content: "";
  139. height: 26px;
  140. width: 26px;
  141. left: 4px;
  142. bottom: 4px;
  143. background-color: white;
  144. -webkit-transition: .4s;
  145. transition: .4s;
  146. }
  147.  
  148. input:checked + .slider {
  149. background-color: lime;
  150. }
  151.  
  152. input:focus + .slider {
  153. box-shadow: 0 0 1px lime;
  154. }
  155.  
  156. input:checked + .slider:before {
  157. -webkit-transform: translateX(26px);
  158. -ms-transform: translateX(26px);
  159. transform: translateX(26px);
  160. }
  161.  
  162. .slider.round {
  163. border-radius: 34px;
  164. }
  165.  
  166. .slider.round:before {
  167. border-radius: 50%;
  168. }
  169.  
  170. .description {
  171. font-size: 11px;
  172. }
  173. `);
  174.  
  175. const textFieldXPath = '//*[@id="chat-body"]/div[2]/div/div/div/div[1]/textarea';
  176. const sendButtonXPath = '//*[@id="chat-body"]/div[2]/div/div/div/div[2]/button';
  177. const voiceButtonXPath = '//*[@id="chat-messages"]/div[1]/div[1]/div/div/div[1]/div/div[1]/div[1]/div[2]/div[1]/div[2]/div[2]/div/button';
  178.  
  179. // Создаем элементы управления
  180. const overlay = document.createElement('div');
  181. overlay.className = 'control-overlay';
  182. overlay.innerHTML = `
  183. <div class="header">
  184. <h3 style="margin:0; display:inline-block;">Character.AI Auto-Requester</h3>
  185. <button class="minimize" style="float:right;">▼</button>
  186. </div>
  187. <div class="content">
  188. <div class="slider-container">
  189. <label>Задержка голоса:</label>
  190. <input type="range" min="1" max="60" value="${delayVoice/1000}" class="voice-delay">
  191. <div class="value-display">${delayVoice/1000} сек</div>
  192. </div>
  193. <div class="slider-container">
  194. <label>Интервал сообщений:</label>
  195. <input type="range" min="3" max="120" value="${interval/1000}" class="send-interval">
  196. <div class="value-display">${interval/1000} сек</div>
  197. </div>
  198. <div class="switch-container">
  199. <label>Вкл/Выкл</label>
  200. <label class="switch">
  201. <input type="checkbox" class="toggle-auto-send">
  202. <span class="slider round"></span>
  203. </label>
  204. </div>
  205. <div class="description">
  206. Пользуйтесь аккуратно; не совсем понятно, за какие скорости могут забанить аккаунт. В любом случае даже простое использование стороннего софта типа этого, скорее всего, не приветствуется.
  207. </div>
  208. </div>
  209. `;
  210.  
  211. document.body.appendChild(overlay);
  212.  
  213. // Обработчики ползунков
  214. overlay.querySelector('.voice-delay').addEventListener('input', function(e) {
  215. delayVoice = e.target.value * 1000;
  216. e.target.nextElementSibling.textContent = `${e.target.value} сек`;
  217.  
  218. // Останавливаем таймер
  219. clearInterval(timer);
  220. overlay.querySelector('.toggle-auto-send').checked = false;
  221. });
  222.  
  223. let currentUrl = window.location.href;
  224.  
  225. overlay.querySelector('.send-interval').addEventListener('input', function(e) {
  226. interval = e.target.value * 1000;
  227. e.target.nextElementSibling.textContent = `${e.target.value} сек`;
  228.  
  229. // Останавливаем таймер
  230. clearInterval(timer);
  231. overlay.querySelector('.toggle-auto-send').checked = false;
  232. });
  233.  
  234. function clickVoiceButton() {
  235. const voiceButton = document.evaluate(
  236. voiceButtonXPath,
  237. document,
  238. null,
  239. XPathResult.FIRST_ORDERED_NODE_TYPE,
  240. null
  241. ).singleNodeValue;
  242. voiceButton?.click();
  243. }
  244.  
  245. function simulateSend() {
  246. if (window.location.href !== currentUrl) {
  247. // Останавливаем таймер
  248. clearInterval(timer);
  249. overlay.querySelector('.toggle-auto-send').checked = false;
  250.  
  251. currentUrl = window.location.href;
  252. return
  253. }
  254. const textField = document.evaluate(
  255. textFieldXPath,
  256. document,
  257. null,
  258. XPathResult.FIRST_ORDERED_NODE_TYPE,
  259. null
  260. ).singleNodeValue;
  261.  
  262. const sendButton = document.evaluate(
  263. sendButtonXPath,
  264. document,
  265. null,
  266. XPathResult.FIRST_ORDERED_NODE_TYPE,
  267. null
  268. ).singleNodeValue;
  269.  
  270. if (textField && sendButton && !sendButton.disabled) {
  271. const inputText = textField.value.trim();
  272.  
  273. if (inputText === '') {
  274. sendButton.click();
  275. textField.value = '';
  276. setTimeout(clickVoiceButton, delayVoice);
  277. }
  278. }
  279. }
  280.  
  281. // Вкл/Выкл
  282. overlay.querySelector('.toggle-auto-send').addEventListener('change', function(e) {
  283. if (e.target.checked) {
  284. // Включаем автоматическую отправку
  285. clearInterval(timer);
  286. timer = setInterval(simulateSend, interval);
  287.  
  288. // Запоминаем страницу, на которой запустили автоотправку
  289. let currentUrl = window.location.href;
  290. } else {
  291. // Выключаем автоматическую отправку
  292. clearInterval(timer);
  293. }
  294. });
  295.  
  296. // Перемещение оверлея
  297. let isDown = false;
  298. let offset = [0, 0];
  299. overlay.querySelector('.header').addEventListener('mousedown', function(event) {
  300. if (event.button === 0) { // Левая кнопка мыши
  301. isDown = true;
  302. offset = [
  303. overlay.offsetLeft - event.clientX,
  304. overlay.offsetTop - event.clientY
  305. ];
  306. event.preventDefault(); // Предотвратить выделение текста
  307. }
  308. });
  309.  
  310. document.addEventListener('mouseup', function() {
  311. isDown = false;
  312. });
  313.  
  314. document.addEventListener('mousemove', function(event) {
  315. if (isDown) {
  316. overlay.style.top = `${event.clientY + offset[1]}px`;
  317. overlay.style.right = 'auto'; // Чтобы не было привязки к правому краю
  318. overlay.style.left = `${event.clientX + offset[0]}px`;
  319. }
  320. });
  321. // Минимизация/сворачивание
  322. overlay.querySelector('.minimize').addEventListener('click', function() {
  323. if (overlay.classList.contains('minimized')) {
  324. overlay.classList.remove('minimized');
  325. this.textContent = '▼'; // Используем Unicode для символа
  326. } else {
  327. overlay.classList.add('minimized');
  328. this.textContent = '▲'; // Используем Unicode для символа
  329. }
  330. });
  331. })();