Created
April 11, 2022 14:24
-
-
Save GREEB/c2b9a118fbcf5061adf238fb0a6f8f6f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name VeggieBot | |
// @namespace https://discord.gg/grHtzeRFAf | |
// @version 1.49 | |
// @description Bot for vegan banners on pixelcanvas.io | |
// @author Vegans | |
// @match https://pixelcanvas.io/* | |
// @icon https://pixelcanvas.io/favicon.ico | |
// @updateURL https://www.thechristmasstation.org/vegan.user.js | |
// @downloadURL https://www.thechristmasstation.org/vegan.user.js | |
// @grant none | |
// ==/UserScript== | |
//jshint esversion: 10 | |
(function() { | |
'use strict'; | |
//check or generate bot ID | |
window.botID = Math.floor(Date.now() / 1000) | |
//load library for png manipulation | |
const pngLib = document.createElement("script"); | |
pngLib.src = "https://www.thechristmasstation.org/veggiebot/pngtoy.min.js"; | |
pngLib.type = "application/javascript"; | |
document.body.appendChild(pngLib); | |
//info panel popup | |
const infoPanel = document.createElement("div"); | |
infoPanel.classList.add("infoPanel"); | |
infoPanel.style.display = "none"; | |
infoPanel.innerHTML = '<div style="position: absolute;z-index: 999;width: 100vw;height: 100vh;background-color: #000a;display: flex;justify-content: center;align-items: center;"><div style="background-color: white;padding: 20px;border-radius: 10px;"><strong>Debug Info</strong><br>Version: <span class="version">a</span><br>Bot ID: <span class="botID">123</span><br><button class="closeInfoButton" style="background-color: cornflowerblue;padding: 10px;border-radius: 6px;margin-top: 15px;">Close</button></div></div>'; | |
document.body.appendChild(infoPanel); | |
//close button function | |
document.querySelector(".closeInfoButton").onclick = function closeInfo() {document.querySelector(".infoPanel").style.display = "none";}; | |
//load values into info panel | |
document.querySelector(".botID").innerHTML = botID; | |
document.querySelector(".version").innerHTML = GM_info.script.version; | |
//create flex container for UI | |
const flex = document.createElement("div"); | |
flex.style = "position: absolute; display: flex; flex-flow: row wrap; gap: 5px; padding: 5px; background-color: black; border-radius: 0 0 13px 0;"; | |
document.body.appendChild(flex); | |
//add loading indicator | |
const loadingIndicator = document.createElement("div"); | |
loadingIndicator.innerHTML = "Loading..."; | |
loadingIndicator.style = "background-color: cornflowerblue; border-radius: 10px; padding: 10px;"; | |
flex.appendChild(loadingIndicator); | |
//add pixels placed counter to UI | |
const pixelCounter = document.createElement("div"); | |
pixelCounter.style = "background-color: cornflowerblue; border-radius: 10px; padding: 10px; display: none;"; | |
//add todo counter to UI | |
const todoCounter = document.createElement("div"); | |
todoCounter.style = "background-color: cornflowerblue; border-radius: 10px; padding: 10px; display: none;"; | |
todoCounter.innerHTML = "Loading..."; | |
flex.appendChild(todoCounter); | |
//add info button to UI | |
const infoButton = document.createElement("button"); | |
infoButton.style = "background-color: cornflowerblue; border-radius: 10px; padding: 10px; font-weight: 900; width: 44px; text-align: center;"; | |
infoButton.onclick = function showInfo() {document.querySelector(".infoPanel").style.display = "block";}; | |
infoButton.innerHTML = "?"; | |
flex.appendChild(infoButton); | |
let design = { //old design variable | |
xCoord: -148, | |
yCoord: 9950, | |
}; | |
const designArray = []; //array of design objects | |
//when page is done loading, start bot | |
window.onload = async function() { | |
var pngtoy = new PngToy(); | |
const rawDesignArray = [ | |
{ | |
url: "https://www.thechristmasstation.org/veggiebot/design.png", | |
xCoord: -148, | |
yCoord: 9950, | |
}, | |
]; | |
for (const designA of rawDesignArray) { | |
designA.data = await pngtoy.fetch(designA.url) | |
.then(() => pngtoy.decode()) | |
.then(bmp => bmp); | |
console.log(designA); | |
designArray.push(designA); | |
} | |
console.log(designArray); | |
design.data = await pngtoy.fetch("https://www.thechristmasstation.org/veggiebot/design.png").then(() => pngtoy.decode()).then(bmp => bmp); //old, remove later | |
//hide loading indicator and display UI | |
loadingIndicator.style.display = "none"; | |
pixelCounter.style.display = "block"; | |
todoCounter.style.display = "block"; | |
webhook(`Connected.`); //send connection message to webhook | |
pixelTimer(); //start pixel placement loop | |
setTimeout(refresh, (30 * 60 * 1000)); //refresh page after 30 mins | |
}; | |
function getIncorrectPixels () { //returns an array of the pixel objects that need to be painted | |
const incorrectPixels = []; | |
if (!design.data) { //if design isn't loaded | |
return; | |
} | |
const state = store.getState(); | |
for (var x = 0; x < design.data.width; x++) { //for each pixel column of the design | |
for(var y = 0; y < design.data.height; y++) { //for each pixel row of the design | |
const color = getPixelColor(design, x, y); //get pixel color | |
const pixel = { //create pixel object | |
x: x + design.xCoord, | |
y: y + design.yCoord, | |
color: color, | |
}; | |
if (!isSameColorIn(state,[pixel.x, pixel.y], pixel.color)) { //if pixel isn't correct | |
incorrectPixels.push(pixel); | |
} | |
} | |
} | |
todoCounter.innerHTML = "Pixels todo: " + incorrectPixels.length; //update pixel todo counter | |
return incorrectPixels; | |
} | |
function refresh() { | |
window.location.reload(); | |
} | |
async function pixelTimer() { //the loop responsible for placing pixels | |
const pixel = choosePixel(); //get a random pixel object to be painted | |
if (pixel) { //if a pixel was returned | |
const noDelay = await placePixel(pixel); | |
if (noDelay) { | |
console.log("Pixel is already correct, trying another..."); | |
setTimeout(pixelTimer, (0.3 * 1000)); //run again after 0.3 seconds | |
} | |
else { | |
const randomDelay = Math.round(Math.random() * 5 * 1000); //random number of milliseconds to delay, up to 5 seconds | |
setTimeout(pixelTimer, (60 * 1000) + randomDelay); //run again after one minute plus random delay | |
} | |
} | |
else { //if no pixel was returned (design is complete) | |
setTimeout(pixelTimer, (30 * 1000)); //run again in 30 seconds | |
} | |
} | |
function choosePixel() { //selects the pixel to write | |
const incorrectPixels = getIncorrectPixels(); //get array of pixels that need to be painted | |
return incorrectPixels[randomInteger(1, incorrectPixels.length) - 1]; //return random pixel from array | |
} | |
async function placePixel(pixel) { //attempts to place a pixel. returns true if the pixel is already there. | |
console.log("Building pixel:" + JSON.stringify(pixel)); | |
const state = store.getState(); | |
if (isSameColorIn(state,[pixel.x, pixel.y], pixel.color)) { //if pixel is already there | |
// console.log("isSameColorIn = true"); | |
return true; | |
} | |
const fingerprint = await getFingerprint(); | |
const firebaseToken = (await getToken$3(appCheck, !1)).token; | |
const wasabi = pixel.x + pixel.y + 2342; | |
const headers = new Headers(); | |
headers.append("x-firebase-appcheck", firebaseToken); | |
headers.append("Content-Type", "application/json"); | |
var raw = JSON.stringify({ | |
"x": pixel.x, | |
"y": pixel.y, | |
"color": pixel.color, | |
"fingerprint": fingerprint, | |
"token": null, | |
"wasabi": wasabi | |
}); | |
var requestOptions = { | |
method: 'POST', | |
headers: headers, | |
body: raw, | |
redirect: 'follow' | |
}; | |
fetch("https://pixelcanvas.io/api/pixel", requestOptions) | |
.then(response => response.text()) | |
.then(result => { | |
if (JSON.parse(result).result?.data.success) { //if server says the pixel was placed | |
console.log("Success"); | |
//send message to webhook | |
webhook("Pixel placed."); | |
} | |
else { //server returned 200 but gives an error message | |
console.log(result); | |
} | |
}) | |
.catch(error => console.log('error', error)); //network error | |
} | |
function getPixelColor(design, x, y) { //returns color code for pixel coords (DESIGN COORDS, NOT MAP COORDS) (from top left, starts at 0) | |
const offset = (design.data.width*y+x)*4; | |
let rawColor = design.data.bitmap; //rbg array from canvas | |
let color = null; //pixel color code | |
if (rawColor[offset + 0] == 255 && rawColor[offset + 1] == 255 && rawColor[offset + 2] == 255) { //white | |
color = 0; | |
} | |
if (rawColor[offset + 0] == 228 && rawColor[offset + 1] == 228 && rawColor[offset + 2] == 228) { //light grey | |
color = 1; | |
} | |
if (rawColor[offset + 0] == 136 && rawColor[offset + 1] == 136 && rawColor[offset + 2] == 136) { //dark grey | |
color = 2; | |
} | |
if (rawColor[offset + 0] == 34 && rawColor[offset + 1] == 34 && rawColor[offset + 2] == 34) { //black | |
color = 3; | |
} | |
if (rawColor[offset + 0] == 255 && rawColor[offset + 1] == 167 && rawColor[offset + 2] == 210) { //pink | |
color = 4; | |
} | |
if (rawColor[offset + 0] == 229 && rawColor[offset + 1] == 0 && rawColor[offset + 2] == 0) { //red | |
color = 5; | |
} | |
if (rawColor[offset + 0] == 229 && rawColor[offset + 1] == 149 && rawColor[offset + 2] == 0) { //orange | |
color = 6; | |
} | |
if (rawColor[offset + 0] == 160 && rawColor[offset + 1] == 106 && rawColor[offset + 2] == 66) { //brown | |
color = 7; | |
} | |
if (rawColor[offset + 0] == 229 && rawColor[offset + 1] == 218 && rawColor[offset + 2] == 0) { //yellow | |
color = 8; | |
} | |
if (rawColor[offset + 0] == 148 && rawColor[offset + 1] == 224 && rawColor[offset + 2] == 68) { //light green | |
color = 9; | |
} | |
if (rawColor[offset + 0] == 2 && rawColor[offset + 1] == 190 && rawColor[offset + 2] == 1) { //dark green | |
color = 10; | |
} | |
if (rawColor[offset + 0] == 0 && rawColor[offset + 1] == 211 && rawColor[offset + 2] == 221) { //light blue | |
color = 11; | |
} | |
if (rawColor[offset + 0] == 0 && rawColor[offset + 1] == 131 && rawColor[offset + 2] == 199) { //middle blue | |
color = 12; | |
} | |
if (rawColor[offset + 0] == 0 && rawColor[offset + 1] == 0 && rawColor[offset + 2] == 234) { //dark blue | |
color = 13; | |
} | |
if (rawColor[offset + 0] == 207 && rawColor[offset + 1] == 110 && rawColor[offset + 2] == 228) { //light purple | |
color = 14; | |
} | |
if (rawColor[offset + 0] == 130 && rawColor[offset + 1] == 0 && rawColor[offset + 2] == 128) { //dark purple | |
color = 15; | |
} | |
if (color !== null) { //if color code is found, return color | |
return color; | |
} | |
else { | |
console.log("color not found at " + x + ", " + y); | |
} | |
} | |
function webhook(content) { //sends log/error message to discord webhook | |
const headers = new Headers(); | |
headers.append("Content-Type", "application/json"); | |
const webhookBody = JSON.stringify({ | |
"content": `\`${window.botID}\` \`v${GM_info.script.version}\`: ` + content, | |
}); | |
const webhookOpts = { | |
method: 'POST', | |
headers: headers, | |
body: webhookBody, | |
}; | |
fetch("https://discord.com/api/webhooks/962127487519834152/9yWREq9fItx7dnaWfvzPZ5B7euCqd_UwvVat8YyhZTK-fdIAvb4i8TUMwieokms1Wz3J", webhookOpts); | |
} | |
function randomInteger(min, max) { //returns random int between min and max inclusive | |
return Math.floor(Math.random() * (max - min + 1)) + min; | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment