Skip to content

Kaelith69/Ana

Repository files navigation

Ana Banner

Ana

Ana is a Discord chatbot that acts less like a helpdesk macro and more like an online person with timing, mood, and occasional restraint.

Under the hood, it is a single Python runtime with:

  • discord.py event handling
  • a multi-stage AI fallback pipeline
  • per-channel short-term memory
  • per-user profile extraction and persistence
  • a keepalive HTTP endpoint for hosting platforms that distrust silence

Table of Contents

System Overview

Ana processes Discord message events and decides whether to:

  1. ignore,
  2. react,
  3. reply,
  4. run a roast/flirt mode,
  5. send a random dad joke on non-trigger traffic,
  6. update a per-user profile in the background.

Core execution traits implemented in code:

  • user cooldown: 25 seconds
  • channel cooldown: 7 seconds
  • low-signal skip: 5 percent
  • ghost-typing: 6 percent
  • reaction overlay on text reply: 10 percent
  • typo injection: 4 percent (70 percent chance of correction follow-up)
  • follow-up probabilities: roast 25 percent, flirt 20 percent, normal 8 percent

Architecture

Architecture

Ana runs in one process with two execution contexts:

  • main async context: Discord gateway, command handling, message pipeline
  • daemon thread: Flask keepalive server on port 8080

The AI call path is intentionally offloaded with asyncio.to_thread(...) so HTTP model calls do not block Discord event processing.

Module Dependency Diagram

Module Dependency

Component Interaction Diagram

Component Interaction

Data Flow

System Data Flow

Request Lifecycle Diagram

Request Lifecycle

End-to-end triggered request flow:

  1. main.py:on_message receives message.
  2. mention/roast/flirt/trigger detection runs via precompiled regex patterns.
  3. cooldown and behavior gates run.
  4. message text is context-enriched:
    • mention tokens resolved to display names
    • optional reply-thread context injected
    • channel history appended
  5. nlp.process_with_nlp(...) executes in worker thread.
  6. Groq waterfall attempts models in priority order.
  7. if Groq fails: Gemini Gen1 then Gemini Gen2.
  8. if all providers fail: static short fallback response list.
  9. output is normalized and post-processed for style cleanup.
  10. response is sent in one or multiple chunks.
  11. optional mode-specific follow-up is sent.
  12. profile extraction task runs asynchronously and updates JSON profile store.

Module Breakdown

main.py

Responsibilities:

  • Discord bot bootstrap and event loop wiring
  • command handlers: !joke, !shutdown
  • trigger mode selection (normal, roast, flirt)
  • cooldown and behavior simulation gates
  • message history maintenance (deque(maxlen=20) per channel)
  • asynchronous NLP dispatch with typing/read-delay simulation
  • optional follow-up line scheduling
  • background profile extraction task creation

nlp.py

Responsibilities:

  • prompt assembly for normal/roast/flirt modes
  • profile-access classifier using backup Groq key path
  • Groq model waterfall execution with per-model settings
  • Gemini fallback execution
  • response normalization (normalize_response)
  • artifact stripping and style cleanup (post_process)

Default model sequence from config:

  • moonshotai/kimi-k2-instruct
  • llama-3.1-8b-instant
  • llama-3.1-8b-instant (deduped at runtime)
  • qwen/qwen3-32b

Fallback chain:

  • gemini-1.5-flash-latest
  • gemini-2.5-flash-lite
  • static fallback responses

profiles.py

Responsibilities:

  • per-user profile file resolution and caching
  • deep-merge of extracted structured facts
  • thread-safe file update with atomic replace
  • compact profile context formatting for prompt injection
  • Gemini-based personal fact extraction (extract_profile_info)

Persistence model:

  • path: data/profiles/*.json
  • includes internal fields _id, _name
  • stores extracted public facts and preferences

jokes.py

Responsibilities:

  • fetch live jokes from configured endpoint
  • enforce random chance, cooldown, and daily cap
  • wrap send behavior with typing simulation and intro/outro variants

Implemented constraints:

  • default chance: 0.15
  • default cooldown: 60s
  • daily cap: 3

config.py

Responsibilities:

  • load environment variables with load_dotenv(override=True)
  • parse typed numeric env values
  • expose trigger, roast, flirt word sets
  • expose joke settings dataclass
  • expose model waterfall overrides and per-model generation settings

keepalive.py

Responsibilities:

  • Flask app serving GET / => Bot is alive!
  • daemon thread launch for host uptime probes

Dependencies

Runtime dependencies from requirements.txt:

  • discord.py>=2.3.2
  • flask>=3.0.0
  • python-dotenv>=1.0.0
  • requests>=2.32.0
  • groq>=1.0.0

Configuration Reference

All configuration is env-driven.

Required for baseline operation:

  • DISCORD_TOKEN
  • GROQ_API_KEY (recommended for primary model path)

Optional but important:

  • GROQ_BACKUP_API_KEY (reserved for profile-access classifier path)
  • GEN1_API_KEY and GEN2_API_KEY (Gemini fallback and profile extraction)
  • SYSTEM_PROMPT (inline prompt override)
  • CHARACTER_PROFILE_PATH (file-based prompt source override)
  • JOKE_CHANCE
  • JOKE_COOLDOWN
  • JOKE_FETCH_TIMEOUT
  • JOKE_API_URL
  • GROQ_MODEL_PRIMARY
  • GROQ_MODEL_BACKUP1
  • GROQ_MODEL_BACKUP2
  • GROQ_MODEL_BACKUP3

Environment template is provided in .env.example.

API Reference

Discord command surface

  • !joke: force joke fetch and send
  • !shutdown: owner-only graceful shutdown

Event surface

  • on_ready: startup log and cleanup-task activation
  • on_message: full request decision tree

HTTP surface

  • GET / on keepalive.py: health probe response Bot is alive!

Internal callable interfaces

  • process_with_nlp(...)
  • call_groq(...)
  • call_gemini(...)
  • normalize_response(...)
  • post_process(...)
  • extract_profile_info(...)
  • ProfileStore.update(...)
  • DadJokeService.maybe_send_joke(...)

Setup

  1. Clone repository.
  2. Create virtual environment.
  3. Install dependencies.
  4. Copy .env.example to .env and fill values.
  5. Run python main.py.

Example:

git clone https://github.com/Kaelith69/Ana.git
cd Ana
python -m venv .venv
# Windows
.venv\Scripts\activate
# Linux/macOS
# source .venv/bin/activate
pip install -r requirements.txt
copy .env.example .env   # Windows
# cp .env.example .env   # Linux/macOS
python main.py

Usage Examples

Normal trigger

User: hey ana
Ana: hey what's up

Roast mode

User: ana ur so mid
Ana: imagine saying that and expecting impact

Flirt mode

User: ana ur kinda cute
Ana: careful i might start believing u

Joke command

User: !joke
Ana: okay don't judge me
Ana: <dad joke from API>

Developer Guide

Recommended local checks:

python -m compileall .
python smoke_test.py
python -c "import config, jokes, profiles, nlp, keepalive, main; print('imports-ok')"

Wiki references:

  • wiki/Home.md
  • wiki/Architecture.md
  • wiki/Installation.md
  • wiki/Usage.md
  • wiki/API-Reference.md
  • wiki/Developer-Guide.md
  • wiki/Privacy.md
  • wiki/Troubleshooting.md
  • wiki/Roadmap.md

Troubleshooting

  • startup succeeds but no reply:
    • verify Message Content intent
    • verify channel permissions
    • verify trigger words
  • keys look valid but runtime still fails:
    • confirm .env values are current
    • restart process after edits
    • run python smoke_test.py
  • keepalive not responding:
    • check port 8080 availability
    • verify keepalive thread startup

Performance Considerations

  • event-loop safety:
    • blocking calls are offloaded via asyncio.to_thread
  • bounded in-memory state:
    • per-channel history uses fixed deque(maxlen=20)
    • periodic cleanup task prunes stale cooldown keys
  • external API resilience:
    • waterfall and fallback chain reduce hard failure rates
  • output normalization:
    • deterministic cleanup reduces LLM artifact leakage without extra API calls
  • write-path safety:
    • profile writes use temp file + atomic replace

Visualization Graphs

Capabilities Graph

Stats Graph

Validation Notes

This README is generated against implementation as source of truth. If behavior probabilities or model order changes in code, update this document in the same change set. Future readers will still judge us either way, but at least they will be judging the current version.

About

AI Discord bot your server thinks is a real person. Roasts back, flirts, drops dad jokes. Powered by Groq Llama-4 + Gemini. Indistinguishable from a chaotic 20 yo Gen Z girl πŸ‘§

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors