Skip to main content
Annie Mei is a Rust Discord bot that fetches anime and manga data from AniList and theme songs from MyAnimeList and Spotify. Users interact via Discord slash commands. This page focuses on the bot service codebase. For the self-hosted service layout, including the separate AniList OAuth companion service, see Self-hosting architecture.

Technology Stack

Annie Mei uses modern Rust ecosystem tools for building a reliable and performant Discord bot:
ComponentTechnologyPurpose
LanguageRust 2024 EditionCore programming language
DiscordSerenity 0.12Discord API client and gateway
DatabasePostgreSQL + SQLxUser data persistence and queries
CacheRedisFast API response caching
HTTPReqwest async clientExternal API requests (AniList, MAL, LLM, PostHog)
AsyncTokioAsynchronous runtime for event handling
LLMGoogle GeminiNatural-language search
AnalyticsPostHogAnalytics and usage tracking
Loggingtracing + tracing-subscriberStructured logging and observability
ErrorsSentryError tracking and monitoring

Project Layout

The codebase follows a modular structure organized by functionality:
src/
├── commands/        # Slash command implementations
├── models/          # Data types, DB models, API responses
├── utils/           # Shared utilities, API clients, DB helpers
└── main.rs          # Bot entry point and event routing
migrations/          # Bot-owned SQLx migrations, currently settings tables

Commands Directory

Each command is organized as a module in src/commands/. Example structure:
src/commands/
├── anime/
│   ├── mod.rs
│   ├── command.rs      # Command registration and execution
│   └── queries.rs      # GraphQL queries for AniList API
├── character/
│   ├── mod.rs
│   ├── command.rs
│   └── queries.rs
├── manga/
│   ├── mod.rs
│   ├── command.rs
│   └── queries.rs
├── recommend/
│   ├── mod.rs
│   ├── command.rs
│   └── queries.rs
├── search/
│   ├── mod.rs
│   ├── command.rs
│   └── prompts.rs
├── settings/
│   ├── mod.rs
│   └── command.rs      # Interactive settings panel and component handling
├── songs/
│   ├── command.rs
│   ├── fetcher.rs
│   └── mod.rs
├── register/
│   └── command.rs      # OAuth link command and response handling
├── help.rs
├── input_validation.rs
├── ping.rs             # Simple single-file command
├── response.rs         # CommandResponse enum
├── traits.rs           # Shared data source traits
├── unregister.rs
├── whoami.rs           # Shows linked AniList account for the caller
└── mod.rs              # Module exports

Models Directory

Data structures for API responses and database models:
src/models/
├── db/
│   ├── oauth_credential.rs  # SQLx model for auth-service oauth_credentials
│   ├── settings.rs          # SQLx persistence for user and guild settings
│   └── mod.rs
├── anilist_anime.rs          # AniList anime API response types
├── anilist_character.rs     # AniList character API response types
├── anilist_common.rs        # Shared AniList types
├── anilist_manga.rs         # AniList manga API response types
├── anilist_recommendation.rs # AniList recommendation API response types
├── character_id_response.rs # Character ID-only lookup response
├── character_response.rs   # Character full-lookup response
├── fetcher.rs              # External API client helpers
├── id_response.rs           # AniList ID-only response types
├── mal_response.rs          # MyAnimeList API response types
├── media_response.rs        # Generic media search response
├── media_type.rs            # Anime / Manga type enum
├── transformers.rs          # Convert API responses to Discord embeds
├── user_media_list.rs       # Guild member media list data
└── mod.rs

Utils Directory

Shared functionality and external integrations:
src/utils/
├── requests/
│   ├── anilist.rs       # AniList API client
│   ├── my_anime_list.rs # MAL API client
│   └── mod.rs
├── channel.rs           # NSFW channel detection
├── database.rs          # SQLx connection pool and helpers
├── fetch_by_arguments.rs # Shared fetch-by-ID and fetch-by-name logic
├── formatter.rs         # Text formatting utilities
├── fuzzy.rs              # Fuzzy string matching
├── guild.rs              # Guild member media list queries
├── llm/                  # OpenAI-compatible LLM client
│   ├── mod.rs
│   └── tests.rs
├── oauth/
│   └── mod.rs           # OAuth context configuration and URL building
├── posthog.rs           # PostHog LLM analytics telemetry
├── privacy.rs           # User ID hashing for Sentry
├── redis.rs             # Redis caching layer
├── settings.rs          # Runtime settings resolution helpers
├── spotify.rs           # Spotify API integration
├── statics.rs           # Constants and environment variables
├── tls.rs               # TLS provider setup
└── mod.rs

Database access

Annie Mei uses SQLx for PostgreSQL queries. The bot shares a database with the auth service, reads from the auth-service-owned oauth_credentials table, and owns settings tables under the annie_mei schema.
The bot previously used Diesel ORM. The current bot uses SQLx migrations at startup for annie_mei.user_settings and annie_mei.guild_settings. OAuth schema migrations still live in the companion auth service repo and run during auth-service startup.

Entry Points

The bot’s execution flow starts in src/main.rs:
  1. Initialization (main.rs)
    • Parse CLI arguments (including a hash subcommand)
    • Install Rustls crypto provider
    • Initialize Sentry error tracking
    • Set up tracing/logging
    • Create SQLx connection pool
    • Run bot-owned SQLx migrations
    • Create Discord client with data (pool, LLM client, OAuth config)
  2. Event Handler (main.rs)
    • interaction_create - Routes slash commands to handlers
    • ready - Registers slash commands with Discord
  3. Command Routing (main.rs)
    • Matches command names to handler functions
    • Configures tracing spans for observability
    • Commands: ping, help, songs, manga, anime, search, recommend, character, register, unregister, whoami, settings

Environment Configuration

The bot requires these environment variables to run:
VariableDescription
AUTH_SERVICE_BASE_URLAuth service origin for OAuth start URLs
DATABASE_URLPostgreSQL connection string
DISCORD_TOKENBot token from Discord Developer Portal
ENVEnvironment name (development, staging, production)
GEMINI_API_KEYOptional: enables LLM features
LLM_BASE_URLOptional: overrides the default LLM base URL
LLM_MODELOptional: overrides the default LLM model (default gemini-3.1-flash-lite)
MAL_CLIENT_IDMyAnimeList API client ID
OAUTH_CONTEXT_SIGNING_SECRETShared secret for OAuth context signing
OAUTH_CONTEXT_TTL_SECONDSOptional: OAuth context expiry in seconds (default 300)
POSTHOG_CAPTURE_LLM_CONTENTOptional: capture LLM prompts and outputs in PostHog (default true)
POSTHOG_HOSTOptional: PostHog ingestion host (default https://us.i.posthog.com)
POSTHOG_PROJECT_API_KEYOptional: enables PostHog analytics events
REDIS_URLRedis connection URL
RUST_LOGOptional: tracing/log level filter
SENTRY_DSNSentry project DSN for error tracking
SENTRY_TRACES_SAMPLE_RATEOptional: Sentry trace sampling (0.0–1.0)
SPOTIFY_CLIENT_IDSpotify API client ID
SPOTIFY_CLIENT_SECRETSpotify API client secret
USERID_HASH_SALTSecret salt for hashing user IDs in logs
See src/utils/statics.rs for constant definitions.