DEV Community

Cover image for How to start coding with the Javascripter class
Mark Kop
Mark Kop

Posted on

How to start coding with the Javascripter class

(This tutorial is also available in portuguese)

Open the terminal on your operational system (Windowers can use GIT Bash) and see a black screen.

The flashing cursor on the command line shows that you are in the game. You can move between classes at will, but your experience in each will vary. The Javascripter class is quite on target today and this guide will be based on it.

Stackoverflow Search with the 6 Most Popular Languages ​​of 2019

First steps

There are different ways to use your Javascript skills. We will approach the one that grants some basic equipment by simply casting npm init into a folder.

To enable save mode, use git init once and git commit -am "save" to save. It's a good practice that instead of using the word save you would use a brief semantic message of your progress.

With save mode enabled, your secrets can be exposed to enemies and to protect them you can use dotenv. Create a .env file with value = "key" and add it to a.gitignore file. Then access them with process.get.INFO.

Character leveling up in a generic 3d game

Evolutions and Combos

Your basic skill will be node script.js and soon it can be improved to nodemon script.js, allowing for a better flow of your spells.

A major evolution would be using nodemon --exec babel-node script.js to allow the use of up-to-date spells and keep track of the current skills meta.

npm install nodemon --save-dev
npm install @babel/core @babel/node @babel/preset-env --save-dev

// Create .babelrc file and insert:
{
  "presets": [
    "@babel/preset-env"
  ]
}

node script.js
nodemon script.js
nodemon --exec babel-node script.js

// Add to package.json:
"scripts": {
    "dev": "nodemon --exec babel-node index.js"
},

npm run dev
Enter fullscreen mode Exit fullscreen mode

The text editor will allow the handling of the script.js and create different results according to what you want to do. I recommend VSCode with built-in file browsing, text editor and terminal all together, along with several others advantages.

Quests and other objectives will require different resources, such as express / koa to create routes and open ports within your domain. and react / vue to generate interfaces and visual entities.

statusReport

Tweet written

In this campaign, we will create a Node application that checks the status of a Habitica character and posts a tweet with a summary of the situation. This process should happen every time an endpoint is accessed.

Hereafter it is assumed that you are already prepared with the above upgrades. You can also track quest progress through the commit history of this campaign.

Quest # 1: Get Habitica information

An example of JSON's return from Habitica's api

We will invoke a utility spell with npm install axios that will access the Habitica domain and give us information about a given character. The character ID is stored in the environment variable in .env accessed withprocess.env.HABITICA_USERID.

import 'dotenv/config'
import axios from 'axios'

const getStats = async (userid) => {
    try {
        const response = await axios.get(`https://habitica.com/api/v3/members/${userid}`)
        return response.data.data.stats
    } catch (error) {
        console.log(error)
    }
}

const reportStatus = async () => {
    try {
        const stats = await getStats(process.env.HABITICA_USERID)
        console.log(stats)
    } catch (error) {
        console.log(error)
    }
}

reportStatus()
Enter fullscreen mode Exit fullscreen mode

Here we realize the need of Async / Await with Try / Catch in asynchronous requests.

Quest # 2: Generate message based on stats

This step requires just a little javascripter manipulation. A simple way to exemplify the idea is as follows:

// ...

const selectMessage = ({ hp = 0, maxHealth = 0, exp = 0, toNextLevel = 0 }) => {
    const status = `[HP: ${hp}/${maxHealth}] [EXP: ${exp.toFixed()}/${toNextLevel}]`

    if (hp <= maxHealth * 0.3) {
        return `I'm almost dying, help! ${status}`
    }
    // Could also be:
    // if (hp <= maxHealth * 0.3) return `I'm almost dying, help! ${status}`


    if (exp >= toNextLevel * 0.7) {
        return `I'm almost leveling up! ${status}`
    }

    return `Things are fine for now. ${status}`
}

const reportStatus = async () => {
    try {
        const stats = await getStats(process.env.HABITICA_USERID)
        const message = selectMessage(stats)
        console.log(message)
    } catch (error) {
        console.log(error)
    }
}

reportStatus()
Enter fullscreen mode Exit fullscreen mode

At this point we can identify some peculiarities like Template Literals in strings and Object Destructuring in the selectMessage() parameters.

Quest # 3: Post to twitter

Here the difficulty begins to increase and in this solution you will need to register in the domain of Twitter wizards to get secret tokens. These tokens will be used in conjunction with the OAuth method to send messages to the domain.

// ...
import OAuth from 'oauth'

// ...

const reportTwitter = async (message) => {
    const oauth = new OAuth.OAuth(
        'https://api.twitter.com/oauth/request_token',
        'https://api.twitter.com/oauth/access_token',
        process.env.TWITTER_CONSUMER_APIKEY,
        process.env.TWITTER_CONSUMER_APISECRETKEY,
        '1.0A',
        null,
        'HMAC-SHA1'
    );

    return oauth.post(
        'https://api.twitter.com/1.1/statuses/update.json',
        process.env.TWITTER_ACCESS_TOKEN,
        process.env.TWITTER_ACCESS_SECRETTOKEN,
        { status: message },
        'application/x-www-form-urlencoded',
        function callback(error, data, res) {
            if (error) {
                throw new Error(error.data)
            };

            const jsonData = JSON.parse(data)
            const { user: { screen_name }, text } = jsonData
            console.log(`Tweet created! @${screen_name}: ${text}`)
            return true
        });
}

const reportStatus = async () => {
    try {
        const stats = await getStats(process.env.HABITICA_USERID)
        const message = selectMessage(stats)
        return reportTwitter(message)
    } catch (error) {
        console.log(error)
    }
}

reportStatus()
Enter fullscreen mode Exit fullscreen mode

More secrets are being stored in .env file, JSON.parse shows its face and Object Destructuring appears again and it's applied to the jsonData.

Quest # 4: Trigger Endpoint

Our mission is almost done and here are some interesting things happening.
We are using Koa to prepare the api endpoint that will trigger and return the report result.

//..
import Koa from 'koa';

//...

const reportTwitter = async (message) => {
    //...

    console.log(`Posting tweet with message: ${message}`)
    return new Promise((resolve, reject) => oauth.post(
        'https://api.twitter.com/1.1/statuses/update.json',
        process.env.TWITTER_ACCESS_TOKEN,
        process.env.TWITTER_ACCESS_SECRETTOKEN,
        { status: message },
        'application/x-www-form-urlencoded',
        function callback(error, data, res) {
            if (error) {
                const errorMessage = error.data
                console.log('Error: could not post tweet ', errorMessage)
                return resolve(errorMessage)
            };

            const jsonData = JSON.parse(data)
            const { user: { screen_name }, text } = jsonData
            const successMessage = `Tweet created! @${screen_name}: ${text}`
            console.log(successMessage)
            return resolve(successMessage)
        }));
}

const reportStatus = async () => {
    try {
        const stats = await getStats(process.env.HABITICA_USERID)
        const message = selectMessage(stats)
        const response = await reportTwitter(message)
        return response
    } catch (error) {
        console.log(error)
    }
}


const app = new Koa();
app.use(async (ctx) => {
    const message = await reportStatus()
    ctx.response.body = message
});
app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

And if we take a closer look, we see that the reportTwitter() function now returns a Promise.
This had to be done because oauth.post() does not return a Promise by default and we need this to display the return in ctx.response.body.

Note that the function is not rejected() in error, but resolved() to display the error message on screen (ctx).

Logs after running npm run dev

Quest # 5: Deploy

As a final step in this mission, we will raise our creation to the clouds.
We will use the Now utility tool by installing it globally with npm install -g now, creating an account by typingnow and adding our secrets securely on our account with

now secrets add habitica-userid <userid>
now secrets add twitter-consumer-apikey <key>
now secrets add twitter-consumer-apisecretkey <key>
now secrets add twitter-access-token <token>
now secrets add twitter-access-secrettoken <token>
Enter fullscreen mode Exit fullscreen mode

And with a few more settings in now.json...

{
    "version": 2,
    "builds": [
        {
            "src": "index.js",
            "use": "@now/node-server"
        }
    ],
    "env": {
        "HABITICA_USERID": "@habitica-userid",
        "TWITTER_CONSUMER_APIKEY": "@twitter-consumer-apikey",
        "TWITTER_CONSUMER_APISECRETKEY": "@twitter-consumer-apisecretkey",
        "TWITTER_ACCESS_TOKEN": "@twitter-access-token",
        "TWITTER_ACCESS_SECRETTOKEN": "@twitter-access-secrettoken"
    }
}
Enter fullscreen mode Exit fullscreen mode

Summon now on the command line and mission accomplished.

Logs on the Now service page

Is chronomancy difficult?

The initial idea for this report was it to happen every day at a specific time and it was easily achieved using a simple node-cron:

import cron from 'node-cron'

cron.schedule('30 19 * * *', () => reportStatus())
Enter fullscreen mode Exit fullscreen mode

But Heroku and Now applications goes to sleeping and things get a lot more complicated.

Next campaign?

A good continuation of this campaign would involve doing tests, refactoring, organizing into files, turning it into a Docker container and deploying it on AWS.

What do you think? Would you like more tutorials like this? Leave a message in the comments o/

Top comments (0)