Back to docsRecipe

Recipe: Companion bot

Build a persistent Discord companion that remembers user context across sessions, enforces rate limits, and stays online 24/7 with Meridian's managed runtime.

Ingredients

  • Meridian CLI v2.1+ installed and authenticated
  • Discord bot token with Message Content intent
  • Upstash Redis for session persistence
  • Node.js 20 LTS runtime target

Step 1 — Scaffold

meridian init companion-bot --template discord-bare
cd companion-bot
meridian env set DISCORD_TOKEN "your-token-here"
meridian env set UPSTASH_REDIS_URL "redis://..."

Step 2 — Session store

import { Redis } from "@upstash/redis";

const redis = new Redis({ url: process.env.UPSTASH_REDIS_URL });

export async function getSession(userId: string) {
  const raw = await redis.get(`session:${userId}`);
  return raw ? JSON.parse(raw as string) : { history: [] };
}

export async function saveSession(userId: string, data: object) {
  await redis.set(`session:${userId}`, JSON.stringify(data), { ex: 86400 });
}

Step 3 — Rate limiter

const buckets = new Map<string, { tokens: number; last: number }>();

export function checkRate(userId: string, limit = 5, windowMs = 60000) {
  const now = Date.now();
  let b = buckets.get(userId);
  if (!b || now - b.last > windowMs) {
    b = { tokens: limit, last: now };
    buckets.set(userId, b);
  }
  if (b.tokens <= 0) return false;
  b.tokens--;
  return true;
}

Step 4 — Deploy

meridian deploy --production
# Bot is now online 24/7 with persistent sessions

Next: Add slash command handlers with Meridian's built-in interaction router. See Recipe: Slash commands.