Support-BOT

This commit is contained in:
2024-05-01 19:55:22 +03:00
commit 4280385d32
34 changed files with 1361 additions and 0 deletions

0
app/__init__.py Normal file
View File

0
app/bot/__init__.py Normal file
View File

10
app/bot/filter_media.py Normal file
View File

@@ -0,0 +1,10 @@
from aiogram.filters import BaseFilter
from aiogram.types import Message, ContentType
class SupportedMediaFilter(BaseFilter):
async def __call__(self, message: Message) -> bool:
return message.content_type in (
ContentType.ANIMATION, ContentType.AUDIO, ContentType.DOCUMENT,
ContentType.PHOTO, ContentType.VIDEO, ContentType.VOICE
)

48
app/bot/get_reports.py Normal file
View File

@@ -0,0 +1,48 @@
from datetime import datetime
from dateutil.relativedelta import relativedelta
from sqlalchemy.ext.asyncio import AsyncSession
from app.crud.message import crud_message
async def get_report_from_db(session: AsyncSession,
from_date=None,
to_date=None):
"""Получение отчета за интервал времени из базы данных.
:param session: Асинхронная сессия к БД
:type session: AsyncSession
:param from_date: Дата начала
:type from_date: str
:param to_date: Дата окончания
:type to_date: str
:return: Возвращает словарь с данными для отчета
:rtype: dict
"""
if not to_date:
to_date = datetime.now()
if not from_date:
from_date = to_date + relativedelta(months=-1)
messages = await crud_message.get_by_date_interval(
from_date, to_date, session
)
users_senders = []
answers_amount = 0
questions_amount = 0
for mes in messages:
if (mes.telegram_user_id not in users_senders
and not mes.answer_to_user_id):
users_senders.append(mes.telegram_user_id)
if mes.answer_to_user_id:
answers_amount += 1
else:
questions_amount += 1
users_amount = len(users_senders)
report = {
'from_date': from_date.strftime('%d.%m.%Y'),
'to_date': to_date.strftime('%d.%m.%Y'),
'users_amount': users_amount,
'answers_amount': answers_amount,
'questions_amount': questions_amount,
}
return report

View File

@@ -0,0 +1,195 @@
from aiogram import Bot, F, Router
from aiogram.exceptions import TelegramBadRequest
from aiogram.filters import Command, CommandObject
from aiogram.types import Message
from app.bot.get_reports import get_report_from_db
from app.bot.utils import get_user_name, check_input_date_correct, \
stringdate_to_date, check_user_is_banned, \
get_telegram_user_from_resend_message, parse_ban_command
from app.core.config import settings
from app.core.db import get_async_session
from app.crud.user import crud_user
from app.crud.message import crud_message
router = Router()
@router.message(Command(commands=["start"]))
async def command_start(message: Message):
await message.answer(settings.START_MESSAGE)
session_generator = get_async_session()
session = await session_generator.__anext__()
db_user = await crud_user.get_or_create_user_by_tg_message(message,
session)
if check_user_is_banned(db_user):
return
@router.message(Command(commands="info"),
F.chat.id == int(settings.GROUP_ID),
F.reply_to_message)
async def get_user_info(message: Message, bot: Bot):
session_generator = get_async_session()
session = await session_generator.__anext__()
telegram_user = await get_telegram_user_from_resend_message(message, bot)
if not telegram_user:
return
messages_count = await crud_message.get_count_user_messages(
telegram_user.id, session
)
ans_count = await crud_message.get_count_answers_to_user(
telegram_user.id, session
)
username = f"@{telegram_user.username}" if telegram_user.username else "отсутствует"
await message.reply(text=f'Имя: {get_user_name(telegram_user)}\n'
f'Id: {telegram_user.id}\n'
f'username: {username}\n'
f'Сообщений от пользователя: {messages_count}\n'
f'Ответов пользователю: {ans_count}\n')
@router.message(Command(commands='report'),
F.chat.id == int(settings.GROUP_ID))
async def get_report(message: Message,
bot: Bot,
command: CommandObject):
session_generator = get_async_session()
session = await session_generator.__anext__()
if command.args:
if not check_input_date_correct(command.args):
answer_text = 'Неверный формат даты'
await bot.send_message(
chat_id=int(settings.GROUP_ID), text=answer_text
)
return
from_date, to_date = stringdate_to_date(command.args)
report = await get_report_from_db(session, from_date, to_date)
report['period'] = 'выбранный период'
else:
report = await get_report_from_db(session)
report['period'] = 'последний месяц'
answer_text = (f"Отчет за {report['period']}, c {report['from_date']} до "
f"{report['to_date']}.\n"
f"Всего было получено {report['questions_amount']} "
f"сообщений от {report['users_amount']} клиентов.\n"
f"Количество ответов от администраторов: {report['answers_amount']}")
await bot.send_message(chat_id=int(settings.GROUP_ID), text=answer_text)
@router.message(Command(commands='ban'),
F.chat.id == int(settings.GROUP_ID))
async def handler_ban_user(message: Message,
bot: Bot,
command: CommandObject):
if not command.args and not message.reply_to_message:
return await message.reply(
text='Команда некорректна. Укажите ID или ответьте на сообщение')
session_generator = get_async_session()
session = await session_generator.__anext__()
if command.args:
telegram_user_id = parse_ban_command(command)
if not telegram_user_id:
return await message.reply(
text='Невозможно извлечь id пользователя. '
'Нужно ввести id в формате "12345", либо ответить на '
'сообщение пользователя, которого хотите забанить')
try:
telegram_user = await bot.get_chat(telegram_user_id)
except TelegramBadRequest:
return await message.reply(
text='Пользователя с таким id не существует ')
else:
telegram_user = await get_telegram_user_from_resend_message(message, bot)
if not telegram_user:
return
db_user = await crud_user.get_user_by_telegram_id(telegram_user.id,
session)
await crud_user.ban_user(db_user, session)
await message.reply(text=f'Пользователь {db_user.first_name} '
f'{db_user.last_name} забанен.'
f'Чтобы разбанить, отправьте /unban\n'
f'Тикет: #id{db_user.telegram_id}')
@router.message(Command(commands='unban'),
F.chat.id == int(settings.GROUP_ID))
async def handler_unban_user(message: Message,
bot: Bot,
command: CommandObject):
if not command.args and not message.reply_to_message:
return await message.reply(
text='Команда некорректна. Укажите ID или ответьте на сообщение')
session_generator = get_async_session()
session = await session_generator.__anext__()
if command.args:
telegram_user_id = parse_ban_command(command)
if not telegram_user_id:
return await message.reply(
text='Невозможно извлечь id пользователя. '
'Нужно ввести id в формате "12345", либо ответить на '
'сообщение пользователя, которого хотите забанить')
db_user = await crud_user.get_user_by_telegram_id(telegram_user_id,
session)
else:
telegram_user = await get_telegram_user_from_resend_message(message, bot)
if not telegram_user:
return
db_user = await crud_user.get_user_by_telegram_id(telegram_user.id,
session)
await crud_user.unban_user(db_user, session)
await message.reply(text=f'Пользователь с id '
f'{db_user.first_name} {db_user.last_name} разбанен\n'
f'Тикет: #id{db_user.telegram_id}')
@router.message(Command(commands='banlist'),
F.chat.id == int(settings.GROUP_ID))
async def handler_unban_user(message: Message,
bot: Bot):
session_generator = get_async_session()
session = await session_generator.__anext__()
banned_users = await crud_user.get_banned_users(session)
text = 'Список забанненых пользователей:\n'
for user in banned_users:
text += f'{user.telegram_id} - {user.first_name} {user.last_name}\n'
await message.reply(text=text)
@router.message(Command(commands='registeradmin'),
F.chat.id == int(settings.GROUP_ID))
async def handle_register_admin(message: Message,
bot: Bot):
if not message.reply_to_message:
return message.reply(text="Введите команду как ответ на сообщение "
"пользователя")
session_generator = get_async_session()
session = await session_generator.__anext__()
db_user = await crud_user.get_or_create_user_by_tg_message(
message.reply_to_message,
session
)
db_user = await crud_user.register_admin(db_user, session)
text = (f'Пользователь {db_user.first_name} {db_user.last_name} теперь '
f'администратор')
await message.reply(text=text)
@router.message(Command(commands='deleteadmin'),
F.chat.id == int(settings.GROUP_ID))
async def handle_remove_admin(message: Message,
bot: Bot):
if not message.reply_to_message:
return message.reply(text="Введите команду как ответ на сообщение "
"пользователя")
session_generator = get_async_session()
session = await session_generator.__anext__()
db_user = await crud_user.get_or_create_user_by_tg_message(
message.reply_to_message,
session
)
db_user = await crud_user.remove_admin(db_user, session)
text = f'Администратор {db_user.first_name} {db_user.last_name} удален'
await message.reply(text=text)

View File

@@ -0,0 +1,101 @@
from aiogram import Bot, F, Router
from aiogram.exceptions import TelegramForbiddenError
from aiogram.types import Message
from app.bot.utils import extract_user_id, check_user_is_banned
from app.core.config import settings
from app.core.db import get_async_session
from app.crud.message import crud_message
from app.crud.user import crud_user
from filter_media import SupportedMediaFilter
router = Router()
@router.message(F.chat.type == 'private', F.text)
async def send_message_to_group(message: Message, bot: Bot):
if message.text and len(message.text) > 4000:
return await message.reply(text='Пожалуйста, уменьшите размер '
'сообщения, чтобы оно было менее '
'4000 символов')
await bot.send_message(
chat_id=settings.GROUP_ID,
text=(
f'{message.text}\n\n'
f'Тикет: #id{message.from_user.id}'
),
parse_mode='HTML'
)
session_generator = get_async_session()
session = await session_generator.__anext__()
db_user = await crud_user.get_or_create_user_by_tg_message(message, session)
if check_user_is_banned(db_user):
return
message_data = {
'text': message.text,
'telegram_user_id': message.from_user.id,
'attachments': False,
}
await crud_message.create(message_data, session)
@router.message(SupportedMediaFilter(), F.chat.type == 'private')
async def supported_media(message: Message):
if message.caption and len(message.caption) > 1000:
return await message.reply(text='Слишком длинное описание. Описание '
'не может быть больше 1000 символов')
await message.copy_to(
chat_id=settings.GROUP_ID,
caption=((message.caption or "") +
f"\n\n Тикет: #id{message.from_user.id}"),
parse_mode="HTML"
)
session_generator = get_async_session()
session = await session_generator.__anext__()
db_user = await crud_user.get_or_create_user_by_tg_message(message, session)
if check_user_is_banned(db_user):
return
message_data = {
'telegram_user_id': message.from_user.id,
'attachments': True,
}
if message.caption:
message_data['text'] = message.caption
await crud_message.create(message_data, session)
@router.message(F.chat.id == int(settings.GROUP_ID),
F.reply_to_message)
async def send_message_answer(message: Message,
bot: Bot):
if not message.reply_to_message.from_user.is_bot:
return
try:
chat_id = extract_user_id(message.reply_to_message)
except ValueError as err:
return await message.reply(text=f'Не могу извлечь Id. Возможно он '
f'некорректный. Текст ошибки:\n'
f'{str(err)}')
try:
await message.copy_to(chat_id)
except TelegramForbiddenError:
await message.reply(text='Сообщение не доставлено. Бот был '
'заблокировн пользователем, '
'либо пользователь удален')
session_generator = get_async_session()
session = await session_generator.__anext__()
db_user = await crud_user.get_or_create_user_by_tg_message(message, session)
await crud_user.register_admin(db_user, session)
message_data = {
'telegram_user_id': message.from_user.id,
'answer_to_user_id': chat_id,
}
if message.text:
message_data['text'] = message.text
else:
if message.caption:
message_data['text'] = message.caption
message_data['attachments'] = True
await crud_message.create(message_data, session)

57
app/bot/main.py Normal file
View File

@@ -0,0 +1,57 @@
import asyncio
import logging
import sys
from aiogram import Bot, Dispatcher
from aiogram.webhook.aiohttp_server import SimpleRequestHandler
from aiohttp import web
from app.core.config import settings
from handlers_commands import router as commands_router
from handlers_messages import router as messages_router
async def main():
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
bot = Bot(token=settings.TELEGRAM_TOKEN, parse_mode="HTML")
dp = Dispatcher()
dp.include_router(commands_router)
dp.include_router(messages_router)
try:
if not settings.WEBHOOK_DOMAIN:
await bot.delete_webhook()
await dp.start_polling(
bot,
allowed_updates=dp.resolve_used_update_types()
)
else:
aiohttp_logger = logging.getLogger('aiohttp.access')
aiohttp_logger.setLevel(logging.DEBUG)
await bot.set_webhook(
url=settings.WEBHOOK_DOMAIN + settings.WEBHOOK_PATH,
drop_pending_updates=True,
allowed_updates=dp.resolve_used_update_types()
)
app = web.Application()
SimpleRequestHandler(
dispatcher=dp, bot=bot
).register(app, path=settings.WEBHOOK_PATH)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner,
host=settings.APP_HOST,
port=settings.APP_PORT
)
await site.start()
await asyncio.Event().wait()
except RuntimeError:
pass
finally:
await bot.session.close()
if __name__ == "__main__":
asyncio.run(main())

88
app/bot/utils.py Normal file
View File

@@ -0,0 +1,88 @@
import re
from datetime import datetime
from aiogram import Bot
from aiogram.exceptions import TelegramAPIError
from aiogram.filters import CommandObject
from aiogram.types import Message, Chat
from dateutil.relativedelta import relativedelta
from app.schemas.user import UserFromDBScheme
from app.schemas.user import UserBaseScheme
DATE_PATTERN = r'^(0?[1-9]|[12][0-9]|3[01]).(0?[1-9]|1[012]).((19|20)\d\d)$'
def extract_user_id(message: Message) -> int:
text = message.text if message.text else message.caption
if '#id' not in text:
return False
telegram_user_id = int(text.split(sep='#id')[-1])
return telegram_user_id
def parse_ban_command(command: CommandObject) -> int:
telegram_user_id = command.args.strip()
try:
telegram_user_id = int(telegram_user_id)
except ValueError:
return False
return telegram_user_id
def get_user_name(chat: Chat):
"""Получение полного имени пользователя из чата"""
if not chat.first_name:
return ""
if not chat.last_name:
return chat.first_name
return f"{chat.first_name} {chat.last_name}"
def check_input_date_correct(date_args):
"""Проверка интервала дат на соотвествие паттерну"""
date_from, date_to = date_args.split()
pattern = re.compile(DATE_PATTERN)
if not (pattern.match(date_from) and pattern.match(date_to)):
return False
return True
def stringdate_to_date(date_args):
"""Конвертация текстового интервала дат в формат datetime"""
from_date, to_date = date_args.split()
from_date = datetime.strptime(from_date, '%d.%m.%Y')
to_date = datetime.strptime(to_date, '%d.%m.%Y') + relativedelta(days=+1)
return from_date, to_date
def get_user_data(message: Message):
user_data = {
'telegram_id': message.from_user.id,
'telegram_username': message.from_user.username,
'first_name': message.from_user.first_name,
'last_name': message.from_user.last_name,
}
return user_data
def check_user_is_banned(user: UserBaseScheme):
return user.is_banned
def check_user_is_admin(user: UserFromDBScheme):
return user.is_admin
async def get_telegram_user_from_resend_message(message: Message, bot: Bot):
telegram_user_id = extract_user_id(message.reply_to_message)
if not telegram_user_id:
return await message.reply(
text='Невозможно найти пользователя с таким Id'
)
try:
return await bot.get_chat(telegram_user_id)
except TelegramAPIError as err:
return await message.reply(
text=(f'Невозможно найти пользователя с таким Id. Текст ошибки:\n'
f'{err.message}')
)

0
app/core/__init__.py Normal file
View File

3
app/core/base.py Normal file
View File

@@ -0,0 +1,3 @@
"""Импорты класса Base и всех моделей для Alembic."""
from app.core.db import Base # noqa
from app.models import User, Message # noqa

29
app/core/config.py Normal file
View File

@@ -0,0 +1,29 @@
import os
from typing import Optional
from pydantic import BaseSettings
from dotenv import load_dotenv
load_dotenv()
class Settings(BaseSettings):
TELEGRAM_TOKEN: str
GROUP_ID: str
WEBHOOK_DOMAIN: Optional[str]
WEBHOOK_PATH: Optional[str]
APP_HOST: str
APP_PORT: int
DATABASE_URL: str
DB_HOST: str
DB_PORT: str
DB_USER: str = os.getenv('POSTGRES_USER')
DB_PASSWORD: str = os.getenv('POSTGRES_PASSWORD')
START_MESSAGE: str = os.getenv('START_MESSAGE')
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
settings = Settings()

36
app/core/db.py Normal file
View File

@@ -0,0 +1,36 @@
from sqlalchemy import Integer, TIMESTAMP, func
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import declared_attr, declarative_base, sessionmaker, \
mapped_column
from app.core.config import settings
class PreBase:
"""Абстрактная модель для наследования"""
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
id = mapped_column(Integer, primary_key=True)
created_at = mapped_column(TIMESTAMP,
server_default=func.current_timestamp(),
nullable=False)
updated_at = mapped_column(TIMESTAMP,
server_default=func.current_timestamp(),
nullable=False,
onupdate=func.current_timestamp())
Base = declarative_base(cls=PreBase)
engine = create_async_engine(settings.DATABASE_URL)
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession)
async def get_async_session():
"""Генератор асинхронной сессии"""
async with AsyncSessionLocal() as async_session_gen:
yield async_session_gen

0
app/crud/__init__.py Normal file
View File

55
app/crud/base.py Normal file
View File

@@ -0,0 +1,55 @@
from fastapi.encoders import jsonable_encoder
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
class CRUDBase:
def __init__(self, model):
self.model = model
async def get(
self,
obj_id: int,
session: AsyncSession
):
db_obj = await session.execute(
select(self.model).where(
self.model.db == obj_id
)
)
return db_obj.scalars().first()
async def get_multi(self, session: AsyncSession):
db_objs = await session.execute(select(self.model))
return db_objs.scalars().all()
async def create(self,
obj_in,
session: AsyncSession):
obj_in_data = obj_in
db_obj = self.model(**obj_in_data)
session.add(db_obj)
await session.commit()
await session.refresh(db_obj)
return db_obj
async def update(
self,
db_obj,
obj_in,
session: AsyncSession
):
obj_data = jsonable_encoder(db_obj)
update_data = obj_in.dict(exclude_unsets=True)
for field in obj_data:
if field in update_data:
setattr(db_obj, field, update_data[field])
session.add(db_obj)
await session.commit()
await session.refresh(db_obj)
return db_obj

46
app/crud/message.py Normal file
View File

@@ -0,0 +1,46 @@
from datetime import datetime
from sqlalchemy import select, and_, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.crud.base import CRUDBase
from app.models import Message
class CRUDMessage(CRUDBase):
async def get_by_date_interval(
self,
from_date: datetime,
to_date: datetime,
session: AsyncSession
):
query = select(Message).where(and_(Message.created_at >= from_date,
Message.created_at <= to_date))
messages = await session.execute(query)
messages = messages.scalars().all()
return messages
async def get_count_user_messages(
self,
telegram_id: int,
session: AsyncSession
):
stmt = select(func.count()).select_from(
select(Message).where(Message.telegram_user_id == telegram_id)
)
mes_count = await session.execute(stmt)
return mes_count.scalars().one()
async def get_count_answers_to_user(
self,
telegram_id: int,
session: AsyncSession
):
stmt = select(func.count()).select_from(
select(Message).where(Message.answer_to_user_id == telegram_id)
)
answers_count = await session.execute(stmt)
return answers_count.scalars().one()
crud_message = CRUDMessage(Message)

110
app/crud/user.py Normal file
View File

@@ -0,0 +1,110 @@
import asyncio
from typing import Optional
from fastapi.encoders import jsonable_encoder
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from aiogram.types import Message as TelegramMessage
from app.bot.utils import get_user_data
from app.crud.base import CRUDBase
from app.models import User
from app.schemas.user import UserBaseScheme
class CRUDUser(CRUDBase):
async def get_user_by_telegram_id(
self,
telegram_id: int,
session: AsyncSession
) -> Optional[User]:
user = await session.execute(
select(User).where(User.telegram_id == telegram_id))
return user.scalars().first()
async def update(
self,
db_obj: User,
obj_in: UserBaseScheme,
session: AsyncSession
) -> User:
obj_data = jsonable_encoder(db_obj)
update_data = obj_in.dict(exclude_unset=True)
for field in obj_data:
if field in update_data:
setattr(db_obj, field, update_data[field])
session.add(db_obj)
await session.commit()
await session.refresh(db_obj)
return db_obj
async def update_with_db_obj(
self,
updating_db_obj: User,
session: AsyncSession
) -> User:
session.add(updating_db_obj)
await session.commit()
await session.refresh(updating_db_obj)
return updating_db_obj
async def get_banned_users(
self,
session: AsyncSession
):
banned_users = await session.execute(
select(User).where(User.is_banned)
)
return banned_users.scalars().all()
async def register_admin(self,
user: User,
session: AsyncSession) -> User:
if user.is_admin:
return user
user.is_admin = True
updated_user = await self.update_with_db_obj(user, session)
return updated_user
async def remove_admin(self,
user: User,
session: AsyncSession) -> User:
if not user.is_admin:
return user
user.is_admin = False
updated_user = await self.update_with_db_obj(user, session)
return updated_user
async def ban_user(self,
user: User,
session: AsyncSession) -> User:
if user.is_banned:
return user
user.is_banned = True
updated_user = await self.update_with_db_obj(user, session)
return updated_user
async def unban_user(self,
user: User,
session: AsyncSession) -> User:
if not user.is_banned:
return user
user.is_banned = False
updated_user = await self.update_with_db_obj(user, session)
return updated_user
async def get_or_create_user_by_tg_message(
self,
message: TelegramMessage,
session: AsyncSession
) -> Optional[User]:
telegram_id = message.from_user.id
user = await self.get_user_by_telegram_id(telegram_id, session)
if user:
return user
user_data = get_user_data(message)
new_user = await self.create(user_data, session)
return new_user
crud_user = CRUDUser(User)

2
app/models/__init__.py Normal file
View File

@@ -0,0 +1,2 @@
from .user import User
from .message import Message

19
app/models/message.py Normal file
View File

@@ -0,0 +1,19 @@
from sqlalchemy import Boolean, ForeignKey, Text, BigInteger
from sqlalchemy.orm import mapped_column, relationship
from app.core.db import Base
class Message(Base):
"""Модель сообщений"""
telegram_user_id = mapped_column(BigInteger, ForeignKey(
'user.telegram_id'), nullable=False)
answer_to_user_id = mapped_column(BigInteger, ForeignKey(
'user.telegram_id'), nullable=True)
text = mapped_column(Text, nullable=True)
attachments = mapped_column(Boolean, default=False)
telegram_user = relationship('User', backref='messages', foreign_keys=[
telegram_user_id], lazy='subquery')
answer_to_user = relationship('User', backref='answers', foreign_keys=[
answer_to_user_id])

17
app/models/user.py Normal file
View File

@@ -0,0 +1,17 @@
from sqlalchemy import BigInteger, String, Boolean
from sqlalchemy.orm import mapped_column, relationship
from app.core.db import Base
class User(Base):
"""Модель пользователя телеграм"""
telegram_id = mapped_column(BigInteger, unique=True, nullable=False)
telegram_username = mapped_column(String(100), nullable=True)
is_banned = mapped_column(Boolean, default=False)
first_name = mapped_column(String(100), nullable=True)
last_name = mapped_column(String(100), nullable=True)
is_admin = mapped_column(Boolean, default=False)
# messages = relationship('Message', backref='telegram_user',
# foreign_keys=[telegram_id])

0
app/schemas/__init__.py Normal file
View File

24
app/schemas/message.py Normal file
View File

@@ -0,0 +1,24 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel
class MessageBaseScheme(BaseModel):
telegram_user_id: int
text: Optional[str]
attachments: Optional[bool]
answer_to_user: Optional[int]
class MessageCreateScheme(MessageBaseScheme):
pass
class MessageFromDBScheme(MessageBaseScheme):
id: int
created_at: datetime
updated_at: datetime
class Config:
orm_mode = True

27
app/schemas/user.py Normal file
View File

@@ -0,0 +1,27 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
class UserBaseScheme(BaseModel):
telegram_id: int
telegram_username: Optional[str] = Field(None, max_length=100)
is_banned: Optional[bool]
first_name: Optional[str] = Field(None, max_length=100)
last_name: Optional[str] = Field(None, max_length=100)
is_admin: Optional[bool] = Field(None)
class UserCreateScheme(UserBaseScheme):
pass
class UserFromDBScheme(UserBaseScheme):
id: int
is_banned: bool
created_at: datetime
updated_at: datetime
class Config:
orm_mode = True

0
app/services/__init__.py Normal file
View File