Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

25 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Dadida

Build AI personas for Discord community engagement & management.

Two ways to use:

  • πŸš€ Deploy β€” use dadida-starter β€” clone, configure your persona, deploy to Railway.
  • πŸ“¦ Library β€” npm install dadida and build your own bot with a decoupled, customizable plugin system.

Vision

Traditional Discord bots are reactive β€” they respond only when mentioned or triggered by a command.

Dadida enables AI personas to observe, decide, and participate naturally in community conversations. An AI persona continuously watches discussions and selectively engages when its participation adds value β€” just like a real community member.

Community Message
  β†’ Observe (receive message)
  β†’ Filter (should we even look at this?)
  β†’ Classify (what is this about?)
  β†’ Decide (should the persona engage?)
  β†’ Participate (respond in character)

Architecture

discord.js          β†’ Discord WebSocket, events, API
        ↓
Dadida              β†’ observe β†’ filter β†’ classify β†’ decide β†’ act
        ↓
@openai/agents      β†’ agent reasoning, structured output, tools
        ↓
Dadida              β†’ interpret result β†’ execute action

Dadida is the community presence layer. It owns when and whether to engage. OpenAI Agents SDK handles how the agent reasons.

Quick Start

Install

npm install dadida @openai/agents zod

Create a bot

// index.ts
import { createBot, discord, definePlugin } from 'dadida'
import { Agent, run } from '@openai/agents'
import { z } from 'zod'

const classifier = new Agent({
  name: 'topic-classifier',
  instructions: 'Classify whether this message is about investing.',
  outputType: z.object({
    is_relevant: z.boolean(),
    confidence: z.number(),
  }),
})

const persona = new Agent({
  name: 'community-member',
  instructions: 'You are a helpful community member. Reply in 1-2 sentences.',
})

const bot = createBot({
  platform: discord({
    token: process.env.DISCORD_TOKEN!,
    channels: process.env.LISTEN_CHANNEL_IDS?.split(','),
  }),
  plugins: [
    definePlugin({
      name: 'my-persona',
      async classify(message) {
        const result = await run(classifier, message.content)
        return result.finalOutput ?? { is_relevant: false, confidence: 0 }
      },
      async policy(classification) {
        if (!classification.is_relevant || classification.confidence < 0.75) {
          return { shouldAct: false }
        }
        return { shouldAct: true, action: 'reply' }
      },
      async action(decision, message, ctx) {
        const result = await run(persona, message.content)
        if (result.finalOutput) {
          await ctx.platform.reply(message.channelId, message.id, result.finalOutput)
        }
      },
    }),
  ],
})

bot.start()

Run

export DISCORD_TOKEN=your-token
export OPENAI_API_KEY=your-key
export LISTEN_CHANNEL_IDS=your-channel-id   # optional; omit to listen on all channels

# Development
npm run dev

# Production
npm run build && npm start

Plugin System

Plugins implement lifecycle hooks. A plugin can implement any combination of hooks:

Hook Purpose Behavior on error
filter Should we process this message? Fail-open (continue)
classify What is this message about? Skip classification
policy Should the persona act? Fail-closed (no action)
action Execute the response Log and continue
onReady Bot connected β€”
onError Pipeline error β€”
import { definePlugin } from 'dadida'

export function myPlugin() {
  return definePlugin({
    name: 'my-plugin',
    async filter(message, ctx) {
      // return false to skip this message
    },
    async classify(message, ctx) {
      // return classification object
    },
    async policy(classification, message, ctx) {
      // return { shouldAct: true/false, action: '...' }
    },
    async action(decision, message, ctx) {
      // execute the action
    },
  })
}

Persona Files

Define your AI persona's identity, personality, and knowledge as markdown files:

personas/
β”œβ”€β”€ identity.md    ← the business card: name, role, vibe, avatar (facts, rarely changes)
β”œβ”€β”€ soul.md        ← the personality: voice, tone, boundaries, examples (how you behave)
knowledge/
β”œβ”€β”€ trading-rules.md   ← domain knowledge the persona can reference
└── current-views.md   ← updatable context (change often without touching personality)

Load them into your agent:

import { loadPersona, loadKnowledge } from 'dadida'
import { Agent } from '@openai/agents'

const responder = new Agent({
  name: 'my-persona',
  model: 'gpt-4.1-mini',
  instructions: [
    loadPersona('./personas/identity.md'),
    loadPersona('./personas/soul.md'),
    loadKnowledge('./knowledge/'),
  ].join('\n\n'),
})

Edit markdown to change personality or knowledge β€” no code changes needed.

Self-Hosting

For deployment, use dadida-starter. The instructions below apply to the starter repo.

Dadida runs as a long-lived worker process (it holds a Discord gateway / WebSocket connection). There is no HTTP server and no health-check endpoint β€” the host just needs to keep one process alive. Run exactly one instance: a second one opens a duplicate gateway connection and double-replies.

Railway

  1. Create a Railway project
  2. Connect your GitHub repo
  3. Set environment variables (at minimum DISCORD_TOKEN and OPENAI_API_KEY)
  4. Build command: npm run build
  5. Start command: npm start
  6. Deploy β€” Railway runs it as a worker and monitors process health directly (no health check needed).

Persisting history

The bot writes message history to a SQLite file at ./data/messages.db. On Railway that directory is ephemeral and wiped on every redeploy. To keep history across restarts, attach a persistent volume at /app/data:

  1. Open the project and select your bot service.
  2. Right-click the service β†’ Attach Volume (or Settings β†’ Volumes β†’ Add Volume).
  3. Set the mount path to /app/data and create it β€” Railway redeploys with the volume attached.
  4. Keep the service at 1 replica (volumes can't attach to multi-replica services β€” which also matches the single-gateway-connection requirement).

History is optional β€” without a volume the bot still runs, it just starts each deploy with an empty memory.

Environment Variables

Variable Required Description
DISCORD_TOKEN Yes Discord bot token
OPENAI_API_KEY Yes OpenAI API key (used by @openai/agents)
LISTEN_CHANNEL_IDS No Channel ID(s) to listen on; comma-separate for several (123,456). Empty = all channels
CONFIDENCE_THRESHOLD No Minimum confidence (0–1) for the persona to reply. Default 0.75

Design Principles

  • Silent by default β€” not every message deserves a response
  • LLM judges, code enforces β€” LLM decides severity and action, code applies safety caps
  • Fail-closed β€” prefer false negatives over false positives
  • Plugins are functions β€” no magic, no YAML, no auto-discovery
  • Platform-agnostic β€” Discord first, extensible to Slack/Telegram

Roadmap

  • Recent context β€” fetch last N messages from channel, pass to agent as conversation context

  • Message history β€” store all messages to SQLite, expose search_history tool to agent for on-demand retrieval

  • Memory system β€” inspired by OpenClaw's three-layer architecture:

    Layer Storage Purpose
    Session context Recent N messages from channel Immediate conversational awareness
    Message history SQLite (all messages, auto-stored) Agent searches on demand via tool
    Long-term memory MEMORY.md Curated durable facts
  • Multi-agent β€” multiple personas coexisting in one community (founder, moderator, support)

  • Channel awareness β€” personas behave differently per channel

  • Human approval queue β€” draft replies sent to admin channel before posting

  • Slack / Telegram connectors

  • RAG tool β€” @openai/agents tool for large knowledge base retrieval

License

MIT

About

Build AI personas for Discord communities engagement & management

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages