Compare commits
22 Commits
5b09353b06
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 652400e50f | |||
| 95195c2749 | |||
| 9979937113 | |||
| be80c4044b | |||
| 6549c3d70b | |||
| cc520caef5 | |||
| 51eebcc165 | |||
| ffe5e2e938 | |||
| 77a00d7623 | |||
| 369bd7a6e1 | |||
| 8604774741 | |||
| 5f2a00ba4a | |||
| 355f81520c | |||
| 1af9a7b4d9 | |||
| de9c0e6724 | |||
| 3ad35c6980 | |||
| b5a2938b12 | |||
| 9af6b71712 | |||
| bda32cca6a | |||
| b23c1a8b62 | |||
| 882a06f226 | |||
| 50b329bd35 |
@@ -4,6 +4,7 @@ from aiogram.fsm.state import State, StatesGroup
|
||||
class SearchForm(StatesGroup):
|
||||
search_option = State()
|
||||
data_to_search = State()
|
||||
sent_messages = State()
|
||||
search_result = State()
|
||||
|
||||
|
||||
|
||||
@@ -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')}/{os.getenv('DATABASE')}")
|
||||
print(DATABASE_URL)
|
||||
engine = create_async_engine(DATABASE_URL, echo=True)
|
||||
async_session_ = async_sessionmaker(bind=engine, expire_on_commit=False)
|
||||
|
||||
@@ -13,10 +13,11 @@ 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
|
||||
"""
|
||||
@@ -27,7 +28,7 @@ class Worker(Base):
|
||||
name = Column(String, nullable=False)
|
||||
email = Column(String, nullable=True)
|
||||
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())
|
||||
updated_at = Column(Date, onupdate=func.now())
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
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__)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
from .Filters import IsAdmin
|
||||
from .Filters import IsRegister
|
||||
|
||||
__all__ = ["IsRegister", "IsAdmin"]
|
||||
@@ -1,33 +1,28 @@
|
||||
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.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": "Поиск по описанию ",
|
||||
@@ -36,9 +31,9 @@ find_order_params = {"search_by_name": "Поиск по названию", "sear
|
||||
|
||||
@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"))
|
||||
@@ -69,10 +64,6 @@ async def get_order_customer(callback: CallbackQuery, state: FSMContext):
|
||||
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 order_description(message: Message, state: FSMContext):
|
||||
await message.answer("Введите описание заказа в виде ключевых слов (АВР, ПСС, НКУ и т.д.) )")
|
||||
@@ -119,33 +110,30 @@ async def search_by_item(message: Message, state: FSMContext):
|
||||
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}", f"Описание: {order.description or "Отсутствует"}"
|
||||
f"Заказ: №{order.id}\n"
|
||||
f"Заказчик: {order.customer}\n"
|
||||
) for order in
|
||||
selected_orders)))
|
||||
await state.update_data(search_result=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.set_state(SearchForm.search_result)
|
||||
await state.update_data(search_result=selected_orders)
|
||||
else:
|
||||
await message.answer(text="Заказов по вашему запросу не найдено")
|
||||
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 {}
|
||||
orders = await state.get_value("search_result")
|
||||
order = next(filter(lambda item: order_id == item.id, orders), None)
|
||||
|
||||
if order:
|
||||
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.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:
|
||||
@@ -177,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:
|
||||
@@ -188,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"))
|
||||
@@ -204,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]
|
||||
@@ -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)
|
||||
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()
|
||||
|
||||
@@ -20,7 +20,7 @@ user_info_template = ("Новый пользователь ждет регист
|
||||
"Юзернейм: @{}\n"
|
||||
"ID: @msg_{}\n")
|
||||
|
||||
admins_ids = list(map(int, os.getenv("BOT_ADMINS").split(",")))
|
||||
|
||||
|
||||
|
||||
@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'}
|
||||
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:
|
||||
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='Зарегистрировать пользователя',
|
||||
|
||||
@@ -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 SessionMiddleware
|
||||
|
||||
load_dotenv(".env")
|
||||
bot = Bot(token=os.getenv("TOKEN"))
|
||||
|
||||
|
||||
|
||||
41
drone.yaml
41
drone.yaml
@@ -20,28 +20,35 @@ steps:
|
||||
- docker build -t myapp:${DRONE_COMMIT_SHA} .
|
||||
|
||||
- name: deploy
|
||||
image: alpine
|
||||
image: docker
|
||||
volumes:
|
||||
- name: dockersock
|
||||
path: /var/run/docker.sock
|
||||
- name: env
|
||||
path: /srv/prod/telegram_bot/
|
||||
environment:
|
||||
ENV_CONTENT:
|
||||
from_secret: DOT_ENV_CONTENT
|
||||
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:
|
||||
- apk add --no-cache openssh
|
||||
- echo "$ENV_CONTENT" > .env
|
||||
settings:
|
||||
host: localhost
|
||||
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}
|
||||
- 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/
|
||||
Reference in New Issue
Block a user