Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@
.vscode
tests/
env
venv
.venv
.pytest_cache
.ruff_cache
config_override.json
config_gs.json
9 changes: 7 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,17 @@ jobs:
with:
python-version: "3.8"

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.11.21"

- name: Install dependencies
run: |
sudo apt-get install -y git
pip install -r requirements.txt
uv sync --locked

- name: Run unit tests
env:
PYTHONPATH: ${{ github.workspace }}:${{ github.workspace }}/tests
run: pytest tests/unit/
run: uv run pytest tests/unit/
2 changes: 0 additions & 2 deletions .isort.cfg

This file was deleted.

11 changes: 6 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

RUN apk add --no-cache build-base gcc python3-dev jpeg-dev zlib-dev libressl-dev musl-dev libffi-dev sqlite git; rm -rf /var/lib/apt/lists/*

RUN pip install --no-cache-dir uv==0.11.21
ENV UV_PYTHON_DOWNLOADS=never UV_LINK_MODE=copy UV_PROJECT_ENVIRONMENT=/app/.venv

WORKDIR /app
ADD ./requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt
COPY pyproject.toml uv.lock /app/
RUN uv sync --locked --no-dev
COPY . /app

ARG COMMIT_HASH
Expand All @@ -15,8 +18,8 @@
ENV UPTRACE_DSN=$UPTRACE_DSN


ENV MUSL_LOCALE_DEPS cmake make musl-dev gcc gettext-dev libintl

Check warning on line 21 in Dockerfile

View workflow job for this annotation

GitHub Actions / push

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
ENV MUSL_LOCPATH /usr/share/i18n/locales/musl

Check warning on line 22 in Dockerfile

View workflow job for this annotation

GitHub Actions / push

Legacy key/value format with whitespace separator should not be used

LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format More info: https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/
# Last musl-locales commit before upstream switched to -std=c23, which Alpine 3.13 GCC does not support.
ARG MUSL_LOCALES_VERSION=e3c3bef8df744ae9c09382a11e10831168d82311

Expand All @@ -28,8 +31,6 @@
&& cmake -DLOCALE_PROFILE=OFF -DCMAKE_INSTALL_PREFIX:PATH=/usr . && make && make install \
&& cd .. && rm -rf musl-locales-${MUSL_LOCALES_VERSION} musl-locales.zip

RUN pip install .

RUN apk del libressl-dev musl-dev libffi-dev gcc git

CMD python ./app.py
CMD ["/app/.venv/bin/python", "./app.py"]
12 changes: 6 additions & 6 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ FROM python:3.8-alpine3.13 AS builder
RUN apk add --no-cache build-base gcc python3-dev jpeg-dev zlib-dev libressl-dev musl-dev libffi-dev sqlite git \
&& rm -rf /var/lib/apt/lists/*

RUN pip install --no-cache-dir uv==0.11.21
ENV UV_PYTHON_DOWNLOADS=never UV_LINK_MODE=copy UV_PROJECT_ENVIRONMENT=/app/.venv

WORKDIR /app

COPY ./requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
COPY pyproject.toml uv.lock /app/
RUN uv sync --locked --no-dev

COPY . /app

RUN pip install .

FROM python:3.8-alpine3.13 as production

RUN apk add --no-cache libressl-dev musl-dev libffi-dev gcc
Expand All @@ -38,7 +39,6 @@ RUN apk add --no-cache \

WORKDIR /app

COPY --from=builder /usr/local /usr/local
COPY --from=builder /app /app

CMD ["python", "./app.py"]
CMD ["/app/.venv/bin/python", "./app.py"]
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ Bot for SysBlok ("Системный Блокъ") editorial processes automatiza
- Run `export INFRA_HOST=<SYSBLOK_INFRA_HOST>`, putting in the actual URL
- Run `sh get_keys.sh`. This will download `config_override.json` and `config_gs.json` to your folder.
- Replace `YOUR_CHAT_ID`, `YOUR_USERNAME` and `BOT_TOKEN` with appropriate values
- `pip install -e .`
- `pip install -r requirements.txt`
- `pre-commit install`
- Install [uv](https://docs.astral.sh/uv/getting-started/installation/) if you don't have it yet
- `uv sync`
- `uv run pre-commit install`
- If access to Telegram servers is restricted by your internet provider: install and run any VPN service
- `python3 app.py`
- `uv run app.py`

## How to develop
- Fork the repo.
Expand Down
42 changes: 42 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[project]
name = "sysblokbot"
version = "0.1.0"
requires-python = "==3.8.*"
dependencies = [
"beautifulsoup4==4.10.0",
"cachetools==4.1.0",
"cryptography==3.1.1",
"deepdiff==4.3.2",
"google-api-python-client==2.198.0",
"grpcio==1.63.0",
"nest-asyncio==1.6.0",
"oauth2client==4.1.3",
"opentelemetry-api==1.24.0",
"opentelemetry-exporter-otlp-proto-grpc==1.24.0",
"opentelemetry-sdk==1.24.0",
"python-dateutil==2.8.1",
"python-telegram-bot==21.4",
"pytz==2020.1",
"requests==2.28.1",
"schedule==1.2.2",
"setuptools",
"sheetfu==1.5.3",
"SQLAlchemy==1.3.17",
"telethon",
"tenacity==9.0.0",
]

[tool.uv]
package = false

[tool.uv.sources]
telethon = { git = "https://github.com/alexeyqu/Telethon.git", rev = "7cd20d1e5c0082450c53dbf242fb35145e448260" }

[dependency-groups]
dev = [
"freezegun==0.3.15",
"pre-commit==2.21.0",
"pytest==5.4.1",
"pytest-asyncio==0.12.0",
"ruff==0.11.2",
]
107 changes: 0 additions & 107 deletions requirements.txt

This file was deleted.

3 changes: 0 additions & 3 deletions setup.py

This file was deleted.

28 changes: 22 additions & 6 deletions src/bot.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import asyncio
import functools
import logging
import os
import pickle
from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor
from typing import Callable

from telegram.ext import (
Expand Down Expand Up @@ -38,6 +40,24 @@ def usage(self, message, *args, **kws):
logging.Logger.usage = usage
logger = logging.getLogger(__name__)

# Handler code (Google Sheets/Drive/Planka/Telegram-sending clients) is
# synchronous and blocking. python-telegram-bot's Application runs on a
# single asyncio event loop thread, so calling that code directly there
# means one slow/stuck call (e.g. a network hiccup) freezes command
# dispatch for every chat, not just the one being handled. Running sync
# handlers on a worker thread keeps the event loop free to keep polling
# and dispatching other updates while a handler is blocked on I/O.
_HANDLER_EXECUTOR = ThreadPoolExecutor(max_workers=16, thread_name_prefix="handler")


async def _run_handler(func: Callable, *args, **kwargs):
if asyncio.iscoroutinefunction(func):
return await func(*args, **kwargs)
loop = asyncio.get_running_loop()
return await loop.run_in_executor(
_HANDLER_EXECUTOR, functools.partial(func, *args, **kwargs)
)


class SysBlokBot:
"""
Expand Down Expand Up @@ -155,8 +175,7 @@ def init_handlers(self):

def asyncify(func):
async def wrapper(*args, **kwargs):
results = func(*args, **kwargs)
return results
return await _run_handler(func, *args, **kwargs)

return wrapper

Expand Down Expand Up @@ -197,10 +216,7 @@ async def wrapper(*args, **kwargs):
except BaseException:
logger.usage(f"Handler {handler_cmd} was called...")

# Check if func is async and await if necessary
results = func(*args, **kwargs)
if asyncio.iscoroutine(results):
results = await results
results = await _run_handler(func, *args, **kwargs)

logger.usage(f"Handler {handler_cmd} finished")
return results
Expand Down
Loading
Loading