Character.AI Auto-Requester

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

目前為 2025-02-15 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Character.AI Auto-Requester
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.95555553
  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: 12px;
  178. text-indent: 25px;
  179. line-height: 10px;
  180. color: gray;
  181. }
  182. .description p {
  183. margin-top: 10px; /* Отступ сверху */
  184. margin-bottom: 10px; /* Отступ снизу */
  185. color: gray
  186. }
  187.  
  188. `);
  189.  
  190. const textFieldXPath = '//*[@id="chat-body"]/div[2]/div/div/div/div[1]/textarea';
  191. const sendButtonXPath = '//*[@id="chat-body"]/div[2]/div/div/div/div[2]/button';
  192. 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';
  193.  
  194. // Создаем элементы управления
  195. const overlay = document.createElement('div');
  196. overlay.className = 'control-overlay';
  197. overlay.innerHTML = `
  198. <div class="header">
  199. <h3 style="margin:0; display:inline-block;">Character.AI Auto-Requester</h3>
  200. <button class="minimize" style="float:right;">▼</button>
  201. </div>
  202. <div class="content">
  203. <div class="slider-container">
  204. <label>Задержка голоса:</label>
  205. <input type="range" min="1" max="60" value="${delayVoice/1000}" class="voice-delay">
  206. <div class="value-display">${delayVoice/1000} сек</div>
  207. </div>
  208. <div class="slider-container">
  209. <label>Интервал сообщений:</label>
  210. <input type="range" min="3" max="120" value="${interval/1000}" class="send-interval">
  211. <div class="value-display">${interval/1000} сек</div>
  212. </div>
  213. <div class="switch-container">
  214. <label>Вкл/Выкл</label>
  215. <label class="switch">
  216. <input type="checkbox" class="toggle-auto-send">
  217. <span class="slider round"></span>
  218. </label>
  219. </div>
  220. <div class="description">
  221. Пользуйтесь аккуратно; не совсем понятно, за какие скорости могут забанить аккаунт. В любом случае даже простое использование стороннего софта типа этого, скорее всего, не приветствуется.
  222. <p>
  223. Осторожно, смартфон, например, может не отключать экран и не блокироваться из-за работающего скрипта. Даже в состоянии "Выкл".
  224. </div>
  225. </div>
  226. `;
  227.  
  228. document.body.appendChild(overlay);
  229.  
  230. // Обработчики ползунков
  231. overlay.querySelector('.voice-delay').addEventListener('input', function(e) {
  232. delayVoice = e.target.value * 1000;
  233. e.target.nextElementSibling.textContent = `${e.target.value} сек`;
  234.  
  235. // Останавливаем таймер
  236. clearInterval(timer);
  237. overlay.querySelector('.toggle-auto-send').checked = false;
  238. });
  239.  
  240. let currentUrl = window.location.href;
  241.  
  242. overlay.querySelector('.send-interval').addEventListener('input', function(e) {
  243. interval = e.target.value * 1000;
  244. e.target.nextElementSibling.textContent = `${e.target.value} сек`;
  245.  
  246. // Останавливаем таймер
  247. clearInterval(timer);
  248. overlay.querySelector('.toggle-auto-send').checked = false;
  249. });
  250.  
  251. function clickVoiceButton() {
  252. const voiceButton = document.evaluate(
  253. voiceButtonXPath,
  254. document,
  255. null,
  256. XPathResult.FIRST_ORDERED_NODE_TYPE,
  257. null
  258. ).singleNodeValue;
  259. voiceButton?.click();
  260. }
  261.  
  262. function simulateSend() {
  263. if (window.location.href !== currentUrl) {
  264. // Останавливаем таймер
  265. clearInterval(timer);
  266. overlay.querySelector('.toggle-auto-send').checked = false;
  267.  
  268. currentUrl = window.location.href;
  269. return
  270. }
  271. const textField = document.evaluate(
  272. textFieldXPath,
  273. document,
  274. null,
  275. XPathResult.FIRST_ORDERED_NODE_TYPE,
  276. null
  277. ).singleNodeValue;
  278.  
  279. const sendButton = document.evaluate(
  280. sendButtonXPath,
  281. document,
  282. null,
  283. XPathResult.FIRST_ORDERED_NODE_TYPE,
  284. null
  285. ).singleNodeValue;
  286.  
  287. if (textField && sendButton && !sendButton.disabled) {
  288. const inputText = textField.value.trim();
  289.  
  290. if (inputText === '') {
  291. sendButton.click();
  292. textField.value = '';
  293. setTimeout(clickVoiceButton, delayVoice);
  294. }
  295. }
  296. }
  297.  
  298. // Вкл/Выкл
  299. overlay.querySelector('.toggle-auto-send').addEventListener('change', function(e) {
  300. if (e.target.checked) {
  301. // Включаем автоматическую отправку
  302. clearInterval(timer);
  303. timer = setInterval(simulateSend, interval);
  304.  
  305. // Запоминаем страницу, на которой запустили автоотправку
  306. let currentUrl = window.location.href;
  307. } else {
  308. // Выключаем автоматическую отправку
  309. clearInterval(timer);
  310. }
  311. });
  312.  
  313. // Перемещение оверлея
  314. let isDown = false;
  315. let offset = [0, 0];
  316.  
  317. // Функция для обработки начала касания
  318. function handleTouchStart(event) {
  319. if (event.touches.length === 1) { // Только одно касание
  320. isDown = true;
  321. const touch = event.touches[0];
  322. offset = [
  323. overlay.offsetLeft - touch.clientX,
  324. overlay.offsetTop - touch.clientY
  325. ];
  326. event.preventDefault(); // Предотвратить выделение текста
  327. }
  328. }
  329.  
  330. // Функция для обработки движения касания
  331. function handleTouchMove(event) {
  332. if (isDown) {
  333. const touch = event.touches[0];
  334. overlay.style.top = `${touch.clientY + offset[1]}px`;
  335. overlay.style.right = 'auto'; // Чтобы не было привязки к правому краю
  336. overlay.style.left = `${touch.clientX + offset[0]}px`;
  337. event.preventDefault(); // Предотвратить прокрутку страницы
  338. }
  339. }
  340.  
  341. // Функция для обработки окончания касания
  342. function handleTouchEnd() {
  343. isDown = false;
  344. }
  345.  
  346. // Добавление обработчиков событий для касаний
  347. overlay.querySelector('.header h3').addEventListener('touchstart', handleTouchStart);
  348. document.addEventListener('touchmove', handleTouchMove);
  349. document.addEventListener('touchend', handleTouchEnd);
  350.  
  351. // Добавление обработчиков событий для мыши (чтобы не потерять функциональность на компьютере)
  352. overlay.querySelector('.header h3').addEventListener('mousedown', function(event) {
  353. if (event.button === 0) { // Левая кнопка мыши
  354. isDown = true;
  355. offset = [
  356. overlay.offsetLeft - event.clientX,
  357. overlay.offsetTop - event.clientY
  358. ];
  359. event.preventDefault(); // Предотвратить выделение текста
  360. }
  361. });
  362.  
  363. document.addEventListener('mouseup', function() {
  364. isDown = false;
  365. });
  366.  
  367. document.addEventListener('mousemove', function(event) {
  368. if (isDown) {
  369. overlay.style.top = `${event.clientY + offset[1]}px`;
  370. overlay.style.right = 'auto'; // Чтобы не было привязки к правому краю
  371. overlay.style.left = `${event.clientX + offset[0]}px`;
  372. }
  373. });
  374. // Минимизация/сворачивание
  375. overlay.querySelector('.minimize').addEventListener('click', function() {
  376. if (overlay.classList.contains('minimized')) {
  377. overlay.classList.remove('minimized');
  378. this.textContent = '▼'; // Используем Unicode для символа
  379. } else {
  380. overlay.classList.add('minimized');
  381. this.textContent = '▲'; // Используем Unicode для символа
  382. }
  383. });
  384. })();