Better Smeg than Deadle - hacking smegle

Smegle is the Red Dwarf equivalent of Wordle, but instead of words you have to guess the episode from the screenshot shown. The screenshot is revealed in a catch phrase style, square by square.

Anyhows, I wrote a script to answer the daily Smegle, completely ruining any fun that might be had.

This script will only run in newer versions of nodejs as it uses the inbuilt fetch library.

It will not run in the browser due to CORS, so if you wanted to make a website with this code you will have to proxy it or port it to PHP and use cURL.

// var to hold hash to test
var today = '';

// gets current page, and as such, current answer
async function getPage(){
    const resp = await fetch('https://smegle.ganymede.tv/')
    const html = await resp.text();

    // answer is stored as hash in hidden fields on page
    const episode = html.match(/episodehash" value="(?<hash>[a-z0-9]+)"/m);
    const series = html.match(/serieshash" value="(?<hash>[a-z0-9]+)"/m);

    // some debug output
    console.log('episode', episode.groups.hash);
    console.log('series', series.groups.hash);

    // set hash to compare
    today = episode.groups.hash + '-' + series.groups.hash;

}

// generate array of characters
const alphaLower = Array.from(Array(26)).map((e, i) => i + 97).map((x) => String.fromCharCode(x));

// find matching episode
async function findMatch(){

    // what this does is loop through each letter and use the episode search endpoint to list out possible answers
    // we then check the hash reference (value, on the response) to our answer scraped from the page in the first function
    var resp, html, match;
    for(var i = 0; i < alphaLower.length; i++){
        console.log('checking', alphaLower[i]);
        resp = await fetch(`https://smegle.ganymede.tv/ajax/search_episodes/${alphaLower[i]}`);
        html = await resp.json();
        match = html.find(e => e.value === today);

        // on match we echo answer and break
        if(match){
            console.log(match.label);
            break;
        }
    }

}

// just main function
async function main(){
    await getPage();

    console.log('hash to match', today);

    findMatch();

}

// start
main();

How it works

The answer to Smegle is hidden in the page itself, in hidden form fields and checked against when a guess is made. The answer is stored as two hidden fields each containing a hash, one for the episode and another for series.

Episode titles are searched using an ajax endpoint in realtime, then checked against the correct answer. When searched, the ajax endpoint returns a label - episode name - and a value - the two hashes separated by a hyphen.

The above script will grab the webpage and pull the episode and series hash from it, forming the answer hash.

As we don’t know the name at this point, the script then loops through characters pulling titles to check against via the ajax endpoint. This was the simplest way to ensure checking against all answers. Realistically, we could cache this.

When the correct match is found, it prints the title to console and exits.

How it could be improved

As you can see, the webpage gives us the answer upon visiting the page, albeit in a hash form we have to look up. As this is meant for fun anyway it doesn’t make any difference, as to be honest I don’t see any reason to cheat at this.

All session information is stored in local storage also, so there is possibly some tomfoolery to be had there messing about with your scores and submitting them.

What could be done to improve it, and if it was for anything more serious to prevent cheating, is all checking to be performed server side, in a way that the webpage loads in the days image as normal but not store the hashes on client side. This way the current title searching and guessing could remain as is but the guesses would be sent to the server, returning a simple yes/no and whether series matches on submission.

This could also provide more control over the catch phrase reveal, as at present it looks like this.

But with a little bit of right click, inspect element and typing the following into the console

document.querySelectorAll('.piece').forEach(el => el.style.visibility = 'hidden');

We have the entire image revealed.

This should be generated server side also, to ensure all guesses are logged and accounted with no tampering. This would all be stored in sessions on the server.

Enough of that however, as it’s all just a bit of a laugh anyways, and I’m fine thank you Susan.