Compare commits
14 Commits
3ad35c6980
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 95195c2749 | |||
| 9979937113 | |||
| be80c4044b | |||
| 6549c3d70b | |||
| cc520caef5 | |||
| 51eebcc165 | |||
| ffe5e2e938 | |||
| 77a00d7623 | |||
| 369bd7a6e1 | |||
| 8604774741 | |||
| 5f2a00ba4a | |||
| 355f81520c | |||
| 1af9a7b4d9 | |||
| de9c0e6724 |
@@ -4,6 +4,7 @@ from aiogram.fsm.state import State, StatesGroup
|
|||||||
class SearchForm(StatesGroup):
|
class SearchForm(StatesGroup):
|
||||||
search_option = State()
|
search_option = State()
|
||||||
data_to_search = State()
|
data_to_search = State()
|
||||||
|
sent_messages = State()
|
||||||
search_result = State()
|
search_result = State()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import asyncpg
|
|
||||||
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine, session
|
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')}@"
|
DATABASE_URL = (f"postgresql+asyncpg://{os.getenv('DB_USER')}:{os.getenv('DB_PASSWORD')}@"
|
||||||
f"{os.getenv('DB_HOST')}/{os.getenv('DATABASE')}")
|
f"{os.getenv('DB_HOST')}/{os.getenv('DATABASE')}")
|
||||||
print(DATABASE_URL)
|
|
||||||
engine = create_async_engine(DATABASE_URL, echo=True)
|
engine = create_async_engine(DATABASE_URL, echo=True)
|
||||||
async_session_ = async_sessionmaker(bind=engine, expire_on_commit=False)
|
async_session_ = async_sessionmaker(bind=engine, expire_on_commit=False)
|
||||||
|
|||||||
@@ -13,10 +13,11 @@ class Base(DeclarativeBase):
|
|||||||
class Worker(Base):
|
class Worker(Base):
|
||||||
"""
|
"""
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
telegram_id INTEGER UNIQUE NOT NULL,
|
telegram_id BIGINT UNIQUE NOT NULL,
|
||||||
name VARCHAR NOT NULL,
|
name VARCHAR NOT NULL,
|
||||||
email VARCHAR(50),
|
email VARCHAR(50),
|
||||||
phone_number VARCHAR(20) NOT NULL,
|
phone_number VARCHAR(20),
|
||||||
|
job_title job_title default 'Сборщик',
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP
|
updated_at TIMESTAMP
|
||||||
"""
|
"""
|
||||||
@@ -27,7 +28,7 @@ class Worker(Base):
|
|||||||
name = Column(String, nullable=False)
|
name = Column(String, nullable=False)
|
||||||
email = Column(String, nullable=True)
|
email = Column(String, nullable=True)
|
||||||
phone_number = Column(String, default=None)
|
phone_number = Column(String, default=None)
|
||||||
job_title = Column(job_title, default='Сборщик'),
|
job_title = Column(job_title, default='Сборщик')
|
||||||
created_at = Column(Date, server_default=func.now())
|
created_at = Column(Date, server_default=func.now())
|
||||||
updated_at = Column(Date, onupdate=func.now())
|
updated_at = Column(Date, onupdate=func.now())
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +1,28 @@
|
|||||||
import asyncio
|
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
import re
|
import re
|
||||||
|
from pathlib import Path
|
||||||
from aiogram import Router, Bot, F
|
from aiogram import Router, Bot, F
|
||||||
from aiogram.filters import CommandStart, Command
|
from aiogram.filters import Command
|
||||||
from aiogram.types import Message, CallbackQuery, FSInputFile, InputMediaPhoto, InputMediaVideo, ReplyKeyboardRemove
|
from aiogram.types import Message, CallbackQuery, FSInputFile, InputMediaPhoto, InputMediaVideo, ReplyKeyboardRemove
|
||||||
from aiogram.exceptions import AiogramError
|
from aiogram.exceptions import AiogramError
|
||||||
from aiogram.fsm.state import State, StatesGroup
|
|
||||||
from aiogram.fsm.context import FSMContext
|
from aiogram.fsm.context import FSMContext
|
||||||
|
from sqlalchemy import select
|
||||||
from sqlalchemy import select, insert
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from filters import IsAdmin
|
from keyboards import create_inline_kb, button_create
|
||||||
from keyboards import create_inline_kb, commands, button_create
|
|
||||||
from database import async_session_, Order, Worker
|
from database import async_session_, Order, Worker
|
||||||
from FSM import SearchForm, OrderForm
|
from FSM import SearchForm, OrderForm
|
||||||
|
|
||||||
orders_router = Router()
|
orders_router = Router()
|
||||||
|
|
||||||
# orders_router.message.filter()
|
|
||||||
|
|
||||||
order_operation_base = {"add_order_photo": "Добавить фото",
|
order_operation_base = {"add_order_photo": "Добавить фото",
|
||||||
"get_order_photo": "Получить фото",
|
"get_order_photo": "Получить фото",
|
||||||
"get_order_components": "Получить список комплектующих",
|
"get_order_components": "Получить список комплектующих",
|
||||||
"get_order_documentation": "Получить документацию"
|
"get_order_documentation": "Получить документацию",
|
||||||
|
"finish_work_on_order": "❌ Закрыть"
|
||||||
}
|
}
|
||||||
order_operation_update = {"add_order_documentation": "Добавить документацию"}
|
order_operation_update = {"add_order_documentation": "Добавить документацию"}
|
||||||
|
|
||||||
order_main = {"find_orders": "Найти заказ"}
|
order_main = {"find_orders": "Найти заказ", "create_order": "Создать заказ"}
|
||||||
order_main_update = {"create_order": "Создать заказ"}
|
order_main_update = {"create_order": "Создать заказ"}
|
||||||
|
|
||||||
find_order_params = {"search_by_name": "Поиск по названию", "search_by_description": "Поиск по описанию ",
|
find_order_params = {"search_by_name": "Поиск по названию", "search_by_description": "Поиск по описанию ",
|
||||||
@@ -36,9 +31,9 @@ find_order_params = {"search_by_name": "Поиск по названию", "sear
|
|||||||
|
|
||||||
@orders_router.message(Command(commands="orders"))
|
@orders_router.message(Command(commands="orders"))
|
||||||
async def orders_menu(message: Message):
|
async def orders_menu(message: Message):
|
||||||
order_main_upd = order_main_update if await IsAdmin()(message) else {}
|
|
||||||
await message.answer(text="Доступные действия с заказами:",
|
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"))
|
@orders_router.callback_query(lambda x: x.data.startswith("create_order"))
|
||||||
@@ -69,10 +64,6 @@ async def get_order_customer(callback: CallbackQuery, state: FSMContext):
|
|||||||
await state.set_state(OrderForm.customer)
|
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)
|
@orders_router.message(OrderForm.customer)
|
||||||
async def order_description(message: Message, state: FSMContext):
|
async def order_description(message: Message, state: FSMContext):
|
||||||
await message.answer("Введите описание заказа в виде ключевых слов (АВР, ПСС, НКУ и т.д.) )")
|
await message.answer("Введите описание заказа в виде ключевых слов (АВР, ПСС, НКУ и т.д.) )")
|
||||||
@@ -119,13 +110,18 @@ async def search_by_item(message: Message, state: FSMContext):
|
|||||||
selected_orders = result.scalars().all()
|
selected_orders = result.scalars().all()
|
||||||
|
|
||||||
if selected_orders:
|
if selected_orders:
|
||||||
await message.answer(text="Список найденных заказов: ",
|
await message.answer(text="Список найденных заказов: ")
|
||||||
reply_markup=create_inline_kb(width=1, **dict(
|
sent_messages = []
|
||||||
(f"show_order_{order.id}", f"Описание: {order.description or "Отсутствует"}"
|
for order in selected_orders:
|
||||||
f"Заказ: №{order.id}\n"
|
sent_messages.append(await message.answer(text=f"Номер заказа: {order.id}\n"
|
||||||
f"Заказчик: {order.customer}\n"
|
f"Заказчик: {order.customer}\n"
|
||||||
) for order in
|
f"Статус: {order.status_id}\n"
|
||||||
selected_orders)))
|
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.update_data(search_result=selected_orders)
|
||||||
await state.set_state(SearchForm.search_result)
|
await state.set_state(SearchForm.search_result)
|
||||||
else:
|
else:
|
||||||
@@ -133,19 +129,11 @@ async def search_by_item(message: Message, state: FSMContext):
|
|||||||
await state.clear()
|
await state.clear()
|
||||||
|
|
||||||
|
|
||||||
@orders_router.callback_query(SearchForm.search_result)
|
@orders_router.callback_query(SearchForm.search_result and (lambda x: "show_order_" in x.data))
|
||||||
async def show_order(callback: CallbackQuery, state: FSMContext):
|
async def show_order(callback: CallbackQuery, state: FSMContext, bot: Bot):
|
||||||
order_id = int(re.search(r"(\d+)", callback.data).group())
|
order_id = int(re.search(r"(\d+)", callback.data).group())
|
||||||
|
|
||||||
try:
|
order = next(filter(lambda item: order_id == item.id, await state.get_value("selected_orders")), None)
|
||||||
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 {}
|
|
||||||
|
|
||||||
if order:
|
if order:
|
||||||
await callback.message.answer(text=f"Номер заказа: {order.id}\n"
|
await callback.message.answer(text=f"Номер заказа: {order.id}\n"
|
||||||
@@ -155,21 +143,22 @@ async def show_order(callback: CallbackQuery, state: FSMContext):
|
|||||||
f"Дата отгрузки: {order.end_work}\n"
|
f"Дата отгрузки: {order.end_work}\n"
|
||||||
f"Дата создания: {order.created_at}\n"
|
f"Дата создания: {order.created_at}\n"
|
||||||
f"Описание: {order.description}",
|
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()))
|
(f"{clbk}_{order.id}", text) for clbk, text in order_operation_base.items()))
|
||||||
)
|
)
|
||||||
|
|
||||||
await callback.message.delete()
|
for message in await state.get_value("sent_messages"):
|
||||||
await state.clear()
|
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"))
|
@orders_router.callback_query(lambda x: x.data.startswith("get_order_photo"))
|
||||||
async def send_order_photos(callback: CallbackQuery, bot: Bot):
|
async def send_order_photos(callback: CallbackQuery, bot: Bot):
|
||||||
order_id = callback.data.split("_")[-1]
|
order_id = callback.data.split("_")[-1]
|
||||||
media_item: Path
|
|
||||||
media_group = []
|
media_group = []
|
||||||
os.makedirs(Path(f"./photos/{order_id}/"), exist_ok=True)
|
order_photos_path = Path(f"/app/photos/{order_id}/")
|
||||||
media_path = Path(f"./photos/{order_id}/").iterdir()
|
os.makedirs(order_photos_path, exist_ok=True)
|
||||||
|
media_path = order_photos_path.iterdir()
|
||||||
if not (media_item := next(media_path, None)):
|
if not (media_item := next(media_path, None)):
|
||||||
text = f"Фото по заказу \"{order_id}\" отсутствуют "
|
text = f"Фото по заказу \"{order_id}\" отсутствуют "
|
||||||
else:
|
else:
|
||||||
@@ -177,7 +166,7 @@ async def send_order_photos(callback: CallbackQuery, bot: Bot):
|
|||||||
f"")
|
f"")
|
||||||
await bot.send_message(chat_id=callback.from_user.id, text=text)
|
await bot.send_message(chat_id=callback.from_user.id, text=text)
|
||||||
while media_item or media_group:
|
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)
|
await bot.send_media_group(chat_id=callback.from_user.id, media=media_group)
|
||||||
media_group.clear()
|
media_group.clear()
|
||||||
if media_item:
|
if media_item:
|
||||||
@@ -188,8 +177,10 @@ async def send_order_photos(callback: CallbackQuery, bot: Bot):
|
|||||||
logger.error(f"Ошибка при обработке {media_path}: {err}")
|
logger.error(f"Ошибка при обработке {media_path}: {err}")
|
||||||
|
|
||||||
media_item = next(media_path, None)
|
media_item = next(media_path, None)
|
||||||
await asyncio.sleep(600)
|
try:
|
||||||
await callback.message.delete()
|
pass
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@orders_router.callback_query(lambda x: x.data.startswith("add_order_photo"))
|
@orders_router.callback_query(lambda x: x.data.startswith("add_order_photo"))
|
||||||
@@ -204,7 +195,7 @@ async def reply_for_photo(callback: CallbackQuery, bot: Bot):
|
|||||||
F.reply_to_message)
|
F.reply_to_message)
|
||||||
async def add_order_photo(message: Message, bot: Bot):
|
async def add_order_photo(message: Message, bot: Bot):
|
||||||
order_id = re.search(r"(\d+)", message.reply_to_message.text).group()
|
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)
|
os.makedirs(order_photos_path, exist_ok=True)
|
||||||
|
|
||||||
item = message.video or message.photo[-1]
|
item = message.video or message.photo[-1]
|
||||||
@@ -224,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)
|
await bot.delete_message(chat_id=message.chat.id, message_id=message.reply_to_message.message_id)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@orders_router.callback_query(F.data.startswith("finish_work_on_order"))
|
||||||
|
async def finish_work_on_order(callback: CallbackQuery):
|
||||||
|
await callback.message.delete()
|
||||||
@@ -20,7 +20,7 @@ user_info_template = ("Новый пользователь ждет регист
|
|||||||
"Юзернейм: @{}\n"
|
"Юзернейм: @{}\n"
|
||||||
"ID: @msg_{}\n")
|
"ID: @msg_{}\n")
|
||||||
|
|
||||||
admins_ids = list(map(int, os.getenv("BOT_ADMINS").split(",")))
|
|
||||||
|
|
||||||
|
|
||||||
@registration_router.message(CommandStart())
|
@registration_router.message(CommandStart())
|
||||||
@@ -33,7 +33,7 @@ async def start_command(message: Message, bot: Bot):
|
|||||||
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_info = user_info_template.format(user.first_name, user.last_name if user.last_name else 'Не указана',
|
||||||
user.username if user.username else 'Не указан', user.id)
|
user.username if user.username else 'Не указан', user.id)
|
||||||
for admin in admins_ids:
|
for admin in list(map(int, os.getenv("BOT_ADMINS").split(","))):
|
||||||
try:
|
try:
|
||||||
await bot.send_message(chat_id=admin, text=user_info)
|
await bot.send_message(chat_id=admin, text=user_info)
|
||||||
await bot.send_message(chat_id=admin, text='Зарегистрировать пользователя',
|
await bot.send_message(chat_id=admin, text='Зарегистрировать пользователя',
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import os
|
import os
|
||||||
import asyncio
|
import asyncio
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv(".env")
|
||||||
|
|
||||||
from aiogram import Dispatcher, Bot
|
from aiogram import Dispatcher, Bot
|
||||||
from handlers import *
|
from handlers import *
|
||||||
from keyboards import set_main_menu
|
from keyboards import set_main_menu
|
||||||
from middlewares import SessionMiddleware
|
from middlewares import SessionMiddleware
|
||||||
|
|
||||||
load_dotenv(".env")
|
|
||||||
bot = Bot(token=os.getenv("TOKEN"))
|
bot = Bot(token=os.getenv("TOKEN"))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ steps:
|
|||||||
- name: dockersock
|
- name: dockersock
|
||||||
path: /var/run/docker.sock
|
path: /var/run/docker.sock
|
||||||
commands:
|
commands:
|
||||||
- docker build -t myapp:${DRONE_COMMIT_BRANCH} .
|
- docker build -t myapp:${DRONE_COMMIT_SHA} .
|
||||||
|
|
||||||
- name: deploy
|
- name: deploy
|
||||||
image: docker
|
image: docker
|
||||||
@@ -40,10 +40,9 @@ steps:
|
|||||||
DB_PASSWORD:
|
DB_PASSWORD:
|
||||||
from_secret: DB_PASSWORD
|
from_secret: DB_PASSWORD
|
||||||
commands:
|
commands:
|
||||||
- echo "$ENV_CONTENT" > .env
|
|
||||||
- docker stop myapp || true
|
- docker stop myapp || true
|
||||||
- docker rm myapp || true
|
- docker rm myapp || true
|
||||||
- docker run --name=myapp --network=prod_net -v /srv/prod/telegram_bot/photos:/app/photos/ -v /srv/prod/telegram_bot/.env:/app/.env:ro myapp:${DRONE_COMMIT_BRANCH}
|
- 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:
|
volumes:
|
||||||
|
|||||||
Reference in New Issue
Block a user