Skip to content

Commit

Permalink
Dockerize
Browse files Browse the repository at this point in the history
Add basic Docker Compose setup with external binding. Probably should
also have nginx reverse proxy to bind tellerbot on localhost, so far I
just haven't managed to make dockerized nginx receive Telegram webhook
updates.

Signed-off-by: alfred richardsn <rchrdsn@protonmail.ch>
  • Loading branch information
r4rdsn committed Nov 19, 2019
1 parent 825fe9f commit e5df308
Show file tree
Hide file tree
Showing 16 changed files with 223 additions and 56 deletions.
51 changes: 51 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Git
.git/
.gitignore
.gitattributes

# Docker
docker-compose.yml
.dockerignore

# Markdown files
**/*.md

# Development files
.pre-commit-config.yml
requirements-dev.txt
setup.cfg

# GitHub
.github/

# Examples
*.example

# Logs
**/*.log

# Byte-compiled / optimized / DLL files
**/__pycache__/
**/*.pyc

# Unit test / coverage reports
tests/
.pytest_cache/

# Translations
**/*.mo
**/*.pot

# Sphinx documentation
docs/

# Environments
.env

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# IDEA folder
.idea/
25 changes: 25 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Connection
TOKEN_FILENAME=/run/secrets/tbtoken
INTERNAL_HOST=0.0.0.0
SERVER_HOST=example.com
SERVER_PORT=5000
WEBHOOK_PATH=/tellerbot/webhook
DATABASE_NAME=tellerbot
DATABASE_HOST=database

# Logging
LOGGER_LEVEL=INFO
LOG_FILENAME=/var/log/tellerbot.log

# Chat IDs
SUPPORT_CHAT_ID=-123456789
EXCEPTIONS_CHAT_ID=-1234567890123

# Orders
ORDERS_COUNT=10
ORDERS_LIMIT_HOURS=24
ORDERS_LIMIT_COUNT=10

# Escrow
ESCROW_ENABLED=true
WIF_FILENAME=/run/secrets/wif.json
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM python:3.8-slim-buster

LABEL mantainer="alfred richardsn <rchrdsn@protonmail.ch>"

ARG USER=tellerbot
ARG GROUP=tellerbot

ENV HOME /home/$USER

RUN groupadd -g 999 $GROUP \
&& useradd -g $GROUP -u 999 -l -s /sbin/nologin -m -d $HOME $USER
WORKDIR $HOME
USER $USER:$GROUP

COPY --chown=$USER:$GROUP requirements.txt .
ENV PATH $PATH:$HOME/.local/bin
RUN pip install --user --no-cache-dir --requirement requirements.txt

COPY --chown=$USER:$GROUP locale/ locale/
RUN pybabel compile --directory=locale/ --domain=bot

COPY --chown=$USER:$GROUP . .

ENTRYPOINT ["python", "."]
34 changes: 29 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@


## Installation and launch
### Using Docker (recommended)
1. Clone the repository:
```bash
git clone https://github.com/fincubator/tellerbot
cd tellerbot
```
2. Create environment file from example:
```bash
cp .env.example .env
```
3. Personalize settings by modifying ```.env``` with your preferable text editor.
4. Create a new Telegram bot by talking to [@BotFather](https://t.me/BotFather) and get its API token.
5. Create a file containing Telegram bot's API token with filename specified in ```TOKEN_FILENAME``` from ```.env```.
6. Install [Docker Compose](https://docs.docker.com/compose/install/).
7. Start container:
```bash
docker-compose up
```

### Manual
1. Clone the repository:
```bash
git clone https://github.com/fincubator/tellerbot
Expand All @@ -28,15 +48,19 @@ cd tellerbot
```bash
pip install -r requirements.txt
```
4. Create config file from template:
4. Create environment file from example:
```bash
cp config.ini.example config.ini
cp .env.example .env
```
5. Personalize settings by modifying ```config.ini``` with your preferable text editor.
5. Personalize settings by modifying ```.env``` with your preferable text editor. Remove ```INTERNAL_HOST``` and ```DATABASE_HOST``` if you want bot and database running on localhost.
6. Create a new Telegram bot by talking to [@BotFather](https://t.me/BotFather) and get its API token.
7. Create a file containing Telegram bot's API token with filename specified in ```token_filename``` from ```config.ini```.
7. Create a file containing Telegram bot's API token with filename specified in ```TOKEN_FILENAME``` from ```.env```.
8. Install and start MongoDB server.
9. Launch TellerBot:
9. Set environment variables:
```bash
export $(grep -v '^#' .env | xargs)
```
10. Launch TellerBot:
```bash
python .
```
Expand Down
22 changes: 0 additions & 22 deletions config.ini.example

This file was deleted.

24 changes: 24 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
version: "3.7"

services:
tellerbot:
build: .
depends_on:
- database
env_file:
- .env
restart: on-failure
volumes:
- ${TOKEN_FILENAME}:${TOKEN_FILENAME}:ro
- ${WIF_FILENAME}:${WIF_FILENAME}:ro
ports:
- ${SERVER_PORT}:${SERVER_PORT}

database:
image: mongo:4.2.1
container_name: ${DATABASE_HOST}
command: mongod --quiet
ports:
- 27017
volumes:
- ~/data/db:/data/db
1 change: 0 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,3 @@
html_static_path = ["_static"]

master_doc = "index"
autodoc_mock_imports = ["src.config"]
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ emoji
golos-python
motor
pymongo
requests
1 change: 1 addition & 0 deletions secrets/tbtoken
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
000000000:ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789
3 changes: 3 additions & 0 deletions secrets/wif.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"golos": "abcdefghijklmopqrstuABCDEFGHIJKLMNOPQRSTU1234567890"
}
2 changes: 1 addition & 1 deletion src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def main():
dispatcher=dp,
webhook_path=webhook_path,
on_startup=lambda *args: on_startup(webhook_path, *args),
host="127.0.0.1",
host=config.INTERNAL_HOST,
port=config.SERVER_PORT,
)
print() # Executor stopped with ^C
Expand Down
6 changes: 2 additions & 4 deletions src/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,15 @@

def setup():
"""Set API token from config to bot and setup dispatcher."""
with open(config.TOKEN_FILE, "r") as token_file:
with open(config.TOKEN_FILENAME, "r") as token_file:
tg._ctx_token.set(token_file.read().strip())

dp.storage = MongoStorage()

i18n.reload()
dp.middleware.setup(i18n)

logging.basicConfig(
filename=config.LOG_FILENAME, filemode="a", level=config.LOGGER_LEVEL
)
logging.basicConfig(level=config.LOGGER_LEVEL)
dp.middleware.setup(LoggingMiddleware())


Expand Down
65 changes: 47 additions & 18 deletions src/config.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,54 @@
from configparser import ConfigParser
from pathlib import Path
# Copyright (C) 2019 alfred richardsn
#
# This file is part of TellerBot.
#
# TellerBot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with TellerBot. If not, see <https://www.gnu.org/licenses/>.
from os import getenv

ROOT_DIR = Path(__file__).parent.parent.resolve()

parser = ConfigParser()
parser.read(ROOT_DIR / "config.ini")
def getenv_int(key, default=None):
"""Convert the value of the environment variable key to an integer."""
env = getenv(key)
try:
return int(env)
except (TypeError, ValueError):
return default

TOKEN_FILE = parser.get("Connection", "token_filename")
SERVER_HOST = parser.get("Connection", "server_host")
SERVER_PORT = parser.getint("Connection", "server_port")
WEBHOOK_PATH = parser.get("Connection", "webhook_path")
DATABASE_NAME = parser.get("Connection", "database_name")

LOGGER_LEVEL = parser.get("Logging", "logger_level")
LOG_FILENAME = parser.get("Logging", "log_filename")
def getenv_bool(key, default=None):
"""Convert the value of the environment variable key to a boolean."""
env = getenv(key)
return env == "true" if env in ("true", "false") else default

SUPPORT_CHAT_ID = parser.getint("Chat IDs", "support")
EXCEPTIONS_CHAT_ID = parser.getint("Chat IDs", "exceptions")

ORDERS_COUNT = parser.getint("Orders", "count")
ORDERS_LIMIT_HOURS = parser.getint("Orders", "limit_hours")
ORDERS_LIMIT_COUNT = parser.getint("Orders", "limit_count")
TOKEN_FILENAME = getenv("TOKEN_FILENAME")
INTERNAL_HOST = getenv("INTERNAL_HOST", "localhost")
SERVER_HOST = getenv("SERVER_HOST")
SERVER_PORT = getenv_int("SERVER_PORT")
WEBHOOK_PATH = getenv("WEBHOOK_PATH")
DATABASE_NAME = getenv("DATABASE_NAME", "tellerbot")
DATABASE_HOST = getenv("DATABASE_HOST", "localhost")

WIF_FILENAME = parser.get("Blockchain", "wif_filename")
LOGGER_LEVEL = getenv("LOGGER_LEVEL")
LOG_FILENAME = getenv("LOG_FILENAME")

SUPPORT_CHAT_ID = getenv_int("SUPPORT_CHAT_ID")
EXCEPTIONS_CHAT_ID = getenv_int("EXCEPTIONS_CHAT_ID")

ORDERS_COUNT = getenv_int("ORDERS_COUNT")
ORDERS_LIMIT_HOURS = getenv_int("ORDERS_LIMIT_HOURS")
ORDERS_LIMIT_COUNT = getenv_int("ORDERS_LIMIT_COUNT")

ESCROW_ENABLED = getenv_bool("WIF_FILENAME")
WIF_FILENAME = getenv("WIF_FILENAME")
6 changes: 3 additions & 3 deletions src/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@
from aiogram.dispatcher.storage import BaseStorage
from motor.motor_asyncio import AsyncIOMotorClient

from src.config import DATABASE_NAME
from src import config


client = AsyncIOMotorClient()
database = client[DATABASE_NAME if isinstance(DATABASE_NAME, str) else "tellerbot"]
client = AsyncIOMotorClient(config.DATABASE_HOST)
database = client[config.DATABASE_NAME]

STATE_KEY = "state"

Expand Down
11 changes: 9 additions & 2 deletions src/escrow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with TellerBot. If not, see <https://www.gnu.org/licenses/>.
from src.escrow.blockchain.golos_blockchain import GolosBlockchain
from src import config


if config.ESCROW_ENABLED:
from src.escrow.blockchain.golos_blockchain import GolosBlockchain

SUPPORTED_BLOCKCHAINS = [GolosBlockchain()]
else:
SUPPORTED_BLOCKCHAINS = []


SUPPORTED_BLOCKCHAINS = (GolosBlockchain(),)
SUPPORTED_BANKS = ("Alfa-Bank", "Rocketbank", "Sberbank", "Tinkoff")


Expand Down
3 changes: 3 additions & 0 deletions src/escrow/blockchain/golos_blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ async def get_limits(self, asset: str):
return limits.get(asset)

async def transfer(self, to: str, amount: Decimal, asset: str):
if not config.WIF_FILENAME:
raise Exception("WIF_FILENAME is not set")

with open(config.WIF_FILENAME) as wif_file:
transaction = await get_running_loop().run_in_executor(
None,
Expand Down

0 comments on commit e5df308

Please sign in to comment.