Автоматический ввод текста в CollabVM
// ==UserScript==
// @name CollabVM Autotype Pro
// @namespace https://greasyfork.org/users/yourprofile
// @version 2.1
// @description Автоматический ввод текста в CollabVM
// @author YourName
// @match https://computernewb.com/collab-vm/*
// @match http://computernewb.com/collab-vm/*
// @match https://collabvm.com/*
// @match http://collabvm.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
let active = false;
let stopFlag = false;
let ws = null;
let panelVisible = true;
function findWebSocket() {
if (window.collabVM?.socket) { ws = window.collabVM.socket; return true; }
if (window.ws) { ws = window.ws; return true; }
if (window.socket) { ws = window.socket; return true; }
return false;
}
function sendChar(char) {
if (!ws || ws.readyState !== WebSocket.OPEN) {
findWebSocket();
if (!ws || ws.readyState !== WebSocket.OPEN) return false;
}
ws.send(JSON.stringify([3, char]));
return true;
}
function playSound() {
try {
let audio = new Audio('data:audio/wav;base64,UklGRnoAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoAAACBhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqFhYqF');
audio.volume = 0.3;
audio.play();
} catch(e) {}
}
async function typeText(text, delay, repeat, statusDiv) {
if (!findWebSocket()) {
statusDiv.innerText = "❌ WebSocket не найден";
return false;
}
let total = text.length * repeat;
let current = 0;
for (let r = 0; r < repeat; r++) {
for (let i = 0; i < text.length; i++) {
if (stopFlag) {
statusDiv.innerText = "⏹ Остановлено";
return false;
}
sendChar(text[i]);
current++;
statusDiv.innerText = `⌨ ${current}/${total}`;
await new Promise(r => setTimeout(r, delay));
}
}
return true;
}
function createPanel() {
if (document.getElementById('cvm-autotype-panel')) return;
let panel = document.createElement('div');
panel.id = 'cvm-autotype-panel';
panel.style.cssText = `position:fixed; bottom:10px; right:10px; background:#1a1a2e; color:#eee; padding:12px; z-index:9999; border-radius:8px; width:280px; font-family:'Segoe UI',Arial,sans-serif; font-size:13px; box-shadow:0 4px 12px rgba(0,0,0,0.3); border:1px solid #16213e;`;
panel.innerHTML = `
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;">
<div style="font-weight:bold; color:#e94560;">🤖 CollabVM Autotype</div>
<button id="cvm-autotype-hide" style="background:none; border:none; color:#666; cursor:pointer; font-size:16px;">✕</button>
</div>
<textarea id="cvm-autotype-text" style="width:100%; margin:5px 0; padding:6px; background:#0f3460; color:#fff; border:none; border-radius:4px; box-sizing:border-box; font-family:monospace;" rows="2" placeholder="Введите текст..."></textarea>
<div style="margin:5px 0;">
<button class="cvm-preset" data-text="Hello World!" style="background:#16213e; border:1px solid #0f3460; color:#fff; padding:2px 8px; border-radius:3px; cursor:pointer; font-size:11px;">Hello</button>
<button class="cvm-preset" data-text="Test 123" style="background:#16213e; border:1px solid #0f3460; color:#fff; padding:2px 8px; border-radius:3px; cursor:pointer; font-size:11px;">Test</button>
<button class="cvm-preset" data-text="Lorem ipsum" style="background:#16213e; border:1px solid #0f3460; color:#fff; padding:2px 8px; border-radius:3px; cursor:pointer; font-size:11px;">Lorem</button>
<button class="cvm-preset" data-text="1234567890" style="background:#16213e; border:1px solid #0f3460; color:#fff; padding:2px 8px; border-radius:3px; cursor:pointer; font-size:11px;">123</button>
</div>
<div style="display:flex; gap:8px; margin:5px 0;">
<div style="flex:1;">
<label style="font-size:12px;">⏱ Задержка:</label>
<input type="number" id="cvm-autotype-delay" style="width:100%; margin-top:2px; padding:3px; background:#0f3460; color:#fff; border:none; border-radius:4px;">
</div>
<div style="flex:1;">
<label style="font-size:12px;">🔄 Повторы:</label>
<input type="number" id="cvm-autotype-repeat" value="1" min="1" max="99" style="width:100%; margin-top:2px; padding:3px; background:#0f3460; color:#fff; border:none; border-radius:4px;">
</div>
</div>
<div style="display:flex; gap:8px; margin:8px 0;">
<button id="cvm-autotype-start" style="flex:1; background:#2ecc71; color:#fff; border:none; padding:6px; border-radius:4px; cursor:pointer;">▶ Старт</button>
<button id="cvm-autotype-stop" style="flex:1; background:#e74c3c; color:#fff; border:none; padding:6px; border-radius:4px; cursor:pointer;">⏹ Стоп</button>
</div>
<div id="cvm-autotype-status" style="font-size:11px; color:#aaa; text-align:center;">🟡 Ожидание</div>
<div style="font-size:10px; color:#555; text-align:center; margin-top:5px;">
💾 Сохраняется | 🎵 Звук по окончании
</div>
`;
document.body.appendChild(panel);
let textarea = document.getElementById('cvm-autotype-text');
let delayInput = document.getElementById('cvm-autotype-delay');
let repeatInput = document.getElementById('cvm-autotype-repeat');
let statusDiv = document.getElementById('cvm-autotype-status');
textarea.value = localStorage.getItem('cvm_autotype_text') || '';
delayInput.value = localStorage.getItem('cvm_autotype_delay') || '30';
repeatInput.value = localStorage.getItem('cvm_autotype_repeat') || '1';
textarea.onchange = () => localStorage.setItem('cvm_autotype_text', textarea.value);
delayInput.onchange = () => localStorage.setItem('cvm_autotype_delay', delayInput.value);
repeatInput.onchange = () => localStorage.setItem('cvm_autotype_repeat', repeatInput.value);
document.querySelectorAll('.cvm-preset').forEach(btn => {
btn.onclick = () => {
textarea.value = btn.dataset.text;
localStorage.setItem('cvm_autotype_text', btn.dataset.text);
statusDiv.innerText = `📋 Выбран: ${btn.dataset.text.substring(0, 20)}${btn.dataset.text.length > 20 ? '...' : ''}`;
};
});
document.getElementById('cvm-autotype-hide').onclick = () => {
panelVisible = !panelVisible;
panel.style.display = panelVisible ? '' : 'none';
};
document.getElementById('cvm-autotype-start').onclick = async () => {
if (active) { statusDiv.innerText = "⚠ Уже работает"; return; }
let text = textarea.value;
let delay = parseInt(delayInput.value) || 30;
let repeat = parseInt(repeatInput.value) || 1;
if (!text.trim()) { statusDiv.innerText = "❌ Введите текст"; return; }
if (repeat < 1) { statusDiv.innerText = "❌ Повторов должно быть >= 1"; return; }
active = true;
stopFlag = false;
let success = await typeText(text, delay, repeat, statusDiv);
active = false;
if (!stopFlag && success) {
statusDiv.innerText = "✅ Готово!";
playSound();
}
};
document.getElementById('cvm-autotype-stop').onclick = () => {
if (active) {
stopFlag = true;
active = false;
statusDiv.innerText = "⏹ Остановлено";
} else {
statusDiv.innerText = "⏸ Нет активного ввода";
}
};
document.addEventListener('keydown', (e) => {
if (e.altKey && e.code === 'KeyT') {
e.preventDefault();
document.getElementById('cvm-autotype-start').click();
}
if (e.altKey && e.code === 'KeyS') {
e.preventDefault();
document.getElementById('cvm-autotype-stop').click();
}
if (e.altKey && e.code === 'KeyH') {
e.preventDefault();
panelVisible = !panelVisible;
panel.style.display = panelVisible ? '' : 'none';
statusDiv.innerText = panelVisible ? "🟡 Панель показана" : "🟡 Панель скрыта";
}
});
setInterval(() => {
if (findWebSocket() && ws.readyState === WebSocket.OPEN) {
if (!statusDiv.innerText.includes("✅") && !active && !statusDiv.innerText.includes("📋")) {
statusDiv.innerText = "🟢 Подключено";
}
} else {
if (!active && !statusDiv.innerText.includes("📋")) {
statusDiv.innerText = "🔴 Нет соединения";
}
}
}, 3000);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createPanel);
} else {
createPanel();
}
})();