loading...

Release 0.3 part 1, more Discord!

fluentinstroll profile image Ray ・3 min read

For the first part of Release 0.3, I decided to revisit my favorite repo from last month: MHTimer!

If you're new to Discord bots or have started reading with this post, I recommend checking out my other post about this bot and how I got it working as I won't be going over it again.

This time I took on an issue that had to do with error handling.

findThings sends empty response on error. #210

findThings: Error getting item item_type=mouse&item_id=402 - FetchError: invalid json respons

So basically when the bot gets a request for some information, and the website is down, the bot just prints an error in the terminal and the Discord chat has no printout!

Nothing!

So the first step was to take a look at the get call.

findThing function

async function findThing(type, id, options) {
    if (!type || !id)
        return [];

    // If caching is ever implemented it'd be checked here
    const qsOptions = new URLSearchParams(options);
    qsOptions.append('item_type', type);
    qsOptions.append('item_id', id);
    const url = 'https://www.agiletravels.com/searchByItem.php?' + qsOptions.toString();
    return await fetch(url)
        .then((response) => {
            if(response.ok){
                return response.json();
            }
            else {
                return null;
            }
        })
        .catch(err => {
            Logger.log(`findThings: Error getting item ${qsOptions.toString()} - ${err}`);
        });
}
Enter fullscreen mode Exit fullscreen mode

So what this did before simply sent back an error when any problem happened. Instead, I decided to return null if the response from the server was not ok. Next, let us take a look at the function that will craft our message.

formatMice function

async function formatMice(isDM, mouse, opts) {
    const results = await findThing('mouse', mouse.id, opts);
    if (results === null) {
        const reply = 'Looks like I\'m having a bit of trouble finding your mouse right now.' ;
        return reply;
    }
    const no_stage = ' N/A ';
    const target_url = `<https://www.agiletravels.com/attractions.php?mouse=${mouse.id}&timefilter=${opts.timefilter ? opts.timefilter : 'all_time'}>`;
    const attracts = results.filter(mouse => mouse.total_hunts > 99)
        .map(mice => {
            return {
                location: mice.location.substring(0, 20),
                stage: mice.stage === null ? no_stage : mice.stage.substring(0, 20),
                cheese: mice.cheese.substring(0,15),
                total_hunts: intToHuman(mice.total_hunts),
                ar: mice.rate / 100,
            };
        });
    if (!attracts.length)
        return `There were no results with 100 or more hunts for ${mouse.value}, see more at ${target_url}`;
    const order = ['location', 'stage', 'cheese', 'ar', 'total_hunts'];
    const labels = { location: 'Location', stage: 'Stage', total_hunts: 'Hunts',
        ar: '/Hunt', cheese: 'Cheese' };
    //Sort the results
    attracts.sort((a, b) => parseFloat(b.ar) - parseFloat(a.ar));
    attracts.splice(isDM ? 100 : 10);
    if (attracts.every(row => row.stage === no_stage))
        order.splice(order.indexOf('stage'), 1);
    // Column Formatting specification.
    /** @type {Object <string, ColumnFormatOptions>} */
    const columnFormatting = {};
    const headers = order.map(key => {
        columnFormatting[key] = {
            columnWidth: labels[key].length,
            alignRight: !isNaN(parseInt(attracts[0][key], 10)),
        };
        return { 'key': key, 'label': labels[key] };
    });
    // Give the numeric column proper formatting.
    // TODO: toLocaleString - can it replace integerComma too?
    columnFormatting['ar'] = {
        alignRight: true,
        isFixedWidth: true,
        suffix: '%',
        columnWidth: 7,
    };
    let reply = `${mouse.value} (mouse) can be found the following ways:\n\`\`\``;
    reply += prettyPrintArrayAsString(attracts, columnFormatting, headers, '=');
    reply += '```

\n' + `HTML version at: ${target_url}`;
    return reply;
}


Enter fullscreen mode Exit fullscreen mode

It's a bit long, but let us take a look at the most important part.



const results = await findThing('mouse', mouse.id, opts);
    if (results === null) {
        const reply = 'Looks like I\'m having a bit of trouble finding your mouse right now.' ;
        return reply;
    }


Enter fullscreen mode Exit fullscreen mode

Basically, what we do here is call our findThing function, which this function was already doing, and if our return value is null, then we craft our message and skip the rest of the validation and separation. Who needs it if we know our response is bunk?

So let's put up our fix for review.

Whoops! Turns out I ran prettier on my code before submitting and now it's gone and messed up tons of stuff. Let's patch it up and submit it properly.

We've succeeded! All in all some good work. To be honest, I didn't learn a lot doing this fix. I became more familiar with the project and the features of git - but I think what really shows my growth was how easily I was able to take an issue, fix it up, and merge it quickly! I really feel like I've progressed as a developer because of that.

Discussion

pic
Editor guide