Skip to content

Instantly share code, notes, and snippets.

@q00u
Last active July 10, 2022 15:14
Show Gist options
  • Save q00u/f3579bf7fe7f1c6de2214e47f6e63da9 to your computer and use it in GitHub Desktop.
Save q00u/f3579bf7fe7f1c6de2214e47f6e63da9 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Share Worble Guesses
// @namespace https://gist.github.com/q00u
// @version 0.3
// @description Copy Worble guesses and evals in dark+colorblind mode (but not _answer_) for Slack comment
// @author Phoenix G
// @match https://www.worble.fun
// @icon https://www.google.com/s2/favicons?sz=64&domain=worble.fun
// @grant unsafeWindow
// ==/UserScript==
(async function() {
'use strict';
// Your code here...
const grabGuesses = (dailyShareText, guesses, interval) => {
clearInterval(interval);
// Build output strings
//console.log('dailyShareText:', dailyShareText);
//console.log('guesses:', guesses);
const shareLines = dailyShareText.split('\n');
//console.log('shareLines:', shareLines);
const evalOutput = [];
const guessOutput = [];
for (let i=1; i<shareLines.length; i++) {
let thisLineGuess = ''; // The potential output
let thisLineEval = '';
let word = guesses[i-1]; // The line as-is
const evaluation = [...shareLines[i]]; // The evaluation for this word
//console.log('Evaluating line:', word, evaluation);
// For each letter...
for (let j=0; j<evaluation.length; j++) {
const c = word[j];
switch(evaluation[j]) {
case '⬜':
thisLineEval += '⬛';
thisLineGuess += `${c.toLowerCase()}`;
break;
case '🟨': // Slack can't apply italics inside code lines nor around letters that touch
thisLineEval += '🟦';
thisLineGuess += `${c.toLowerCase()}`;
break;
case '🟩':
thisLineEval += '🟧';
thisLineGuess += `${c.toUpperCase()}`;
break;
default: throw 'Unexpected row evaluation : ' + evaluation[j];
}
}
evalOutput.push(thisLineEval);
guessOutput.push(thisLineGuess);
}
// We're done, pretty-print results
let finalOut = `${shareLines[0]}\n`;
for (let k=0; k<evalOutput.length; k++) {
finalOut += `${evalOutput[k]}\n`;
}
for (let k=0; k<guessOutput.length-1; k++) {
finalOut += `\`${guessOutput[k]}\`\n`;
}
console.log('finalOut:', finalOut);
// Add snackbar for copy button to use later
const snackbarDiv = document.createElement('div');
snackbarDiv.id = 'snackbar';
snackbarDiv.innerText = 'Guesses copied!';
//For testing:
//snackbarDiv.className = 'show';
document.body.append(snackbarDiv);
// Style it
function GM_addStyle(css) {
const style = document.getElementById("GM_addStyleBy8626") || (function() {
const style = document.createElement('style');
style.type = 'text/css';
style.id = "GM_addStyleBy8626";
document.head.appendChild(style);
return style;
})();
const sheet = style.sheet;
sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length);
}
// Position snackbar at the top right of the screen
GM_addStyle('#snackbar{background-color:rgb(18,18,18);border:1px solid black;border-radius:2px;box-shadow:0 4px 23px 0 rgba(0,0,0,.2);color:white;height:34px;padding:4px 8px;position:fixed;right:24px;text-align:center;top:64px;visibility:hidden;z-index:10000}');
// Show the snackbar when clicking a button
GM_addStyle('#snackbar.show{animation:.5s fadein,.5s 2.5s fadeout;visibility:visible;webkit-animation:fadein 0.5s,fadeout 0.5s 2.5s}');
// Animations to fade the snackbar in and out
GM_addStyle('@-webkit-keyframes fadein {from {bottom: 0;opacity: 0;}to {bottom: 30px;opacity: 1;}}');
GM_addStyle('@keyframes fadein {from {bottom: 0;opacity: 0;}to {bottom: 30px;opacity: 1;}}');
GM_addStyle('@-webkit-keyframes fadeout {from {bottom: 30px;opacity: 1;}to {bottom: 0;opacity: 0;}}');
GM_addStyle('@keyframes fadeout {from {bottom: 30px;opacity: 1;}to {bottom: 0;opacity: 0;}}');
// Build SVG
const xmlns = 'http://www.w3.org/2000/svg';
const svg = document.createElementNS(xmlns, 'svg');
svg.setAttribute('width', '24px');
svg.setAttribute('height', '24px');
svg.setAttribute('viewBox', '-4 -4 39 39');
svg.setAttribute('class', 'game-icon');
svg.setAttribute('data-testid', 'icon-copy');
svg.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', 'http://www.w3.org/1999/xlink');
const rect1 = document.createElementNS(xmlns, 'rect');
rect1.setAttribute('x', '2');
rect1.setAttribute('y', '1');
rect1.setAttribute('rx', '1');
rect1.setAttribute('ry', '1');
rect1.setAttribute('width', '16');
rect1.setAttribute('height', '20');
rect1.setAttribute('style', 'fill: none; stroke:black; strike-width: 1');
const rect2 = document.createElementNS(xmlns, 'rect');
rect2.setAttribute('x', '5');
rect2.setAttribute('y', '5');
rect2.setAttribute('rx', '0.5');
rect2.setAttribute('ry', '0.5');
rect2.setAttribute('width', '10');
rect2.setAttribute('height', '1');
rect2.setAttribute('style', 'fill:black');
const rect3 = document.createElementNS(xmlns, 'rect');
rect3.setAttribute('x', '5');
rect3.setAttribute('y', '8');
rect3.setAttribute('rx', '0.5');
rect3.setAttribute('ry', '0.5');
rect3.setAttribute('width', '10');
rect3.setAttribute('height', '1');
rect3.setAttribute('style', 'fill:black');
const rect4 = document.createElementNS(xmlns, 'rect');
rect4.setAttribute('x', '5');
rect4.setAttribute('y', '11');
rect4.setAttribute('rx', '0.5');
rect4.setAttribute('ry', '0.5');
rect4.setAttribute('width', '10');
rect4.setAttribute('height', '1');
rect4.setAttribute('style', 'fill:black');
const rect5 = document.createElementNS(xmlns, 'rect');
rect5.setAttribute('x', '5');
rect5.setAttribute('y', '14');
rect5.setAttribute('rx', '0.5');
rect5.setAttribute('ry', '0.5');
rect5.setAttribute('width', '6');
rect5.setAttribute('height', '1');
rect5.setAttribute('style', 'fill:black');
const path1 = document.createElementNS(xmlns, 'path');
path1.setAttribute('d', 'M18,3h3a1,1 0 0 1 1,1v18a-1,1 0 0 1 -1,1h-14a-1,-1 0 0 1 -1,-1v-1');
path1.setAttribute('style', 'fill: none; stroke: black; stroke-width: 1');
svg.appendChild(rect1);
svg.appendChild(rect2);
svg.appendChild(rect3);
svg.appendChild(rect4);
svg.appendChild(rect5);
svg.appendChild(path1);
//console.log(svg);
let btn = document.createElement('button');
btn.appendChild(svg);
//btn.onclick = copyToClipBoard(finalOut);
btn.addEventListener('click', function() {
const el = document.createElement('textarea');
el.value = finalOut;
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
//console.log('Copied to clipboard:', el.value);
// Now show the snackbar
snackbarDiv.className = 'show';
setTimeout(function(){
snackbarDiv.className = snackbarDiv.className.replace('show','');
}, 3000);
});
btn.type = 'button';
btn.id = 'copy-button';
//btn.class = 'AppHeader-module_icon__x7b46';
btn.ariaLabel = 'Copy Guesses';
btn.tabIndex = -1;
btn.className = 'mt-0.5 text-sm absolute w-6 h-6 mr-6 bg-white border border-neutral-300 border-2 hover:border-transparent rounded-full';
btn.style.right = '48px';
// Find the header
const hFull = document.getElementsByClassName('h-full')[2];
//console.log('hFull:', hFull);
const header = hFull.children[0];
//console.log('header:', header);
const rightButton = header.children[2];
//console.log('rButton:', rightButton);
header.insertBefore(btn, rightButton);
}
// WAIT FOR PUZZLE TO BE SOLVED
console.log('Waiting for game to resolve.');
const repeatcheck = () => {
//const worbleState = unsafeWindow.localStorage.dailyShareText;
const dailyStatsRaw = unsafeWindow.localStorage.dailyStatistics;
if (dailyStatsRaw) {
const dailyStats = JSON.parse(dailyStatsRaw);
const values = Object.values(dailyStats?.count);
const sum = values.reduce((acc, v) => {return acc + v}, 0);
//console.log('dailyStats',dailyStats,'values',values,'sum',sum);
if (sum > 0) {
//clearInterval(clearcheck);
const guesses = JSON.parse(unsafeWindow.localStorage.guesses);
const dailyShareText = JSON.parse(unsafeWindow.localStorage.dailyShareText);
//console.log('Game has ended!', guesses.guesses, dailyShareText.text);
if (guesses.guesses && dailyShareText.text) {
grabGuesses(dailyShareText.text, guesses.guesses, clearcheck);
}
}
}
// console.log('worbleState:', worbleState);
// if (worbleState !== undefined) {
// console.log('Game has ended!');
// clearInterval(clearcheck);
// const dailyShareText = JSON.parse(worbleState);
// const guesses = JSON.parse(unsafeWindow.localStorage.guesses);
// grabGuesses(dailyShareText.text, guesses.guesses);
// }
}
const clearcheck = setInterval(repeatcheck, 500);
})();
@q00u
Copy link
Author

q00u commented Jul 3, 2022

0.1:

  • Initial commit

0.2:

  • Cleanup
    • Remove testing console.logs
    • Fit copy icon to button
    • Correct snackbar style
    • Remove var colors (worble doesn't have light/dark switchable styles like wordle)

0.3:

  • DailyShareText exists after the first day, it's not a good place to check if the puzzle is done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment