One place for hosting & domains

      Nodejs

      How To Work With Zip Files in Node.js


      The author selected Open Sourcing Mental Illness to receive a donation as part of the Write for DOnations program.

      Introduction

      Working with files is one of the common tasks among developers. As your files grow in size, they start taking significant space on your hard drive. Sooner or later you may need to transfer the files to other servers or upload multiple files from your local machine to different platforms. Some of these platforms have file size limits, and won’t accept large files. To get around this, you can group the files into a single ZIP file. A ZIP file is an archive format that packs and compresses files with the lossless compression algorithm. The algorithm can reconstruct the data without any data loss. In Node.js, you can use the adm-zip module to create and read ZIP archives.

      In this tutorial, you will use adm-zip module to compress, read, and decompress files. First, you’ll combine multiple files into a ZIP archive using adm-zip. You’ll then list the ZIP archive contents. After that, you’ll add a file to an existing ZIP archive, and then finally, you’ll extract a ZIP archive into a directory.

      Prerequisites

      To follow this tutorial, you’ll need:

      Step 1 — Setting Up the Project

      In this step, you’ll create the directory for your project and install adm-zip as a dependency. This directory is where you’ll keep your program files. You’ll also create another directory containing text files and an image. You’ll archive this directory in the next section.

      Create a directory called zip_app with the following command:

      Navigate into the newly created directory with the cd command:

      Inside the directory, create a package.json file to manage the project dependencies:

      The -y option creates a default package.json file.

      Next, install adm-zip with the npm install command:

      After you run the command, npm will install adm-zip and update the package.json file.

      Next, create a directory called test and move into it:

      In this directory, you will create three text files and download an image. The three files will be filled with dummy content to make their file sizes larger. This will help to demonstrate ZIP compression when you archive this directory.

      Create the file1.txt and fill it with dummy content using the following command:

      • yes "dummy content" | head -n 100000 > file1.txt

      The yes command logs the string dummy content repeatedly. Using the pipe command |, you send the output from the yes command to be used as input for the head command. The head command prints part of the given input into the standard output. The -n option specifies the number of lines that should be written to the standard output. Finally, you redirect the head output to a new file file1.txt using >.

      Create a second file with the string “dummy content” repeated 300,000 lines:

      • yes "dummy content" | head -n 300000 > file2.txt

      Create another file with the dummy content string repeated 600,000 lines:

      • yes "dummy content" | head -n 600000 > file3.txt

      Finally, download an image into the directory using curl:

      • curl -O https://assets.digitalocean.com/how-to-process-images-in-node-js-with-sharp/underwater.png

      Move back into the main project directory with the following command:

      The .. will move you to the parent directory, which is zip_app.

      You’ve now created the project directory, installed adm-zip, and created a directory with files for archiving. In the next step, you’ll archive a directory using the adm-zip module.

      Step 2 — Creating a ZIP Archive

      In this step, you’ll use adm-zip to compress and archive the directory you created in the previous section.

      To archive the directory, you’ll import the adm-zip module and use the module’s addLocalFolder() method to add the directory to the adm-zip module’s ZIP object. Afterward, you’ll use the module’s writeZip() method to save the archive in your local system.

      Create and open a new file createArchive.js in your preferred text editor. This tutorial uses nano, a command-line text editor:

      Next, require in the adm-zip module in your createArchive.js file:

      zip_app/createArchive.js

      const AdmZip = require("adm-zip");
      

      The adm-zip module provides a class that contains methods for creating ZIP archives.

      Since it’s common to encounter large files during the archiving process, you might end up blocking the main thread until the ZIP archive is saved. To write non-blocking code, you’ll define an asynchronous function to create and save a ZIP archive.

      In your createArchive.js file, add the following highlighted code:

      zip_app/createArchive.js

      
      const AdmZip = require("adm-zip");
      
      async function createZipArchive() {
        const zip = new AdmZip();
        const outputFile = "test.zip";
        zip.addLocalFolder("./test");
        zip.writeZip(outputFile);
        console.log(`Created ${outputFile} successfully`);
      }
      
      createZipArchive();
      

      createZipArchive is an asynchronous function that creates a ZIP archive from a given directory. What makes it asynchronous is the async keyword you defined before the function label. Within the function, you create an instance of the adm-zip module, which provides methods you can use for reading and creating archives. When you create an instance, adm-zip creates an in-memory ZIP where you can add files or directories.

      Next, you define the archive name and store it in the outputDir variable. To add the test directory to the in-memory archive, you invoke the addLocalFolder() method from adm-zip with the directory path as an argument.

      After the directory is added, you invoke the writeZip() method from adm-zip with a variable containing the name of the ZIP archive. The writeZip() method saves the archive to your local disk.

      Once that’s done, you invoke console.log() to log that the ZIP file has been created successfully.

      Finally, you call the createZipArchive() function.

      Before you run the file, wrap the code in a try…catch block to handle runtime errors:

      zip_app/createArchive.js

      const AdmZip = require("adm-zip");
      
      async function createZipArchive() {
        try {
          const zip = new AdmZip();
          const outputFile = "test.zip";
          zip.addLocalFolder("./test");
          zip.writeZip(outputFile);
          console.log(`Created ${outputFile} successfully`);
        } catch (e) {
          console.log(`Something went wrong. ${e}`);
        }
      }
      
      createZipArchive();
      

      Within the try block, the code will attempt to create a ZIP archive. If successful, the createZipArchive() function will exit, skipping the catch block. If creating a ZIP archive triggers an error, execution will skip to the catch block and log the error in the console.

      Save and exit the file in nano with CTRL+X. Enter y to save the changes, and confirm the file by pressing ENTER on Windows, or the RETURN key on the Mac.

      Run the createArchive.js file using the node command:

      You’ll receive the following output:

      Output

      Created test.zip successfully

      List the directory contents to see if the ZIP archive has been created:

      You’ll receive the following output showing the archive among the contents:

      Output

      createArchive.js node_modules package-lock.json package.json test test.zip

      With the confirmation that the ZIP archive has been created, you’ll compare the ZIP archive, and the test directory file size to see if the compression works.

      Check the test directory size using the du command:

      The -h flag instructs du to show the directory size in a human-readable format.

      After running the command, you will receive the following output:

      Output

      15M test

      Next, check the test.zip archive file size:

      The du command logs the following output:

      Output

      760K test.zip

      As you can see, creating the ZIP file has dropped the directory size from 15 Megabytes(MB) to 760 Kilobytes(KB), which is a huge difference. The ZIP file is more portable and smaller in size.

      Now that you created a ZIP archive, you’re ready to list the contents in a ZIP file.

      Step 3 — Listing Files in a ZIP Archive

      In this step, you’ll read and list all files in a ZIP archive using adm-zip. To do that, you’ll instantiate the adm-zip module with your ZIP archive path. You’ll then call the module’s getEntries() method which returns an array of objects. Each object holds important information about an item in the ZIP archive. To list the files, you’ll iterate over the array and access the filename from the object, and log it in the console.

      Create and open readArchive.js in your favorite text editor:

      In your readArchive.js, add the following code to read and list contents in a ZIP archive:

      zip_app/readArchive.js

      const AdmZip = require("adm-zip");
      
      async function readZipArchive(filepath) {
        try {
          const zip = new AdmZip(filepath);
      
          for (const zipEntry of zip.getEntries()) {
            console.log(zipEntry.toString());
          }
        } catch (e) {
          console.log(`Something went wrong. ${e}`);
        }
      }
      
      readZipArchive("./test.zip");
      

      First, you require in the adm-zip module.

      Next, you define the readZipArchive() function, which is an asynchronous function. Within the function, you create an instance of adm-zip with the path of the ZIP file you want to read. The file path is provided by the filepath parameter. adm-zip will read the file and parse it.

      After reading the archive, you define a for....of statement that iterates over objects in an array that the getEntries() method from adm-zip returns when invoked. On each iteration, the object is assigned to the zipEntry variable. Inside the loop, you convert the object into a string that represents the object using the Node.js toString() method, then log it in the console using the console.log() method.

      Finally, you invoke the readZipArchive() function with the ZIP archive file path as an argument.

      Save and exit your file, then run the file with the following command:

      You will get output that resembles the following(edited for brevity):

      Output

      { "entryName": "file1.txt", "name": "file1.txt", "comment": "", "isDirectory": false, "header": { ... }, "compressedData": "<27547 bytes buffer>", "data": "<null>" } ...

      The console will log four objects. The other objects have been edited out to keep the tutorial brief.

      Each file in the archive is represented with an object similar to the one in the preceding output. To get the filename for each file, you need to access the name property.

      In your readArchive.js file, add the following highlighted code to access each filename:

      zip_app/readArchive.js

      const AdmZip = require("adm-zip");
      
      async function readZipArchive(filepath) {
        try {
          const zip = new AdmZip(filepath);
      
          for (const zipEntry of zip.getEntries()) {
            console.log(zipEntry.name);
          }
        } catch (e) {
          console.log(`Something went wrong. ${e}`);
        }
      }
      
      readZipArchive("./test.zip");
      

      Save and exit your text editor. Now, run the file again with the node command:

      Running the file results in the following output:

      Output

      file1.txt file2.txt file3.txt underwater.png

      The output now logs the filename of each file in the ZIP archive.

      You can now read and list each file in a ZIP archive. In the next section, you’ll add a file to an existing ZIP archive.

      Step 4 — Adding a File to an Existing Archive

      In this step, you’ll create a file and add it to the ZIP archive you created earlier without extracting it. First, you’ll read the ZIP archive by creating an adm-zip instance. Second, you’ll invoke the module’s addFile() method to add the file in the ZIP. Finally, you’ll save the ZIP archive in the local system.

      Create another file file4.txt with dummy content repeated 600,000 lines:

      • yes "dummy content" | head -n 600000 > file4.txt

      Create and open updateArchive.js in your text editor:

      Require in the adm-zip module and the fs module that allows you to work with files in your updateArchive.js file:

      const AdmZip = require("adm-zip");
      const fs = require("fs").promises;
      

      You require in the promise-based version of the fs module version, which allows you to write asynchronous code. When you invoke an fs method, it will return a promise.

      Next in your updateArchive.js file, add the following highlighted code to add a new file to the ZIP archive:

      zip_app/updateArchive.js

      const AdmZip = require("adm-zip");
      const fs = require("fs").promises;
      
      async function updateZipArchive(filepath) {
        try {
          const zip = new AdmZip(filepath);
      
          content = await fs.readFile("./file4.txt");
          zip.addFile("file4.txt", content);
          zip.writeZip(filepath);
          console.log(`Updated ${filepath} successfully`);
        } catch (e) {
          console.log(`Something went wrong. ${e}`);
        }
      }
      
      updateZipArchive("./test.zip");
      

      updateZipArchive is an asynchronous function that reads a file in the filesystem and adds it to an existing ZIP. In the function, you create an instance of adm-zip with the ZIP archive file path in the filepath as a parameter. Next, you invoke the fs module’s readFile() method to read the file in the file system. The readFile() method returns a promise, which you resolve with the await keyword (await is valid in only asynchronous functions). Once resolved, the method returns a buffer object, which contains the file contents.

      Next, you invoke the addFile() method from adm-zip. The method takes two arguments. The first argument is the filename you want to add to the archive, and the second argument is the buffer object containing the contents of the file that the readFile() method reads.

      Afterwards, you invoke adm-zip module’s writeZip() method to save and write new changes in the ZIP archive. Once that’s done, you call the console.log() method to log a success message.

      Finally, you invoke the updateZipArchive() function with the Zip archive file path as an argument.

      Save and exit your file. Run the updateArchive.js file with the following command:

      You’ll see output like this:

      Output

      Updated ./test.zip successfully

      Now, confirm that the ZIP archive contains the new file. Run the readArchive.js file to list the contents in the ZIP archive with the following command:

      You’ll receive the following output:

      file1.txt
      file2.txt
      file3.txt
      file4.txt
      underwater.png
      

      This confirms that the file has been added to the ZIP.

      Now that you can add a file to an existing archive, you’ll extract the archive in the next section.

      In this step, you’ll read and extract all contents in a ZIP archive into a directory. To extract a ZIP archive, you’ll instantiate adm-zip with the archive file path. After that, you’ll invoke the module’s extractAllTo() method with the directory name you want your extracted ZIP contents to reside.

      Create and open extractArchive.js in your text editor:

      Require in the adm-zip module and the path module in your extractArchive.js file:

      zip_app/extractArchive.js

      const AdmZip = require("adm-zip");
      const path = require("path");
      

      The path module provides helpful methods for dealing with file paths.

      Still in your extractArchive.js file, add the following highlighted code to extract an archive:

      zip_app/extractArchive.js

      const AdmZip = require("adm-zip");
      const path = require("path");
      
      async function extractArchive(filepath) {
        try {
          const zip = new AdmZip(filepath);
          const outputDir = `${path.parse(filepath).name}_extracted`;
          zip.extractAllTo(outputDir);
      
          console.log(`Extracted to "${outputDir}" successfully`);
        } catch (e) {
          console.log(`Something went wrong. ${e}`);
        }
      }
      
      extractArchive("./test.zip");
      

      extractArchive() is an asynchronous function that takes a parameter containing the file path of the ZIP archive. Within the function, you instantiate adm-zip with the ZIP archive file path provided by the filepath parameter.

      Next, you define a template literal. Inside the template literal placeholder (${}), you invoke the parse() method from the path module with the file path. The parse() method returns an object. To get the name of the ZIP file without the file extension, you append the name property to the object that the parse() method returns. Once the archive name is returned, the template literal interpolates the value with the _extracted string. The value is then stored in the outputDir variable. This will be the name of the extracted directory.

      Next, you invoke adm-zip module’s extractAllTo method with the directory name stored in the outputDir to extract the contents in the directory. After that, you invoke console.log() to log a success message.

      Finally, you call the extractArchive() function with the ZIP archive path.

      Save your file and exit the editor, then run the extractArchive.js file with the following command:

      You receive the following output:

      Output

      Extracted to "test_extracted" successfully

      Confirm that the directory containing the ZIP contents has been created:

      You will receive the following output:

      Output

      createArchive.js file4.txt package-lock.json readArchive.js test.zip updateArchive.js extractArchive.js node_modules package.json test test_extracted

      Now, navigate into the directory containing the extracted contents:

      List the contents in the directory:

      You will receive the following output:

      Output

      file1.txt file2.txt file3.txt file4.txt underwater.png

      You can now see that the directory has all the files that were in the original directory.

      You’ve now extracted the ZIP archive contents into a directory.

      Conclusion

      In this tutorial, you created a ZIP archive, listed its contents, added a new file to the archive, and extracted all of its content into a directory using adm-zip module. This will serve as a good foundation for working with ZIP archives in Node.js.

      To learn more about adm-zip module, view the adm-zip documentation. To continue building your Node.js knowledge, see How To Code in Node.js series



      Source link

      How To Use PostgreSQL With Node.js on Ubuntu 20.04


      The author selected Society of Women Engineers to receive a donation as part of the Write for DOnations program.

      Introduction

      The Node.js ecosystem provides a set of tools for interfacing with databases. One of those tools is node-postgres, which contains modules that allow Node.js to interface with the PostgreSQL database. Using node-postgres, you will be able to write Node.js programs that can access and store data in a PostgreSQL database.

      In this tutorial, you’ll use node-postgres to connect and query the PostgreSQL (Postgres in short) database. First, you’ll create a database user and the database in Postgres. You will then connect your application to the Postgres database using the node-postgres module. Afterwards, you will use node-postgres to insert, retrieve, and modify data in the PostgreSQL database.

      Prerequisites

      To complete this tutorial, you will need:

      Step 1 – Setting Up the Project Directory

      In this step, you will create the directory for the node application and install node-postgres using npm. This directory is where you will work on building your PostgreSQL database and configuration files to interact.

      Create the directory for your project using the mkdir command:

      Navigate into the newly created directory using the cd command:

      Initialize the directory with a package.json file using the npm init command:

      The -y flag creates a default package.json file.

      Next, install the node-postgres module with npm install:

      You’ve now set up the directory for your project and installed node-postgres as a dependency. You’re now ready to create a user and a database in Postgres.

      Step 2 — Creating A Database User and a Database in PostgreSQL

      In this step, you’ll create a database user and the database for your application.

      When you install Postgres on Ubuntu for the first time, it creates a user postgres on your system, a database user named postgres, and a database postgres. The user postgres allows you to open a PostgreSQL session where you can do administrative tasks such as creating users and databases.

      PostgreSQL uses ident authentication connection scheme which allows a user on Ubuntu to login to the Postgres shell as long as the username is similar to the Postgres user. Since you already have a postgres user on Ubuntu and a postgres user in PostgreSQL created on your behalf, you’ll be able to log in to the Postgres shell.

      To login, switch the Ubuntu user to postgres with sudo and login into the Postgres shell using the psql command:

      The command’s arguments represents:

      • -u: a flag that switches the user to the given user on Ubuntu. Passing postgres user as an argument will switch the user on Ubuntu to postgres.
      • psql: a Postgres interactive terminal program where you can enter SQL commands to create databases, roles, tables, and many more.

      Once you login into the Postgres shell, your terminal will look like the following:

      postgres is the name of the database you’ll be interacting with and the # denotes that you’re logged in as a superuser.

      For the Node application, you’ll create a separate user and database that the application will use to connect to Postgres.

      To do that, create a new role with a strong password:

      • CREATE USER fish_user WITH PASSWORD 'password';

      A role in Postgres can be considered as a user or group depending on your use case. In this tutorial, you’ll use it as a user.

      Next, create a database and assign ownership to the user you created:

      • CREATE DATABASE fish OWNER fish_user;

      Assigning the database ownership to fish_user grants the role privileges to create, drop, and insert data into the tables in the fish database.

      With the user and database created, exit out of the Postgres interactive shell:

      To login into the Postgres shell as fish_user, you need to create a user on Ubuntu with a name similar to the Postgres user you created.

      Create a user with the adduser command:

      You have now created a user on Ubuntu, a PostgreSQL user, and a database for your Node application. Next, you’ll log in to the PostgreSQL interactive shell using the fish_user and create a table.

      Step 3 — Opening A Postgres Shell With a Role and Creating a Table

      In this section, you’ll open the Postgres shell with the user you created in the previous section on Ubuntu. Once you login into the shell, you’ll create a table for the Node.js app.

      To open the shell as the fish_user, enter the following command:

      • sudo -u fish_user psql -d fish

      sudo -u fish_user switches your Ubuntu user to fish_user and then runs the psql command as that user. The -d flag specifies the database you want to connect to, which is fish in this case. If you don’t specify the database, psql will try to connect to fish_user database by default, which it won’t find and it will throw an error.

      Once you’re logged in the psql shell, your shell prompt will look like the following:

      fish denotes that you’re now connected to the fish database.

      You can verify the connection using the conninfo command:

      You will receive output similar to the following:

      Output

      You are connected to database "fish" as user "fish_user" via socket in "/var/run/postgresql" at port "5432".

      The output confirms that you have indeed logged in as a fish_user and you’re connected to the fish database.

      Next, you’ll create a table that will contain the data your application will insert.

      The table you’ll create will keep track of shark names and their colors. When populated with data, it will look like the following:

      id name color
      1 sammy blue
      2 jose teal

      Using the SQL create table command, create a table:

      • CREATE TABLE shark(
      • id SERIAL PRIMARY KEY,
      • name VARCHAR(50) NOT NULL,
      • color VARCHAR(50) NOT NULL);

      The CREATE TABLE shark command creates a table with 3 columns:

      • id: an auto-incrementing field and primary key for the table. Each time you insert a row, Postgres will increment and populate the id value.

      • name and color: fields that can store 50 characters. NOT NULL is a constraint that prevents the fields from being empty.

      Verify if the table has been created with the right owner:

      The dt command list all tables in the database.

      When you run the command, the output will resemble the following:

               List of relations
       Schema | Name  | Type  |   Owner
      --------+-------+-------+-----------
       public | shark | table | fish_user
      (1 row)
      
      

      The output confirms that the fish_user owns the shark table.

      Now exit out of the Postgres shell:

      It will take you back to the project directory.

      With the table created, you’ll use the node-postgres module to connect to Postgres.

      Step 4 — Connecting To a Postgres Database

      In this step, you’ll use node-postgres to connect your Node.js application to the PostgreSQL database. To do that, you’ll use node-postgres to create a connection pool. A connection pool functions as a cache for database connections allowing your app to reuse the connections for all the database requests. This can speed up your application and save your server resources.

      Create and open a db.js file in your preferred editor. In this tutorial, you’ll use nano, a terminal text editor:

      In your db.js file, require in the node-postgres module and use destructuring assignment to extract a class Pool from node-postgres.

      node_pg_app/db.js

      const { Pool } = require('pg')
      
      

      Next, create a Pool instance to create a connection pool:

      node_pg_app/db.js

      const { Pool} = require('pg')
      
      const pool = new Pool({
        user: 'fish_user',
        database: 'fish',
        password: 'password',
        port: 5432,
        host: 'localhost',
      })
      
      

      When you create the Pool instance, you pass a configuration object as an argument. This object contains the details node-postgres will use to establish a connection to Postgres.

      The object defines the following properties:

      • user: the user you created in Postgres.
      • database: the name of the database you created in Postgres.
      • password: the password for the user fish_user.
      • port: the port Postgres is listening on. 5432 is the default port.
      • host: the Postgres server you want node-postgres to connect to. Passing it localhost will connect the node-postgres to the Postgres server installed on your system. If your Postgres server resided on another droplet, your host would look like this:host: server_ip_address.

      Note: In production, it’s recommended to keep the configuration values in a different file, such as the .env file. This file is then added to the .gitignore file if using Git to avoid tracking it with version control. The advantage is that it hides sensitive information, such as your password, user, and database from attackers.

      Once you create the instance, the database connection is established and the Pool object is stored in the pool variable. To use this anywhere in your app, you will need to export it. In your db.js file, require in and define an instance of the Pool object, and set its properties and values:

      node_pg_app/db.js

      const { Pool } = require("pg");
      
      const pool = new Pool({
        user: "fish_user",
        database: "fish",
        password: "password",
        port: 5432,
        host: "localhost",
      });
      
      module.exports = { pool };
      
      

      Save the file and exit nano by pressing CTRL+X. Enter y to save the changes, and confirm your file name by pressing ENTER or RETURN key on Mac.

      Now that you’ve connected your application to Postgres, you’ll use this connection to insert data in Postgres.

      Step 5 — Inserting Data Into the Postgres Database

      In this step, you’ll create a program that adds data into the PostgreSQL database using the connection pool you created in the db.js file. To ensure that the program inserts different data each time it runs, you’ll give it functionality to accept command-line arguments. When running the program, you’ll pass it the name and color of the shark.

      Create and open insertData.js file in your editor:

      In your insertData.js file, add the following code to make the script process command-line arguments:

      node_pg_app/insertData.js

      const { pool } = require("./db");
      
      async function insertData() {
        const [name, color] = process.argv.slice(2);
        console.log(name, color);
      }
      
      insertData();
      

      First, you require in the pool object from the db.js file. This allows your program to use the database connection to query the database.

      Next, you declare the insertData() function as an asynchronous function with the async keyword. This lets you use the await keyword to make database requests asynchronous.

      Within the insertData() function, you use the process module to access the command-line arguments. The Node.js process.argv method returns all arguments in an array including the node and insertData.js arguments.

      For example, when you run the script on the terminal with node insertData.js sammy blue, the process.argv method will return an array: ['node', 'insertData.js', 'sammy', 'blue'] (the array has been edited for brevity).

      To skip the first two elements: node and insertData.js, you append JavaScript’s slice() method to the process.argv method. This returns elements starting from index 2 onwards. These arguments are then destructured into name and color variables.

      Save your file and exit nano with CTRL+X. Run the file using node and pass it the arguments sammy, and blue:

      • node insertData.js sammy blue

      After running the command, you will see the following output:

      Output

      sammy blue

      The function can now access the name and shark color from the command-line arguments. Next, you’ll modify the insertData() function to insert data into the shark table.

      Open the insertData.js file in your text editor again and add the highlighted code:

      node_pg_app/insertData.js

      const { pool } = require("./db");
      
      async function insertData() {
        const [name, color] = process.argv.slice(2);
        const res = await pool.query(
            "INSERT INTO shark (name, color) VALUES ($1, $2)",
            [name, color]
          );
        console.log(`Added a shark with the name ${name}`);
      }
      
      insertData();
      

      Now, the insertData() function defines the name and color of the shark. Next, it awaits the pool.query method from node-postgres that takes an SQL statement INSERT INTO shark (name, color) ... as the first argument. The SQL statement inserts a record into the shark table. It uses what’s called a parameterized query. $1, and $2 corresponds to the name and color variables in the array provided in the pool.query() method as a second argument: [name, color]. When Postgres is executing the statement, the variables are substituted safely protecting your application from SQL injection. After the query executes, the function logs a success message using console.log().

      Before you run the script, wrap the code inside insertData() function in a try...catch block to handle runtime errors:

      node_pg_app/insertData.js

      const { pool } = require("./db");
      
      async function insertData() {
        const [name, color] = process.argv.slice(2);
        try {
          const res = await pool.query(
            "INSERT INTO shark (name, color) VALUES ($1, $2)",
            [name, color]
          );
          console.log(`Added a shark with the name ${name}`);
        } catch (error) {
          console.error(error)
        }
      }
      
      insertData()
      

      When the function runs, the code inside the try block executes. If successful, the function will skip the catch block and exit. However, if an error is triggered inside the try block, the catch block will execute and log the error in the console.

      Your program can now take command-line arguments and use them to insert a record into the shark table.

      Save and exit out of your text editor. Run the insertData.js file with sammy and blue as command-line arguments:

      • node insertData.js sammy blue

      You’ll receive the following output:

      Output

      Added a shark with the name sammy

      Running the command insert’s a record in the shark table with the name sammy and the color blue.

      Next, execute the file again with jose and teal as command-line arguments:

      • node insertData.js jose teal

      Your output will look similar to the following:

      Output

      Added a shark with the name jose

      This confirms you inserted another record into the shark table with the name jose and the color teal.

      You’ve now inserted two records in the shark table. In the next step, you’ll retrieve the data from the database.

      Step 6 — Retrieving Data From the Postgres Database

      In this step, you’ll retrieve all records in the shark table using node-postgres, and log them into the console.

      Create and open a file retrieveData.js in your favorite editor:

      In your retrieveData.js, add the following code to retrieve data from the database:

      node_pg_app/retrieveData.js

      const { pool } = require("./db");
      
      async function retrieveData() {
        try {
          const res = await pool.query("SELECT * FROM shark");
          console.log(res.rows);
        } catch (error) {
          console.error(error);
        }
      }
      
      retrieveData()
      

      The retrieveData() function reads all rows in the shark table and logs them in the console. Within the function try block, you invoke the pool.query() method from node-postgres with an SQL statement as an argument. The SQL statement SELECT * FROM shark retrieves all records in the shark table. Once they’re retrieved, the console.log() statement logs the rows.

      If an error is triggered, execution will skip to the catch block, and log the error. In the last line, you invoke the retrieveData() function.

      Next, save and close your editor. Run the retrieveData.js file:

      You will see output similar to this:

      Output

      [ { id: 1, name: 'sammy', color: 'blue' }, { id: 2, name: 'jose', color: 'teal' } ]

      node-postgres returns the table rows in a JSON-like object. These objects are stored in an array.

      You can now retrieve data from the database. You’ll now modify data in the table using node-postgres.

      Step 7 — Modifying Data In the Postgres Database

      In this step, you’ll use node-postgres to modify data in the Postgres database. This will allow you to change the data in any of the shark table records.

      You’ll create a script that takes two command-line arguments: id and name. You will use the id value to select the record you want in the table. The name argument will be the new value for the record whose name you want to change.

      Create and open the modifyData.js file:

      In your modifyData.js file, add the following code to modify a record in the shark table:

      node_pg_app/modifyingData.js

      const { pool } = require("./db");
      
      async function modifyData() {
        const [id, name] = process.argv.slice(2);
        try {
          const res = await pool.query("UPDATE shark SET name = $1 WHERE id = $2", [
            name,
            id,
          ]);
          console.log(`Updated the shark name to ${name}`);
        } catch (error) {
          console.error(error);
        }
      }
      
      modifyData();
      

      First, you require the pool object from the db.js file in your modifyData.js file.

      Next, you define an asynchronous function modifyData() to modify a record in Postgres. Inside the function, you define two variables id and name from the command-line arguments using the destructuring assignment.

      Within the try block, you invoke the pool.query method from node-postgres by passing it an SQL statement as the first argument. On the UPDATE SQL statement, the WHERE clause selects the record that matches the id value. Once selected, SET name = $1 changes the value in the name field to the new value.

      Next, console.log logs a message that executes once the record name has been changed. Finally, you call the modifyData() function on the last line.

      Save and exit out of the file using CTRL+X. Run the modifyData.js file with 2 and san as the arguments:

      You will receive the following output:

      Output

      Updated the shark name to san

      To confirm that the record name has been changed from jose to san, run the retrieveData.js file:

      You will get output similar to the following:

      Output

      output [ { id: 1, name: 'sammy', color: 'blue' }, { id: 2, name: 'san', color: 'teal' } ]

      You should now see that the record with the id 2 now has a new name san replacing jose.

      With that done, you’ve now successfully updated a record in the database using node-postgres.

      Conclusion

      In this tutorial, you used node-postgres to connect and query a Postgres database. You began by creating a user and database in Postgres. You then created a table, connected your application to Postgres using node-postgres, and inserted, retrieved, and modified data in Postgres using the node-postgres module.

      For more information about node-postgres, visit their documentation. To improve your Node.js skills, you can explore the How To Code in Node.js series.



      Source link

      How To Build a Telegram Quotes Generator Bot With Node.js, Telegraf, Jimp, and Pexels


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

      Introduction

      In this tutorial, you will use Node.js, telegraf, jimp, and the Pexels API to build a Telegram chatbot that will send you a randomly selected image with a fact overlayed. A Telegram bot is a bot you can interact with using custom slash commands through your preferred Telegram client. You will create the bot through Telegram, and define its logic to select a random animal image and a fact on the animal using JavaScript.

      At the end of this tutorial you will have a Telegram chatbot that looks like the following:

      Imgur

      Once you’ve completed your bot, you will receive a fact about an animal whenever you send a custom Telegram slash command.

      Prerequisites

      In order to follow this tutorial the reader will need the following tools:

      This tutorial was verified with Node v12.18.2 and npm v6.14.8.

      Step 1 — Creating the Project Root Directory

      In this section, you will create the directory where you will build the chatbot, create a Node project, and install the required dependencies.

      Open a terminal window and create a new directory called facts-bot:

      Navigate into the directory:

      Create a directory named temp:

      With the command above, you created a directory named temp. In this directory, you will temporarily store the images that your bot will send to the user.

      Now, you’ll create a new Node.js project. Running npm’s init command will create a package.json file, which will manage your dependencies and metadata.

      Run the initialization command:

      To accept the default values, press ENTER to all prompts. Alternately, you can personalize your responses. To do this, review npm’s initialization settings in Step 1 of the tutorial How To Use Node.js Modules with npm and package.json.

      Open the package.json file and edit it:

      Now, you’ll update the properties in your package.json file. Replace the contents inside the file with the highlighted code:

      package.json

      {
        "name": "facts-bot",
        "version": "1.0.0",
        "description": "",
        "main": "main.js",
        "scripts": {
          "start": "nodemon main.js"
        },
        "author": "",
        "license": "ISC"
      }
      

      Here you changed the main and scripts properties. By changing the main property, you have set the application main file to main.js. This will inform Node the main.js file is the primary entry point to your program. In the scripts property you have have added a script named start, which allows you to set the command that is supposed to run when you start the application. Once you call the script the command nodemon will run the main.js file you will create in the next step.

      With your settings now defined in your package.json file, you will now create a file that will store your environment variables. In your terminal, create a file named .env:

      touch .env
      

      In your .env file, you will store your Telegram bot token and Pexels API key. A Telegram Bot token allows you to interact with your Telegram bot. The Pexels API key allows you to interact with the Pexels API. You will store your environment variables in a later step.

      This time, you’ll use npm to install the dependencies telegraf, dotenv, pexels, jimp, and uuid. You’ll also use the --save flag to save the dependencies. In your terminal, run the following command:

      • npm install telegraf dotenv pexels jimp uuid --save

      In this command, you have installed:

      • telegraf: a library that helps you develop your own Telegram bots using JavaScript or TypeScript. You are going to use it to build your bot.
      • dotenv: a zero-dependency module that loads environment variables from a .env file into process.env. You are going to use this module to retrieve the bot token and Pexels API key from the .env file you created.
      • pexels: a convenient wrapper around the Pexels API that can be used both on the server in Node.js and the browser. You are going to use this module to retrieve animal images from Pexels.
      • jimp: an image processing library written entirely in JavaScript for Node, with zero external or native dependencies. You are going to use this library to edit images retrieved from Pexels and insert a fact about an animal in them.
      • uuid: a module that allows you to generate RFC-compliant UUIDs in JavaScript. You are going to use this module to create a unique name for the image retrieved from Pexels.

      Now, install nodemon as a dev dependency:

      • npm install nodemon --save-dev

      nodemon is a tool that develops Node.js based applications by automatically restarting the Node application when it detects file changes in the directory. You will use this module to start and keep your app running as you test your bot.

      Note: At the time of writing these are the versions of the modules that are being used:telegraf : 4.3.0 ; dotenv : 8.2.0; pexels : 1.2.1 ;jimp : 0.16.1 ; uuid : 8.3.2; nodemon : 2.0.12.

      In this step, you created a project directory and initialized a Node.js project for your bot. You also installed the modules needed to build the bot. In the next step, you will register a bot in Telegram and retrieve an API key for the Pexels API.

      Step 2 — Registering Your Bot and Retrieving an API Key From the Pexels API

      In this section, you will first register a bot with BotFather, then retrieve an API key for the Pexels API. BotFather is a chatbot managed by Telegram that allows users to create and manage chatbots.

      Open your preferred Telegram client, search for @BotFather, and start the chat. Send the /newbot slash command and follow the instructions sent by the BotFather:

      Imgur

      After choosing your bot name and username you will receive a message containing your bot access token:

      Imgur

      Copy the bot token, and open your .env file:

      Save the Bot token in a variable named BOT_TOKEN:

      .env

      BOT_TOKEN = "Your bot token"
      

      Now that you have saved your bot token in the .env file, it’s time to retrieve the Pexels API key.

      Navigate to Pexels, and log in to your Pexels account. Click on the Image & Video API tab and create a new API key:

      Imgur

      Copy the API key, and open your .env file:

      Save the API key in a variable named PEXELS_API_KEY. Your .env should look like the following:

      .env

      BOT_TOKEN = "Your_bot_token"
      PEXELS_API_KEY = "Your_Pexels_API_key"
      

      In this section, You have registered your bot, retrieved your Pexels API key, and saved your bot token and Pexels API key in your .env file. In the next section, you are going to create the file responsible for running the bot.

      Step 3 — Creating the main.js File

      In this section, you will create and build out your bot. You will create a file with the label main.js, and this will contain your bot’s logic.

      In the root directory of your project, create and open the main.js file using your preferred text editor:

      Within the main.js file, add the following code to import the libraries you’ll use:

      main.js

      const { Telegraf } = require('telegraf')
      const { v4: uuidV4 } = require('uuid')
      require('dotenv').config()
      let factGenerator = require('./factGenerator')
      

      In this code block, you have required in the telegraf, the uuid, the dotenv module, and a file named factGenerator.js. You are going to use the telegraf module to start and manage the bot, the uuidmodule to generate a unique file name for the image, and the dotenv module to get your Telegram bot token and Pexels API key stored in the .env file. The factGenerator.js file will be used to retrieve a random animal image from Pexels, insert a fact about the animal, and delete the image after it’s sent to the user. You will create this file in the next section.

      Below the require statements, add the following code to create an instance of the bot:

      main.js

      . . .
      
      const bot = new Telegraf(process.env.BOT_TOKEN)
      
      bot.start((ctx) => {
          let message = ` Please use the /fact command to receive a new fact`
          ctx.reply(message)
      })
      

      Here, you retrieved and used the BOT_TOKEN that BotFather sent, created a new bot instance, and assigned it to a variable called bot. After creating a new bot instance, you added a command listener for the /start command. This command is responsible for initiating a conversation between a user and the bot. Once a user sends a message containing /start the bot replies with a message asking the user to use the /fact command to receive a new fact.

      You have now created the command handler responsible for starting the interaction with your chatbot. Now, let’s create the command handler for generating a fact. Below the .start() command, add the following code:

      main.js

      . . .
      
      bot.command('fact', async (ctx) => {
          try {
              ctx.reply('Generating image, Please wait !!!')
              let imagePath = `./temp/${uuidV4()}.jpg`
              await factGenerator.generateImage(imagePath)
              await ctx.replyWithPhoto({ source: imagePath })
              factGenerator.deleteImage(imagePath)
          } catch (error) {
              console.log('error', error)
              ctx.reply('error sending image')
          }
      })
      
      bot.launch()
      

      In this code block, you created a command listener for the custom /fact slash command. Once this command is triggered from the Telegram user interface, the bot sends a message to the user. The uuid module is used to generate the image name and path. The image will be stored in the /temp directory that you created in Step 1. Afterwards, the image path is passed to a method named generateImage() you’ll define in the factGenerator.js file to generate an image containing a fact about an animal. Once the image is generated, the image is sent to the user. Then, the image path is passed to a method named deleteFile in the factGenerator.js file to delete the image. Lastly, you launched your bot by calling the bot.launch() method.

      The main.js file will look like the following:

      main.js

      const { Telegraf } = require('telegraf')
      const { v4: uuidV4 } = require('uuid')
      require('dotenv').config()
      let factGenerator = require('./factGenerator')
      
      
      const bot = new Telegraf(process.env.BOT_TOKEN)
      
      bot.start((ctx) => {
          let message = ` Please use the /fact command to receive a new fact`
          ctx.reply(message)
      })
      
      
      bot.command('fact', async (ctx) => {
          try {
              ctx.reply('Generating image, Please wait !!!')
              let imagePath = `./temp/${uuidV4()}.jpg`
              await factGenerator.generateImage(imagePath)
              await ctx.replyWithPhoto({ source: imagePath })
              factGenerator.deleteImage(imagePath)
          } catch (error) {
              console.log('error', error)
              ctx.reply('error sending image')
          }
      });
      
      
      bot.launch()
      

      You have created the file responsible for running and managing your bot. You will now set facts for the animal and build out the bot’s logic in the factGenerator.js file.

      Step 4 — Creating the Fact Generator File and Building the Bot Logic

      In this section, you will create files named fact.js and factGenerator.js. fact.js will store facts about animals in one data source. The factGenerator.js file will contain the code needed to retrieve a random fact about an animal from a file, retrieve an image from Pexels, use jimp to write the fact in the retrieved image, and delete the image.

      In the root directory of your project, create and open the facts.js file using your preferred text editor:

      Within the facts.js file add the following code to create your data source:

      facts.js

      const facts = [
          {
              fact: "Mother pandas keep contact with their cub nearly 100% of the time during their first month - with the cub resting on her front and remaining covered by her paw, arm or head.",
              animal: "Panda"
          },
          {
              fact: "The elephant's temporal lobe (the area of the brain associated with memory) is larger and denser than that of people - hence the saying 'elephants never forget'.",
              animal: "Elephant"
          },
          {
              fact: "On average, males weigh 190kg and females weigh 126kg . They need this weight and power behind them to hunt large prey and defend their pride.  ",
              animal: "Lion"
          },
          {
              fact: "The Amazon river is home to four species of river dolphin that are found nowhere else on Earth. ",
              animal: "Dolphin"
          },
      ]
      
      module.exports = { facts }
      

      In this code block, you defined an object with an array containing facts about animals and stored in a variable named facts. Each object has the following properties: fact and animal. In the property named fact, its value is a fact about an animal, while the property animal stores the name of the animal. Lastly, you are exporting the facts array.

      Now, create a file named factGenerator.js:

      Inside the factGenerator.js file, add the following code to require in the dependencies you’ll use to build out the logic to make your animal image:

      factGenerator.js

      let { createClient } = require('pexels')
      let Jimp = require('jimp')
      const fs = require('fs')
      let { facts } = require('./facts')
      

      Here, you required in the pexels, the jimp, the fs module, and your facts.js file. You will use the pexels module to retrieve animal images from Pexels, the jimp module to edit the image retrieved from Pexels, and the fs module to delete the image from your file directory after it’s sent to the user.

      Below the require statements, add the following code to generate an image:

      factGenerator.js

      . . .
      
      async function generateImage(imagePath) {
        let fact = randomFact()
        let photo = await getRandomImage(fact.animal)
        await editImage(photo, imagePath, fact.fact)
      }
      

      In this code block, you created a function named generateImage(). This function takes as an argument the path of the Pexel image in your file directory. Once this function is called a function named randomFact() is called and the value returned is stored in a variable named fact. The randomFact() function randomly selects an object in the facts.js file. After receiving the object, its property animal is passed to a function named getRandomImage(). The getRandomImage() function will use the pexels module to search for images containing the name of the animal passed, and selects a random image. The value returned is stored in a variable named photo. Finally, the photo, imagePath, and the fact property from the facts.js file are passed to a function named editImage(). The editImage() function uses the jimp module to insert the random fact in the random image and then save the edited image in the imagePath.

      Here, you have created the function that is called when you send the /fact slash command to the bot. Now you’ll create the functions getRandomImage() and editImage() and construct the logic behind selecting and editing a random image.

      Below the generateImage() function, add the following code to set the randomization logic:

      factGenerator.js

      . . .
      
      function randomFact() {
        let fact = facts[randomInteger(0, (facts.length - 1))]
        return fact
      }
      
      
      function randomInteger(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
      }
      

      You have now created the functions randomFact() and randomInteger(). The randomFact() function selects a random fact in the facts.js file by calling the randomInteger() function, and returns this object. The randomInteger() function returns a random integer in the interval of 0 and the number of facts in the facts.js file.

      Now that you’ve defined functions to return a random fact and random integer, you’ll need to create a function to get a random image from Pexels. Below the randomInteger() function, add the following code to get a random image:

      factGenerator.js

      . . .
      
      async function getRandomImage(animal) {
        try {
          const client = createClient(process.env.PEXELS_API_KEY)
          const query = animal
          let image
      
          await client.photos.search({ query, per_page: 10 }).then(res => {
            let images = res.photos
            image = images[randomInteger(0, (images.length - 1))]
      
          })
      
          return image
      
        } catch (error) {
          console.log('error downloading image', error)
          getRandomImage(animal)
        }
      }
      

      In this code block, you have created a function named getRandomImage(). This function takes as an argument an animal name. When this function is called a client object is created by using createClient() method object from the pexels module and the Pexels API key stored in the .env file. The animal name is stored in a variable called query, then the client object is used to search for images containing the value in the query. Once the images are found, a random image is selected with the help of the randomInteger() function. Finally, the random image is returned to the generateImage() method in your main.js file.

      With your getRandomImage() function in place, the image selected needs to have a text overlay before it’s sent to your Telegram bot. Below the getRandomImage() function, add the following code to set the overlay:

      factGenerator.js

      . . .
      
      async function editImage(image, imagePath, fact) {
        try {
          let imgURL = image.src.medium
          let animalImage = await Jimp.read(imgURL).catch(error => console.log('error ', error))
          let animalImageWidth = animalImage.bitmap.width
          let animalImageHeight = animalImage.bitmap.height
          let imgDarkener = await new Jimp(animalImageWidth, animalImageHeight, '#000000')
          imgDarkener = await imgDarkener.opacity(0.5)
          animalImage = await animalImage.composite(imgDarkener, 0, 0);
      
      
        } catch (error) {
          console.log("error editing image", error)
        } 
      
      }
      
      

      Here, you created a function named editImage(). This function takes as arguments a random animal labeled image, the imagePath, and a fact about this random animal. In the variable imgURL, the URL for the medium size of the image is retrieved from the Pexels API. Afterwards the read() method of jimp is used to load the image. Once the image is loaded and stored in a variable named animalImage, the image width and height are retrieved and stored in the variables animalImageWidth and animalImageHeight respectively. The variable imgDarkener stores new instance of Jimp() and darkens the image. The opacity() method of jimp is used to set imgDarkener’s opacity to 50%. Finally, the composite() method of jimp is used to put the contents in imgDarkener over the image in animalImage. This in return makes the image in animalImage darker before adding the text stored in the fact variable, and make the text visible over the image.

      Note: Jimp by default provides a method named color() that allows you to adjust an image’s tonal levels. For the purpose of this tutorial, you’ll write a custom tonal adjuster as the color() method does not offer the precision necessary here.

      At the bottom of the try block inside the editImage() function, add the following code:

      factGenerator.js

      . . .
      
      async function editImage(image, imagePath,fact) {
        try {
          . . .
      
          let posX = animalImageWidth / 15
          let posY = animalImageHeight / 15
          let maxWidth = animalImageWidth - (posX * 2)
          let maxHeight = animalImageHeight - posY
      
          let font = await Jimp.loadFont(Jimp.FONT_SANS_16_WHITE)
          await animalImage.print(font, posX, posY, {
            text: fact,
            alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
            alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
          }, maxWidth, maxHeight)
      
          await animalImage.writeAsync(imagePath)
          console.log("Image generated successfully")
      
      
        } catch (error) {
          . . .
        }
      }
      
      

      In this code block, you used the animalImageWidth, and animalImageHeight to get the values that will be used to center the text in the animalImage. After, you used the loadFont() method of jimp to load the font, and store the font in a variable named font. The font color is white, the type is sans-serif (SANS), and the size is 16. Finally, you used the print() method of jimp to insert the fact in the animalImage, and the write() method to save the animalImage in the imagePath.

      Now that you’ve created the function responsible for editing the image, you’ll need a function to delete the image from your file structure after it is sent to the user. Below your editImage() function, add the following code:

      factGenerator.js

      . . .
      
      const deleteImage = (imagePath) => {
          fs.unlink(imagePath, (err) => {
              if (err) {
                  return
              }
              console.log('file deleted')
          })
      }
      
      
      module.exports = { generateImage, deleteImage }
      

      Here, you have created a function named deleteImage(). This function takes as an argument the variable imagePath. Once this function is called, the fs module is used to delete the image stored in the variable imagePath. Lastly, you exported the generateImage() function and the deleteImage() function.

      With your functions in place, the factGenerator.js file will look like the following:

      factGenerator.js

      let { createClient } = require('pexels')
      let Jimp = require('jimp')
      const fs = require('fs')
      let { facts } = require('./facts')
      
      async function generateImage(imagePath) {
        let fact = randomFact()
        let photo = await getRandomImage(fact.animal)
        await editImage(photo, imagePath, fact.fact)
      }
      
      
      function randomFact() {
        let fact = facts[randomInteger(0, (facts.length - 1))]
        return fact
      }
      
      
      function randomInteger(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
      }
      
      
      async function getRandomImage(animal) {
        try {
          const client = createClient(process.env.PEXELS_API_KEY)
          const query = animal
          let image
      
          await client.photos.search({ query, per_page: 10 }).then(res => {
            let images = res.photos
            image = images[randomInteger(0, (images.length - 1))]
      
          })
      
          return image
      
        } catch (error) {
          console.log('error downloading image', error)
          getRandomImage(animal)
        }
      }
      
      
      async function editImage(image, imagePath, fact) {
        try {
          let imgURL = image.src.medium
          let animalImage = await Jimp.read(imgURL).catch(error => console.log('error ', error))
          let animalImageWidth = animalImage.bitmap.width
          let animalImageHeight = animalImage.bitmap.height
          let imgDarkener = await new Jimp(animalImageWidth, animalImageHeight, '#000000')
          imgDarkener = await imgDarkener.opacity(0.5)
          animalImage = await animalImage.composite(imgDarkener, 0, 0);
      
          let posX = animalImageWidth / 15
          let posY = animalImageHeight / 15
          let maxWidth = animalImageWidth - (posX * 2)
          let maxHeight = animalImageHeight - posY
      
          let font = await Jimp.loadFont(Jimp.FONT_SANS_16_WHITE)
          await animalImage.print(font, posX, posY, {
            text: fact,
            alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
            alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
          }, maxWidth, maxHeight)
      
          await animalImage.writeAsync(imagePath)
          console.log("Image generated successfully")
      
        } catch (error) {
          console.log("error editing image", error)
        }
      
      }
      
      
      const deleteImage = (imagePath) => {
        fs.unlink(imagePath, (err) => {
          if (err) {
            return
          }
          console.log('file deleted')
        })
      }
      
      
      module.exports = { generateImage, deleteImage }
      
      

      Save your factGenerator.js file. Return to your terminal, and run the following command to start your bot:

      Open your preferred Telegram client, and search for your bot. Send a message with the /start command to initiate the conversation, or click the Start button. Then, send a message with the /fact command to receive your image.

      You will receive an image similar to the following:

      Imgur

      You now see the image in your preferred Telegram client with a fact imposed over the image. You’ve created the file and functions responsible for retrieving a random fact from the facts.js file, retrieving an animal image from Pexels, and inserting a fact onto the image.

      Conclusion

      In this tutorial, you built a Telegram chatbot that sends an image of an animal with a fact overlayed through a custom slash command. You created the command handlers for the bot through the telegraf module. You also created functions responsible for retrieving a random fact, random images from Pexels using the pexels module, and inserting a fact over the random image using the jimp module. For more information about the Pexels API, telegraf and jimp modules please refer to documentation on the Pexels API, telegraf, jimp.



      Source link