您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Modern draggable menu for flag drawing in Drawaria (Paste in conosole)
// ==UserScript== // @name Drawaria Flag Drawing Tool // @namespace http://tampermonkey.net/ // @version 1.0 // @description Modern draggable menu for flag drawing in Drawaria (Paste in conosole) // @author YouTubeDrawaria // @match *://drawaria.online/* // @grant none // @license MIT // @require https://unpkg.com/[email protected]/css/boxicons.min.css // ==/UserScript== (function() { 'use strict'; // Main styles const style = document.createElement('style'); style.textContent = ` .flag-tool-container { position: fixed; top: 20px; right: 20px; width: 320px; background: #2c3e50; border-radius: 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); color: #ecf0f1; 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; z-index: 9999; overflow: hidden; transition: all 0.3s ease; } .flag-tool-header { background: #34495e; padding: 12px 15px; cursor: move; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #2c3e50; } .flag-tool-title { font-weight: 600; font-size: 16px; color: #ecf0f1; } .flag-tool-close { background: none; border: none; color: #ecf0f1; font-size: 18px; cursor: pointer; transition: color 0.2s; } .flag-tool-close:hover { color: #e74c3c; } .flag-tool-content { padding: 15px; } .form-group { 15px; } .form-group label { display: block; 5px; font-size: 13px; color: #bdc3c7; } .form-control { width: 100%; padding: 8px 12px; background: #34495e; border: 1px solid #2c3e50; border-radius: 4px; color: #ecf0f1; font-size: 13px; transition: border-color 0.2s; } .form-control:focus { outline: none; border-color: #3498db; } .btn { padding: 8px 15px; border-radius: 4px; font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; justify-content: center; gap: 5px; } .btn-primary { background: #3498db; color: white; border: none; } .btn-primary:hover { background: #2980b9; } .btn-danger { background: #e74c3c; color: white; border: none; } .btn-danger:hover { background: #c0392b; } .btn-outline { background: transparent; border: 1px solid #3498db; color: #3498db; } .btn-outline:hover { background: rgba(52, 152, 219, 0.1); } .btn-block { width: 100%; } .btn-group { display: flex; gap: 8px; } .btn-group .btn { flex: 1; } .select-control { position: relative; } .select-control:after { content: "▼"; font-size: 10px; color: #bdc3c7; position: absolute; right: 10px; top: 50%; transform: translateY(-50%); pointer-events: none; } .slider-container { 10px 0; } .slider-container label { display: block; 5px; font-size: 13px; color: #bdc3c7; } .slider-value { display: flex; justify-content: space-between; 5px; font-size: 12px; color: #7f8c8d; } input[type="range"] { width: 100%; height: 6px; -webkit-appearance: none; background: #34495e; border-radius: 3px; 5px 0; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; background: #3498db; border-radius: 50%; cursor: pointer; } .section-title { font-size: 14px; color: #3498db; 10px; padding-bottom: 5px; border-bottom: 1px solid #2c3e50; } .status-indicator { display: inline-block; width: 10px; height: 10px; border-radius: 50%; 5px; } .status-connected { background-color: #2ecc71; } .status-disconnected { background-color: #e74c3c; } .dragging { opacity: 0.8; cursor: moving; } `; document.head.appendChild(style); // Flag configurations const flags = { Russia: 'https://flagcdn.com/w320/ru.png', Ukraine: 'https://flagcdn.com/w320/ua.png', Uzbekistan: 'https://flagcdn.com/w320/uz.png', India: 'https://flagcdn.com/w320/in.png', Armenia: 'https://flagcdn.com/w320/am.png', Latvia: 'https://flagcdn.com/w320/lv.png', Estonia: 'https://flagcdn.com/w320/ee.png', Bulgaria: 'https://flagcdn.com/w320/bg.png', Germany: 'https://flagcdn.com/w320/de.png', Netherlands: 'https://flagcdn.com/w320/nl.png', Hungary: 'https://flagcdn.com/w320/hu.png', Luxembourg: 'https://flagcdn.com/w320/lu.png' }; // Drawing Tools let previewCanvas = document.createElement('canvas'); let originalCanvas = null; let data; let cw, ch; let executionLine = []; let isDrawing = false; let socketStatus = 'disconnected'; // Create the draggable container const container = document.createElement('div'); container.className = 'flag-tool-container'; container.innerHTML = ` <div class="flag-tool-header"> <div class="flag-tool-title">Flag Drawing Tool</div> <button class="flag-tool-close">×</button> </div> <div class="flag-tool-content"> <div class="section-title">Connection Status</div> <div id="connectionStatus" class="status-indicator status-disconnected"></div> <span id="statusText">Disconnected</span> <div class="section-title">Bot Configuration</div> <div class="form-group"> <label for="botNameInput">Bot Name</label> <input type="text" id="botNameInput" class="form-control" placeholder="FlagBot"> </div> <div class="btn-group"> <button id="botJoinBtn" class="btn btn-primary"> <i class='bx bx-user-plus'></i> Join </button> <button id="botLeaveBtn" class="btn btn-danger"> <i class='bx bx-user-minus'></i> Leave </button> </div> <div class="section-title">Flag Selection</div> <div class="form-group"> <label for="flagSelect">Select Flag</label> <div class="select-control"> <select id="flagSelect" class="form-control"> <option value="">Select a Flag</option> ${Object.keys(flags).map(flag => `<option value="${flag}">${flag}</option>`).join('')} </select> </div> </div> <div class="section-title">Drawing Settings</div> <div class="slider-container"> <label for="flag_imagesize">Image Size</label> <div class="slider-value"> <span>Big</span> <span id="imagesizeValue">2</span> <span>Small</span> </div> <input type="range" id="flag_imagesize" min="1" max="10" value="2"> </div> <div class="slider-container"> <label for="flag_brushsize">Brush Size</label> <div class="slider-value"> <span>Thin</span> <span id="brushsizeValue">20</span> <span>Thick</span> </div> <input type="range" id="flag_brushsize" min="2" max="20" value="20"> </div> <div class="slider-container"> <label for="flag_pixelsize">Pixel Distance</label> <div class="slider-value"> <span>Far</span> <span id="pixelsizeValue">2</span> <span>Close</span> </div> <input type="range" id="flag_pixelsize" min="2" max="20" value="2"> </div> <div class="slider-container"> <label for="flag_offset_x">Horizontal Offset</label> <div class="slider-value"> <span>0</span> <span id="offsetXValue">0</span> <span>100</span> </div> <input type="range" id="flag_offset_x" min="0" max="100" value="0"> </div> <div class="slider-container"> <label for="flag_offset_y">Vertical Offset</label> <div class="slider-value"> <span>0</span> <span id="offsetYValue">0</span> <span>100</span> </div> <input type="range" id="flag_offset_y" min="0" max="100" value="0"> </div> <div class="btn-group"> <button id="startFlagDrawing" class="btn btn-primary"> <i class='bx bx-play-circle'></i> Start </button> <button id="stopFlagDrawing" class="btn btn-danger"> <i class='bx bx-stop-circle'></i> Stop </button> </div> <div class="btn-group"> <button id="canvasClearBtn" class="btn btn-outline"> <i class='bx bxs-eraser'></i> Clear Canvas </button> </div> </div> `; document.body.appendChild(container); // Make the container draggable let isDragging = false; let offsetX, offsetY; const header = container.querySelector('.flag-tool-header'); header.addEventListener('mousedown', (e) => { isDragging = true; offsetX = e.clientX - container.getBoundingClientRect().left; offsetY = e.clientY - container.getBoundingClientRect().top; container.classList.add('dragging'); e.preventDefault(); }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; container.style.left = `${e.clientX - offsetX}px`; container.style.top = `${e.clientY - offsetY}px`; }); document.addEventListener('mouseup', () => { isDragging = false; container.classList.remove('dragging'); }); // Close button container.querySelector('.flag-tool-close').addEventListener('click', () => { container.style.display = 'none'; }); // Update slider values const updateSliderValue = (sliderId, valueId) => { const slider = document.getElementById(sliderId); const value = document.getElementById(valueId); slider.addEventListener('input', () => { value.textContent = slider.value; }); }; updateSliderValue('flag_imagesize', 'imagesizeValue'); updateSliderValue('flag_brushsize', 'brushsizeValue'); updateSliderValue('flag_pixelsize', 'pixelsizeValue'); updateSliderValue('flag_offset_x', 'offsetXValue'); updateSliderValue('flag_offset_y', 'offsetYValue'); // Room & Socket Control window.myRoom = {}; window.sockets = []; const originalSend = WebSocket.prototype.send; WebSocket.prototype.send = function (...args) { if (window.sockets.indexOf(this) === -1) { window.sockets.push(this); if (window.sockets.indexOf(this) === 0) { this.addEventListener('message', (event) => { let message = String(event.data); if (message.startsWith('42')) { let payload = JSON.parse(message.slice(2)); if (payload[0] == 'bc_uc_freedrawsession_changedroom') { window.myRoom.players = payload[3]; } if (payload[0] == 'mc_roomplayerschange') { window.myRoom.players = payload[3]; } } else if (message.startsWith('41')) { console.debug('WebSocket disconnected'); updateConnectionStatus('disconnected'); } else if (message.startsWith('430')) { let configs = JSON.parse(message.slice(3))[0]; window.myRoom.players = configs.players; window.myRoom.id = configs.roomid; console.debug('Room joined, ID:', window.myRoom.id); updateConnectionStatus('connected'); } }); } } return originalSend.call(this, ...args); }; function updateConnectionStatus(status) { socketStatus = status; const statusIndicator = document.getElementById('connectionStatus'); const statusText = document.getElementById('statusText'); if (status === 'connected') { statusIndicator.className = 'status-indicator status-connected'; statusText.textContent = 'Connected'; } else { statusIndicator.className = 'status-indicator status-disconnected'; statusText.textContent = 'Disconnected'; } } // Button event listeners document.getElementById('botJoinBtn').addEventListener('click', () => { if (window['__FLAG_BOT'] && window['__FLAG_BOT'].room) { window['__FLAG_BOT'].room.join(document.getElementById('invurl')?.value || ''); } }); document.getElementById('botLeaveBtn').addEventListener('click', () => { if (window['__FLAG_BOT'] && window['__FLAG_BOT'].conn && window['__FLAG_BOT'].conn.socket) { window['__FLAG_BOT'].conn.socket.close(); updateConnectionStatus('disconnected'); } }); document.getElementById('canvasClearBtn').addEventListener('click', () => { if (window['__FLAG_BOT'] && window['__FLAG_BOT'].action) { window['__FLAG_BOT'].action.DrawLine(50, 50, 50, 50, 2000); } }); document.getElementById('stopFlagDrawing').addEventListener('click', () => { executionLine = []; isDrawing = false; console.debug('Drawing stopped'); }); document.getElementById('startFlagDrawing').addEventListener('click', () => { const selectedFlag = document.getElementById('flagSelect').value; if (!selectedFlag || !flags[selectedFlag]) { console.debug('No flag selected'); return; } originalCanvas = document.getElementById('canvas'); if (!originalCanvas) { console.debug('Canvas not found'); return; } isDrawing = true; loadImage(flags[selectedFlag]); }); // Image Processing function loadImage(url) { let img = new Image(); img.crossOrigin = 'anonymous'; img.src = url; img.addEventListener('load', () => { cw = originalCanvas?.width || 800; ch = originalCanvas?.height || 600; if (cw <= 0 || ch <= 0) { console.debug('Invalid canvas dimensions:', cw, ch); return; } previewCanvas.width = cw; previewCanvas.height = ch; let ctx = previewCanvas.getContext('2d'); let scale = Math.max(cw / img.width, ch / img.height); let scaledWidth = img.width * scale; let scaledHeight = img.height * scale; let dx = (cw - scaledWidth) / 2; let dy = (ch - scaledHeight) / 2; ctx.drawImage(img, dx, dy, scaledWidth, scaledHeight); let imgData = ctx.getImageData(0, 0, cw, ch); data = imgData.data; ctx.clearRect(0, 0, cw, ch); console.debug('Flag loaded, dimensions:', cw, ch); let size = parseFloat(document.getElementById('flag_imagesize').value); let modifier = parseFloat(document.getElementById('flag_pixelsize').value); let thickness = parseFloat(document.getElementById('flag_brushsize').value); let offset = { x: parseFloat(document.getElementById('flag_offset_x').value), y: parseFloat(document.getElementById('flag_offset_y').value) }; drawImage(size, modifier, thickness, offset); if (window['__FLAG_BOT'] && window['__FLAG_BOT'].conn && window['__FLAG_BOT'].conn.socket && window['__FLAG_BOT'].conn.socket.readyState === WebSocket.OPEN) { execute(window['__FLAG_BOT'].conn.socket); } else { console.debug('WebSocket not open, state:', window['__FLAG_BOT']?.conn?.socket?.readyState); } }); img.addEventListener('error', () => { console.debug('Failed to load flag image:', url); }); } function drawImage(size, modifier = 1, thickness = 20, offset = { x: 0, y: 0 }) { executionLine = []; if (!data) { console.debug('No image data available'); return; } const step = size * modifier * 2; // Reduced step for reliability for (let y = 0; y < ch; y += step) { let startX = 0; let currentColor = null; for (let x = 0; x < cw; x += step / 2) { let index = (Math.floor(y) * cw + Math.floor(x)) * 4; let a = data[index + 3] || 0; if (a > 20) { let r = data[index + 0] || 0, g = data[index + 1] || 0, b = data[index + 2] || 0; let color = `rgb(${r},${g},${b})`; if (color !== currentColor || x >= cw - step / 2) { if (currentColor && x - startX >= step / 2) { executionLine.push({ pos1: recalc([startX, y], offset), pos2: recalc([x, y], offset), color: currentColor, thickness: thickness }); } currentColor = color; startX = x; } } else { if (currentColor && x - startX >= step / 2) { executionLine.push({ pos1: recalc([startX, y], offset), pos2: recalc([x, y], offset), color: currentColor, thickness: thickness }); } currentColor = null; } } if (currentColor && cw - startX >= step / 2) { executionLine.push({ pos1: recalc([startX, y], offset), pos2: recalc([cw, y], offset), color: currentColor, thickness: thickness }); } } console.debug('Flag processing complete, lines:', executionLine.length); } async function execute(socket) { if (executionLine.length === 0) { console.debug('No drawing commands generated'); return; } for (let i = 0; i < executionLine.length; i++) { let line = executionLine[i]; if (socket.readyState === WebSocket.OPEN) { drawCmd(socket, line.pos1, line.pos2, line.color, line.thickness); } else { console.debug('WebSocket closed during drawing, state:', socket.readyState); break; } await delay(1); } console.debug('Drawing complete'); } function drawCmd(socket, start, end, color, thickness) { try { socket.send(`42["drawcmd",0,[${start[0]},${start[1]},${end[0]},${end[1]},false,${0 - thickness},"${color}",0,0,{}]]`); } catch (e) { console.debug('Error sending draw command:', e.message); } } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function recalc(value, offset) { return [ Math.min(1, Math.max(0, (value[0] / cw + offset.x / 100))).toFixed(4), Math.min(1, Math.max(0, (value[1] / ch + offset.y / 100))).toFixed(4) ]; } // Player and Connection Classes const Player = function (name = undefined) { this.name = name; this.sid1 = null; this.uid = ''; this.wt = ''; this.conn = new Connection(this); this.room = new Room(this.conn); this.action = new Actions(this.conn); }; Player.prototype.annonymize = function (name) { this.name = name; this.uid = undefined; this.wt = undefined; }; const Connection = function (player) { this.player = player; }; Connection.prototype.onopen = function () { this.Heartbeat(25000); console.debug('WebSocket opened'); updateConnectionStatus('connected'); }; Connection.prototype.onclose = function () { console.debug('WebSocket closed'); updateConnectionStatus('disconnected'); }; Connection.prototype.onerror = function (error) { console.debug('WebSocket error:', error); updateConnectionStatus('disconnected'); }; Connection.prototype.onmessage = function (event) { let message = String(event.data); if (message.startsWith('42')) { this.onBroadcast(message.slice(2)); } else if (message.startsWith('40')) { this.onRequest(); } else if (message.startsWith('41')) { this.player.room.join(this.player.room.id); } else if (message.startsWith('430')) { let configs = JSON.parse(message.slice(3))[0]; this.player.room.players = configs.players; this.player.room.id = configs.roomid; } }; Connection.prototype.onBroadcast = function (payload) { try { payload = JSON.parse(payload); if (payload[0] == 'bc_uc_freedrawsession_changedroom') { this.player.room.players = payload[3]; this.player.room.id = payload[4]; } if (payload[0] == 'mc_roomplayerschange') { this.player.room.players = payload[3]; } } catch (e) { console.debug('Error parsing broadcast:', e.message); } }; Connection.prototype.onRequest = function () {}; Connection.prototype.open = function (url) { this.socket = new WebSocket(url); this.socket.onopen = this.onopen.bind(this); this.socket.onclose = this.onclose.bind(this); this.socket.onerror = this.onerror.bind(this); this.socket.onmessage = this.onmessage.bind(this); }; Connection.prototype.close = function (code, reason) { try { this.socket.close(code, reason); } catch (e) { console.debug('Error closing WebSocket:', e.message); } }; Connection.prototype.Heartbeat = function (interval) { setTimeout(() => { if (this.socket.readyState == this.socket.OPEN) { try { this.socket.send('2'); this.Heartbeat(interval); } catch (e) { console.debug('Heartbeat error:', e.message); } } }, interval); }; Connection.prototype.serverconnect = function (server, room) { if (!this.socket || this.socket.readyState != this.socket.OPEN) { this.open(server); } else { try { this.socket.send('41'); this.socket.send('40'); } catch (e) { console.debug('Error sending server connect:', e.message); } } this.onRequest = () => { try { this.socket.send(room); } catch (e) { console.debug('Error sending room connect:', e.message); } }; }; const Room = function (conn) { this.conn = conn; this.id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; this.players = []; }; Room.prototype.join = function (inviteLink) { let gamemode = 2; let server = ''; if (!inviteLink) { this.id = null; server = 'sv3.'; } else { this.id = inviteLink.startsWith('http') ? inviteLink.split('/').pop() : inviteLink; if (inviteLink.endsWith('.3')) { server = 'sv3.'; gamemode = 2; } else if (inviteLink.endsWith('.2')) { server = 'sv2.'; gamemode = 2; } else { server = ''; gamemode = 1; } } let serverUrl = `wss://${server}drawaria.online/socket.io/?sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`; let player = this.conn.player; let playerName = document.getElementById('botNameInput')?.value || 'FlagBot'; let connectString = `420["startplay","${playerName}",${gamemode},"en",${nullify(this.id)},null,[null,"https://drawaria.online/",1000,1000,[${nullify(player.sid1)},${nullify(player.uid)},${nullify(player.wt)}],null]]`; this.conn.serverconnect(serverUrl, connectString); }; Room.prototype.next = function () { if (this.conn.socket.readyState != this.conn.socket.OPEN) { this.join(null); } else { try { this.conn.socket.send('42["pgswtichroom"]'); } catch (e) { console.debug('Error switching room:', e.message); } } }; const Actions = function (conn) { this.conn = conn; }; Actions.prototype.DrawLine = function (bx = 50, by = 50, ex = 50, ey = 50, thickness = 50, color = '#FFFFFF', algo = 0) { bx = bx / 100; by = by / 100; ex = ex / 100; ey = ey / 100; try { this.conn.socket.send(`42["drawcmd",0,[${bx},${by},${ex},${ey},true,${0 - thickness},"${color}",0,0,{"2":${algo},"3":0.5,"4":0.5}]]`); this.conn.socket.send(`42["drawcmd",0,[${bx},${by},${ex},${ey},false,${0 - thickness},"${color}",0,0,{"2":${algo},"3":0.5,"4":0.5}]]`); } catch (e) { console.debug('Error drawing line:', e.message); } }; const nullify = (value = null) => value == null ? null : `"${value}"`; // Initialize window['__FLAG_BOT'] = new Player('FlagBot'); window['__FLAG_ENGINE'] = { loadImage, drawImage }; })();