Character.AI Auto-Requester

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

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

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