diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a295864 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +*.pyc +__pycache__ diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml new file mode 100644 index 0000000..a50924d --- /dev/null +++ b/.github/workflows/image.yml @@ -0,0 +1,44 @@ +name: Docker Image + +on: + push: + pull_request: + +jobs: + image: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: fallenbreath + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + fallenbreath/chatbridge + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Build and push + uses: docker/build-push-action@v4 + with: + platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64 + file: ./docker/Dockerfile + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/README.md b/README.md index 18ff92e..f276d3e 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,23 @@ Enter `python ChatBridge.pyz` in command line to see possible helps At launch, if the configure file is missing, chatbridge will automatically generate a default one and exit +## Docker Image + +[![Docker](https://img.shields.io/docker/v/fallenbreath/chatbridge/latest)](https://hub.docker.com/r/fallenbreath/chatbridge) + +Docker Hub image: [`fallenbreath/chatbridge`](https://hub.docker.com/r/fallenbreath/chatbridge) + +Image name examples: + +- `fallenbreath/chatbridge:latest` +- `fallenbreath/chatbridge:v2.5.3` + +Working directory: `/app` + +Example usages: `docker run --rm fallenbreath/chatbridge:latest server` + +See the [./docker](docker) directory in the repository for more details and docker compose example + ## Requirement Python 3.6+ required diff --git a/chatbridge/impl/cli/cli_server.py b/chatbridge/impl/cli/cli_server.py index 893a0f1..bbc263c 100644 --- a/chatbridge/impl/cli/cli_server.py +++ b/chatbridge/impl/cli/cli_server.py @@ -1,3 +1,4 @@ +import sys import threading import time import traceback @@ -87,7 +88,12 @@ def main(): print('- Client #{}: name = {}, password = {}'.format(i + 1, client_info.name, client_info.password)) server.add_client(client_info) server.start() - server.console_loop() + + if sys.stdin.isatty(): + server.console_loop() + else: + utils.wait_until_terminate() + server.stop() if __name__ == '__main__': diff --git a/chatbridge/impl/online/entry.py b/chatbridge/impl/online/entry.py index 34751b3..e1b8560 100644 --- a/chatbridge/impl/online/entry.py +++ b/chatbridge/impl/online/entry.py @@ -4,6 +4,7 @@ import collections import functools import re +import sys import traceback from concurrent.futures.thread import ThreadPoolExecutor from threading import Lock @@ -135,7 +136,11 @@ def main(): config = utils.load_config(ClientConfigFile, OnlineConfig) chatClient = OnlineChatClient.create(config) utils.start_guardian(chatClient) - console_input_loop() + if sys.stdin.isatty(): + console_input_loop() + else: + utils.wait_until_terminate() + chatClient.stop() if __name__ == '__main__': diff --git a/chatbridge/impl/utils.py b/chatbridge/impl/utils.py index 29cf935..3a1fe78 100644 --- a/chatbridge/impl/utils.py +++ b/chatbridge/impl/utils.py @@ -1,5 +1,7 @@ import json import os +import queue +import signal import time from threading import Thread from typing import Type, TypeVar, Callable @@ -40,3 +42,11 @@ def loop(): thread = Thread(name='ChatBridge Guardian', target=loop, daemon=True) thread.start() return thread + + +def wait_until_terminate(): + q = queue.Queue() + signal.signal(signal.SIGINT, lambda s, _: q.put(s)) + signal.signal(signal.SIGTERM, lambda s, _: q.put(s)) + sig = q.get() + print('Interrupted with {} ({})'.format(signal.Signals(sig).name, sig)) diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..1d6a8b1 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,25 @@ +ARG PYTHON_VERSION=3.11 + +FROM python:${PYTHON_VERSION} as builder + +RUN pip3 install mcdreforged + +COPY chatbridge /build/chatbridge +COPY lang /build/lang +COPY __main__.py LICENSE mcdreforged.plugin.json requirements.txt /build/ +RUN cd /build \ + && mcdreforged pack \ + && find . -name "requirements.txt" -exec cat '{}' \; > requirements.all.txt + +FROM python:${PYTHON_VERSION}-slim + +COPY --from=builder /build/requirements.all.txt /app/ +RUN apt-get update \ + && apt-get install -y --no-install-recommends gcc build-essential libffi-dev \ + && rm -rf /var/lib/apt/lists/* \ + && pip3 install -r /app/requirements.all.txt \ + && apt-get purge -y --auto-remove gcc build-essential libffi-dev + +COPY --from=builder /build/ChatBridge.pyz /app/ +WORKDIR /app +ENTRYPOINT ["python3", "ChatBridge.pyz"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..3336580 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,19 @@ +version: '3' +services: + server: + container_name: chatbridge_server + restart: unless-stopped + image: fallenbreath/chatbridge:latest + command: server + volumes: + - ./ChatBridge_server.json:/app/ChatBridge_server.json + ports: + - '30001:30001' + + khl_bot: + container_name: chatbridge_khl_bot + restart: unless-stopped + image: fallenbreath/chatbridge:latest + command: kaiheila_bot + volumes: + - ./ChatBridge_kaiheila.json:/app/ChatBridge_kaiheila.json