DEV Community

Cover image for Morning Daily Routine Bot for Your Discord Server
Titus Efferian
Titus Efferian

Posted on

Morning Daily Routine Bot for Your Discord Server

Just after New Year's 2024, I suddenly had a simple idea to create a progress bar for every day until the 365th day of this year.

Currently, I am active on Discord in a server with my friends, and I want to try making a daily "good morning" message to greet all of my friends on Discord before we all start our day.

Here are the things:

  • I want to create a bot that will send a message every morning.
  • I don't want to pay any extra fees for the resources I will use.

I came up with using CircleCI for this, because CircleCI offers free tier automation.

Draw an image using nodejs canvas

To draw our custom image, we need to use
const Canvas = require('@napi-rs/canvas');.

First, let's try to draw the progress bar, as this is the most challenging part. The rest of the content is just text, and we need to adjust the coordinates.

// Coordinates and dimensions for the rectangle
const rectX = 50;
const rectY = canvas.height / 2 - 25;
const rectWidth = canvas.width - 100;
const rectHeight = 80;

// Draw a white rectangle
context.fillStyle = "white";
context.fillRect(rectX, rectY, rectWidth, rectHeight);

// Calculate the percentage of the year that has passed
const now = new Date();
const start = new Date(now.getFullYear(), 0, 0);
const diff = now - start;
const oneDay = 1000 * 60 * 60 * 24;
const day = Math.floor(diff / oneDay);
const yearLength = (new Date(now.getFullYear(), 11, 31) - start) / oneDay + 1; // Account for leap year
const progress = (day / yearLength) * rectWidth;

// Draw the green background color inside the rect, filling the calculated percentage of the width
context.fillStyle = "green";
context.fillRect(rectX, rectY, progress, rectHeight);

// Set up stroke style
context.strokeStyle = "black"; // Stroke color
context.lineWidth = 8; // Stroke width

// Draw the stroke on top of the filled rectangles
context.strokeRect(rectX, rectY, rectWidth, rectHeight);
Enter fullscreen mode Exit fullscreen mode

And the result will look like this:

Canvas Image Result

After that, it's just a matter of adding more text and positioning it in the right place.

// Set up the font style for the text
context.font = "bold 48px NotoSansJP";
context.fillStyle = "white";
context.textAlign = "center"; // This will align the text centrally
context.textBaseline = "middle"; // This will align the text in the middle of the baseline
const ohayouText = `おはようございました`;
const ohayouTextX = canvas.width / 2;
const ohayouTextY = rectY - 52;

// Draw the Japanese text
context.fillText(ohayouText, ohayouTextX, ohayouTextY);

// Set up the font style for the text
context.font = "bold 40px NotoSansJP";
context.fillStyle = "white";
context.textAlign = "center"; // This will align the text centrally
context.textBaseline = "middle"; // This will align the text in the middle of the baseline

// Calculate the position for the percentage text
const text = `${((day / yearLength) * 100).toFixed(2)}% HAS PASSED FOR THIS YEAR`;
const textX = canvas.width / 2; // This will center the text in the x-axis
const textY = rectY + rectHeight + 48; // This will position the text below the rectangle

// Draw the percentage text
context.fillText(text, textX, textY);

// Set up the font style for the Japanese text
context.font = "bold 28px NotoSansJP";
context.textAlign = "center"; // This will align the text centrally
context.textBaseline = "middle"; // This will align the text in the middle of the baseline
const keepItUpText = `今日も頑張りましょう。`;
const keepItUpTextX = canvas.width / 2;
const keepItUpTextY = textY + 48;

// Draw the Japanese text
context.fillText(keepItUpText, keepItUpTextX, keepItUpTextY);
Enter fullscreen mode Exit fullscreen mode

And it will look like this:

Canvas Image Result

Okay, now that we have finished drawing our custom image, it's time to send our image to the Discord channel using discord.js.

const {
  Client,
  Events,
  GatewayIntentBits,
  AttachmentBuilder,
} = require("discord.js");
const Canvas = require("@napi-rs/canvas");

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

const TARGETED_CHANNEL = process.env.DISCORD_BOT_TARGETED_CHANNEL;
const token = process.env.DISCORD_TOKEN;

client.once(Events.ClientReady, async (readyClient) => {
  const canvas = Canvas.createCanvas(1000, 500);
  const context = canvas.getContext("2d");

  // ...Our previous canvas drawing code.

  try {
    // Convert canvas to buffer
    const buffer = canvas.toBuffer("image/png");
    // Create an attachment and send it
    const attachment = new AttachmentBuilder(buffer, { name: "progress.png" });
    readyClient.channels.cache
      .get(TARGETED_CHANNEL)
      .send({ files: [attachment] });
  } catch (error) {
    console.error("Error creating buffer:", error);
    readyClient.channels.cache
      .get(TARGETED_CHANNEL)
      .send("An error occurred while creating the image.");
  }
  client.destroy();
});

client.login(token);
Enter fullscreen mode Exit fullscreen mode

CircleCI Configuration

Now, we are going to run our code inside the Node.js cimg container runtime.

version: 2.1
jobs:
  main_script:
    docker:
      - image: 'cimg/node:21.5.0'
    steps:
      - checkout
      - run:
          name: Install System Fonts
          command: |
            sudo apt-get update
            sudo apt-get install -y fontconfig
            sudo apt-get install -y libfontconfig1
      - run:
          name: Install Dependencies
          command: npm install
      - run:
          name: Run Node.js Script
          command: node main.js
workflows:
  version: 2
  build:
    jobs:
      - main_script

Enter fullscreen mode Exit fullscreen mode

We need additional installations so our font can run inside the Docker container, as we are currently running our canvas inside a Docker container.

run:
  name: Install System Fonts
  command: |
    sudo apt-get update
    sudo apt-get install -y fontconfig
    sudo apt-get install -y libfontconfig1
Enter fullscreen mode Exit fullscreen mode

Now, you just need to set up the scheduler using the CircleCI console.

Circle CI Dashboard

Full Source Code
https://github.com/TitusEfferian/discord-cron-daily-ci

Top comments (0)