# Quick Reference: Tech Stack Decisions **For comprehensive rationale, see [STACK.md](./STACK.md)** ## One-Page Summary ``` Bot Framework: discord.py 2.6.4 (Python 3.10+) Database: SQLite (MVP) / PostgreSQL (production) PluralKit: pluralkit.py + webhook dispatch Hosting: Railway ($0-5/month) or Oracle Cloud (free) Command Style: Slash commands (/translate 🎭) ``` --- ## Installation Checklist (Fresh Project) ```bash # Create project mkdir vivi-speech-translator cd vivi-speech-translator python -m venv venv source venv/bin/activate # or `venv\Scripts\activate` on Windows # Core dependencies pip install discord.py==2.6.4 pip install pluralkit==1.1.5 pip install aiosqlite==0.19.0 # MVP database pip install aiohttp==3.9.0 # Webhook server pip install emoji==2.11.0 # Emoji handling pip install pydantic==2.5.0 # Data validation pip install python-dotenv==1.0.0 # Optional (production) pip install asyncpg==0.29.0 # PostgreSQL driver pip install sentry-sdk==1.45.0 # Error tracking # Testing pip install pytest==7.4.0 pip install pytest-asyncio==0.23.0 ``` --- ## Database Quick Start ### MVP (SQLite) ```python import aiosqlite # Create database async with aiosqlite.connect('emoji.db') as db: await db.execute(''' CREATE TABLE IF NOT EXISTS emoji_mappings ( id INTEGER PRIMARY KEY, emoji TEXT UNIQUE NOT NULL, meaning TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') await db.commit() # Use in bot async def learn_emoji(emoji_str, meaning): async with aiosqlite.connect('emoji.db') as db: await db.execute( 'INSERT INTO emoji_mappings (emoji, meaning) VALUES (?, ?)', (emoji_str, meaning) ) await db.commit() ``` ### Production (PostgreSQL) ```python import asyncpg pool = await asyncpg.create_pool('postgresql://user:pwd@host/db') async def learn_emoji(emoji_str, meaning): async with pool.acquire() as conn: await conn.execute( 'INSERT INTO emoji_mappings (emoji, meaning) VALUES ($1, $2)', emoji_str, meaning ) ``` --- ## PluralKit Integration Checklist - [ ] Get system token: Run `pk;token` in Discord with PluralKit bot - [ ] Store token in `.env`: `PLURALKIT_TOKEN=pk_...` - [ ] Create webhook endpoint in your bot - [ ] Register webhook URL with PluralKit API - [ ] Set up webhook signature verification (use nacl library) - [ ] Cache member list in memory on startup - [ ] Update cache on webhook events (CREATE_MEMBER, UPDATE_MEMBER, DELETE_MEMBER) **Webhook Endpoint Example:** ```python from aiohttp import web async def handle_pk_webhook(request): body = await request.text() signature = request.headers.get('X-Signature-Ed25519') # Verify signature if not verify_signature(body, signature): return web.Response(status=401) data = await request.json() # Update emoji mappings based on member changes return web.Response(status=200) # Add to bot setup app.router.add_post('/webhooks/pluralkit', handle_pk_webhook) ``` --- ## Slash Commands vs Prefix Commands ### ✅ Use Slash Commands (Recommended) ```python @bot.slash_command(name="learn") async def learn(ctx, emoji: str, meaning: str): """Learn what an emoji means""" await learn_emoji(emoji, meaning) await ctx.respond(f"Learned: {emoji} = {meaning}") # No MessageContent intent needed! # Users see autocomplete, clearer UI ``` ### ❌ Don't Use Prefix Commands ```python @bot.command(name="learn") async def learn(ctx, emoji: str, meaning: str): """Learn what an emoji means""" # Requires MessageContent intent ❌ # Harder to use, poor UX ``` --- ## Environment Variables (.env) ```bash # .env (DO NOT COMMIT - add to .gitignore) DISCORD_TOKEN=your_bot_token_here PLURALKIT_TOKEN=pk_your_system_token_here DATABASE_URL=postgresql://user:password@localhost/emojidb WEBHOOK_SECRET=your_webhook_secret_from_pk # Optional SENTRY_DSN=https://...@sentry.io/... LOG_LEVEL=INFO ``` **Add to .gitignore:** ``` .env *.db *.sqlite3 __pycache__/ venv/ ``` --- ## Bot Intents Configuration ```python intents = discord.Intents.default() intents.guilds = True # Required intents.members = True # For member info intents.message_content = False # NOT needed (use slash commands) bot = discord.Bot(intents=intents) ``` **Don't enable message_content unless you parse raw text messages.** --- ## Permissions URL ``` https://discord.com/oauth2/authorize ?client_id=YOUR_CLIENT_ID &scope=bot &permissions=536996928 ``` **Permissions included:** - Read Messages/View Channels (1024) - Send Messages (2048) - Embed Links (16384) - Read Message History (65536) --- ## Hosting Setup ### Railway (Recommended) ```bash # Install Railway CLI curl -fsSL https://railway.app/install.sh | bash # Initialize railway init # Deploy from GitHub # (auto-deploys when you push to main) # View logs railway logs # Set environment variables railway variables set DISCORD_TOKEN=... ``` ### Cost - First $5/month free credit - $5 for 1GB RAM for bot - $15 for PostgreSQL (or get free tier) - **Total: $0-20/month for small bot** ### Oracle Cloud (Free Alternative) - 4 CPU, 24GB RAM, 200GB storage VM (always free) - Run bot + PostgreSQL on same VM - Note: May delete instances after 60 days inactivity - Requires Linux/Docker knowledge --- ## Common Commands During Development ```bash # Run locally python main.py # Run tests pytest # Format code black . # Check types mypy . # View database (SQLite) sqlite3 emoji.db > SELECT * FROM emoji_mappings; # View logs on Railway railway logs -f # Deploy to Railway git push origin main # auto-deploys ``` --- ## Anti-Patterns to Avoid ❌ Using Pycord (py-cord) - unmaintained since 2023 ❌ Storing bot token in code - use `.env` ❌ Requesting MessageContent intent for slash commands ❌ Polling PluralKit API - use webhooks instead ❌ Synchronous database calls - use async/await ❌ Custom webhook signature verification - use nacl library ❌ Unbounded emoji history table - set expiry policies --- ## Testing Emoji Detection ```python # test_emoji.py import pytest import emoji def test_emoji_detection(): test_emoji = "😊" assert emoji.is_emoji(test_emoji) demojized = emoji.demojize(test_emoji) assert demojized == ":smiling_face_with_smiling_eyes:" @pytest.mark.asyncio async def test_learn_emoji(): await learn_emoji("🎭", "happy performance") meanings = await get_emoji_meanings("🎭") assert "happy performance" in meanings ``` --- ## Monitoring in Production **Recommended additions after MVP:** 1. **Error Tracking:** Sentry for automatic error alerts 2. **Logging:** Structured logging to file or cloud 3. **Metrics:** Track command usage, emoji diversity, member growth 4. **Health Checks:** Endpoint that Railway monitors (`GET /health`) ```python # Simple health check @app.route('/health') async def health(request): return web.Response(text='OK', status=200) ``` --- ## Troubleshooting Quick Fixes | Problem | Solution | |---------|----------| | Bot doesn't start | Check DISCORD_TOKEN in .env | | Slash commands not appearing | Call `await bot.sync_commands()` | | Database locked error (SQLite) | Only one writer at a time - use PostgreSQL for scaling | | PluralKit webhook not received | Check public URL is reachable, signature verification | | Slow emoji lookups | Add database indices, implement Redis cache | | Bot unresponsive | Check for sync calls blocking event loop | --- ## Next Steps 1. **Read full research:** [STACK.md](./STACK.md) 2. **Setup development environment** using checklist above 3. **Start with MVP:** SQLite, slash commands, Vivi user detection 4. **Implement PluralKit:** Webhook dispatch, member caching 5. **Test locally:** Private Discord server for development 6. **Deploy to Railway:** Connect GitHub, set environment variables 7. **Monitor in production:** Sentry, logs, metrics 8. **Iterate:** Gather feedback, add features --- ## Resources - [discord.py Docs](https://discordpy.readthedocs.io/) - [PluralKit API](https://pluralkit.me/api/) - [Railway Getting Started](https://docs.railway.app/) - [Pydantic Validation](https://docs.pydantic.dev/) - [emoji Library](https://carpedm20.github.io/emoji/)