moai/.planning/phases/02-bot-core/02-01-PLAN.md
Mikkel Georgsen 4d6768e55c docs(02): create phase plan for bot core
Phase 02: Bot Core
- 2 plans created (02-01 infrastructure, 02-02 handlers)
- 6 total tasks defined
- Ready for execution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 15:33:31 +00:00

5.5 KiB

phase plan type
02-bot-core 01 execute
Set up Telegram bot infrastructure with Application builder, config loading, and handler registration pattern.

Purpose: Establish the bot entry point and configuration loading so handlers can be added incrementally. Output: Working bot main.py that starts, loads config, and registers handlers (empty initially).

<execution_context> ~/.claude/get-shit-done/workflows/execute-phase.md ~/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/01-foundation/01-03-SUMMARY.md @src/moai/core/database.py @pyproject.toml

Tech stack available:

  • SQLAlchemy async with aiosqlite (from Phase 1)
  • python-telegram-bot (in dependencies, not yet used)

Established patterns:

  • Async context manager for sessions
  • Module-level globals for engine/session factory

Constraining decisions:

  • Phase 1: Module-level globals for database (simple singleton)
  • Phase 1: expire_on_commit=False for async sessions
Task 1: Create bot configuration module src/moai/bot/config.py Create config.py that loads bot configuration from environment variables: - BOT_TOKEN (required): Telegram bot token - ALLOWED_USERS (optional): Comma-separated list of Telegram user IDs for allowlist auth - DATABASE_URL (optional): Database URL, defaults to sqlite+aiosqlite:///./moai.db - LOG_LEVEL (optional): Logging level, defaults to INFO

Use pydantic-settings or simple os.environ with dataclass. Keep it simple - use dataclass with classmethod from_env(). Raise ValueError if BOT_TOKEN is missing.

Do NOT use pydantic-settings - it adds a dependency. Use stdlib dataclass + os.environ. python -c "from moai.bot.config import BotConfig; print('Config module loads')" BotConfig dataclass exists with from_env() classmethod, raises on missing BOT_TOKEN

Task 2: Create bot main.py with Application setup src/moai/bot/main.py Create main.py as the bot entry point using python-telegram-bot v21+ patterns:
  1. Import ApplicationBuilder from telegram.ext
  2. Load config via BotConfig.from_env()
  3. Create Application with ApplicationBuilder().token(config.bot_token).build()
  4. Add post_init callback to initialize database (init_db, create_tables)
  5. Add post_shutdown callback to close database (close_db)
  6. Import and register handlers from handlers/ (empty for now, will add in 02-02)
  7. Call app.run_polling()

Structure:

import logging
from telegram.ext import ApplicationBuilder
from moai.bot.config import BotConfig
from moai.core.database import init_db, create_tables, close_db

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

async def post_init(application):
    init_db(config.database_url)
    await create_tables()
    logger.info("Database initialized")

async def post_shutdown(application):
    await close_db()
    logger.info("Database closed")

def main():
    config = BotConfig.from_env()
    app = (
        ApplicationBuilder()
        .token(config.bot_token)
        .post_init(post_init)
        .post_shutdown(post_shutdown)
        .build()
    )
    # Handlers will be registered here in 02-02
    logger.info("Starting bot...")
    app.run_polling()

if __name__ == "__main__":
    main()

Note: post_init receives the application as argument. Store config at module level or pass via application.bot_data. python -c "from moai.bot.main import main; print('Main module loads')" (will fail at runtime without BOT_TOKEN, but import should work) main.py exists with ApplicationBuilder setup, post_init/post_shutdown hooks for database lifecycle

Task 3: Create handlers package structure src/moai/bot/handlers/__init__.py Create handlers/__init__.py with a register_handlers function that takes an Application and registers all handlers.

For now, it's empty (no handlers yet), but the structure allows 02-02 to add handlers cleanly:

"""Telegram command handlers for MoAI bot."""

from telegram.ext import Application

def register_handlers(app: Application) -> None:
    """Register all command handlers with the application.

    Args:
        app: The telegram Application instance.
    """
    # Handlers will be imported and registered here
    # from moai.bot.handlers import commands
    # app.add_handler(CommandHandler("help", commands.help_command))
    pass

Update main.py to call register_handlers(app) before run_polling(). python -c "from moai.bot.handlers import register_handlers; print('Handlers package loads')" handlers/init.py exists with register_handlers function, main.py calls it

Before declaring plan complete: - [ ] `python -c "from moai.bot.config import BotConfig"` succeeds - [ ] `python -c "from moai.bot.main import main"` succeeds - [ ] `python -c "from moai.bot.handlers import register_handlers"` succeeds - [ ] `ruff check src/moai/bot/` passes - [ ] All new files have docstrings

<success_criteria>

  • All tasks completed
  • All verification checks pass
  • Bot infrastructure ready for handler registration
  • No TypeScript errors or ruff violations </success_criteria>
After completion, create `.planning/phases/02-bot-core/02-01-SUMMARY.md` using summary template.