1
0
mirror of https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git synced 2025-08-14 00:25:46 +02:00

Initial commit

This commit is contained in:
MoonTestUse1
2024-12-23 19:27:44 +06:00
commit e81df4c87e
4952 changed files with 1705479 additions and 0 deletions

View File

@@ -0,0 +1 @@
from .bot import dp, start_bot

14
backend/app/bot/bot.py Normal file
View File

@@ -0,0 +1,14 @@
from aiogram import Bot, Dispatcher
from app.bot.config import settings
bot = Bot(token=settings.TELEGRAM_BOT_TOKEN)
dp = Dispatcher()
from .handlers import start, status
async def start_bot():
"""Start the bot"""
try:
await dp.start_polling(bot, skip_updates=True)
finally:
await bot.session.close()

12
backend/app/bot/config.py Normal file
View File

@@ -0,0 +1,12 @@
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
TELEGRAM_BOT_TOKEN: str = "7677506032:AAHoVqFJs3IZKNK2NVzGnzKUn1hjVtU5Ryk"
TELEGRAM_CHAT_ID: str = "5057752127"
class Config:
env_file = ".env"
settings = Settings()

View File

@@ -0,0 +1,73 @@
from enum import Enum
class RequestStatus(str, Enum):
NEW = "new"
IN_PROGRESS = "in_progress"
RESOLVED = "resolved"
CLOSED = "closed"
class RequestPriority(str, Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"
class Department(str, Enum):
AHO = "aho"
GKH = "gkh"
GENERAL = "general"
class RequestType(str, Enum):
HARDWARE = "hardware"
SOFTWARE = "software"
NETWORK = "network"
ACCESS = "access"
OTHER = "other"
STATUS_LABELS = {
RequestStatus.NEW: "Новая",
RequestStatus.IN_PROGRESS: "В работе",
RequestStatus.RESOLVED: "Решена",
RequestStatus.CLOSED: "Закрыта",
}
PRIORITY_LABELS = {
RequestPriority.LOW: "Низкий",
RequestPriority.MEDIUM: "Средний",
RequestPriority.HIGH: "Высокий",
RequestPriority.CRITICAL: "Критический",
}
PRIORITY_EMOJI = {
RequestPriority.LOW: "🟢",
RequestPriority.MEDIUM: "🟡",
RequestPriority.HIGH: "🟠",
RequestPriority.CRITICAL: "🔴",
}
DEPARTMENT_LABELS = {
Department.AHO: "Административно-хозяйственный отдел",
Department.GKH: "Жилищно-коммунальное хозяйство",
Department.GENERAL: "Общий отдел",
}
REQUEST_TYPE_LABELS = {
RequestType.HARDWARE: "Проблемы с оборудованием",
RequestType.SOFTWARE: "Проблемы с ПО",
RequestType.NETWORK: "Проблемы с сетью",
RequestType.ACCESS: "Доступ к системам",
RequestType.OTHER: "Другое",
}
REQUEST_TYPE_EMOJI = {
RequestType.HARDWARE: "🖥️",
RequestType.SOFTWARE: "💿",
RequestType.NETWORK: "🌐",
RequestType.ACCESS: "🔑",
RequestType.OTHER: "📝",
}

View File

@@ -0,0 +1,2 @@
from .start import dp
from .status import dp

View File

@@ -0,0 +1,12 @@
from aiogram import types
from aiogram.filters import CommandStart
from ..bot import dp
@dp.message(CommandStart())
async def start_command(message: types.Message):
"""Handle /start command"""
await message.answer(
"👋 Привет! Я бот технической поддержки.\n"
"Я буду отправлять уведомления о новых заявках и позволю менять их статус."
)

View File

@@ -0,0 +1,61 @@
from aiogram import types, F
from logging import getLogger
from sqlalchemy.orm import Session
from ...database import get_db
from ...crud import requests
from ..bot import dp
from ..keyboards import create_status_keyboard
from ..messages import format_request_message
from ..constants import STATUS_LABELS
logger = getLogger(__name__)
@dp.callback_query(F.data.startswith("status_"))
async def process_status_update(callback: types.CallbackQuery):
try:
parts = callback.data.split("_")
logger.info(f"Received callback data: {callback.data}")
if len(parts) < 3:
logger.error(f"Invalid callback data format: {parts}")
await callback.answer("Неверный формат данных", show_alert=True)
return
request_id = int(parts[1])
new_status = "_".join(parts[2:]) if len(parts) > 3 else parts[2]
logger.info(
f"Processing status update: request_id={request_id}, new_status={new_status}"
)
db = next(get_db())
try:
updated_request = requests.update_request_status(db, request_id, new_status)
if not updated_request:
logger.warning(f"Request not found: {request_id}")
await callback.answer("Заявка не найдена", show_alert=True)
return
new_message = format_request_message(updated_request)
new_keyboard = create_status_keyboard(request_id, new_status)
await callback.message.edit_text(
text=new_message, parse_mode="HTML", reply_markup=new_keyboard
)
await callback.answer(f"Статус обновлен: {STATUS_LABELS[new_status]}")
logger.info(
f"Successfully updated request {request_id} to status {new_status}"
)
except ValueError as e:
logger.error(f"Value error while updating status: {e}")
await callback.answer(str(e), show_alert=True)
finally:
db.close()
except Exception as e:
logger.error(f"Error processing callback: {e}", exc_info=True)
await callback.answer(
"Произошла ошибка при обновлении статуса", show_alert=True
)

View File

@@ -0,0 +1,33 @@
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from logging import getLogger
from .constants import STATUS_LABELS
logger = getLogger(__name__)
def create_status_keyboard(
request_id: int, current_status: str
) -> InlineKeyboardMarkup:
status_transitions = {
"new": ["in_progress"],
"in_progress": ["resolved"],
"resolved": ["closed"],
"closed": [],
}
buttons = []
available_statuses = status_transitions.get(current_status, [])
for status in available_statuses:
callback_data = f"status_{request_id}_{status}"
logger.debug(f"Creating button with callback_data: {callback_data}")
buttons.append(
[
InlineKeyboardButton(
text=STATUS_LABELS[status], callback_data=callback_data
)
]
)
keyboard = InlineKeyboardMarkup(inline_keyboard=buttons)
logger.debug(f"Created keyboard: {keyboard}")
return keyboard

View File

@@ -0,0 +1,37 @@
from datetime import datetime
from .constants import (
STATUS_LABELS,
PRIORITY_LABELS,
PRIORITY_EMOJI,
DEPARTMENT_LABELS,
REQUEST_TYPE_LABELS,
REQUEST_TYPE_EMOJI,
)
def format_request_message(request_data: dict) -> str:
created_at = datetime.fromisoformat(request_data["created_at"]).strftime(
"%d.%m.%Y %H:%M"
)
# Get translated values
department = DEPARTMENT_LABELS.get(
request_data["department"], request_data["department"]
)
request_type = REQUEST_TYPE_LABELS.get(
request_data["request_type"], request_data["request_type"]
)
priority = PRIORITY_LABELS.get(request_data["priority"], request_data["priority"])
status = STATUS_LABELS.get(request_data.get("status", "new"), "Неизвестно")
return (
f"📋 <b>Заявка #{request_data['id']}</b>\n\n"
f"👤 <b>Сотрудник:</b> {request_data['employee_last_name']} {request_data['employee_first_name']}\n"
f"🏢 <b>Отдел:</b> {department}\n"
f"🚪 <b>Кабинет:</b> {request_data['office']}\n"
f"{REQUEST_TYPE_EMOJI.get(request_data['request_type'], '📝')} <b>Тип заявки:</b> {request_type}\n"
f"{PRIORITY_EMOJI.get(request_data['priority'], '')} <b>Приоритет:</b> {priority}\n\n"
f"📝 <b>Описание:</b>\n<blockquote>{request_data['description']}</blockquote>\n\n"
f"🕒 <b>Создана:</b> {created_at}\n"
f"📊 <b>Статус:</b> {status}"
)

View File

@@ -0,0 +1,41 @@
import asyncio
from logging import getLogger
from aiogram.client.session.aiohttp import AiohttpSession
from .bot import bot
from .keyboards import create_status_keyboard
from .messages import format_request_message
from .config import settings
logger = getLogger(__name__)
async def send_request_notification(request_data: dict):
try:
message = format_request_message(request_data)
keyboard = create_status_keyboard(
request_data["id"], request_data.get("status", "new")
)
async with AiohttpSession() as session:
bot.session = session
await bot.send_message(
chat_id=settings.TELEGRAM_CHAT_ID,
text=message,
parse_mode="HTML",
reply_markup=keyboard,
)
except Exception as e:
logger.error(f"Error sending Telegram notification: {e}", exc_info=True)
raise
def send_notification(request_data: dict):
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(send_request_notification(request_data))
loop.close()
except Exception as e:
logger.error(f"Failed to send notification: {e}", exc_info=True)
raise