One place for hosting & domains

      How To Build a Discord Bot with Node.js


      The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Discord is a chat application that allows millions of users across the globe to message and voice chat online in communities called guilds or servers. Discord also provides an extensive API that developers can use to build powerful Discord bots. Bots can perform various actions such as sending messages to servers, DM-ing users, moderating servers, and playing audio in voice chats. This allows developers to craft powerful bots that include advanced, complex features like moderation tools or even games. For example, the utility bot Dyno serves millions of guilds and contains useful features such as spam protection, a music player, and other utility functions. Learning how to create Discord bots allows you to implement many possibilities, which thousands of people could interact with every day.

      In this tutorial, you will build a Discord bot from scratch, using Node.js and the Discord.js library, which allows users to directly interact with the Discord API. You’ll set up a profile for a Discord bot, get authentication tokens for the bot, and program the bot with the ability to process commands, with arguments, from users.

      Prerequisites

      Before you get started, you will need the following:

      Step 1 — Setting Up a Discord Bot

      In this step, you’ll use the Discord developers GUI to set up a Discord bot and get the bot’s token, which you will pass into your program.

      In order to register a bot on the Discord platform, use the Discord application dashboard. Here developers can create Discord applications including Discord bots.

      Image of the Discord application dashboard after first visiting https://discord.com/developers/applications

      To get started, click New Application. Discord will ask you to enter a name for your new application. Then click Create to create the application.

      Image of the prompt to create an application, with

      Note: The name for your application is independent from the name of the bot, and the bot doesn’t have to have the same name as the application.

      Now open up your application dashboard. To add a bot to the application, navigate to the Bot tab on the navigation bar to the left.

      Image of the bot tab of the application dashboard

      Click the Add Bot button to add a bot to the application. Click the Yes, do it! button when it prompts you for confirmation. You will then be on a dashboard containing details of your bot’s name, authentication token, and profile picture.

      Dashboard containing details of your bot

      You can modify your bot’s name or profile picture here on the dashboard. You also need to copy the bot’s authentication token by clicking Click to Reveal Token and copying the token that appears.

      Warning: Never share or upload your bot token as it allows anyone to log in to your bot.

      Now you need to create an invite that allows you to add the bot Discord guilds where you can test the bot. First, navigate to the OAuth2 tab of the application dashboard. To create an invite, scroll down and select bot under scopes. You must also set permissions to control what actions your bot can perform in guilds. For the purposes of this tutorial, select Administrator, which will give your bot permission to perform nearly all actions in guilds. Copy the link with the Copy button.

      OAuth2 tab, with scope set to

      Next, add the bot to a server. Follow the invite link you just created. You can add the bot to any server you own, or have administrator permissions in, from the drop-down menu.

      Page from following the invite link, allowing users to add the bot to servers

      Now click Continue. Ensure you have the tickbox next to Administrator ticked—this will grant the bot administrator permissions. Then click Authorize. Discord will ask you to solve a CAPTCHA before the bot joins the server. You’ll now have the Discord bot on the members list in the server you added the bot to under offline.

      Members list of a Discord server with the newly created bot under the

      You’ve successfully created a Discord bot and added it to a server. Next, you will write a program to log in to the bot.

      Step 2 — Creating Your Project

      In this step, you’ll set up the basic coding environment where you will build your bot and log in to the bot programmatically.

      First, you need to set up a project folder and necessary project files for the bot.

      Create your project folder:

      Move into the project folder you just created:

      Next, use your text editor to create a file named config.json to store your bot’s authentication token:

      Then add the following code to the config file, replacing the highlighted text with your bot’s authentication token:

      config.json

      {
          "BOT_TOKEN": "YOUR BOT TOKEN"
      }
      

      Save and exit the file.

      Next you’ll create a package.json file, which will store details of your project and information about the dependencies you’ll use for the project. You’ll create a package.json file by running the following npm command:

      npm will ask you for various details about your project. If you would like guidance on completing these prompts, you can read about them in How To Use Node.js Modules with npm and package.json.

      You’ll now install the discord.js package that you will use to interact with the Discord API. You can install discord.js through npm with the following command:

      Now you’ve set up the configuration file and installed the necessary dependency, you’re ready to begin building your bot. In a real-world application, a large bot would be split across many files, but for the purposes of this tutorial, the code for your bot will be in one file.

      First, create a file named index.js in the discord-bot folder for the code:

      Begin coding the bot by requiring the discord.js dependency and the config file with the bot’s token:

      index.js

      const Discord = require("discord.js");
      const config = require("./config.json");
      

      Following this, add the next two lines of code:

      index.js

      ...
      const client = new Discord.Client();
      
      client.login(config.BOT_TOKEN);
      

      Save and exit your file.

      The first line of code creates a new Discord.Client and assigns it to the constant client. This client is partly how you will interact with the Discord API and how Discord will notify you of events such as new messages. The client, in effect, represents the Discord bot.

      The second line of code uses the login method on the client to log in to the Discord bot you created, using the token in the config.json file as a password. The token lets the Discord API know which bot the program is for and that you’re authenticated to use the bot.

      Now, execute the index.js file using Node:

      Your bot’s status will change to online in the Discord server you added it to.

      Image of the bot online

      You’ve successfully set up a coding environment and created the basic code for logging in to a Discord bot. In the next step you’ll handle user commands and get your bot to perform actions, such as sending messages.

      Step 3 — Handling Your First User Command

      In this step, you will create a bot that can handle user commands. You will implement your first command ping, which will respond with "pong" and the time taken to respond to the command.

      First, you need to detect and receive any messages users send so you can process any commands. Using the on method on the Discord client, Discord will send you a notification about new events. The on method takes two arguments: the name of an event to wait for and a function to run every time that event occurs. With this method you can wait for the event message—this will occur every time a message is sent to a guild where the bot has permission to view messages. Therefore let’s create a function, which runs every time a message is sent, to process commands.

      First open your file:

      Add the following code to your file:

      index.js

      ...
      const client = new Discord.Client();
      
      
      client.on("message", function(message) { 
                                               
      });                                      
      
      client.login(config.BOT_TOKEN);
      

      This function, which runs on the message event, takes message as a parameter. message will have the value of a Discord.js message instance, which contains information about the sent message and methods to help the bot respond.

      Now add the following line of code to your command-handling function:

      index.js

      ...
      client.on("message", function(message) {
        if (message.author.bot) return;
      });
      ...
      

      This line checks if the author of the message is a bot, and if so, stops processing the command. This is important as generally you don’t want to process, or respond to, bots’ messages. Bots usually don’t need to, or want to, be using our bot, so ignoring their messages saves processing power and helps prevent accidental replies.

      Now you’ll write a command handler. To accomplish this, it’s good to understand the usual format of a Discord command. Typically, the structure of a Discord command contains three parts in the following order: a prefix, a command name, and (sometimes) command arguments.

      An image of a typical Discord command reading

      • Prefix: the prefix can be anything, but is typically a piece of punctuation or abstract phrase that wouldn’t normally be at the start of a message. This means that when you include the prefix at the start of the message, the bot will know that the intention for this command is for a bot to process it.

      • Command name: The name of the command the user wants to use. This means the bot can support multiple commands with different functionality and allow users to choose between them by supplying a different command name.

      • Arguments: Sometimes if the command requires or uses extra information from the user, the user can supply arguments after the command name, with each argument separated by a space.

      Note: There is no enforced command structure and bots can process commands how they like, but the structure presented here is an efficient structure that the vast majority of bots use.

      To begin creating a command parser that handles this format, add the following lines of code to the message handling function:

      index.js

      ...
      const prefix = "!";
      
      client.on("message", function(message) {
        if (message.author.bot) return;
        if (!message.content.startsWith(prefix)) return;
      });
      ...
      

      You add the first line of code to assign the value "!" to the constant prefix, which you will use as the bot’s prefix.

      The second line of code you add checks if the content of the message the bot is processing begins with the prefix you set, and if it doesn’t, stops the message from continuing to process.

      Now you must convert the rest of the message into a command name and any arguments that may exist in the message. Add the following highlighted lines:

      index.js

      ...
      client.on("message", function(message) {
        if (message.author.bot) return;
        if (!message.content.startsWith(prefix)) return;
      
        const commandBody = message.content.slice(prefix.length);
        const args = commandBody.split(' ');
        const command = args.shift().toLowerCase();
      });
      ...
      

      You use the first line here to remove the prefix from the message content and assign the result to the constant commandBody. This is necessary as you don’t want to include the prefix in the parsed command name.

      The second line takes the message with the removed prefix and uses the split method on it, with a space as the separator. This splits it into an array of sub-strings, making a split wherever there is a space. This results in an array containing the command name then, if included in the message, any arguments. You assign this array to the constant args.

      The third line removes the first element from the args array (which will be the command name provided), converts it to lowercase, and then assigns it to the constant command. This allows you to isolate the command name and leave only arguments in the array. You also use the method toLowerCase as commands are typically case insensitive in Discord bots.

      You’ve completed building a command parser, implementing a required prefix, and getting the command name and any arguments from messages. You will now implement and create the code for the specific commands.

      Add the following code to start implementing the ping command:

      index.js

      ...
        const args = commandBody.split(' ');
        const command = args.shift().toLowerCase();
      
        if (command === "ping") {
                                 
        }                        
      });
      ...
      

      This if statement checks if the command name you parsed (assigned to the constant command) matches "ping". If it does, that indicates the user wants to use the "ping" command. You will nest the code for the specific command inside the if statement block. You will repeat this pattern for other commands you want to implement.

      Now, you can implement the code for the "ping" command:

      index.js

      ...
        if (command === "ping") {
          const timeTaken = Date.now() - message.createdTimestamp;
          message.reply(`Pong! This message had a latency of ${timeTaken}ms.`);
        }
      ...
      

      Save and exit your file.

      You add the "ping" command block that calculates the difference between the current time—found using the now method on the Date object—and the timestamp when the message was created in milliseconds. This calculates how long the message took to process and the "ping" of the bot.

      The second line responds to user’s command using the reply method on the message constant. The reply method pings (which notifies the user and highlights the message for the specified user) the user who invoked the command, followed by the content provided as the first argument to the method. You provide a template literal containing a message and the calculated ping as the response that the reply method will use.

      This concludes implementing the "ping" command.

      Run your bot using the following command (in the same folder as index.js):

      You can now use the command "!ping" in any channel the bot can view and message in, resulting in a response.

      Image of bot replying in Discord to

      You have successfully created a bot that can handle user commands and you have implemented your first command. In the next step, you will continue developing your bot by implementing a sum command.

      Step 4 — Implementing the Sum Command

      Now you will extend your program by implementing the "!sum" command. The command will take any number of arguments and add them together, before returning the sum of all the arguments to the user.

      If your Discord bot is still running, you can stop its process with CTRL + C.

      Open your index.js file again:

      To begin implementing the "!sum" command you will use an else-if block. After checking for the ping command name, it will check if the command name is equal to "sum". We use an else-if block since only one command will process at a time, so if the program matches the command name "ping", it doesn’t have to check for the "sum" command. Add the following highlighted lines to your file:

      index.js

      ...
        if (command === "ping") {
          const timeTaken = Date.now() - message.createdTimestamp;
          message.reply(`Ping! This message had a latency of ${timeTaken}ms.`);
        }
      
        else if (command === "sum") {
                                     
        }                            
      });
      ...
      

      You can begin implementing the code for the "sum" command. The code for the "sum" command will go inside the else-if block you just created. Now, add the following code:

      index.js

      ...
        else if (command === "sum") {
          const numArgs = args.map(x => parseFloat(x));
          const sum = numArgs.reduce((counter, x) => counter += x);
          message.reply(`The sum of all the arguments you provided is ${sum}!`);
        }
      ...
      

      You use the map method on the arguments list to create a new list by using the parseFloat function on each item in the args array. This creates a new array (assigned to the constant numArgs) in which all of the items are numbers instead of strings. This means later you can successfully find the sum of the numbers by adding them together.

      The second line uses the reduce method on the constant numArgs providing a function that totals all the elements in the list. You assign the sum of all the elements in numArgs to the constant sum.

      You then use the reply method on the message object to reply to the user’s command with a template literal, which contains the sum of all the arguments the user sends to the bot.

      This concludes implementing the "sum" command. Now run the bot using the following command (in the same folder as index.js):

      You can now use the "!sum" command in any channel the bot can view and message in.

      Image of bot replying

      The following is a completed version of the index.js bot script:

      index.js

      const Discord = require("discord.js");
      const config = require("./config.json");
      
      const client = new Discord.Client();
      
      const prefix = "!";
      
      client.on("message", function(message) {
        if (message.author.bot) return;
        if (!message.content.startsWith(prefix)) return;
      
        const commandBody = message.content.slice(prefix.length);
        const args = commandBody.split(' ');
        const command = args.shift().toLowerCase();
      
        if (command === "ping") {
          const timeTaken = Date.now() - message.createdTimestamp;
          message.reply(`Pong! This message had a latency of ${timeTaken}ms.`);
        }
      
        else if (command === "sum") {
          const numArgs = args.map(x => parseFloat(x));
          const sum = numArgs.reduce((counter, x) => counter += x);
          message.reply(`The sum of all the arguments you provided is ${sum}!`);
        }
      });
      
      client.login(config.BOT_TOKEN);
      

      In this step, you have further developed your Discord bot by implementing the sum command.

      Conclusion

      You have successfully implemented a Discord bot that can handle multiple, different user commands and command arguments. If you want to expand on your bot, you could possibly implement more commands or try out more parts of the Discord API to craft a powerful Discord bot. You can review the Discord.js documentation or the Discord API documentation to expand your knowledge of the Discord API.

      While creating Discord bots, you must always keep in mind the Discord API terms of service, which outlines how developers must use the Discord API. You could also read this set of guidelines on how to best implement a Discord bot and provides tips on how to design Discord bots. If you would like learn more about Node.js check out our How To Code in Node.js series.



      Source link

      How To Build a Slackbot in Python on Ubuntu 20.04


      The author selected the Tech Education Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Slack is a communication platform designed for workplace productivity. It includes features such as direct messaging, public and private channels, voice and video calls, and bot integrations. A Slackbot is an automated program that can perform a variety of functions in Slack, from sending messages to triggering tasks to alerting on certain events.

      In this tutorial you will build a Slackbot in the Python programming language. Python is a popular language that prides itself on simplicity and readability. Slack provides a rich Python Slack API for integrating with Slack to perform common tasks such as sending messages, adding emojis to messages, and much more. Slack also provides a Python Slack Events API for integrating with events in Slack, allowing you to perform actions on events such as messages and mentions.

      As a fun proof-of-concept that will demonstrate the power of Python and its Slack APIs, you will build a CoinBot—a Slackbot that monitors a channel and, when triggered, will flip a coin for you. You can then modify your CoinBot to fulfill any number of slightly more practical applications.

      Note that this tutorial uses Python 3 and is not compatible with Python 2.

      Prerequisites

      In order to follow this guide, you’ll need:

      • A Slack Workspace that you have the ability to install applications into. If you created the workspace you have this ability. If you don’t already have one, you can create one on the Slack website.

      • (Optional) A server or computer with a public IP address for development. We recommend a fresh installation of Ubuntu 20.04, a non-root user with sudo privileges, and SSH enabled. You can follow this guide to initialize your server and complete these steps.

      You may want to test this tutorial on a server that has a public IP address. Slack will need to be able to send events such as messages to your bot. If you are testing on a local machine you will need to port forward traffic through your firewall to your local system. If you are looking for a way to develop on a cloud server, check out this tutorial on How To Use Visual Studio Code for Remote Development via the Remote-SSH Plugin.

      Step 1 — Creating the Slackbot in the Slack UI

      First create your Slack app in the Slack API Control Panel. Log in to your workspace in Slack via a web browser and navigate to the API Control Panel. Now click on the Create an App button.

      Create Your Slack App

      Next you’ll be prompted for the name of your app and to select a development Slack workspace. For this tutorial, name your app CoinBot and select a workspace you have admin access to. Once you have done this click on the Create App button.

      Name Your Slack App and Select a Workspace

      Once your app is created you’ll be presented with the following default app dashboard. This dashboard is where you manage your app by setting permissions, subscribing to events, installing the app into workspaces, and more.

      Default Slack App Panel

      In order for your app to be able to post messages to a channel you need to grant the app permissions to send messages. To do this, click the Permissions button in the control panel.

      Select the Permissions Button in the Control Panel

      When you arrive at the OAuth & Permissions page, scroll down until you find the Scopes section of the page. Then find the Bot Token Scopes subsection in the scope and click on Add an OAuth Scope button.

      Select the Add an OAuth Scope Button

      Click on that button and then type chat:write. Select that permission to add it to your bot. This will allow the app to post messages to channels that it can access. For more information on the available permissions refer to Slack’s Documentation.

      Add the chat:write Permission

      Now that you’ve added the appropriate permission it is time to install your app into your Slack workspace. Scroll back up on the OAuth & Permissions page and click the Install App to Workspace button at the top.

      Install App to Workspace

      Click this button and review the actions that the app can perform in the channel. Once you are satisfied, click the Allow button to finish the installation.

      Install App to Workspace

      Once the bot is installed you’ll be presented with a Bot User OAuth Access Token for your app to use when attempting to perform actions in the workspace. Go ahead and copy this token; you’ll need it later.

      Save the Access Token

      Finally, add your newly installed bot into a channel within your workspace. If you haven’t created a channel yet you can use the #general channel that is created by default in your Slack workspace. Locate the app in the Apps section of the navigation bar in your Slack client and click on it. Once you’ve done that open the Details menu in the top right hand side. If your Slack client isn’t full-screened it will look like an i in a circle.

      Click on the App Details Icon

      To finish adding your app to a channel, click on the More button represented by three dots in the details page and select Add this app to a channel…. Type your channel into the modal that appears and click Add.

      Add App to a Channel

      You’ve now successfully created your app and added it to a channel within your Slack workspace. After you write the code for your app it will be able to post messages in that channel. In the next section you’ll start writing the Python code that will power CoinBot.

      Step 2 — Setting Up Your Python Developer Environment

      First let’s set up your Python environment so you can develop the Slackbot.

      Open a terminal and install python3 and the relevant tools onto your system:

      • sudo apt install python3 python3-venv

      Next you will create a virtual environment to isolate your Python packages from the system installation of Python. To do this, first create a directory into which you will create your virtual environment. Make a new directory at ~/.venvs:

      Now create your Python virtual environment:

      • python3 -m venv ~/.venvs/slackbot

      Next, activate your virtual environment so you can use its Python installation and install packages:

      • source ~/.venvs/slackbot/bin/activate

      Your shell prompt will now show the virtual environment in parenthesis. It will look something like this:

      Now use pip to install the necessary Python packages into your virtual environment:

      • pip install slackclient slackeventsapi Flask

      slackclient and slackeventsapi facilitate Python’s interaction with Slack’s APIs. Flask is a popular micro web framework that you will use to deploy your app:

      Now that you have your developer environment set up, you can start writing your Python Slackbot:

      Step 3 — Creating the Slackbot Message Class in Python

      Messages in Slack are sent via a specifically formatted JSON payload. This is an example of the JSON that your Slackbot will craft and send as a message:

      {
         "channel":"channel",
         "blocks":[
            {
               "type":"section",
               "text":{
                  "type":"mrkdwn",
                  "text":"Sure! Flipping a coin....nn"
               }
            },
            {
               "type":"section",
               "text":{
                  "type":"mrkdwn",
                  "text":"*flips coin* The result is Tails."
               }
            }
         ]
      }
      

      You could manually craft this JSON and send it, but instead let’s build a Python class that not only crafts this payload, but also simulates a coin flip.

      First use the touch command to create a file named coinbot.py:

      Next, open this file with nano or your favorite text editor:

      Now add the following lines of code to import the relevant libraries for your app. The only library you need for this class is the random library from the Python Standard Library. This library will allow us to simulate a coin flip.

      Add the following lines to coinbot.py to import all of the necessary libraries:

      coinbot.py

      # import the random library to help us generate the random numbers
      import random
      

      Next, create your CoinBot class and an instance of this class
      to craft the message payload. Add the following lines to coinbot.py to create the CoinBot class:

      coinbot.py

      ...
      class CoinBot:
      

      Now indent by one and create the constants, constructors, and methods necessary for your class. First let’s create the constant that will hold the base of your message payload. This section specifies that this constant is of the section type and that the text is formatted via markdown. It also specifies what text you wish to display. You can read more about the different payload options in the official Slack message payload documentation.

      Append the following lines to coinbot.py to create the base template for the payload:

      coinbot.py

      ...
          # Create a constant that contains the default text for the message
          COIN_BLOCK = {
              "type": "section",
              "text": {
                  "type": "mrkdwn",
                  "text": (
                      "Sure! Flipping a coin....nn"
                  ),
              },
          }
      

      Next create a constructor for your class so that you can create a separate instance of your bot for every request. Don’t worry about memory overhead here; the Python garbage collector will clean up these instances once they are no longer needed. This code sets the recipient channel based on a parameter passed to the constructor.

      Append the following lines to coinbot.py to create the constructor:

      coinbot.py

      ...
          # The constructor for the class. It takes the channel name as the a
          # parameter and sets it as an instance variable.
          def __init__(self, channel):
              self.channel = channel
      

      Now write the code that simulates to flip a coin. We’ll randomly generate a one or zero, representing heads or tails respectively.

      Append the following lines to coinbot.py to simulate the coin flip and return the crafted payload:

      coinbot.py

      ...
          # Generate a random number to simulate flipping a coin. Then return the 
          # crafted slack payload with the coin flip message.
          def _flip_coin(self):
              rand_int =  random.randint(0,1)
              if rand_int == 0:
                  results = "Heads"
              else:
                  results = "Tails"
      
              text = f"The result is {results}"
      
              return {"type": "section", "text": {"type": "mrkdwn", "text": text}},
      

      Finally, create a method that crafts and returns the entire message payload, including the data from your constructor, by calling your _flip_coin method.

      Append the following lines to coinbot.py to create the method that will generate the finished payload:

      coinbot.py

      ...
          # Craft and return the entire message payload as a dictionary.
          def get_message_payload(self):
              return {
                  "channel": self.channel,
                  "blocks": [
                      self.COIN_BLOCK,
                      *self._flip_coin(),
                  ],
              }
      

      You are now finished with the CoinBot class and it is ready for testing. Before continuing, verify that your finished file, coinbot.py, contains the following:

      coinbot.py

      # import the random library to help us generate the random numbers
      import random
      
      # Create the CoinBot Class
      class CoinBot:
      
          # Create a constant that contains the default text for the message
          COIN_BLOCK = {
              "type": "section",
              "text": {
                  "type": "mrkdwn",
                  "text": (
                      "Sure! Flipping a coin....nn"
                  ),
              },
          }
      
          # The constructor for the class. It takes the channel name as the a 
          # parameter and then sets it as an instance variable
          def __init__(self, channel):
              self.channel = channel
      
          # Generate a random number to simulate flipping a coin. Then return the 
          # crafted slack payload with the coin flip message.
          def _flip_coin(self):
              rand_int =  random.randint(0,1)
              if rand_int == 0:
                  results = "Heads"
              else:
                  results = "Tails"
      
              text = f"The result is {results}"
      
              return {"type": "section", "text": {"type": "mrkdwn", "text": text}},
      
          # Craft and return the entire message payload as a dictionary.
          def get_message_payload(self):
              return {
                  "channel": self.channel,
                  "blocks": [
                      self.COIN_BLOCK,
                      *self._flip_coin(),
                  ],
              }
      

      Save and close the file.

      Now that you have a Python class ready to do the work for your Slackbot, let’s ensure that this class produces a useful message payload and that you can send it to your workspace.

      Step 4 — Testing Your Message

      Now let’s test that this class produces a proper payload. Create a file named
      coinbot_test.py:

      Now add the following code. Be sure to change the channel name in the instantiation of the coinbot class coin_bot = coinbot("#YOUR_CHANNEL_HERE"). This code will create a Slack client in Python that will send a message to the channel you specify that you have already installed the app into:

      coinbot_test.py

      from slack import WebClient
      from coinbot import CoinBot
      import os
      
      # Create a slack client
      slack_web_client = WebClient(token=os.environ.get("SLACK_TOKEN"))
      
      # Get a new CoinBot
      coin_bot = CoinBot("#YOUR_CHANNEL_HERE")
      
      # Get the onboarding message payload
      message = coin_bot.get_message_payload()
      
      # Post the onboarding message in Slack
      slack_web_client.chat_postMessage(**message)
      

      Save and close the file.

      Before you can run this file you will need to export the Slack token that you saved in Step 1 as an environment variable:

      • export SLACK_TOKEN="your_bot_user_token"

      Now test this file and verify that the payload is produced and sent by running the following script in your terminal. Make sure that your virtual environment is activated. You can verify this by seeing the (slackbot) text at the front of your bash prompt. Run this command you will receive a message from your Slackbot with the results of a coin flip:

      Check the channel that you installed your app into and verify that your bot did indeed send the coin flip message. Your result will be heads or tails.

      Coin Flip Test

      Now that you’ve verified that your Slackbot can flip a coin, create a message, and deliver the message, let’s create a Flask to perpetually run this app and make it simulate a coin flip and share the results whenever it sees certain text in messages sent in the channel.

      Step 5 — Creating a Flask Application to Run Your Slackbot

      Now that you have a functioning application that can send messages to your Slack workspace, you need to create a long running process so your bot can listen to messages sent in the channel and reply to them if the text meets certain criteria. You’re going to use the Python web framework Flask to run this process and listen for events in your channel.

      In this section you will be running your Flask application from a server with a public IP address so that the Slack API can send you events. If you are running this locally on your personal workstation you will need to forward the port from your personal firewall to the port that will be running on your workstation. These ports can be the same, and this tutorial will be set up to use port 3000.

      First adjust your firewall settings to allow traffic through port 3000:

      Now check the status of ufw:

      You will see an output like this:

      Output

      Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 3000 ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6) 3000 (v6) ALLOW Anywhere (v6)

      Now create the file for your Flask app. Name this file app.py:

      Next, open this file in your favorite text editor:

      Now add the following import statements. You’ll import the following libraries for the following reasons:

      • import os – To access environment variables
      • import logging – To log the events of the app
      • from flask import Flask – To create a Flask app
      • from slack import WebClient – To send messages via Slack
      • from slackeventsapi import SlackEventAdapter – To receive events from Slack and process them
      • from coinbot import CoinBot – To create an instance of your CoinBot and generate the message payload.

      Append the following lines to app.py to import all of the necessary libraries:

      app.py

      import os
      import logging
      from flask import Flask
      from slack import WebClient
      from slackeventsapi import SlackEventAdapter
      from coinbot import CoinBot
      

      Now create your Flask app and register a Slack Event Adapter to your Slack app at the /slack/events endpoint. This will create a route in your Slack app where Slack events will be sent and ingested. To do this you will need to get another token from your Slack app, which you will do later in the tutorial. Once you get this variable you will export it as an environment variable named SLACK_EVENTS_TOKEN. Go ahead and write your code to read it in when creating the SlackEventAdapter, even though you haven’t set the token yet.

      Append the following lines to app.py to create the Flask app and register the events adapter into this app:

      app.py

      ...
      # Initialize a Flask app to host the events adapter
      app = Flask(__name__)
      
      # Create an events adapter and register it to an endpoint in the slack app for event ingestion.
      slack_events_adapter = SlackEventAdapter(os.environ.get("SLACK_EVENTS_TOKEN"), "/slack/events", app)
      

      Next create a web client object that will allow your app to perform actions in the workspace, specifically to send messages. This is similar to what you did when you tested your coinbot.py file previously.

      Append the following line to app.py to create this slack_web_client:

      app.py

      ...
      # Initialize a Web API client
      slack_web_client = WebClient(token=os.environ.get("SLACK_TOKEN"))
      

      Now create a function that can be called that will create an instance of CoinBot, and then use this instance to create a message payload and pass the message payload to the Slack web client for delivery. This function will take in a single parameter, channel, which will specify what channel receives the message.

      Append the following lines to app.py to create this function:

      app.py

      ...
      def flip_coin(channel):
          """Craft the CoinBot, flip the coin and send the message to the channel
          """
          # Create a new CoinBot
          coin_bot = CoinBot(channel)
      
          # Get the onboarding message payload
          message = coin_bot.get_message_payload()
      
          # Post the onboarding message in Slack
          slack_web_client.chat_postMessage(**message)
      

      Now that you have created a function to handle the messaging aspects of your app, create one that monitors Slack events for a certain action and then executes your bot. You’re going to configure your app to respond with the results of a simulated coin flip when it sees the phrase “Hey Sammy, Flip a coin”. You’re going to accept any version of this—case won’t prevent the app from responding.

      First decorate your function with the @slack_events_adapter.on syntax that allows your function to receive events. Specify that you only want the message events and have your function accept a payload parameter containing all of the necessary Slack information. Once you have this payload you will parse out the text and analyze it. Then, if it receives the activation phrase, your app will send the results of a simulated coin flip.

      Append the following code to app.py to receive, analyze, and act on incoming messages:

      app.py

      # When a 'message' event is detected by the events adapter, forward that payload
      # to this function.
      @slack_events_adapter.on("message")
      def message(payload):
          """Parse the message event, and if the activation string is in the text,
          simulate a coin flip and send the result.
          """
      
          # Get the event data from the payload
          event = payload.get("event", {})
      
          # Get the text from the event that came through
          text = event.get("text")
      
          # Check and see if the activation phrase was in the text of the message.
          # If so, execute the code to flip a coin.
          if "hey sammy, flip a coin" in text.lower():
              # Since the activation phrase was met, get the channel ID that the event
              # was executed on
              channel_id = event.get("channel")
      
              # Execute the flip_coin function and send the results of
              # flipping a coin to the channel
              return flip_coin(channel_id)
      

      Finally, create a main section that will create a logger so you can see the internals of your application as well as launch the app on your external IP address on port 3000. In order to ingest the events from Slack, such as when a new message is sent, you must test your application on a public-facing IP address.

      Append the following lines to app.py to set up your main section:

      app.py

      if __name__ == "__main__":
          # Create the logging object
          logger = logging.getLogger()
      
          # Set the log level to DEBUG. This will increase verbosity of logging messages
          logger.setLevel(logging.DEBUG)
      
          # Add the StreamHandler as a logging handler
          logger.addHandler(logging.StreamHandler())
      
          # Run your app on your externally facing IP address on port 3000 instead of
          # running it on localhost, which is traditional for development.
          app.run(host="0.0.0.0", port=3000)
      

      You are now finished with the Flask app and it is ready for testing. Before you move on verify that your finished file, app.py contains the following:

      app.py

      import os
      import logging
      from flask import Flask
      from slack import WebClient
      from slackeventsapi import SlackEventAdapter
      from coinbot import CoinBot
      
      # Initialize a Flask app to host the events adapter
      app = Flask(__name__)
      # Create an events adapter and register it to an endpoint in the slack app for event injestion.
      slack_events_adapter = SlackEventAdapter(os.environ.get("SLACK_EVENTS_TOKEN"), "/slack/events", app)
      
      # Initialize a Web API client
      slack_web_client = WebClient(token=os.environ.get("SLACK_TOKEN"))
      
      def flip_coin(channel):
          """Craft the CoinBot, flip the coin and send the message to the channel
          """
          # Create a new CoinBot
          coin_bot = CoinBot(channel)
      
          # Get the onboarding message payload
          message = coin_bot.get_message_payload()
      
          # Post the onboarding message in Slack
          slack_web_client.chat_postMessage(**message)
      
      
      # When a 'message' event is detected by the events adapter, forward that payload
      # to this function.
      @slack_events_adapter.on("message")
      def message(payload):
          """Parse the message event, and if the activation string is in the text, 
          simulate a coin flip and send the result.
          """
      
          # Get the event data from the payload
          event = payload.get("event", {})
      
          # Get the text from the event that came through
          text = event.get("text")
      
          # Check and see if the activation phrase was in the text of the message.
          # If so, execute the code to flip a coin.
          if "hey sammy, flip a coin" in text.lower():
              # Since the activation phrase was met, get the channel ID that the event
              # was executed on
              channel_id = event.get("channel")
      
              # Execute the flip_coin function and send the results of
              # flipping a coin to the channel
              return flip_coin(channel_id)
      
      if __name__ == "__main__":
          # Create the logging object
          logger = logging.getLogger()
      
          # Set the log level to DEBUG. This will increase verbosity of logging messages
          logger.setLevel(logging.DEBUG)
      
          # Add the StreamHandler as a logging handler
          logger.addHandler(logging.StreamHandler())
      
          # Run our app on our externally facing IP address on port 3000 instead of
          # running it on localhost, which is traditional for development.
          app.run(host="0.0.0.0", port=3000)
      

      Save and close the file.

      Now that your Flask app is ready to serve your application let’s test it out.

      Step 6 — Running Your Flask App

      Finally, bring everything together and execute your app.

      First, add your running application as an authorized handler for your Slackbot.

      Navigate to the Basic Information section of your app in the Slack UI. Scroll down until you find the App Credentials section.

      Slack Signing Secret

      Copy the Signing Secret and export it as the environment variable SLACK_EVENTS_TOKEN:

      • export SLACK_EVENTS_TOKEN="MY_SIGNING_SECRET_TOKEN"

      With this you have all the necessary API tokens to run your app. Refer to Step 1 if you need a refresher on how to export your SLACK_TOKEN. Now you can start your app and verify that it is indeed running. Ensure that your virtual environment is activated and run the following command to start your Flask app:

      You will see an output like this:

      (slackbot) [20:04:03] sammy:coinbot$ python app.py
       * Serving Flask app "app" (lazy loading)
       * Environment: production
         WARNING: This is a development server. Do not use it in a production deployment.
         Use a production WSGI server instead.
       * Debug mode: off
       * Running on http://0.0.0.0:3000/ (Press CTRL+C to quit)
      

      To verify that your app is up, open a new terminal window and curl the IP address of your server with the correct port at /slack/events:

      • curl http://YOUR_IP_ADDRESS:3000/slack/events

      curl will return the following:

      Output

      These are not the slackbots you're looking for.

      Receiving the message These are not the slackbots you're looking for., indicates that your app is up and running.

      Now leave this Flask application running while you finish configuring your app in the Slack UI.

      First grant your app the appropriate permissions so that it can listen to messages and respond accordingly. Click on Event Subscriptions in the UI sidebar and toggle the Enable Events radio button.

      Enable Events Button

      Once you’ve done that, type in your IP address, port, and /slack/events endpoint into the Request URL field. Don’t forget the HTTP protocol prefix. Slack will make an attempt to connect to your endpoint. Once it has successfully done so you’ll see a green check mark with the word Verified next to it.

      Event Subscriptions Request URL

      Next, expand the Subscribe to bot events and add the message.channels permission to your app. This will allow your app to receive messages from your channel and process them.

      Subscribe to bot events permissions

      Once you’ve done this you will see the event listed in your Subscribe to bot events section. Next click the green Save Changes button in the bottom right hand corner.

      Confirm and Save changes

      Once you do this you’ll see a yellow banner across the top of the screen informing you that you need to reinstall your app for the following changes to apply. Every time you change permissions you’ll need to reinstall your app. Click on the reinstall your app link in this banner to reinstall your app.

      Reinstall your app banner

      You’ll be presented with a confirmation screen summarizing the permissions your bot will have and asking if you want to allow its installation. Click on the green Allow button to finish the installation process.

      Reinstall confirmation

      Now that you’ve done this your app should be ready. Go back to the channel that you installed CoinBot into and send a message containing the phrase Hey Sammy, Flip a coin in it. Your bot will flip a coin and reply with the results. Congrats! You’ve created a Slackbot!

      Hey Sammy, Flip a coin

      Conclusion

      Once you are done developing your application and you are ready to move it to production, you’ll need to deploy it to a server. This is necessary because the Flask development server is not a secure production environment. You’ll be better served if you deploy your app using a WSGI and maybe even securing a domain name and giving your server a DNS record. There are many options for deploying Flask applications, some of which are listed below:

      There are many more ways to deploy your application than just these. As always, when it comes to deployments and infrastucture, do what works best for you.

      In any case, you now have a Slackbot that you can use to flip a coin to help you make decisions, like what to eat for lunch.

      You can also take this base code and modify it to fit your needs, whether it be automated support, resource management, pictures of cats, or whatever you can think of. You can view the complete Python Slack API docs here.



      Source link

      How To Build a Node.js Application with Docker on Ubuntu 20.04


      Introduction

      The Docker platform allows developers to package and run applications as containers. A container is an isolated process that runs on a shared operating system, offering a lighter weight alternative to virtual machines. Though containers are not new, they offer benefits — including process isolation and environment standardization — that are growing in importance as more developers use distributed application architectures.

      When building and scaling an application with Docker, the starting point is typically creating an image for your application, which you can then run in a container. The image includes your application code, libraries, configuration files, environment variables, and runtime. Using an image ensures that the environment in your container is standardized and contains only what is necessary to build and run your application.

      In this tutorial, you will create an application image for a static website that uses the Express framework and Bootstrap. You will then build a container using that image and push it to Docker Hub for future use. Finally, you will pull the stored image from your Docker Hub repository and build another container, demonstrating how you can recreate and scale your application.

      Prerequisites

      To follow this tutorial, you will need:

      Step 1 — Installing Your Application Dependencies

      To create your image, you will first need to make your application files, which you can then copy to your container. These files will include your application’s static content, code, and dependencies.

      First, create a directory for your project in your non-root user’s home directory. We will call ours node_project, but you should feel free to replace this with something else:

      Navigate to this directory:

      This will be the root directory of the project.

      Next, create a package.json file with your project’s dependencies and other identifying information. Open the file with nano or your favorite editor:

      Add the following information about the project, including its name, author, license, entrypoint, and dependencies. Be sure to replace the author information with your own name and contact details:

      ~/node_project/package.json

      {
        "name": "nodejs-image-demo",
        "version": "1.0.0",
        "description": "nodejs image demo",
        "author": "Sammy the Shark <sammy@example.com>",
        "license": "MIT",
        "main": "app.js",
        "keywords": [
          "nodejs",
          "bootstrap",
          "express"
        ],
        "dependencies": {
          "express": "^4.16.4"
        }
      }
      

      This file includes the project name, author, and license under which it is being shared. Npm recommends making your project name short and descriptive, and avoiding duplicates in the npm registry. We’ve listed the MIT license in the license field, permitting the free use and distribution of the application code.

      Additionally, the file specifies:

      • "main": The entrypoint for the application, app.js. You will create this file next.
      • "dependencies": The project dependencies — in this case, Express 4.16.4 or above.

      Though this file does not list a repository, you can add one by following these guidelines on adding a repository to your package.json file. This is a good addition if you are versioning your application.

      Save and close the file when you’ve finished making changes.

      To install your project’s dependencies, run the following command:

      This will install the packages you’ve listed in your package.json file in your project directory.

      We can now move on to building the application files.

      Step 2 — Creating the Application Files

      We will create a website that offers users information about sharks. Our application will have a main entrypoint, app.js, and a views directory that will include the project’s static assets. The landing page, index.html, will offer users some preliminary information and a link to a page with more detailed shark information, sharks.html. In the views directory, we will create both the landing page and sharks.html.

      First, open app.js in the main project directory to define the project’s routes:

      The first part of the file will create the Express application and Router objects, and define the base directory and port as constants:

      ~/node_project/app.js

      const express = require('express');
      const app = express();
      const router = express.Router();
      
      const path = __dirname + '/views/';
      const port = 8080;
      

      The require function loads the express module, which we then use to create the app and router objects. The router object will perform the routing function of the application, and as we define HTTP method routes we will add them to this object to define how our application will handle requests.

      This section of the file also sets a couple of constants, path and port:

      • path: Defines the base directory, which will be the views subdirectory within the current project directory.
      • port: Tells the app to listen on and bind to port 8080.

      Next, set the routes for the application using the router object:

      ~/node_project/app.js

      ...
      
      router.use(function (req,res,next) {
        console.log("https://www.digitalocean.com/" + req.method);
        next();
      });
      
      router.get("https://www.digitalocean.com/", function(req,res){
        res.sendFile(path + 'index.html');
      });
      
      router.get("https://www.digitalocean.com/sharks", function(req,res){
        res.sendFile(path + 'sharks.html');
      });
      

      The router.use function loads a middleware function that will log the router’s requests and pass them on to the application’s routes. These are defined in the subsequent functions, which specify that a GET request to the base project URL should return the index.html page, while a GET request to the /sharks route should return sharks.html.

      Finally, mount the router middleware and the application’s static assets and tell the app to listen on port 8080:

      ~/node_project/app.js

      ...
      
      app.use(express.static(path));
      app.use("https://www.digitalocean.com/", router);
      
      app.listen(port, function () {
        console.log('Example app listening on port 8080!')
      })
      

      The finished app.js file will look like this:

      ~/node_project/app.js

      const express = require('express');
      const app = express();
      const router = express.Router();
      
      const path = __dirname + '/views/';
      const port = 8080;
      
      router.use(function (req,res,next) {
        console.log("https://www.digitalocean.com/" + req.method);
        next();
      });
      
      router.get("https://www.digitalocean.com/", function(req,res){
        res.sendFile(path + 'index.html');
      });
      
      router.get("https://www.digitalocean.com/sharks", function(req,res){
        res.sendFile(path + 'sharks.html');
      });
      
      app.use(express.static(path));
      app.use("https://www.digitalocean.com/", router);
      
      app.listen(port, function () {
        console.log('Example app listening on port 8080!')
      })
      

      Save and close the file when you are finished.

      Next, let’s add some static content to the application. Start by creating the views directory:

      Open the landing page file, index.html:

      Add the following code to the file, which will import Boostrap and create a jumbotron component with a link to the more detailed sharks.html info page:

      ~/node_project/views/index.html

      <!DOCTYPE html>
      <html lang="en">
      
      <head>
          <title>About Sharks</title>
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
          <link href="https://www.digitalocean.com/css/styles.css" rel="stylesheet">
          <link href="https://fonts.googleapis.com/css?family=Merriweather:400,700" rel="stylesheet" type="text/css">
      </head>
      
      <body>
          <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md">
              <div class="container">
                  <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span>
                  </button> <a class="navbar-brand" href="#">Everything Sharks</a>
                  <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                      <ul class="nav navbar-nav mr-auto">
                          <li class="active nav-item"><a href="https://www.digitalocean.com/" class="nav-link">Home</a>
                          </li>
                          <li class="nav-item"><a href="https://www.digitalocean.com/sharks" class="nav-link">Sharks</a>
                          </li>
                      </ul>
                  </div>
              </div>
          </nav>
          <div class="jumbotron">
              <div class="container">
                  <h1>Want to Learn About Sharks?</h1>
                  <p>Are you ready to learn about sharks?</p>
                  <br>
                  <p><a class="btn btn-primary btn-lg" href="https://www.digitalocean.com/sharks" role="button">Get Shark Info</a>
                  </p>
              </div>
          </div>
          <div class="container">
              <div class="row">
                  <div class="col-lg-6">
                      <h3>Not all sharks are alike</h3>
                      <p>Though some are dangerous, sharks generally do not attack humans. Out of the 500 species known to researchers, only 30 have been known to attack humans.
                      </p>
                  </div>
                  <div class="col-lg-6">
                      <h3>Sharks are ancient</h3>
                      <p>There is evidence to suggest that sharks lived up to 400 million years ago.
                      </p>
                  </div>
              </div>
          </div>
      </body>
      
      </html>
      

      The top-level navbar here allows users to toggle between the Home and Sharks pages. In the navbar-nav subcomponent, we are using Bootstrap’s active class to indicate the current page to the user. We’ve also specified the routes to our static pages, which match the routes we defined in app.js:

      ~/node_project/views/index.html

      ...
      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
         <ul class="nav navbar-nav mr-auto">
            <li class="active nav-item"><a href="https://www.digitalocean.com/" class="nav-link">Home</a>
            </li>
            <li class="nav-item"><a href="https://www.digitalocean.com/sharks" class="nav-link">Sharks</a>
            </li>
         </ul>
      </div>
      ...
      

      Additionally, we’ve created a link to our shark information page in our jumbotron’s button:

      ~/node_project/views/index.html

      ...
      <div class="jumbotron">
         <div class="container">
            <h1>Want to Learn About Sharks?</h1>
            <p>Are you ready to learn about sharks?</p>
            <br>
            <p><a class="btn btn-primary btn-lg" href="https://www.digitalocean.com/sharks" role="button">Get Shark Info</a>
            </p>
         </div>
      </div>
      ...
      

      There is also a link to a custom style sheet in the header:

      ~/node_project/views/index.html

      ...
      <link href="https://www.digitalocean.com/css/styles.css" rel="stylesheet">
      ...
      

      We will create this style sheet at the end of this step.

      Save and close the file when you are finished.

      With the application landing page in place, we can create our shark information page, sharks.html, which will offer interested users more information about sharks.

      Open the file:

      Add the following code, which imports Bootstrap and the custom style sheet and offers users detailed information about certain sharks:

      ~/node_project/views/sharks.html

      <!DOCTYPE html>
      <html lang="en">
      
      <head>
          <title>About Sharks</title>
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
          <link href="https://www.digitalocean.com/css/styles.css" rel="stylesheet">
          <link href="https://fonts.googleapis.com/css?family=Merriweather:400,700" rel="stylesheet" type="text/css">
      </head>
      <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md">
          <div class="container">
              <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span>
              </button> <a class="navbar-brand" href="https://www.digitalocean.com/">Everything Sharks</a>
              <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                  <ul class="nav navbar-nav mr-auto">
                      <li class="nav-item"><a href="https://www.digitalocean.com/" class="nav-link">Home</a>
                      </li>
                      <li class="active nav-item"><a href="https://www.digitalocean.com/sharks" class="nav-link">Sharks</a>
                      </li>
                  </ul>
              </div>
          </div>
      </nav>
      <div class="jumbotron text-center">
          <h1>Shark Info</h1>
      </div>
      <div class="container">
          <div class="row">
              <div class="col-lg-6">
                  <p>
                      <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.
                      </div>
                      <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
                  </p>
              </div>
              <div class="col-lg-6">
                  <p>
                      <div class="caption">Other sharks are known to be friendly and welcoming!</div>
                      <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
                  </p>
              </div>
          </div>
      </div>
      
      </html>
      

      Note that in this file, we again use the active class to indicate the current page.

      Save and close the file when you are finished.

      Finally, create the custom CSS style sheet that you’ve linked to in index.html and sharks.html by first creating a css folder in the views directory:

      Open the style sheet:

      • nano views/css/styles.css

      Add the following code, which will set the desired color and font for our pages:

      ~/node_project/views/css/styles.css

      .navbar {
          margin-bottom: 0;
      }
      
      body {
          background: #020A1B;
          color: #ffffff;
          font-family: 'Merriweather', sans-serif;
      }
      
      h1,
      h2 {
          font-weight: bold;
      }
      
      p {
          font-size: 16px;
          color: #ffffff;
      }
      
      .jumbotron {
          background: #0048CD;
          color: white;
          text-align: center;
      }
      
      .jumbotron p {
          color: white;
          font-size: 26px;
      }
      
      .btn-primary {
          color: #fff;
          text-color: #000000;
          border-color: white;
          margin-bottom: 5px;
      }
      
      img,
      video,
      audio {
          margin-top: 20px;
          max-width: 80%;
      }
      
      div.caption: {
          float: left;
          clear: both;
      }
      

      In addition to setting font and color, this file also limits the size of the images by specifying a max-width of 80%. This will prevent them from taking up more room than we would like on the page.

      Save and close the file when you are finished.

      With the application files in place and the project dependencies installed, you are ready to start the application.

      If you followed the initial server setup tutorial in the prerequisites, you will have an active firewall permitting only SSH traffic. To permit traffic to port 8080 run:

      To start the application, make sure that you are in your project’s root directory:

      Start the application with node app.js:

      Navigate your browser to http://your_server_ip:8080. You will load the following landing page:

      Application Landing Page

      Click on the Get Shark Info button. The following information page will load:

      Shark Info Page

      You now have an application up and running. When you are ready, quit the server by typing CTRL+C. We can now move on to creating the Dockerfile that will allow us to recreate and scale this application as desired.

      Step 3 — Writing the Dockerfile

      Your Dockerfile specifies what will be included in your application container when it is executed. Using a Dockerfile allows you to define your container environment and avoid discrepancies with dependencies or runtime versions.

      Following these guidelines on building optimized containers, we will make our image as efficient as possible by minimizing the number of image layers and restricting the image’s function to a single purpose — recreating our application files and static content.

      In your project’s root directory, create the Dockerfile:

      Docker images are created using a succession of layered images that build on one another. Our first step will be to add the base image for our application that will form the starting point of the application build.

      Let’s use the node:10-alpine image, since at the time of writing this is the recommended LTS version of Node.js. The alpine image is derived from the Alpine Linux project, and will help us keep our image size down. For more information about whether or not the alpine image is the right choice for your project, please review the full discussion under the Image Variants section of the Docker Hub Node image page.

      Add the following FROM instruction to set the application’s base image:

      ~/node_project/Dockerfile

      FROM node:10-alpine
      

      This image includes Node.js and npm. Each Dockerfile must begin with a FROM instruction.

      By default, the Docker Node image includes a non-root node user that you can use to avoid running your application container as root. It is a recommended security practice to avoid running containers as root and to restrict capabilities within the container to only those required to run its processes. We will therefore use the node user’s home directory as the working directory for our application and set them as our user inside the container. For more information about best practices when working with the Docker Node image, check out this best practices guide.

      To fine-tune the permissions on our application code in the container, let’s create the node_modules subdirectory in /home/node along with the app directory. Creating these directories will ensure that they have the permissions we want, which will be important when we create local node modules in the container with npm install. In addition to creating these directories, we will set ownership on them to our node user:

      ~/node_project/Dockerfile

      ...
      RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
      

      For more information on the utility of consolidating RUN instructions, read through this discussion of how to manage container layers.

      Next, set the working directory of the application to /home/node/app:

      ~/node_project/Dockerfile

      ...
      WORKDIR /home/node/app
      

      If a WORKDIR isn’t set, Docker will create one by default, so it’s a good idea to set it explicitly.

      Next, copy the package.json and package-lock.json (for npm 5+) files:

      ~/node_project/Dockerfile

      ...
      COPY package*.json ./
      

      Adding this COPY instruction before running npm install or copying the application code allows us to take advantage of Docker’s caching mechanism. At each stage in the build, Docker will check whether it has a layer cached for that particular instruction. If we change package.json, this layer will be rebuilt, but if we don’t, this instruction will allow Docker to use the existing image layer and skip reinstalling our node modules.

      To ensure that all of the application files are owned by the non-root node user, including the contents of the node_modules directory, switch the user to node before running npm install:

      ~/node_project/Dockerfile

      ...
      USER node
      

      After copying the project dependencies and switching our user, we can run npm install:

      ~/node_project/Dockerfile

      ...
      RUN npm install
      

      Next, copy your application code with the appropriate permissions to the application directory on the container:

      ~/node_project/Dockerfile

      ...
      COPY --chown=node:node . .
      

      This will ensure that the application files are owned by the non-root node user.

      Finally, expose port 8080 on the container and start the application:

      ~/node_project/Dockerfile

      ...
      EXPOSE 8080
      
      CMD [ "node", "app.js" ]
      

      EXPOSE does not publish the port, but instead functions as a way of documenting which ports on the container will be published at runtime. CMD runs the command to start the application — in this case, node app.js. Note that there should only be one CMD instruction in each Dockerfile. If you include more than one, only the last will take effect.

      There are many things you can do with the Dockerfile. For a complete list of instructions, please refer to Docker’s Dockerfile reference documentation.

      The complete Dockerfile looks like this:

      ~/node_project/Dockerfile

      
      FROM node:10-alpine
      
      RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
      
      WORKDIR /home/node/app
      
      COPY package*.json ./
      
      USER node
      
      RUN npm install
      
      COPY --chown=node:node . .
      
      EXPOSE 8080
      
      CMD [ "node", "app.js" ]
      

      Save and close the file when you are finished editing.

      Before building the application image, let’s add a .dockerignore file. Working in a similar way to a .gitignore file, .dockerignore specifies which files and directories in your project directory should not be copied over to your container.

      Open the .dockerignore file:

      Inside the file, add your local node modules, npm logs, Dockerfile, and .dockerignore file:

      ~/node_project/.dockerignore

      node_modules
      npm-debug.log
      Dockerfile
      .dockerignore
      

      If you are working with Git then you will also want to add your .git directory and .gitignore file.

      Save and close the file when you are finished.

      You are now ready to build the application image using the docker build command. Using the -t flag with docker build will allow you to tag the image with a memorable name. Because we are going to push the image to Docker Hub, let’s include our Docker Hub username in the tag. We will tag the image as nodejs-image-demo, but feel free to replace this with a name of your own choosing. Remember to also replace your_dockerhub_username with your own Docker Hub username:

      • sudo docker build -t your_dockerhub_username/nodejs-image-demo .

      The . specifies that the build context is the current directory.

      It will take a minute or two to build the image. Once it is complete, check your images:

      You will receive the following output:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 8 seconds ago 73MB node 10-alpine f09e7c96b6de 3 weeks ago 70.7MB

      It is now possible to create a container with this image using docker run. We will include three flags with this command:

      • -p: This publishes the port on the container and maps it to a port on our host. We will use port 80 on the host, but you should feel free to modify this as necessary if you have another process running on that port. For more information about how this works, review this discussion in the Docker docs on port binding.
      • -d: This runs the container in the background.
      • --name: This allows us to give the container a memorable name.

      Run the following command to build the container:

      • sudo docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo

      Once your container is up and running, you can inspect a list of your running containers with docker ps:

      You will receive the following output:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 your_dockerhub_username/nodejs-image-demo "node app.js" 8 seconds ago Up 7 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo

      With your container running, you can now visit your application by navigating your browser to your server IP without the port:

      http://your_server_ip
      

      Your application landing page will load once again.

      Application Landing Page

      Now that you have created an image for your application, you can push it to Docker Hub for future use.

      Step 4 — Using a Repository to Work with Images

      By pushing your application image to a registry like Docker Hub, you make it available for subsequent use as you build and scale your containers. We will demonstrate how this works by pushing the application image to a repository and then using the image to recreate our container.

      The first step to pushing the image is to log in to the Docker Hub account you created in the prerequisites:

      • sudo docker login -u your_dockerhub_username

      When prompted, enter your Docker Hub account password. Logging in this way will create a ~/.docker/config.json file in your user’s home directory with your Docker Hub credentials.

      You can now push the application image to Docker Hub using the tag you created earlier, your_dockerhub_username/nodejs-image-demo:

      • sudo docker push your_dockerhub_username/nodejs-image-demo

      Let’s test the utility of the image registry by destroying our current application container and image and rebuilding them with the image in our repository.

      First, list your running containers:

      You will get the following output:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 your_dockerhub_username/nodejs-image-demo "node app.js" 3 minutes ago Up 3 minutes 0.0.0.0:80->8080/tcp nodejs-image-demo

      Using the CONTAINER ID listed in your output, stop the running application container. Be sure to replace the highlighted ID below with your own CONTAINER ID:

      • sudo docker stop e50ad27074a7

      List your all of your images with the -a flag:

      You will receive the following output with the name of your image, your_dockerhub_username/nodejs-image-demo, along with the node image and the other images from your build:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 7 minutes ago 73MB <none> <none> 2e3267d9ac02 4 minutes ago 72.9MB <none> <none> 8352b41730b9 4 minutes ago 73MB <none> <none> 5d58b92823cb 4 minutes ago 73MB <none> <none> 3f1e35d7062a 4 minutes ago 73MB <none> <none> 02176311e4d0 4 minutes ago 73MB <none> <none> 8e84b33edcda 4 minutes ago 70.7MB <none> <none> 6a5ed70f86f2 4 minutes ago 70.7MB <none> <none> 776b2637d3c1 4 minutes ago 70.7MB node 10-alpine f09e7c96b6de 3 weeks ago 70.7MB

      Remove the stopped container and all of the images, including unused or dangling images, with the following command:

      Type y when prompted in the output to confirm that you would like to remove the stopped container and images. Be advised that this will also remove your build cache.

      You have now removed both the container running your application image and the image itself. For more information on removing Docker containers, images, and volumes, please review How To Remove Docker Images, Containers, and Volumes.

      With all of your images and containers deleted, you can now pull the application image from Docker Hub:

      • docker pull your_dockerhub_username/nodejs-image-demo

      List your images once again:

      Your output will have your application image:

      Output

      REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 11 minutes ago 73MB

      You can now rebuild your container using the command from Step 3:

      • docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo

      List your running containers:

      Output

      CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f6bc2f50dff6 your_dockerhub_username/nodejs-image-demo "node app.js" 4 seconds ago Up 3 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo

      Visit http://your_server_ip once again to view your running application.

      Conclusion

      In this tutorial you created a static web application with Express and Bootstrap, as well as a Docker image for this application. You used this image to create a container and pushed the image to Docker Hub. From there, you were able to destroy your image and container and recreate them using your Docker Hub repository.

      If you are interested in learning more about how to work with tools like Docker Compose and Docker Machine to create multi-container setups, you can look at the following guides:

      For general tips on working with container data, check out:

      If you are interested in other Docker-related topics, please find our complete library of Docker tutorials.



      Source link