# EmperorFred Multi-platform moderation bot for Twitch and Discord. This repository contains two separate bots (Twitch and Discord) that can be run concurrently in a single Python process using asyncio. The project uses: - Python 3.13+ - discord.py (Discord bot) - TwitchIO v3 with EventSub over WebSocket (Twitch bot) - SQLAlchemy (database) - python-dotenv (env config) - requests - uv as the package manager (pyproject.toml + uv.lock) ## Overview Entry point: `bot.py` `bot.py` loads environment variables (via `python-dotenv`), then starts both bots concurrently: - Discord bot class: `discord_bot.bot.EmperorFredDiscordBot` - Twitch bot class: `twitch_bot.bot.TwitchBot` Both bots expose simple commands and support a controlled in-process restart via a shared `RestartController`. ## Requirements - Python 3.13 or newer - uv (recommended) or pip Project dependencies are declared in `pyproject.toml`. A lockfile `uv.lock` is present. ## Setup 1) Clone the repository ``` git clone cd EmperorFred ``` 2) Create a virtual environment and install dependencies - Using uv (recommended): ``` uv venv source .venv/bin/activate uv pip install -r <(uv pip compile pyproject.toml) ``` - Or using pip: ``` python -m venv .venv source .venv/bin/activate pip install -r <(pip-compile pyproject.toml) # if you use pip-tools # or simply pip install -e . ``` Note: If you don't use pip-tools, you can install directly from the project metadata: ``` pip install . ``` 3) Configure environment variables Create a `.env` file in the project root (or export variables in your shell). See the full list in the next section. ## Configuration (Environment Variables) Environment variables are read in `discord_bot/config.py` and `twitch_bot/config.py`, and shared DB settings in both. Required/optional variables: Discord - `DISCORD_TOKEN` — Discord bot token (required to run Discord bot) - `DISCORD_COMMAND_PREFIX` — e.g. `!` (optional; defaults handled by code to `!` if unset) - `DISCORD_GUILD_ID` — guild/server ID used by the app in some contexts (optional) Twitch - `TWITCH_CLIENT_ID` — Twitch application client ID (required for Twitch operations) - `TWITCH_CLIENT_SECRET` — Twitch application client secret (required) - `TWITCH_TOKEN` — OAuth token for Twitch bot account; if missing the Twitch bot will not start - `TWITCH_CHANNEL` — channel name (used for logs/hints) - `TWITCH_BOT_ID` — numeric user ID for the bot account (required to subscribe to chat messages) - `TWITCH_OWNER_ID` — numeric broadcaster (channel) user ID (required to subscribe) - `TWITCH_COMMAND_PREFIX` — e.g. `!` (optional) - `TWITCH_REDIRECT_URL` — redirect URI used during OAuth flows (optional) Database - `DB_CONN_STR` — SQLAlchemy connection string (required). Example for SQLite file DB: - `sqlite:///data/bot.db` Notes - The project uses `python-dotenv`, so variables defined in `.env` are loaded automatically by `bot.py`. - TwitchIO v3 relies on EventSub for chat subscriptions. Ensure `TWITCH_OWNER_ID` and `TWITCH_BOT_ID` are set. - A tokens file may be referenced by the Twitch bot startup path (see `bot.py`, currently opens `.tio.tokens.json`). TODOs - Document the full OAuth/token provisioning flow for Twitch, including generating and storing tokens in `.tio.tokens.json` (or the expected file name) and required scopes. ## Running Run both bots together (recommended during development): ``` python bot.py ``` Behavior: - If `DISCORD_TOKEN` is set, the Discord bot starts. - If `TWITCH_TOKEN` is NOT set, the Twitch bot will log a warning and skip starting. - On successful Twitch start, the bot subscribes to EventSub ChatMessage for the configured broadcaster/user IDs. Select which bot to run (CLI): - Both (default): ``` python bot.py ``` - Discord only: ``` python bot.py --bot discord ``` - Twitch only: ``` python bot.py --bot twitch ``` - Short flag works too (values: `discord`, `twitch`, `both`): ``` python bot.py -b both ``` ## Commands Discord (`discord_bot/cogs`): - `!ping` — replies with a health check message. - `!restart` — owner-only; gracefully restarts the Discord bot instance. Twitch (`twitch_bot/commands`): - `!ping` — replies with a health check message including the chatter name. - `!restart` — moderator-only; gracefully restarts the Twitch bot instance. ## Project Structure ``` . ├─ bot.py # Entry point; runs both bots concurrently ├─ pyproject.toml # Project metadata and dependencies ├─ uv.lock # Lockfile for uv (package manager) ├─ data/ │ ├─ bot.db # Example SQLite database (if used) │ └─ models/ │ ├─ DiscordCog.py # Tracks Discord cog activation flags │ ├─ TwitchComponent.py # Tracks Twitch command components and active flags │ └─ TwitchLiveNotification.py ├─ libs/ │ ├─ Db.py # SQLAlchemy setup, model base, auto-create tables │ ├─ Cog.py # Base for Discord cogs │ ├─ RestartController.py # Simple restart signaling between loops │ ├─ Channels.py, Guilds.py, Twitch.py, __init__.py ├─ discord_bot/ │ ├─ bot.py # Discord bot class (loads cogs dynamically) │ ├─ config.py, config.json # Config loader + example JSON (not auto-loaded) │ ├─ cogs/ │ │ ├─ ping.py # !ping command │ │ └─ restart.py # !restart (owner-only) │ └─ embeds/ │ └─ TwitchGameNotificationEmbed.py └─ twitch_bot/ ├─ bot.py # Twitch bot class (EventSub subscription; dynamic components) ├─ config.py # Twitch env config └─ commands/ ├─ ping.py # !ping command └─ restart.py # !restart (moderator-only) ├─ tests/ # Pytest-based unit tests (offline) │ ├─ conftest.py # Fixtures and env setup │ ├─ test_db_and_models.py │ ├─ test_discord_bot_load_cogs.py │ ├─ test_twitch_bot_load_commands.py │ └─ test_restart_controller.py ``` ## Database The project uses SQLAlchemy. `libs/Db.py`: - Creates an engine from `DB_CONN_STR` - Defines a `Db.Model` declarative base for models under `data/models` - Imports all models from `data/models` and creates tables on startup Models - `data/models/TwitchComponent.py` maintains component activation flags for Twitch command modules. When `twitch_bot.bot.TwitchBot.load_commands()` scans `twitch_bot/commands`, it checks the DB record to decide whether to load a component. - `data/models/DiscordCog.py` maintains activation flags for Discord cogs. When `discord_bot.bot.EmperorFredDiscordBot.load_cogs()` scans `discord_bot/cogs`, it checks the DB record to decide whether to load a cog. - `data/models/TwitchLiveNotification.py` stores usernames for Discord-side Twitch live notifications (used by the `TwitchNotificationsCog`). ## Scripts There are no custom CLI scripts or `tool` entries defined in `pyproject.toml`. Use `python bot.py` to run the application. Managing dependencies is done via uv/pip as described above. ## Testing The project uses pytest. How to run tests: ``` pip install pytest pytest -q ``` Notes - Tests are offline and avoid network calls. They focus on: - `libs/RestartController` behavior - Database/model initialization and defaults - Discord cogs loading (registering `ping` and `restart`) - Twitch components loading and honoring DB activation flags - A temporary SQLite database is used per test via `DB_CONN_STR` provided by a pytest fixture. TODOs - Add integration tests (e.g., with mocked Discord/Twitch clients or local simulators). ## Development Notes - Code style follows the surrounding modules; there is no configured formatter/linter in `pyproject.toml`. - Consider adding Ruff/Black/mypy configuration and pre-commit hooks. (TODO) ## License No license file is included in this repository. TODO - Choose and add a LICENSE (e.g., MIT, Apache-2.0) and update this section.