← Back to lessons
DISCORD SYSTEMS

Lesson — Watermark System

Goal: Build a system that applies a watermark to uploaded images using Sharp.

Lesson Overview

In this lesson you will build a professional image watermark system for your Discord bot. Users upload an image using /watermark, and the bot automatically applies a watermark overlay.

Video Lesson

Notes — Understanding the Code

Attachment Input

Users upload an image directly in Discord.

HTTPS Download

The bot downloads the image from Discord's CDN.

Sharp

Processes and edits images efficiently.

Watermark Resize

The watermark is resized to match the image size.

Composite

Overlays the watermark onto the image.

Step-by-Step

Install Dependencies

npm install sharp

Add Watermark Image

Create: images/watermark.png

Full Command Code

Show Full File
const { SlashCommandBuilder, AttachmentBuilder } = require('discord.js');
const sharp = require('sharp');
const fs = require('fs');
const path = require('path');
const https = require('https');

module.exports = {
    data: new SlashCommandBuilder()
        .setName('watermark')
        .setDescription('Add watermark to an image.')
        .addAttachmentOption(option =>
            option.setName('image')
                  .setDescription('The image to watermark')
                  .setRequired(true)
        ),

    run: async ({ interaction }) => {
        const attachment = interaction.options.getAttachment('image');
        if (!attachment || !attachment.contentType.startsWith('image/')) {
            return interaction.reply({ content: 'Please provide a valid image.', ephemeral: true });
        }

        await interaction.deferReply();

        try {
            const imageBuffer = await new Promise((resolve, reject) => {
                https.get(attachment.url, res => {
                    const chunks = [];
                    res.on('data', chunk => chunks.push(chunk));
                    res.on('end', () => resolve(Buffer.concat(chunks)));
                    res.on('error', reject);
                });
            });

            const watermarkPath = path.join(__dirname, 'images', 'watermark.png');
            const watermarkBuffer = fs.readFileSync(watermarkPath);

            const image = sharp(imageBuffer);
            const metadata = await image.metadata();

            const watermarkResized = await sharp(watermarkBuffer)
                .resize(metadata.width, metadata.height)
                .png()
                .toBuffer();

            const result = await sharp(imageBuffer)
                .composite([{ input: watermarkResized }])
                .png()
                .toBuffer();

            const attachmentOut = new AttachmentBuilder(result, { name: 'watermarked.png' });

            await interaction.editReply({ files: [attachmentOut] });

        } catch (err) {
            console.error(err);
            interaction.editReply({ content: 'Error processing image.' });
        }
    }
};

Restart Bot

node index.js

Challenge

← Course Home Next Lesson →