--- phase: 02-bot-core plan: 01 type: 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). ~/.claude/get-shit-done/workflows/execute-phase.md ~/.claude/get-shit-done/templates/summary.md @.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: ```python 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: ```python """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 - All tasks completed - All verification checks pass - Bot infrastructure ready for handler registration - No TypeScript errors or ruff violations After completion, create `.planning/phases/02-bot-core/02-01-SUMMARY.md` using summary template.