Files
BitrixDealsBot/apps/bot/binding.py
T

89 lines
2.6 KiB
Python

import hashlib
import re
from .database import BotDatabase
from .domain import Binding
TOKEN_PATTERN = re.compile(r"^[A-Za-z0-9_-]{20,100}$")
def hash_token(token: str) -> bytes:
return hashlib.sha256(token.encode("utf-8")).digest()
class BotBindingRepository:
"""Доступ бота только к функциям БД схемы binding.
Обертка над хранимыми функциями БД."""
def __init__(self, database: BotDatabase) -> None:
self.database = database
async def consume(
self,
token_hash: bytes,
telegram_user_id: int,
telegram_chat_id: int
) -> Binding | None:
async with self.database.transaction() as connection:
cursor = await connection.execute(
"""
SELECT *
FROM binding.consume_v1(%s, %s, %s)
""",
(token_hash, telegram_user_id, telegram_chat_id)
)
row = await cursor.fetchone()
return self._binding(row) if row else None
async def find(
self,
telegram_user_id: int,
member_id: str | None = None,
) -> Binding | None:
async with self.database.transaction() as connection:
cursor = await connection.execute(
"""
SELECT *
FROM binding.find_by_telegram_v1(%s, %s)
""",
(telegram_user_id, member_id)
)
row = await cursor.fetchone()
return self._binding(row) if row else None
@staticmethod
def _binding(row: dict) -> Binding:
return Binding(
member_id=str(row["member_id"]),
domain=str(row["domain"]),
bitrix_user_id=int(row["bitrix_user_id"]),
telegram_user_id=int(row["telegram_user_id"])
)
class BindingService:
"""Служба управления привязками пользователей."""
def __init__(
self,
repository: BotBindingRepository
) -> None:
self.repository = repository
async def consume(
self,
token: str,
telegram_user_id: int,
telegram_chat_id: int
) -> Binding | None:
if not TOKEN_PATTERN.fullmatch(token):
return None
return await self.repository.consume(
hash_token(token),
telegram_user_id,
telegram_chat_id
)
async def find(self, telegram_user_id: int) -> Binding | None:
return await self.repository.find(telegram_user_id)