-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from soltanoff/global_version_update
Global version update: Python 3.12 and aiogram 3.3.0
- Loading branch information
Showing
26 changed files
with
2,429 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,25 @@ | ||
FROM python:3.10-slim | ||
FROM python:3.12-slim | ||
ENV PYTHONUNBUFFERED 0 | ||
|
||
ENV buildDeps=' \ | ||
build-essential \ | ||
musl-dev \ | ||
gcc \ | ||
' | ||
|
||
RUN apt-get update \ | ||
&& pip install --upgrade --no-cache-dir pip \ | ||
&& pip install --upgrade --no-cache-dir wheel \ | ||
&& pip install --upgrade --no-cache-dir setuptools | ||
&& apt-get install -y $buildDeps --no-install-recommends \ | ||
&& pip install --upgrade --no-cache-dir pip wheel setuptools poetry | ||
|
||
WORKDIR /app | ||
|
||
COPY .. /src | ||
WORKDIR /src | ||
# will be cached if no changes in this files | ||
COPY poetry.lock /app/ | ||
COPY pyproject.toml /app/ | ||
|
||
COPY ../.env.default ./.env | ||
RUN poetry config virtualenvs.create false \ | ||
&& poetry install --no-root --no-interaction | ||
|
||
RUN pip install -r requirements.txt | ||
COPY app /app | ||
|
||
CMD [ "python", "main.py" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1 @@ | ||
LOG_LEVEL=INFO | ||
TELEGRAM_API_KEY= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
name: linters | ||
|
||
on: | ||
push: | ||
branches: [ master ] | ||
pull_request: | ||
branches: [ master ] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
timeout-minutes: 10 | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: actions/setup-python@v3 | ||
with: | ||
python-version: '3.12' | ||
- uses: syphar/restore-virtualenv@v1 | ||
id: cache-virtualenv | ||
- uses: syphar/restore-pip-download-cache@v1 | ||
if: steps.cache-virtualenv.outputs.cache-hit != 'true' | ||
- name: Install dependencies | ||
if: steps.cache-virtualenv.outputs.cache-hit != 'true' | ||
run: | | ||
python -m pip install --upgrade pip wheel setuptools poetry | ||
poetry config virtualenvs.create false | ||
poetry install --no-root --no-interaction | ||
lint: | ||
runs-on: ubuntu-latest | ||
timeout-minutes: 5 | ||
needs: [ build ] | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: actions/setup-python@v3 | ||
with: | ||
python-version: '3.12' | ||
- uses: syphar/restore-virtualenv@v1 | ||
id: cache-virtualenv | ||
- name: Analysing the code with flake8 | ||
run: | | ||
make lint | ||
safety: | ||
runs-on: ubuntu-latest | ||
timeout-minutes: 5 | ||
needs: [ build ] | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: actions/setup-python@v3 | ||
with: | ||
python-version: '3.12' | ||
- uses: syphar/restore-virtualenv@v1 | ||
id: cache-virtualenv | ||
- name: Analysing the dependencies with safety | ||
run: | | ||
make safety |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
.PHONY: static | ||
|
||
docker_compose_path = "docker-compose.yml" | ||
|
||
DC = docker-compose -f $(docker_compose_path) | ||
|
||
|
||
format: # format your code according to project linter tools | ||
poetry run black . | ||
poetry run isort . | ||
|
||
lint: | ||
poetry run black --check app | ||
poetry run isort --check app | ||
poetry run flake8 --inline-quotes '"' | ||
@# For some reason, mypy and pylint fails to resolve PYTHONPATH, set manually. | ||
PYTHONPATH=./app poetry run pylint app | ||
#PYTHONPATH=./app poetry run mypy --namespace-packages --show-error-codes app --check-untyped-defs --ignore-missing-imports --show-traceback | ||
|
||
safety: | ||
poetry run safety check | ||
|
||
app-up: # Up the project using docker-compose | ||
$(DC) up -d --build | ||
|
||
down: # Down the project using docker-compose | ||
$(DC) down |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .bot_controller import BotController # noqa: F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import logging | ||
from asyncio import CancelledError | ||
|
||
from aiogram import Bot, Dispatcher | ||
from aiogram.enums import ParseMode | ||
from aiohttp.web_runner import GracefulExit | ||
|
||
from bot_controller import middlewares, services | ||
|
||
|
||
class BotController: | ||
MIDDLEWARES = [ | ||
middlewares.DbTransactionMiddleware, | ||
middlewares.UserMiddleware, | ||
middlewares.AutoReplyMiddleware, | ||
] | ||
ROUTERS = [ | ||
services.subscriptions_router, | ||
] | ||
|
||
def __init__(self, telegram_api_key: str): | ||
self._bot = Bot(token=telegram_api_key) | ||
self._dispatcher = Dispatcher() | ||
|
||
self._register_middlewares() | ||
self._register_routers() | ||
|
||
async def start(self): | ||
try: | ||
await self._dispatcher.start_polling(self._bot) | ||
except Exception as error: | ||
logging.exception("Unexpected error: %r", error, exc_info=error) | ||
except (GracefulExit, KeyboardInterrupt, CancelledError): | ||
logging.info("Bot graceful shutdown...") | ||
|
||
async def send_message(self, user_external_id: int, answer: str, parse_mode=ParseMode.HTML): | ||
await self._bot.send_message(user_external_id, answer, parse_mode=parse_mode) | ||
|
||
def _register_middlewares(self): | ||
for middleware in self.MIDDLEWARES: | ||
self._dispatcher.update.outer_middleware.register(middleware()) | ||
|
||
def _register_routers(self): | ||
self._dispatcher.include_routers(*self.ROUTERS) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from functools import wraps | ||
from typing import Callable, Optional | ||
|
||
from aiogram import types | ||
|
||
|
||
def skip_empty_command(command: str) -> Callable: | ||
command_len = len(command) + 1 | ||
error_answer = "Empty message? Please, check /help" | ||
|
||
def decorator(handler: Callable) -> Callable: | ||
@wraps(handler) | ||
async def wrapper(message: types.Message, *args, **kwargs) -> Optional[str]: | ||
request_message: str = message.text[command_len:].strip() | ||
if not request_message: | ||
return error_answer | ||
|
||
return await handler(message, *args, **kwargs) | ||
|
||
return wrapper | ||
|
||
return decorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
from typing import Any, Awaitable, Callable, Dict | ||
|
||
from aiogram import BaseMiddleware, types | ||
|
||
import db_helper | ||
from bot_controller.services import logs | ||
from models import async_session | ||
|
||
|
||
class DbTransactionMiddleware(BaseMiddleware): | ||
async def __call__( | ||
self, | ||
handler: Callable[[types.TelegramObject, Dict[str, Any]], Awaitable[Any]], | ||
event: types.TelegramObject, | ||
data: Dict[str, Any], | ||
) -> Any: | ||
async with async_session() as session: | ||
data["session"] = session | ||
return await handler(event, data) | ||
|
||
|
||
class UserMiddleware(BaseMiddleware): | ||
async def __call__( | ||
self, | ||
handler: Callable[[types.TelegramObject, Dict[str, Any]], Awaitable[Any]], | ||
event: types.TelegramObject, | ||
data: Dict[str, Any], | ||
) -> Any: | ||
session = data["session"] | ||
data["user"] = await db_helper.get_or_create_user(session, event.message) | ||
return await handler(event, data) | ||
|
||
|
||
class AutoReplyMiddleware(BaseMiddleware): | ||
async def __call__( | ||
self, | ||
handler: Callable[[types.TelegramObject, Dict[str, Any]], Awaitable[Any]], | ||
event: types.TelegramObject, | ||
data: Dict[str, Any], | ||
) -> Any: | ||
message = event.message | ||
logs.log_bot_incomming_message(message) | ||
result = await handler(event, data) | ||
logs.log_bot_outgoing_message(message, result) | ||
|
||
if result is not None: | ||
await message.reply(text=result, disable_web_page_preview=False) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from typing import Callable, List, Optional | ||
|
||
import aiogram | ||
from aiogram.filters import Command | ||
|
||
from bot_controller.decorators import skip_empty_command | ||
|
||
|
||
class Router(aiogram.Router): | ||
def __init__(self, *, name: Optional[str] = None) -> None: | ||
super().__init__(name=name) | ||
self.command_list: List[str] = [] | ||
|
||
def register( | ||
self, | ||
command: Optional[str] = None, | ||
description: Optional[str] = None, | ||
skip_empty_messages: bool = False, | ||
) -> Callable: | ||
def decorator(command_handler: Callable) -> Callable: | ||
if command is None: | ||
self.message()(command_handler) | ||
return command_handler | ||
|
||
command_filter = Command(command) | ||
handler = command_handler | ||
if skip_empty_messages: | ||
handler = skip_empty_command(command=command)(command_handler) | ||
|
||
self.message(command_filter)(handler) | ||
|
||
if description: | ||
self.command_list.append(f"/{command} - {description}") | ||
|
||
return handler | ||
|
||
return decorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .handlers import router as subscriptions_router # noqa: F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from aiogram import types | ||
from sqlalchemy.ext.asyncio import AsyncSession | ||
|
||
from bot_controller.router import Router | ||
from models import User | ||
|
||
|
||
router = Router(name=__name__) | ||
|
||
|
||
@router.register( | ||
command="start", | ||
description="base command for user registration", | ||
) | ||
@router.register( | ||
command="help", | ||
description="view all commands", | ||
) | ||
async def send_welcome(*_) -> str: | ||
return "\n".join(router.command_list) | ||
|
||
|
||
@router.register( | ||
command="hello", | ||
description="just hello command", | ||
) | ||
async def hello(*_) -> str: | ||
return "Well, hello!" | ||
|
||
|
||
@router.register( | ||
command="user_info", | ||
description="user info", | ||
) | ||
async def user_info(message: types.Message, session: AsyncSession, user: User) -> str: # noqa; pylint: disable=unused-argument | ||
return f"User ID: {user.external_id}\nChat ID: {message.chat.id}\nRegistration date: {user.created_at}" | ||
|
||
|
||
@router.register() | ||
async def echo(message: types.Message, *_) -> str: | ||
return message.text |
Oops, something went wrong.