您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Allows custom brush sizes, colors, and even importing images into Jackbox's drawing games!
当前为
// ==UserScript== // @name JackboxDrawer // @description Allows custom brush sizes, colors, and even importing images into Jackbox's drawing games! // @namespace ipodtouch0218/JackboxDrawer // @version 1.6.0 // @include *://jackbox.tv/* // ==/UserScript== //Catch outgoing messages through stringify and replace drawing data. //Has to be done through eval to break through GreaseMonkey's sandboxing. window.eval(` ignore = 0 oldStringify = JSON.stringify JSON.stringify = function(arg) { if (ignore > 0) { console.log('ignoring ' + arg.params) ignore-- return oldStringify(arg) } if (typeof(arg.params) == 'undefined' || arg.params == null) { return oldStringify(arg) } data = arg.params if (typeof(tempvar) == 'undefined' || tempvar === null) { //No custom code ready, most likely a vanilla subimssion. Ignore this one. return oldStringify(arg) } eval(tempvar) tempvar = null return oldStringify(arg) } `) //Handle games and their differences. var games = { "drawful_1": { submitDrawing: function() { document.getElementById("drawful-submitdrawing").click() }, isInDrawingMode: function() { return !document.getElementsByClassName("state-draw")[0].getAttribute("class").includes("pt-page-off") }, getSketchpad: function() { return document.getElementsByClassName("sketchpad")[0] } }, "drawful_2": { submitDrawing: function() { document.getElementById("submitdrawing").click() }, isInDrawingMode: function() { return document.getElementsByClassName("Draw")[0] != null }, getSketchpad: function() { return document.getElementById("fullLayer") } }, "bidiots": { submitDrawing: function() { document.getElementById("auction-submitdrawing").click() }, isInDrawingMode: function() { return !document.getElementById("state-draw").getAttribute("class").includes("pt-page-off") }, getSketchpad: function() { return document.getElementById("auction-sketchpad") } }, "tee_ko": { submitDrawing: function() { document.getElementById("awshirt-submitdrawing").click() }, isInDrawingMode: function() { return !document.getElementById("state-draw").getAttribute("class").includes("pt-page-off") }, getSketchpad: function() { return document.getElementsByClassName("awshirt-sketchpad")[0] } }, "push_the_button": { submitDrawing: function() { document.getElementById("submitdrawing").click() }, isInDrawingMode: function() { return document.getElementsByClassName("Draw")[0] != null }, getSketchpad: function() { return document.getElementById("fullLayer") } }, "trivia_murder_party_1": { submitDrawing: function() { document.getElementById("enter-single-drawing-submit").click() }, isInDrawingMode: function() { return !document.getElementById("state-enter-single-drawing").getAttribute("class").includes("pt-page-off") }, getSketchpad: function() { return document.getElementById("sketchpad") } }, "patentlystupid": { submitDrawing: function() { document.getElementById("submitdrawing").click() }, isInDrawingMode: function() { return document.getElementsByClassName("Draw")[0] != null }, getSketchpad: function() { return document.getElementById("fullLayer") } }, "champd_up": { submitDrawing: function() { if (document.getElementsByClassName("button choice-button btn btn-lg")[0].innerText == "SUBMIT") { document.getElementsByClassName("button choice-button btn btn-lg")[0].click() } }, submitName: function() { btn = document.getElementsByClassName("button choice-button btn btn-lg")[0] if (btn.getAttribute("data-action") == "name") { btn.click() document.getElementsByClassName("swal2-input")[0].value = "test" document.getElementsByClassName("swal2-confirm swal2-styled")[0].click() } }, canSubmitNormally: function() { return document.getElementsByClassName("button choice-button btn btn-lg")[0].innerText == "SUBMIT" }, isInDrawingMode: function() { return document.getElementsByClassName("Draw")[0] != null }, getSketchpad: function() { return document.getElementsByClassName("sketchpad fullLayer")[0] } } } //Keeps track of the game we're currently playing. gameID = null function updateGame(id) { gameID = id if (typeof(socket) !== 'undefined' && socket !== null) { //Update the drawing app on what game we're playing. socket.send("updategame:" + id) } } //Is ran every time the document changes. Useful for finding which game we're currently playing. var callback = function(mutationsList, observer) { if (document.getElementById("page-drawful") != null) { updateGame("drawful_1") } else if (document.getElementsByClassName("drawful2international")[0] != null) { updateGame("drawful_2") } else if (document.getElementById("page-auction") !== null) { updateGame("bidiots") } else if (document.getElementById("page-awshirt") !== null) { //Fun fact. Tee KO is actually internally called "awshirt" both on the website and in the game files. updateGame("tee_ko") } else if (document.getElementsByClassName("pushthebutton")[0] != null) { //Yes, the class name has spaces. updateGame("push_the_button") } else if (document.getElementById("page-triviadeath") !== null) { updateGame("trivia_murder_party_1") } else if (document.getElementsByClassName("worldchamps")[0] != null) { updateGame("champd_up") } else if (document.getElementsByClassName("patentlystupid")[0] != null) { updateGame("patentlystupid") } } //Initiate the DOM observer to run "callback" every time it changes. var observer = new MutationObserver(callback) var targetNode = document.getElementById('app') var config = { attributes: false, childList: true, subtree: true } observer.observe(targetNode, config) //Info related to communicating with the Java app. var socket = null var open = false var firsttry = false //We want to automatically attempt reconnects if the connection is dropped, use setInterval with some //checks to make sure we don't make multiple connections. setInterval(function() { if (open || socket !== null) { return } socket = new WebSocket("ws://127.0.0.1:2460") socket.onopen = function(e) { alert("Connection established with JackboxDrawer program.") open = true callback(null,null) } socket.onmessage = function(event) { //Check for the proper version. if (event.data.startsWith("version")) { var version = event.data.split(":")[1] if (version > 160) { alert("Please update the JackboxDrawer Greasemonkey script!\nThe download can be found here: https://greasyfork.org/en/scripts/406893-jackboxdrawer") } else if (version < 160) { alert("Please update the JackboxDrawer Java program!\nThe download can be found here: https://github.com/ipodtouch0218/JackboxDrawer/releases") } return } //Save incoming code from the websocket in "tempvar". Needs to be eval'd to get through Greasemonkey's sandboxing. //We could also use window.wrappedJSObject but this is what I thought of first. Either way, potental security breach right here. window.eval("var tempvar = `" + event.data.replace("SUBMITNAME;","") + "`") //Check to make sure we can actually DRAW right now. //If not, even attempting to submit a drawing can easily crash our webpage. var currentGame = games[gameID] if (typeof (currentGame) === 'undefined' || currentGame === null) { alert("Game not supported!") return } if (!currentGame.isInDrawingMode()) { alert("Cannot submit: Not in drawing mode!") window.eval("var tempvar = null") return } if (gameID == "champd_up") { if (event.data.startsWith("SUBMITNAME;")) { currentGame.submitName() return } } //Find the current sketchpad. We need it for later... var sketchpad = currentGame.getSketchpad() if (sketchpad === null) { //Couldn't find it, we're probably can't draw right now. Somehow, the previous checks failed. return } submit = true if (gameID == "patentlystupid") { window.eval("ignore = 1") } else if (gameID == "champd_up") { if (currentGame.canSubmitNormally()) { window.eval("ignore = 1") } else { submit = false } } //Simulate drawing on the sketchpad with mouse events. We can't access the sketchpad's info directly //as it's kept track of internally, and the game never attempts to send any data if it's blank. var rect = sketchpad.getBoundingClientRect() var mouseEvent = document.createEvent('MouseEvents') mouseEvent.clientX = rect.x + rect.width / 2 mouseEvent.clientY = rect.y + rect.height / 2 mouseEvent.initEvent("mousedown", true, false) sketchpad.dispatchEvent(mouseEvent) mouseEvent.clientX += 2 mouseEvent.initEvent("mousemove", true, false) sketchpad.dispatchEvent(mouseEvent) mouseEvent.initEvent("mouseup", true, false) sketchpad.dispatchEvent(mouseEvent) //Submit drawing and get ready to switch-a-roo. if (submit) currentGame.submitDrawing() } //Socket died, my dude. socket.onclose = function(event) { if (!open) return alert("Connection lost with JackboxDrawer program. Retrying...") open = false socket = null } //Socket died PAINFULLY, my dude. socket.onerror = function(error) { if (!firsttry) { alert("Failed to connect to JackboxDrawer. \nI will attempt to reconnect in the background...") firsttry = true } socket = null } }, 1000)