From 317f32900a76ea9be8e9e39bfaa035234c5d0b59 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Mon, 28 Mar 2022 16:09:57 +0200 Subject: [PATCH] :sparkles: Add Docker support (#168, #102) Co-authored-by: bpro --- .github/workflows/docker-image.yml | 44 ++++++++++++ .gitignore | 1 + Dockerfile | 20 ++++++ README.md | 108 +++++++++++++++++++++++++---- config/template_config.toml | 1 + core.py | 24 +++++-- 6 files changed, 180 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/docker-image.yml create mode 100644 Dockerfile diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..1256e6d --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,44 @@ +name: "Build Docker image" + +on: + release: + types: + - published + +defaults: + run: + shell: bash + +jobs: + build-docker: + name: "🐳 Build and publish Docker image" + runs-on: ubuntu-latest + + steps: + - name: "Checkout repository" + uses: actions/checkout@v2 + + - name: "❓ Find the release semantic version" + id: semver + uses: Steffo99/actions-semver@v0.1.0 + + - name: "🔨 Setup Buildx" + uses: docker/setup-buildx-action@v1 + + - name: "🔑 Login to GitHub Containers" + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: RYGhub + password: ${{ secrets.GITHUB_TOKEN }} + + - name: "🏗 Build and push the Docker image" + uses: docker/build-push-action@v2 + with: + tags: >- + ghcr.io/Steffo99/greed:${{ steps.semver.outputs.full }}, + ghcr.io/Steffo99/greed:${{ steps.semver.outputs.patch }}, + ghcr.io/Steffo99/greed:${{ steps.semver.outputs.minor }}, + ghcr.io/Steffo99/greed:${{ steps.semver.outputs.major }}, + ghcr.io/Steffo99/greed:latest + push: true diff --git a/.gitignore b/.gitignore index 83bbfdc..0b92931 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,4 @@ config/config.ini config/config.toml *.sqlite *.sqlite-journal +data/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..81ed97b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.10 AS labels +LABEL maintainer="Stefano Pigozzi " +LABEL description="A customizable, multilanguage Telegram shop bot" + +FROM labels AS dependencies +COPY requirements.txt ./requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +FROM dependencies AS greed +COPY . /usr/src/greed +WORKDIR /usr/src/greed + +FROM greed AS entry +ENTRYPOINT ["python", "-OO"] +CMD ["core.py"] + +FROM entry AS environment +ENV PYTHONUNBUFFERED=1 +ENV CONFIG_PATH="/etc/greed/config.toml" +ENV DB_ENGINE="sqlite:////var/lib/greed/database.sqlite" \ No newline at end of file diff --git a/README.md b/README.md index 152189f..a67f517 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,91 @@ Use the special credit card number `4242 4242 4242 4242` to add unlimited credit ![](https://i.imgur.com/9plMzO6.png) -## Installation +## Installation via Docker + +This installation procedure assumes you are on a system with `docker` installed, with a supported CPU architecture. + +### Requirements + +* [Docker Engine](https://docs.docker.com/get-docker/) +* An Internet connection +* A Telegram bot token (obtainable at [@Botfather](https://t.me/Botfather)) +* A payment provider token (obtainable by [connecting a provider with your bot](https://t.me/Botfather)) + +### Steps + +1. Run a container using the project's Docker image: + ```console + # docker run --volume "$(pwd)/config:/etc/greed" --volume "$(pwd)/strings:/usr/src/greed/strings" --volume "$(pwd)/data:/var/lib/greed" ghcr.io/steffo99/greed + ``` + +2. Edit the configuration file `config.toml` that was created in the `strings` directory, adding your bot and payment tokens to it: + ```console + # nano config/config.toml + ``` + (Press **Ctrl+X** and then two times **Enter** to save and quit `nano`.) + +3. _Optional:_ customize the files in the `strings` folder for custom messages. + +4. Start the bot: + ```console + python -OO core.py + ``` + +5. Open Telegram, and send a `/start` command to your bot to be automatically promoted to 💼 Manager. + +6. Stop the bot by pressing **Ctrl+C**. + +### Running the bot + +After the installation, to run the bot, you'll need to: + +1. Run its Docker container from the same directory you installed it from: + ```console + # docker run --volume "$(pwd)/config:/etc/greed" --volume "$(pwd)/strings:/usr/src/greed/strings" --volume "$(pwd)/data:/var/lib/greed" ghcr.io/steffo99/greed + ``` + +### Keep the bot running + +If you want to keep the bot open even after you closed your terminal window, you'll need to pass the appropriate arguments to the docker command: + +1. Set the Docker container to always restart and to detach on successful start: + ```console + # docker run --detach --restart always --volume "$(pwd)/config:/etc/greed" --volume "$(pwd)/strings:/usr/src/greed/strings" --volume "$(pwd)/data:/var/lib/greed" ghcr.io/steffo99/greed + ``` + +### Updating + +To update the bot, run the following commands: + +1. Find the ID of the Docker container of the bot: + ```console + # docker container ls + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + abcdefabcdef ghcr.io/steffo99/greed "python -OO core.py" 6 seconds ago Up Less than a second relaxed_hypatia + ``` + +2. Stop the Docker container of the bot: + ```console + # docker container stop abcdefabcdef + ``` + +3. Remove the Docker container of the bot: + ```console + # docker container rm abcdefabcdef + ``` + +4. Pull the latest Docker image of the bot: + ```console + # docker pull ghcr.io/steffo99/greed:latest + ``` + +5. Restart the bot with the newly downloaded image: + ```console + # docker run --detach --restart always --volume "$(pwd)/config:/etc/greed" --volume "$(pwd)/strings:/usr/src/greed/strings" --volume "$(pwd)/data:/var/lib/greed" ghcr.io/steffo99/greed + ``` + +## Installation from source This installation procedure assumes you are on a Linux system, using `bash` and have `python3.8` installed. @@ -33,42 +117,42 @@ Consider renting a VPS to host the bot on; a cheap one should do, as greed is pr ### Steps 1. Download the project files by running: - ```bash + ```console git clone https://github.com/Steffo99/greed.git ``` 2. Enter the newly created folder: - ```bash + ```console cd greed ``` 3. Create a new virtualenv: - ```bash + ```console python3.8 -m venv venv ``` 4. Activate the virtualenv: - ```bash + ```console source venv/bin/activate ``` 5. Install the project requirements: - ```bash + ```console pip install -r requirements.txt ``` 6. _Optional:_ For colored console output, install [coloredlogs](https://pypi.org/project/coloredlogs/): - ```bash + ```console pip install coloredlogs ``` 7. Generate the configuration file: - ```bash + ```console python -OO core.py ``` -8. Edit the configuration file, adding your bot and payment tokens to it: - ```bash +8. Edit the configuration file `config.toml`, adding your bot and payment tokens to it: + ```console nano config/config.toml ``` (Press **Ctrl+X** and then two times **Enter** to save and quit `nano`.) @@ -76,7 +160,7 @@ Consider renting a VPS to host the bot on; a cheap one should do, as greed is pr 9. _Optional:_ customize the files in the `strings` folder for custom messages. 10. Start the bot: - ```bash + ```console python -OO core.py ``` @@ -157,7 +241,7 @@ Assuming you downloaded `greed` in `/srv/greed`: ```bash systemctl enable bot-greed ``` - + ### Updating To update the bot, run the following commands: diff --git a/config/template_config.toml b/config/template_config.toml index b056152..def76b4 100644 --- a/config/template_config.toml +++ b/config/template_config.toml @@ -24,6 +24,7 @@ fallback_language = "en" [Database] # The database engine you want to use. # Refer to http://docs.sqlalchemy.org/en/latest/core/engines.html for the possible settings. +# This value is ignored if you're running greed via Docker, or if the DB_ENGINE environment variable is set. engine = "sqlite:///database.sqlite" diff --git a/core.py b/core.py index de2c03a..0a63ad9 100644 --- a/core.py +++ b/core.py @@ -33,26 +33,29 @@ def main(): log.fatal("config/template_config.toml does not exist!") exit(254) + # Check where the config path is located from the CONFIG_PATH environment variable + config_path = os.environ.get("CONFIG_PATH", "config/config.toml") + # If the config file does not exist, clone the template and exit - if not os.path.isfile("config/config.toml"): + if not os.path.isfile(config_path): log.debug("config/config.toml does not exist.") with open("config/template_config.toml", encoding="utf8") as template_cfg_file, \ - open("config/config.toml", "w", encoding="utf8") as user_cfg_file: + open(config_path, "w", encoding="utf8") as user_cfg_file: # Copy the template file to the config file user_cfg_file.write(template_cfg_file.read()) - log.fatal("A config file has been created in config/config.toml." + log.fatal("A config file has been created." " Customize it, then restart greed!") exit(1) # Compare the template config with the user-made one with open("config/template_config.toml", encoding="utf8") as template_cfg_file, \ - open("config/config.toml", encoding="utf8") as user_cfg_file: + open(config_path, encoding="utf8") as user_cfg_file: template_cfg = nuconfig.NuConfig(template_cfg_file) user_cfg = nuconfig.NuConfig(user_cfg_file) if not template_cfg.cmplog(user_cfg): - log.fatal("There were errors while parsing the config.toml file. Please fix them and restart greed!") + log.fatal("There were errors while parsing the config file. Please fix them and restart greed!") exit(2) else: log.debug("Configuration parsed successfully!") @@ -71,9 +74,18 @@ def main(): # Ignore most python-telegram-bot logs, as they are useless most of the time logging.getLogger("telegram").setLevel("ERROR") + # Find the database URI + # Through environment variables first + if db_engine := os.environ.get("DB_ENGINE"): + log.debug("Sqlalchemy engine overridden by the DB_ENGINE envvar.") + # Then via the config file + else: + db_engine = user_cfg["Database"]["engine"] + log.debug("Using sqlalchemy engine set in the configuration file.") + # Create the database engine log.debug("Creating the sqlalchemy engine...") - engine = sqlalchemy.create_engine(user_cfg["Database"]["engine"]) + engine = sqlalchemy.create_engine(db_engine) log.debug("Binding metadata to the engine...") database.TableDeclarativeBase.metadata.bind = engine log.debug("Creating all missing tables...")