Compare commits

...

36 Commits

Author SHA1 Message Date
95195c2749 fix order module
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-26 22:54:46 +03:00
9979937113 fix drone file
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-26 22:44:54 +03:00
be80c4044b new inline button for order ,message "finish_work_on_order"
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone Build is passing
2025-09-26 22:34:45 +03:00
6549c3d70b fix state value
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-26 22:21:47 +03:00
cc520caef5 Merge branch 'master' into dev
Some checks reported errors
continuous-integration/drone/push Build was killed
# Conflicts:
#	drone.yaml
2025-09-26 21:29:27 +03:00
51eebcc165 fix env
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-26 21:20:31 +03:00
ffe5e2e938 update orders list message sending 2025-09-26 21:20:29 +03:00
77a00d7623 clear and fix database modul 2025-09-26 21:19:30 +03:00
369bd7a6e1 Revert "clear database_engine imports"
Some checks reported errors
continuous-integration/drone/push Build was killed
This reverts commit de9c0e6724.
2025-09-26 20:53:36 +03:00
8604774741 fix imports
Some checks failed
continuous-integration/drone/push Build is failing
update search_by_item and show_order
2025-09-26 20:43:02 +03:00
5f2a00ba4a fix env
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is failing
2025-09-26 20:26:48 +03:00
355f81520c fix imports
Some checks failed
continuous-integration/drone/push Build is failing
change sending list of orders
2025-09-26 19:49:03 +03:00
1af9a7b4d9 fix bug :)
Some checks failed
continuous-integration/drone/push Build is failing
update Worker table doc
2025-09-26 16:23:05 +03:00
de9c0e6724 clear database_engine imports 2025-09-26 16:21:41 +03:00
3ad35c6980 Merge branch 'drone-conf'
Some checks reported errors
continuous-integration/drone/push Build was killed
2025-09-26 16:08:12 +03:00
b5a2938b12 Merge branch 'refs/heads/dev' 2025-09-26 16:07:52 +03:00
5b09353b06 separate FSM
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-26 15:04:51 +03:00
872db63c0c fix filters
add new filter
2025-09-26 15:04:51 +03:00
9af6b71712 fix drone env 2 2025-09-26 14:39:18 +03:00
bda32cca6a Update drone.yaml
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-26 10:56:52 +03:00
b23c1a8b62 fix imports in filters module
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2025-09-26 10:20:48 +03:00
882a06f226 drone config fix
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-25 23:35:09 +03:00
50b329bd35 drone confix fix
Some checks failed
continuous-integration/drone/push Build is failing
2025-09-25 23:18:48 +03:00
a2509c629e fixup! fix registration,add filter
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-25 22:46:24 +03:00
2307637c80 separate FSM 2025-09-25 22:44:59 +03:00
631e7b4c13 fix imports 2025-09-25 22:43:41 +03:00
9fae1cbe1c fix registration,add filter 2025-09-25 22:41:11 +03:00
bbf2b1e6f9 fix session middleware 2025-09-25 22:37:54 +03:00
e8e2d3dc58 configure drone file
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-25 22:01:45 +03:00
20aefeb110 add 'job_title' column to workers table
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2025-08-26 22:04:13 +03:00
173e8959ea rearranged db_port to env 2025-08-20 19:36:48 +03:00
d405e74aad change worker.telegram_id type (INTEGER>>BIGINT) 2025-08-20 19:35:41 +03:00
c0f4be1179 change worker.telegram_id type (INTEGER>>BIGINT)
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone Build is failing
2025-07-28 23:48:33 +03:00
4748127399 Fixed reg_confirm
admins_ids value type
2025-07-28 23:47:17 +03:00
1148d91598 - change worker_id choice
- append possibility to stop create order
- change inline keyboard text
2025-07-28 23:45:40 +03:00
cbb33f6da8 Fixed reg_confirm -\n admins_ids value type \n 2025-07-28 23:39:27 +03:00
15 changed files with 225 additions and 167 deletions

4
app/FSM/__init__.py Normal file
View File

@@ -0,0 +1,4 @@
from .states import OrderForm, SearchForm, OrderingForm
__all__ = ["SearchForm", "OrderForm", "OrderingForm"]

25
app/FSM/states.py Normal file
View File

@@ -0,0 +1,25 @@
from aiogram.fsm.state import State, StatesGroup
class SearchForm(StatesGroup):
search_option = State()
data_to_search = State()
sent_messages = State()
search_result = State()
class OrderForm(StatesGroup):
id = State()
worker_id = State()
status_id = State()
counterparty = State()
customer = State()
commencement_work = State()
end_work = State()
description = State()
class OrderingForm(StatesGroup):
tools_list = State()

View File

@@ -1,5 +1,4 @@
from .database_engine import async_session_
from .models import Worker,Component, Order
from .models import Worker, Component, Order, job_title
__all__ = ["Worker", "Component", "Order", "async_session_"]
__all__ = ["Worker", "Component", "Order", "async_session_", "job_title"]

View File

@@ -1,14 +1,8 @@
import os
import asyncpg
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine, session
import dotenv
# connection = psycopg2.connect(*(os.getenv(key) for key in ["DATABASE", "DB_HOST", "DB_USER", "DB_PASSWORD"]))
# connection.autocommit = True
dotenv.load_dotenv(".env")
DATABASE_URL = (f"postgresql+asyncpg://{os.getenv('DB_USER')}:{os.getenv('DB_PASSWORD')}@"
f"{os.getenv('DB_HOST')}:9432/{os.getenv('DATABASE')}")
print(DATABASE_URL)
f"{os.getenv('DB_HOST')}/{os.getenv('DATABASE')}")
engine = create_async_engine(DATABASE_URL, echo=True)
async_session_ = async_sessionmaker(bind=engine, expire_on_commit=False)

View File

@@ -1,8 +1,9 @@
from sqlalchemy import Column, Integer, String, Date, ForeignKey, func, Null
from sqlalchemy import Column, Integer, String, Date, ForeignKey, func, Null, BIGINT
from sqlalchemy.dialects.postgresql import ENUM
from sqlalchemy.orm import relationship, DeclarativeBase
status_enum = ENUM('Выполнено', 'В процессе', 'Создано', 'Ожидание комплектующих', name='status')
job_title = ENUM('Начальник цеха', 'Мастер', 'Сборщик', name='job_title')
class Base(DeclarativeBase):
@@ -12,20 +13,22 @@ class Base(DeclarativeBase):
class Worker(Base):
"""
id SERIAL PRIMARY KEY,
telegram_id INTEGER UNIQUE NOT NULL,
name VARCHAR NOT NULL,
telegram_id BIGINT UNIQUE NOT NULL,
name VARCHAR NOT NULL,
email VARCHAR(50),
phone_number VARCHAR(20) NOT NULL,
phone_number VARCHAR(20),
job_title job_title default 'Сборщик',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP
"""
__tablename__ = "workers"
id = Column(Integer, primary_key=True, autoincrement=True)
telegram_id = Column(Integer, unique=True, nullable=False)
telegram_id = Column(BIGINT, unique=True, nullable=False)
name = Column(String, nullable=False)
email = Column(String, nullable=True)
phone_number = Column(String, nullable=False)
phone_number = Column(String, default=None)
job_title = Column(job_title, default='Сборщик')
created_at = Column(Date, server_default=func.now())
updated_at = Column(Date, onupdate=func.now())
@@ -47,7 +50,7 @@ class Order(Base):
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String)
worker_id = Column(Integer, ForeignKey('workers.telegram_id'), nullable=False)
worker_id = Column(BIGINT, ForeignKey('workers.telegram_id'), nullable=False)
status_id = Column(status_enum)
counterparty = Column(String)
customer = Column(String, nullable=False)

View File

@@ -1,19 +1,18 @@
import logging
import os
from typing import Any
from aiogram.types import Message, CallbackQuery
from aiogram.filters import BaseFilter
from keyboards.menu_commands import commands
import os
loggger = logging.getLogger(__name__)
class IsAdmin(BaseFilter):
def __init__(self):
self.admins_ids = os.getenv("BOT_ADMINS").split(",")
self.admins_ids = list(map(int, os.getenv("BOT_ADMINS").split(",")))
async def __call__(self, message: Message | CallbackQuery) -> bool:
return str(message.from_user.id) in self.admins_ids
return message.from_user.id in self.admins_ids
class CommandFilter(BaseFilter):
@@ -23,3 +22,7 @@ class CommandFilter(BaseFilter):
async def __call__(self, message: Message) -> bool:
return message.text.startswith(tuple(self.commands.keys()))
class IsRegister(BaseFilter):
async def __call__(self, message: Message | CallbackQuery, **data: dict[str, Any]) -> bool:
return data.get("through_registration") is True

View File

@@ -1 +1,4 @@
from .Filters import IsAdmin
from .Filters import IsAdmin
from .Filters import IsRegister
__all__ = ["IsRegister", "IsAdmin"]

View File

@@ -1,18 +1,15 @@
import time
import re
from aiogram import Router, Bot, F
from aiogram.types import (Message, ChatMemberUpdated, FSInputFile, CallbackQuery, ReplyKeyboardRemove)
from loguru import logger
from aiogram import Router, Bot, F
from aiogram.types import Message, CallbackQuery
from handlers.registration import registration_confirm
from filters.Filters import IsAdmin, CommandFilter
from database import async_session_
from filters.Filters import IsAdmin
admin_router = Router()
admin_router.message.filter(IsAdmin())
regex = re.compile(r'(del|reg) @.+')
regex = re.compile(r'(del|reg)_@.+')
@admin_router.callback_query(lambda x: re.fullmatch(regex, x.data))
@@ -30,5 +27,3 @@ async def send_message_command(message: Message, bot: Bot):
chat_id = re.search(r'(\d+)', message.text).group()
print(chat_id)
await bot.send_message(text='Ronis->' + message.text.strip('@msg_' + chat_id), chat_id=chat_id)

View File

@@ -1,91 +1,73 @@
import asyncio
import os
from pathlib import Path
import re
from pathlib import Path
from aiogram import Router, Bot, F
from aiogram.filters import CommandStart, Command
from aiogram.types import Message, CallbackQuery, FSInputFile, InputMediaPhoto, InputMediaVideo
from aiogram.filters import Command
from aiogram.types import Message, CallbackQuery, FSInputFile, InputMediaPhoto, InputMediaVideo, ReplyKeyboardRemove
from aiogram.exceptions import AiogramError
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.context import FSMContext
from sqlalchemy import select, insert
from sqlalchemy import select
from loguru import logger
from filters import IsAdmin
from keyboards import create_inline_kb, commands, button_create
from keyboards import create_inline_kb, button_create
from database import async_session_, Order, Worker
from FSM import SearchForm, OrderForm
orders_router = Router()
# orders_router.message.filter()
order_operation_base = {"add_order_photo": "Добавить фото",
"get_order_photo": "Получить фото",
"get_order_components": "Получить список комплектующих",
"get_order_documentation": "Получить документацию"
"get_order_documentation": "Получить документацию",
"finish_work_on_order": "❌ Закрыть"
}
order_operation_update = {"add_order_documentation": "Добавить документацию"}
order_main = {"find_orders": "Найти заказ"}
order_main = {"find_orders": "Найти заказ", "create_order": "Создать заказ"}
order_main_update = {"create_order": "Создать заказ"}
find_order_params = {"search_by_name": "Поиск по названию", "search_by_description": "Поиск по описанию ",
"search_by_id": "Поиск по номеру заказа", "search_by_customer": "Поиск по заказчику"}
class SearchForm(StatesGroup):
search_option = State()
data_to_search = State()
search_result = State()
class OrderForm(StatesGroup):
id = State()
worker_id = State()
status_id = State()
counterparty = State()
customer = State()
commencement_work = State()
end_work = State()
description = State()
@orders_router.message(Command(commands="orders"))
async def orders_menu(message: Message):
order_main_upd = order_main_update if await IsAdmin()(message) else {}
await message.answer(text="Доступные действия с заказами:",
reply_markup=create_inline_kb(width=1, **order_main, **order_main_upd))
reply_markup=create_inline_kb(width=1, **order_main))
await message.delete()
@orders_router.callback_query(lambda x: x.data.startswith("create_order"))
async def get_order_worker_id(callback: CallbackQuery, state: FSMContext):
await state.set_state(OrderForm.worker_id)
await callback.message.answer("Введите id сборщика который будет собирать заказ:",
reply_markup=create_inline_kb(**{f"{callback.from_user.id}": "Ввести мой id"}))
async with async_session_() as session:
result = await session.execute(select(Worker.name, Worker.telegram_id).where(Worker.job_title == "Сборщик"))
workers = result.all()
await callback.message.answer("Введите сборщика ответственного за заказ:",
reply_markup=create_inline_kb(
**{f"set_worker_in_order_{tg_id}": name for name, tg_id in workers}))
await callback.message.delete()
@orders_router.message(F.text == "❌ Прервать создание заказа")
async def order_description(message: Message, state: FSMContext):
await state.clear()
await message.answer("Создание заказа прервано", reply_markup=ReplyKeyboardRemove())
@orders_router.callback_query(OrderForm.worker_id)
async def get_order_counterparty(message: Message | CallbackQuery, state: FSMContext):
if isinstance(message, Message):
worker_id = int(message.text)
msg = message
else:
worker_id = int(message.data)
msg = message.message
await msg.answer("Введите данные заказчика ")
await msg.delete()
await state.update_data(worker_id=worker_id)
async def get_order_customer(callback: CallbackQuery, state: FSMContext):
worker_tg_id = int(re.search(r"(\d+)", callback.data).group())
await callback.message.answer("Введите данные заказчика ",
reply_markup=button_create(["❌ Прервать создание заказа"]))
await callback.message.delete()
await state.update_data(worker_id=worker_tg_id)
await state.set_state(OrderForm.customer)
# @orders_router.callback_query(OrderForm.counterparty)
# async def get_order_customer(message: Message, state: FSMContext):
#
@orders_router.message(OrderForm.customer)
async def create_order(message: Message, state: FSMContext):
async def order_description(message: Message, state: FSMContext):
await message.answer("Введите описание заказа в виде ключевых слов (АВР, ПСС, НКУ и т.д.) )")
await message.delete()
await state.update_data(customer=message.text)
await state.set_state(OrderForm.description)
@@ -99,7 +81,7 @@ async def create_order(message: Message, state: FSMContext):
session.add(Order(**order_))
await state.clear()
await message.answer("Заказ успешно создан ")
await message.answer("Заказ успешно создан ", reply_markup=ReplyKeyboardRemove())
@orders_router.callback_query(lambda x: x.data == "find_orders")
@@ -123,13 +105,23 @@ async def search_by_item(message: Message, state: FSMContext):
async with async_session_() as local_session:
search_opt = await state.get_value("search_option")
col = getattr(Order, search_opt)
await message.answer(message.text)
result = await local_session.execute(
select(Order).where(col.ilike(f"%{message.text}%") if search_opt != "id" else col == int(message.text)))
selected_orders = result.scalars().all()
if selected_orders:
await message.answer(text="Список заказов", reply_markup=create_inline_kb(width=1, **dict(
(f"show_order_{order.id}", order.description or "Отсутствует") for order in selected_orders)))
await message.answer(text="Список найденных заказов: ")
sent_messages = []
for order in selected_orders:
sent_messages.append(await message.answer(text=f"Номер заказа: {order.id}\n"
f"Заказчик: {order.customer}\n"
f"Статус: {order.status_id}\n"
f"Дата отгрузки: {order.end_work}\n"
f"Дата создания: {order.created_at}\n"
f"Описание: {order.description}",
reply_markup=create_inline_kb(width=1, **{
f"show_order_{order.id}": f"Заказ: №{order.id}"})))
await state.update_data(sent_messages=sent_messages)
await state.update_data(search_result=selected_orders)
await state.set_state(SearchForm.search_result)
else:
@@ -137,19 +129,11 @@ async def search_by_item(message: Message, state: FSMContext):
await state.clear()
@orders_router.callback_query(SearchForm.search_result)
async def show_order(callback: CallbackQuery, state: FSMContext):
@orders_router.callback_query(SearchForm.search_result and (lambda x: "show_order_" in x.data))
async def show_order(callback: CallbackQuery, state: FSMContext, bot: Bot):
order_id = int(re.search(r"(\d+)", callback.data).group())
try:
async with async_session_() as local_session:
result = await local_session.execute(select(Order).filter(Order.id == order_id))
order = result.scalars().first()
except Exception as err:
logger.warning(err)
order_operation_upd = order_operation_update if await IsAdmin()(callback) else {}
order = next(filter(lambda item: order_id == item.id, await state.get_value("selected_orders")), None)
if order:
await callback.message.answer(text=f"Номер заказа: {order.id}\n"
@@ -159,21 +143,22 @@ async def show_order(callback: CallbackQuery, state: FSMContext):
f"Дата отгрузки: {order.end_work}\n"
f"Дата создания: {order.created_at}\n"
f"Описание: {order.description}",
reply_markup=create_inline_kb(width=2, **dict(
reply_markup=create_inline_kb(width=1, **dict(
(f"{clbk}_{order.id}", text) for clbk, text in order_operation_base.items()))
)
await callback.message.delete()
await state.clear()
for message in await state.get_value("sent_messages"):
await bot.delete_message(chat_id=callback.message.chat.id, message_id=message.message_id)
await state.clear()
@orders_router.callback_query(lambda x: x.data.startswith("get_order_photo"))
async def send_order_photos(callback: CallbackQuery, bot: Bot):
order_id = callback.data.split("_")[-1]
media_item: Path
media_group = []
os.makedirs(Path(f"./photos/{order_id}/"), exist_ok=True)
media_path = Path(f"./photos/{order_id}/").iterdir()
order_photos_path = Path(f"/app/photos/{order_id}/")
os.makedirs(order_photos_path, exist_ok=True)
media_path = order_photos_path.iterdir()
if not (media_item := next(media_path, None)):
text = f"Фото по заказу \"{order_id}\" отсутствуют "
else:
@@ -181,7 +166,7 @@ async def send_order_photos(callback: CallbackQuery, bot: Bot):
f"")
await bot.send_message(chat_id=callback.from_user.id, text=text)
while media_item or media_group:
if len(media_group) == 10 or (media_group and not media_item):
if len(media_group) == 10 or not media_item:
await bot.send_media_group(chat_id=callback.from_user.id, media=media_group)
media_group.clear()
if media_item:
@@ -192,8 +177,10 @@ async def send_order_photos(callback: CallbackQuery, bot: Bot):
logger.error(f"Ошибка при обработке {media_path}: {err}")
media_item = next(media_path, None)
await asyncio.sleep(600)
await callback.message.delete()
try:
pass
except:
pass
@orders_router.callback_query(lambda x: x.data.startswith("add_order_photo"))
@@ -208,7 +195,7 @@ async def reply_for_photo(callback: CallbackQuery, bot: Bot):
F.reply_to_message)
async def add_order_photo(message: Message, bot: Bot):
order_id = re.search(r"(\d+)", message.reply_to_message.text).group()
order_photos_path = f"/app/photos/{order_id}/"
order_photos_path = Path(f"/app/photos/{order_id}/")
os.makedirs(order_photos_path, exist_ok=True)
item = message.video or message.photo[-1]
@@ -228,3 +215,8 @@ async def add_order_photo(message: Message, bot: Bot):
await bot.delete_message(chat_id=message.chat.id, message_id=message.reply_to_message.message_id)
except:
pass
@orders_router.callback_query(F.data.startswith("finish_work_on_order"))
async def finish_work_on_order(callback: CallbackQuery):
await callback.message.delete()

View File

@@ -1,18 +1,18 @@
import os
from asyncio import Event
from asyncio import Event, wait_for, TimeoutError
from aiogram import Router, Bot
from aiogram.filters import CommandStart
from aiogram.types import Message, User
from sqlalchemy import insert, select
from sqlalchemy.orm import selectinload
from loguru import logger
from keyboards import create_inline_kb
from database import async_session_, Worker
from filters import IsRegister
registration_router = Router()
registration_router.message.filter(IsRegister() or CommandStart())
registration_confirm: dict[int, Event] = {}
user_info_template = ("Новый пользователь ждет регистрации:\n"
"Имя: {}\n"
@@ -21,30 +21,46 @@ user_info_template = ("Новый пользователь ждет регист
"ID: @msg_{}\n")
@registration_router.message(CommandStart())
async def registration_command(message: Message, bot: Bot):
admins_ids = os.getenv("BOT_ADMINS").split(",")
async with async_session_() as session:
async with session.begin():
result = await session.execute(select(Worker).where(Worker.telegram_id == message.from_user.id))
user = result.scalars().first()
if not user:
@registration_router.message(CommandStart())
async def start_command(message: Message, bot: Bot):
async with async_session_() as session:
result = await session.execute(select(Worker).where(Worker.telegram_id == message.from_user.id))
user = result.scalars().first()
if not user:
user = message.from_user
dict_for_inline = {f'reg @{user.id}': 'Allow', f'del @{user.id}': 'Reject'}
dict_for_inline = {f'reg_@{user.id}': 'Allow', f'del_@{user.id}': 'Reject'}
user_info = user_info_template.format(user.first_name, user.last_name if user.last_name else 'Не указана',
user.username if user.username else 'Не указан', user.id)
for admin in admins_ids:
await bot.send_message(chat_id=admin, text=user_info)
await bot.send_message(chat_id=admin, text='Зарегистрировать пользователя',
reply_markup=create_inline_kb(width=2, **dict_for_inline))
for admin in list(map(int, os.getenv("BOT_ADMINS").split(","))):
try:
await bot.send_message(chat_id=admin, text=user_info)
await bot.send_message(chat_id=admin, text='Зарегистрировать пользователя',
reply_markup=create_inline_kb(width=2, **dict_for_inline))
await message.answer("Запрос на регистрацию отправлен администратору, ожидайте подтверждения.")
except Exception:
logger.error(f"{start_command.__name__} failed")
reg_confirm = Event()
registration_confirm[user.id] = reg_confirm
if await reg_confirm:
try:
await wait_for(reg_confirm.wait(), timeout=60)
async with async_session_() as local_session:
async with local_session.begin():
local_session.add(Worker(telegram_id=int(user.id), name=user.first_name))
del registration_confirm[user.id]
local_session.add(Worker(telegram_id=user.id, name=user.first_name))
await message.answer("Регистрация подтверждена, для просмотра доступных действий нажмите кнопку 'MENU'")
except TimeoutError:
await message.answer("Время ожидания истекло.")
del registration_confirm[user.id]
else:
await message.answer("Работа бота возобновлена")
@registration_router.message()
async def catch_message(message: Message):
await message.answer("Для работы с ботом, требуется регистрация\nНажмите /start для регистрации")

View File

@@ -1,12 +1,14 @@
import os
import asyncio
from dotenv import load_dotenv
load_dotenv(".env")
from aiogram import Dispatcher, Bot
from handlers import *
from keyboards import set_main_menu
from middlewares import AccessCheckMiddleware
from middlewares import SessionMiddleware
load_dotenv(".env")
bot = Bot(token=os.getenv("TOKEN"))
@@ -16,7 +18,7 @@ async def main() -> None:
dp.startup.register(set_main_menu)
dp.include_router(registration_router)
dp.include_router(admin_router)
dp.update.outer_middleware(AccessCheckMiddleware())
dp.update.outer_middleware(SessionMiddleware())
dp.include_router(orders_router)
dp.include_router(components_router)

View File

@@ -1,4 +1,4 @@
from .outer_middlewares import AccessCheckMiddleware
from .outer_middlewares import SessionMiddleware
__all__ = ["AccessCheckMiddleware"]
__all__ = ["SessionMiddleware"]

View File

@@ -1,29 +1,29 @@
import logging
from typing import Any, Awaitable, Callable, Dict
from aiogram import BaseMiddleware, Bot
from aiogram.types import TelegramObject
from database import async_session_, Worker
from sqlalchemy import select
from loguru import logger
from database import async_session_, Worker
class AccessCheckMiddleware(BaseMiddleware):
class SessionMiddleware(BaseMiddleware):
sessions_in_memory_db = set()
async def __call__(
self,
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
event: TelegramObject,
data: Dict[str, Any]
) -> Any:
logger.info("Session check")
event_data = event.message or event.callback_query
user = event_data.from_user.id
if user not in self.sessions_in_memory_db:
async with async_session_() as session:
async with session.begin():
result = await session.execute(select(Worker).where(Worker.telegram_id == event_data.from_user.id))
user = result.scalars().first()
if user:
self.sessions_in_memory_db.add(event_data.from_user.id)
return await handler(event, data)
return None
result = await session.execute(select(Worker).where(Worker.telegram_id == event_data.from_user.id))
user_in_db = result.scalars().first()
if not user_in_db:
data["through_registration"] = True
else:
self.sessions_in_memory_db.add(user)
return await handler(event, data)

View File

@@ -1,12 +1,15 @@
kind: pipeline
type: docker
name: default
name: first_run
clone:
disable: true
steps:
- name: install-dependencies
image: python:3.12
- name: clone
image: alpine/git
commands:
- pip install -r pyproject.toml
- git clone https://git.ronis-0505.ru/ronis_0505/telegram-bot-for-manipulate-orders.git .
- name: build
image: docker:dind
@@ -17,22 +20,35 @@ steps:
- docker build -t myapp:${DRONE_COMMIT_SHA} .
- name: deploy
image: appleboy/ssh
settings:
host: your.server.ip
username: ronis_0505
password: 667766
script:
- docker pull myapp:${DRONE_COMMIT_SHA}
- docker stop myapp || true
- docker rm myapp || true
- docker run -d --name myapp \
--network prod_net \
-v /srv/prod/telegram_bot/photos:/app/photos/ \
myapp:${DRONE_COMMIT_SHA}
image: docker
volumes:
- name: dockersock
path: /var/run/docker.sock
- name: env
path: /srv/prod/telegram_bot/
environment:
TOKEN:
from_secret: TOKEN
BOT_ADMINS:
from_secret: BOT_ADMINS
DATABASE:
from_secret: DATABASE
DB_HOST:
from_secret: DB_HOST
DB_USER:
from_secret: DB_USER
DB_PASSWORD:
from_secret: DB_PASSWORD
commands:
- docker stop myapp || true
- docker rm myapp || true
- docker run --name=myapp -d --network=prod_net -v /srv/prod/telegram_bot/photos:/app/photos/ -v /srv/prod/telegram_bot/.env:/app/.env:ro myapp:${DRONE_COMMIT_SHA}
volumes:
- name: dockersock
host:
path: /var/run/docker.sock
- name: env
host:
path: /srv/prod/telegram_bot/

View File

@@ -1,21 +1,27 @@
CREATE TYPE job_title AS ENUM ('Начальник цеха','Мастер','Сборщик');
CREATE TYPE status AS ENUM ('Выполнено','В процессе','Создано','Ожидание комплектующих');
CREATE TABLE workers
(
id SERIAL PRIMARY KEY,
telegram_id INTEGER UNIQUE NOT NULL,
name VARCHAR NOT NULL,
telegram_id BIGINT UNIQUE NOT NULL,
name VARCHAR NOT NULL,
email VARCHAR(50),
phone_number VARCHAR(20) NOT NULL,
phone_number VARCHAR(20),
job_title job_title default 'Сборщик',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP
);
CREATE TYPE status AS ENUM ('Выполнено','В процессе','Создано','Ожидание комплектующих');
CREATE TABLE orders
(
id SERIAL PRIMARY KEY,
name VARCHAR,
worker_id INTEGER REFERENCES workers (telegram_id),
worker_id BIGINT REFERENCES workers (telegram_id),
status_id status DEFAULT 'Создано',
counterparty VARCHAR(50),
customer VARCHAR NOT NULL,