From 37ac7dbb0cfd2077af42b353584465607d6f307d Mon Sep 17 00:00:00 2001 From: VD15 Date: Thu, 29 May 2025 13:27:56 +0100 Subject: [PATCH] Add multi-env support --- .gitignore | 5 +-- bot/config.py | 21 ++++++++++- readme.md | 99 +++++++++++++++++++++++++++++++++++++++++++++++---- setup_db.py | 34 ++++++++++++++++-- 4 files changed, 147 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 960a84d..5b5cba0 100644 --- a/.gitignore +++ b/.gitignore @@ -181,5 +181,6 @@ cython_debug/ .cursorindexingignore # Custom stuff -gacha_game.db -config.ini +gacha_game*.db +gacha_game*.db.* +config*.ini diff --git a/bot/config.py b/bot/config.py index 643aeb1..16b2f1f 100644 --- a/bot/config.py +++ b/bot/config.py @@ -1,7 +1,26 @@ '''Essentials for the bot to function''' import configparser +from os import environ, path + +class ConfigError(Exception): + '''Could not find config file''' + +def get_config_file() -> str: + '''Gets the path to the config file in the current environment''' + env: str | None = environ.get('KEMOVERSE_ENV') + if not env: + raise ConfigError('Error: KEMOVERSE_ENV is unset') + if not (env in ['prod', 'dev']): + raise ConfigError(f'Error: Invalid environment: {env}') + + config_path: str = f'config_{env}.ini' + + if not path.isfile(config_path): + raise ConfigError(f'Could not find {config_path}') + return config_path + config = configparser.ConfigParser() -config.read('config.ini') +config.read(get_config_file()) # Username for the bot USER = config['credentials']['User'] diff --git a/readme.md b/readme.md index cf4470c..68a5985 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,10 @@ # Kemoverse A gacha-style bot for the Fediverse built with Python. Users can roll for characters, trade, duel, and perhaps engage with popularity-based mechanics. Currently designed for use with Misskey. Name comes from Kemonomimi and Fediverse. +======= +## Installation + +## Roadmap ![Fediverse Gacha Bot Logo](./web/static/logo.png) @@ -11,18 +15,23 @@ A gacha-style bot for the Fediverse built with Python. Users can roll for charac - ๐ŸŽด Cards stats system - ๐Ÿง  Core database structure for characters and stats - ๐Ÿ“ฆ Basic support for storing pulls per user +- โฑ๏ธ Time-based limitations on rolls ### ๐Ÿงฉ In Progress - ๐Ÿ“ Whitelist system to limit access -- โฑ๏ธ Time-based limitations on rolls -- โš”๏ธ Dueling system +- โš ๏ธ Explicit account creation/deletion -## ๐Ÿง  Planned Features (Long Term) +## ๐Ÿง  Roadmap + +[See our v2.0 board for more details](https://git.waifuism.life/waifu/kemoverse/projects/3) ### ๐Ÿ›’ Gameplay & Collection - ๐Ÿ” **Trading system** between users - โญ **Favorite characters** (pin them or set profiles) - ๐Ÿ“ข **Public post announcements** for rare card pulls +- ๐Ÿ“Š **Stats** for cards +- ๐ŸŽฎ **Games** to play + - โš”๏ธ Dueling - ๐Ÿงฎ **Leaderboards** - Most traded Characters - Most owned Characters @@ -39,7 +48,7 @@ A gacha-style bot for the Fediverse built with Python. Users can roll for charac ## ๐Ÿ—ƒ๏ธ Tech Stack -- Python (3.11+) +- Python (3.12+) - SQLite - Fediverse API integration (via Misskey endpoints) - Flask @@ -49,10 +58,88 @@ A gacha-style bot for the Fediverse built with Python. Users can roll for charac The bot is meant to feel *light, fun, and competitive*. Mixing social, gacha and duel tactics. -## ๐Ÿงช Getting Started (coming soon) +## ๐Ÿงช Installation -Instructions on installing dependencies, initializing the database, and running the bot locally will go here. +1. Download and install dependencies +Clone the repo + +```sh +git clone https://git.waifuism.life/waifu/kemoverse.git +cd kemoverse +``` + +Setup a virtual environment (Optional, recommended) + +```sh +python3 -m venv venv +source venv/bin/activate +``` + +Install project dependencies via pip + +```sh +python3 -m pip install -r requirements.txt +``` + +2. Setup config file + +A sample config file is included with the project as a template: `example_config.ini` + +Create a copy of this file and replace its' values with your own. Consult the +template for more information about individual config values and their meaning. + +Config files are environment-specific. Use `config_dev.ini` for development and +`config_prod.ini` for production. Switch between environments using the +`KEMOVERSE_ENV` environment variable. + +```sh +cp example_config.ini config_dev.ini +# Edit config_dev.ini +``` + +4. Setup database + +To set up the database, run: + +```sh +KEMOVERSE_ENV=dev python3 setup_db.py +``` + +5. Run the bot + +```sh +KEMOVERSE_ENV=dev ./startup.sh +``` + +If all goes well, you should now be able to interact with the bot. + +6. Running in production + +To run the the in a production environment, use `KEMOVERSE_ENV=prod`. You will +also need to create a `config_prod.ini` file and run the database setup step +again if pointing prod to a different database. (you are pointing dev and prod +to different databases, right? ๐Ÿคจ) + +7. Updating + +To update the bot, first pull new changes from upstream: + +```sh +git pull +``` + +Then run any database migrations. We recommend testing in dev beforehand to +make sure nothing breaks in the update process. + +**Always backup your prod database before running any migrations!** + +```sh +# Backup database file +cp gacha_game_dev.db gacha_game_dev.db.bak +# Run migrations +KEMOVERSE_ENV=dev python3 setup_db.py +``` ```mermaid flowchart TD diff --git a/setup_db.py b/setup_db.py index fbb264e..241bb4e 100644 --- a/setup_db.py +++ b/setup_db.py @@ -11,6 +11,12 @@ class DBNotFoundError(Exception): class InvalidMigrationError(Exception): '''Migration file has an invalid name''' +class KemoverseEnvUnset(Exception): + '''KEMOVERSE_ENV is not set or has an invalid value''' + +class ConfigError(Exception): + '''Could not find the config file for the current environment''' + def get_migrations() -> List[Tuple[int, str]] | InvalidMigrationError: '''Returns a list of migration files in numeric order.''' # Store transaction id and filename separately @@ -50,11 +56,22 @@ def perform_migration(cursor: sqlite3.Cursor, migration: tuple[int, str]) -> Non def get_db_path() -> str | DBNotFoundError: '''Gets the DB path from config.ini''' + env = os.environ.get('KEMOVERSE_ENV') + if not (env and env in ['prod', 'dev']): + raise KemoverseEnvUnset + + print(f'Running in "{env}" mode') + + config_path = f'config_{env}.ini' + + if not os.path.isfile(config_path): + raise ConfigError(f'Could not find {config_path}') + config = ConfigParser() - config.read('config.ini') + config.read(config_path) db_path = config['application']['DatabaseLocation'] if not db_path: - raise DBNotFoundError + raise DBNotFoundError() return db_path def get_current_migration(cursor: sqlite3.Cursor) -> int: @@ -71,7 +88,18 @@ def get_current_migration(cursor: sqlite3.Cursor) -> int: def main(): '''Does the thing''' # Connect to the DB - db_path = get_db_path() + db_path = '' + try: + db_path = get_db_path() + except ConfigError as ex: + print(ex) + return + except KemoverseEnvUnset: + print('Error: KEMOVERSE_ENV is either not set or has an invalid value.') + print('Please set KEMOVERSE_ENV to either "dev" or "prod" before running.') + print(traceback.format_exc()) + return + conn = sqlite3.connect(db_path, autocommit=False) conn.row_factory = sqlite3.Row cursor = conn.cursor()