From 9f32da9a0a7dec43304decb68e46e4777bd9f879 Mon Sep 17 00:00:00 2001 From: dv Date: Thu, 26 Mar 2026 14:48:52 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B7=D0=B0=D1=8F=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 7 +- .env | 8 +- bot/handlers/create_ticket.py | 76 ++++++++++--- bot/handlers/menu.py | 31 ++++-- bot/handlers/start.py | 11 +- bot/handlers/view_tickets.py | 202 +++++++++++++++++++--------------- bot/keyboards.py | 49 ++++++++- bot/send_helper.py | 69 +++++++++++- config.py | 18 ++- data/support.db-shm | Bin 32768 -> 32768 bytes data/support.db-wal | Bin 5800992 -> 5800992 bytes database/connection.py | 9 ++ database/queries/intradesk.py | 82 ++++++++++++++ database/queries/tickets.py | 10 ++ database/schema.sql | 17 +-- external/intradesk_api.py | 149 +++++++++++++++++++++++++ external/intradesk_sync.py | 26 +++++ main.py | 9 ++ run.bat | 17 --- 19 files changed, 645 insertions(+), 145 deletions(-) create mode 100644 external/intradesk_api.py diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 787b5db..96843cb 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -1,7 +1,12 @@ { "permissions": { "allow": [ - "Bash(python -c \":*)" + "Bash(python -c \":*)", + "Bash(python -c \"from bot.handlers.view_tickets import router; from bot.keyboards import intradesk_tasks_list_kb; from database.queries.intradesk import get_tasks_for_local_org, count_tasks_for_local_org, get_task_for_local_org; print\\(''OK''\\)\")", + "Bash(python -c \"import maxapi; import inspect; members = [m for m in dir\\(maxapi\\) if not m.startswith\\(''_''\\)]; print\\(members\\)\")", + "Bash(python -c \"from maxapi import Bot; methods = [m for m in dir\\(Bot\\) if not m.startswith\\(''_''\\)]; print\\(methods\\)\")", + "Bash(python -c \"from maxapi import Bot; import inspect; print\\(inspect.signature\\(Bot.edit_message\\)\\)\")", + "Bash(python -c \"from maxapi.types.updates.message_callback import MessageCallback; print\\([m for m in dir\\(MessageCallback\\) if not m.startswith\\(''_''\\)]\\)\")" ] } } diff --git a/.env b/.env index 19aefcc..a48ce5e 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -MAX_BOT_TOKEN=f9LHodD0cOJHqFlGFuM7t9dFSt0jA36NxLc5aSP0CwDuCyWzssGKsFwuP4ymzoP64uqe6Ivl4_bEWKAOl6MI +MAX_BOT_TOKEN=f9LHodD0cOKyEOxn1KcWiRbcZSn9HqLPMd6RlI_M0-YMvNNwXyLANk65rHpu8dhatDoRO6YXJ-9-72P4R-cJ # При локальном запуске (python main.py): DB_PATH=data/support.db @@ -17,3 +17,9 @@ INTRADESK_API_KEY=4842AA91805F4630853EF42D74 INTRADESK_TASKS_API_KEY=26221B839B6643E68345FD26BF INTRADESK_EXECUTOR_GROUP=243025 INTRADESK_EXECUTORS=2227907,525111,631481,2222953 +# Ключ для /changes/v3/tasks (создание и изменение заявок) +INTRADESK_CHANGES_API_KEY=26221B839B6643E68345FD26BF +# Путь сервиса (Настройки → Сервисы → карточка → «Путь сервиса»), напр: 84681/84683/ +INTRADESK_DEFAULT_SERVICE=84681/84683/ +# ID типа заявки (Настройки → Заявки → Типы заявок → ID) +INTRADESK_DEFAULT_TASK_TYPE=21026 diff --git a/bot/handlers/create_ticket.py b/bot/handlers/create_ticket.py index a362cf7..6fc1c9c 100644 --- a/bot/handlers/create_ticket.py +++ b/bot/handlers/create_ticket.py @@ -23,12 +23,15 @@ from maxapi.types.updates.message_created import MessageCreated from bot.keyboards import done_ticket_kb, main_menu_kb from bot.logging_setup import get_logger from bot.send_helper import bot_send +from database.connection import get_db +from database.queries.intradesk import get_intradesk_contact_id_for_phone +from database.queries.organizations import get_organization from bot.states import TicketStates logger = get_logger("bot.handlers.create_ticket") from database.queries.ticket_attachments import add_attachment -from database.queries.tickets import create_ticket -import external.client as ext +from database.queries.tickets import create_ticket, set_intradesk_task_id +from external.intradesk_api import create_intradesk_task router = Router(router_id="create_ticket") @@ -164,13 +167,13 @@ async def cb_done_ticket( created_by=bot_user["id"], description=description, ) + pending_atts = data.get("pending_attachments", []) logger.info( "✅ ticket created | ticket_id=%s user_id=%s org_id=%s attachments=%s", - ticket_id, bot_user["max_user_id"], bot_user["org_id"], - len(data.get("pending_attachments", [])), + ticket_id, bot_user["max_user_id"], bot_user["org_id"], len(pending_atts), ) - for att in data.get("pending_attachments", []): + for att in pending_atts: await add_attachment( ticket_id=ticket_id, attachment_type=att["type"], @@ -178,22 +181,63 @@ async def cb_done_ticket( ) await context.clear() - await event.answer(notification="Заявка создана!") + + # ── Отправка в Intradesk ────────────────────────────────────────────────── + org = await get_organization(bot_user["org_id"]) + org_name = org["name"] if org else None + intradesk_id: int | None = org.get("intradesk_id") if org else None + + # Находим id контактного лица в Intradesk по телефону пользователя + contact_person_id: int | None = None + if intradesk_id and bot_user.get("phone"): + db = get_db() + contact_person_id = await get_intradesk_contact_id_for_phone( + db, intradesk_id, bot_user["phone"] + ) + + # Ссылки на вложения (Max CDN) — добавятся в описание задачи + att_urls = [a["url"] for a in pending_atts if a.get("url")] + + async def _create_in_intradesk(): + result = await create_intradesk_task( + name=description[:150], + description=description, + client_intradesk_id=intradesk_id, + contact_person_id=contact_person_id, + attachment_urls=att_urls or None, + ) + if result: + iid = result.get("id") + if iid: + await set_intradesk_task_id(ticket_id, int(iid)) + logger.info("Intradesk task created: id=%s for ticket_id=%s", iid, ticket_id) + + asyncio.create_task(_create_in_intradesk()) + + # ── Подтверждение пользователю ──────────────────────────────────────────── + att_count = len(pending_atts) + confirm_text = ( + f"✅ Заявка успешно создана!\n" + f"Описание: {description[:200]}{'…' if len(description) > 200 else ''}" + ) + if att_count: + confirm_text += f"\nВложений: {att_count}" + confirm_text += "\n\nНаши специалисты свяжутся с вами в ближайшее время." + await bot_send( event.bot, chat_id=chat_id, user_id=bot_user["max_user_id"], - text=( - f"✅ Заявка №{ticket_id} успешно создана!\n" - "Наши специалисты свяжутся с вами в ближайшее время." - ), - attachments=[main_menu_kb()], + text=confirm_text, ) - # Fire-and-forget: отправка во внешнюю систему - async def _sync_external(): - loop = asyncio.get_event_loop() - await loop.run_in_executor(None, ext.sync_ticket, ticket_id) + menu_text = f"🏠 {org_name}\nВыберите действие:" if org_name else "🏠 Главное меню\nВыберите действие:" - asyncio.create_task(_sync_external()) + await bot_send( + event.bot, + chat_id=chat_id, + user_id=bot_user["max_user_id"], + text=menu_text, + attachments=[main_menu_kb()], + ) diff --git a/bot/handlers/menu.py b/bot/handlers/menu.py index 5059aa3..c0af9b0 100644 --- a/bot/handlers/menu.py +++ b/bot/handlers/menu.py @@ -5,11 +5,25 @@ from maxapi.types.updates.message_callback import MessageCallback from maxapi.context import BaseContext from bot.keyboards import main_menu_kb -from bot.send_helper import bot_send +from bot.send_helper import bot_reply +from database.queries.organizations import get_organization router = Router(router_id="menu") +async def _main_menu_text(bot_user: dict) -> str: + org_id = bot_user.get("org_id") + if org_id: + org = await get_organization(org_id) + org_name = org["name"] if org else None + else: + org_name = None + + if org_name: + return f"🏠 {org_name}\nВыберите действие:" + return "🏠 Главное меню\nВыберите действие:" + + @router.message_callback(F.callback.payload == "main_menu") async def cb_main_menu( event: MessageCallback, context: BaseContext, bot_user: dict @@ -17,14 +31,11 @@ async def cb_main_menu( await context.set_state(None) await event.answer(notification="") - chat_id = event.message.recipient.chat_id if event.message else None - if chat_id is None: - return - - await bot_send( - event.bot, - chat_id=chat_id, - user_id=bot_user["max_user_id"], - text="Главное меню:", + text = await _main_menu_text(bot_user) + await bot_reply( + event, + text=text, attachments=[main_menu_kb()], + user_id=bot_user["max_user_id"], + chat_id=event.message.recipient.chat_id if event.message else None, ) diff --git a/bot/handlers/start.py b/bot/handlers/start.py index 6437fe7..09a796b 100644 --- a/bot/handlers/start.py +++ b/bot/handlers/start.py @@ -30,6 +30,7 @@ from database.queries.bot_users import ( ) from database.queries.org_users import find_by_phone from database.queries.message_log import log_message +from database.queries.organizations import get_organization router = Router(router_id="start") @@ -223,10 +224,18 @@ async def on_contact_shared( ) await context.set_state(None) + org = await get_organization(org_user["org_id"]) + org_name = org["name"] if org else None + menu_text = ( + f"✅ Вы успешно авторизованы!\n\n🏠 {org_name}\nВыберите действие:" + if org_name + else "✅ Вы успешно авторизованы! Выберите действие:" + ) + await bot_send( event.bot, chat_id=chat_id, user_id=user_id, - text="✅ Вы успешно авторизованы! Выберите действие:", + text=menu_text, attachments=[main_menu_kb()], ) diff --git a/bot/handlers/view_tickets.py b/bot/handlers/view_tickets.py index 8393128..6fb12bb 100644 --- a/bot/handlers/view_tickets.py +++ b/bot/handlers/view_tickets.py @@ -1,65 +1,67 @@ """ -Обработчики просмотра заявок. +Обработчики просмотра заявок (Intradesk). -Навигация полностью через callback payload — FSM-состояния не нужны. - "view_tickets" → список, страница 0 - "tickets_page:{n}" → список, страница n - "ticket_detail:{id}" → карточка заявки - "main_menu" → в menu.py +Навигация через callback payload — все переходы редактируют текущее сообщение: + "view_tickets" → список, страница 0 + "itickets_page:{n}" → список, страница n + "iticket_detail:{id}" → карточка задачи + "main_menu" → в menu.py """ +import re + from maxapi.context import BaseContext from maxapi.dispatcher import Router from maxapi.filters import F from maxapi.types.updates.message_callback import MessageCallback from bot.filters import PayloadStartsWith -from bot.keyboards import ticket_detail_kb, tickets_list_kb -from bot.send_helper import bot_send -from database.queries.ticket_attachments import get_attachments -from database.queries.tickets import ( - PAGE_SIZE, - count_tickets_for_org, - get_ticket, - get_tickets_for_org, +from bot.keyboards import intradesk_tasks_list_kb, iticket_detail_kb +from bot.send_helper import bot_reply +from database.connection import get_db +from database.queries.intradesk import ( + count_tasks_for_local_org, + get_task_for_local_org, + get_tasks_for_local_org, ) +from external.intradesk_api import get_task_last_comment router = Router(router_id="view_tickets") -STATUS_LABELS = { - "open": "🔴 Открыта", - "in_progress": "🟡 В работе", - "closed": "🟢 Закрыта", -} +_PAGE_SIZE = 8 -async def _send_tickets_page( - event: MessageCallback, page: int, bot_user: dict -) -> None: - org_id = bot_user["org_id"] - tickets = await get_tickets_for_org(org_id, page=page) - total = await count_tickets_for_org(org_id) +def _status_icon(status_name: str) -> str: + sn = status_name.lower() + if "выполнен" in sn: + return "🟢" + if "работ" in sn: + return "🟡" + if "открыт" in sn: + return "🔴" + return "⚪" - chat_id = event.message.recipient.chat_id if event.message else None - if chat_id is None: - return - if not tickets: - await bot_send( - event.bot, - chat_id=chat_id, - user_id=bot_user["max_user_id"], - text="📭 У вашей организации пока нет заявок.", - attachments=[ticket_detail_kb(page=0)], - ) - return +def _priority_icon(priority_name: str) -> str: + pn = priority_name.lower() + if "критич" in pn: + return "🔴" + if "высок" in pn: + return "🟠" + if "средн" in pn: + return "🟡" + return "⚪" - await bot_send( - event.bot, - chat_id=chat_id, - user_id=bot_user["max_user_id"], - text=f"📋 Заявки организации (стр. {page + 1}):", - attachments=[tickets_list_kb(tickets, page=page, total=total)], - ) + +async def _tasks_page_text(org_id: int, page: int) -> tuple[str, list[dict], int]: + """Возвращает (текст, задачи, всего).""" + db = get_db() + tasks = await get_tasks_for_local_org(db, org_id, page=page, page_size=_PAGE_SIZE) + total = await count_tasks_for_local_org(db, org_id) + if not tasks: + return "📭 У вашей организации пока нет заявок.", [], 0 + pages = max(1, -(-total // _PAGE_SIZE)) + text = f"📋 Заявки — стр. {page + 1} из {pages} (всего: {total}):" + return text, tasks, total @router.message_callback(F.callback.payload == "view_tickets") @@ -67,11 +69,19 @@ async def cb_view_tickets( event: MessageCallback, context: BaseContext, bot_user: dict ) -> None: await event.answer(notification="") - await _send_tickets_page(event, page=0, bot_user=bot_user) + text, tasks, total = await _tasks_page_text(bot_user["org_id"], 0) + kb = intradesk_tasks_list_kb(tasks, page=0, total=total) if tasks else None + await bot_reply( + event, + text=text, + attachments=[kb] if kb else None, + user_id=bot_user["max_user_id"], + chat_id=event.message.recipient.chat_id if event.message else None, + ) -@router.message_callback(PayloadStartsWith("tickets_page:")) -async def cb_tickets_page( +@router.message_callback(PayloadStartsWith("itickets_page:")) +async def cb_itickets_page( event: MessageCallback, context: BaseContext, bot_user: dict ) -> None: await event.answer(notification="") @@ -79,63 +89,79 @@ async def cb_tickets_page( page = int(event.callback.payload.split(":")[1]) except (IndexError, ValueError): page = 0 - await _send_tickets_page(event, page=page, bot_user=bot_user) + text, tasks, total = await _tasks_page_text(bot_user["org_id"], page) + kb = intradesk_tasks_list_kb(tasks, page=page, total=total) if tasks else None + await bot_reply( + event, + text=text, + attachments=[kb] if kb else None, + user_id=bot_user["max_user_id"], + chat_id=event.message.recipient.chat_id if event.message else None, + ) -@router.message_callback(PayloadStartsWith("ticket_detail:")) -async def cb_ticket_detail( +@router.message_callback(PayloadStartsWith("iticket_detail:")) +async def cb_iticket_detail( event: MessageCallback, context: BaseContext, bot_user: dict ) -> None: try: - ticket_id = int(event.callback.payload.split(":")[1]) + task_id = int(event.callback.payload.split(":")[1]) except (IndexError, ValueError): await event.answer(notification="Ошибка") return - ticket = await get_ticket(ticket_id) + db = get_db() + task = await get_task_for_local_org(db, task_id, bot_user["org_id"]) - chat_id = event.message.recipient.chat_id if event.message else None - if chat_id is None: - return - - # Проверка доступа: заявка должна принадлежать той же организации - if ticket is None or ticket["org_id"] != bot_user["org_id"]: + if task is None: await event.answer(notification="Нет доступа") return await event.answer(notification="") - status_label = STATUS_LABELS.get(ticket["status"], ticket["status"]) - text = ( - f"📌 Заявка #{ticket['id']}\n" - f"Статус: {status_label}\n" - f"Создана: {ticket['created_at'][:16]}\n\n" - f"📄 Описание:\n{ticket['description']}" - ) - if ticket.get("last_comment"): - text += f"\n\n💬 Последний комментарий:\n{ticket['last_comment']}" + sicon = _status_icon(task.get("status_name") or "") + picon = _priority_icon(task.get("priority_name") or "") - # Вложения - attachments = await get_attachments(ticket_id) - if attachments: - att_lines = [] - for a in attachments: - icon = { - "AttachmentType.IMAGE": "🖼", - "AttachmentType.VIDEO": "🎬", - "AttachmentType.AUDIO": "🎙", - "AttachmentType.FILE": "📎", - }.get(a["attachment_type"], "📎") - line = f"{icon} {a['attachment_type'].split('.')[-1].capitalize()}" - if a.get("attachment_url"): - line += f": {a['attachment_url']}" - att_lines.append(line) - text += "\n\n📎 Вложения:\n" + "\n".join(att_lines) + num = task.get("task_number") or task["id"] + lines = [ + f"📌 Заявка #{num}", + f"Статус: {sicon} {task.get('status_name') or '—'}", + f"Приоритет: {picon} {task.get('priority_name') or '—'}", + ] + if task.get("service_name"): + lines.append(f"Сервис: {task['service_name']}") + if task.get("executor_name"): + lines.append(f"Исполнитель: {task['executor_name']}") + lines.append(f"Создана: {(task.get('created_at') or '')[:10] or '—'}") + lines.append(f"Обновлена: {(task.get('updated_at') or '')[:10] or '—'}") - await bot_send( - event.bot, - chat_id=chat_id, + if task.get("name"): + lines += ["", f"📄 {task['name']}"] + + if task.get("description"): + desc = re.sub(r"<[^>]+>", "", task["description"]).strip() + if desc: + if len(desc) > 800: + desc = desc[:800] + "…" + lines += ["", f"💬 {desc}"] + + # Последний комментарий — запрашиваем из Intradesk API + comment = await get_task_last_comment(task["id"]) + if comment and comment.get("text"): + ctext = comment["text"] + if len(ctext) > 500: + ctext = ctext[:500] + "…" + lines += [ + "", + f"💭 Последний комментарий ({comment['date']}):", + f" {ctext}", + f" — {comment['author']}", + ] + + await bot_reply( + event, + text="\n".join(lines), + attachments=[iticket_detail_kb()], user_id=bot_user["max_user_id"], - text=text, - attachments=[ticket_detail_kb(page=0)], + chat_id=event.message.recipient.chat_id if event.message else None, ) diff --git a/bot/keyboards.py b/bot/keyboards.py index a9fdc8e..185ef6b 100644 --- a/bot/keyboards.py +++ b/bot/keyboards.py @@ -10,7 +10,7 @@ def main_menu_kb() -> Attachment: return ( InlineKeyboardBuilder() .row(CallbackButton(text="📝 Создать заявку", payload="create_ticket")) - .row(CallbackButton(text="📋 Просмотреть заявки", payload="view_tickets")) + .row(CallbackButton(text="📋 Мои заявки", payload="view_tickets")) .as_markup() ) @@ -61,6 +61,53 @@ def tickets_list_kb( return builder.as_markup() +def intradesk_tasks_list_kb( + tasks: list[dict], page: int, total: int, page_size: int = 8 +) -> Attachment: + builder = InlineKeyboardBuilder() + for t in tasks: + name_preview = (t.get("name") or "")[:35] + if len(t.get("name") or "") > 35: + name_preview += "…" + sn = (t.get("status_name") or "").lower() + if "выполнен" in sn: + icon = "🟢" + elif "работ" in sn: + icon = "🟡" + elif "открыт" in sn: + icon = "🔴" + else: + icon = "⚪" + label = t.get("task_number") or t["id"] + builder.row( + CallbackButton( + text=f"#{label} {icon} {name_preview}", + payload=f"iticket_detail:{t['id']}", + ) + ) + + nav = [] + if page > 0: + nav.append(CallbackButton(text="← Пред.", payload=f"itickets_page:{page - 1}")) + if (page + 1) * page_size < total: + nav.append(CallbackButton(text="След. →", payload=f"itickets_page:{page + 1}")) + if nav: + builder.row(*nav) + + builder.row(CallbackButton(text="← Назад в меню", payload="main_menu")) + return builder.as_markup() + + +def iticket_detail_kb() -> Attachment: + """Клавиатура внутри карточки Intradesk-задачи.""" + return ( + InlineKeyboardBuilder() + .row(CallbackButton(text="← К списку заявок", payload="itickets_page:0")) + .row(CallbackButton(text="🏠 Главное меню", payload="main_menu")) + .as_markup() + ) + + def ticket_detail_kb(page: int = 0) -> Attachment: return ( InlineKeyboardBuilder() diff --git a/bot/send_helper.py b/bot/send_helper.py index 64fc51c..e7e262e 100644 --- a/bot/send_helper.py +++ b/bot/send_helper.py @@ -1,4 +1,4 @@ -"""Обёртка над bot.send_message с автоматическим логированием исходящих.""" +"""Обёртки над bot.send_message / bot.edit_message с автоматическим логированием.""" from database.queries.message_log import log_message @@ -10,6 +10,7 @@ async def bot_send( text: str, attachments=None, ) -> None: + """Отправить новое сообщение.""" kw: dict = {"chat_id": chat_id, "text": text} if attachments is not None: kw["attachments"] = attachments @@ -21,3 +22,69 @@ async def bot_send( message_id=None, text=text, ) + + +async def bot_edit( + event, + *, + text: str, + attachments=None, +) -> bool: + """ + Редактировать сообщение, на которое нажата callback-кнопка. + + Возвращает True при успехе. При ошибке (сообщение удалено, слишком старое) + возвращает False — вызывающий код должен откатиться на bot_send. + """ + if event.message is None or event.message.body is None: + return False + mid = event.message.body.mid + chat_id = event.message.recipient.chat_id + user_id: int | None = getattr(event.callback, "user", None) + if user_id is not None: + user_id = getattr(user_id, "user_id", None) + try: + await event.bot.edit_message( + message_id=mid, + text=text, + attachments=attachments if attachments is not None else [], + ) + await log_message( + direction="out", + max_user_id=user_id, + max_chat_id=chat_id, + message_id=mid, + text=text, + ) + return True + except Exception: + return False + + +async def bot_reply( + event, + *, + text: str, + attachments=None, + user_id: int | None = None, + chat_id: int | None = None, +) -> None: + """ + Для callback-событий: пробует отредактировать текущее сообщение. + При неудаче отправляет новое. + + Использование в callback-обработчиках вместо bot_send — не создаёт спам. + """ + edited = await bot_edit(event, text=text, attachments=attachments) + if not edited: + _chat_id = chat_id + if _chat_id is None and event.message is not None: + _chat_id = event.message.recipient.chat_id + if _chat_id is not None: + await bot_send( + event.bot, + chat_id=_chat_id, + user_id=user_id, + text=text, + attachments=attachments, + ) diff --git a/config.py b/config.py index 95a12d1..ef27343 100644 --- a/config.py +++ b/config.py @@ -1,4 +1,5 @@ import os +import sys from pathlib import Path from dotenv import load_dotenv @@ -6,6 +7,15 @@ load_dotenv() BOT_TOKEN: str = os.environ["MAX_BOT_TOKEN"] +# ── Dry-run режим ───────────────────────────────────────────────────────────── +# Включается флагом --dry-run при запуске или переменной DRY_RUN=true в .env. +# В этом режиме все записывающие вызовы к Intradesk НЕ выполняются — +# вместо этого их payload печатается в консоль. +DRY_RUN: bool = ( + "--dry-run" in sys.argv + or os.environ.get("DRY_RUN", "").lower() in ("1", "true", "yes") +) + _project_root = Path(__file__).parent _db_path_raw = os.environ.get("DB_PATH", "data/support.db") DB_PATH: str = str( @@ -20,8 +30,14 @@ ADMIN_PORT: int = int(os.environ.get("ADMIN_PORT", "8080")) INTRADESK_BASE_URL: str = os.environ.get("INTRADESK_BASE_URL", "https://apigw.intradesk.ru") # Ключ для /settings/odata (клиенты, пользователи) INTRADESK_API_KEY: str = os.environ.get("INTRADESK_API_KEY", "") -# Ключ для /tasklist/odata (заявки) +# Ключ для /tasklist/odata (чтение заявок) и /taskhistory (история/комментарии) INTRADESK_TASKS_API_KEY: str = os.environ.get("INTRADESK_TASKS_API_KEY", "") +# Ключ для /changes/v3/tasks (создание и изменение заявок) +INTRADESK_CHANGES_API_KEY: str = os.environ.get("INTRADESK_CHANGES_API_KEY", "") +# Путь сервиса для новых заявок (Настройки → Сервисы → «Путь сервиса», напр. "84681/84683/") +INTRADESK_DEFAULT_SERVICE: str = os.environ.get("INTRADESK_DEFAULT_SERVICE", "") +# ID типа заявки по умолчанию (Настройки → Заявки → Типы заявок → ID) +INTRADESK_DEFAULT_TASK_TYPE: str = os.environ.get("INTRADESK_DEFAULT_TASK_TYPE", "") # ID группы исполнителей для фильтрации заявок INTRADESK_EXECUTOR_GROUP: str = os.environ.get("INTRADESK_EXECUTOR_GROUP", "") # Список ID исполнителей через запятую (доп. фильтр) diff --git a/data/support.db-shm b/data/support.db-shm index 5d200f08c955611ecf64f4c71dcfb7610e54c0c7..79890f8904724401647816d631c66354f737228c 100644 GIT binary patch literal 32768 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 azzTsn1(xb;Th|0qV3`7eY6Vut>iidUYBTl# diff --git a/data/support.db-wal b/data/support.db-wal index 755161b99d938a368cbbd411797a1e0c744e8d12..98a20f5d19934928e6ac8e018fe13fdfedc6e7d0 100644 GIT binary patch delta 251083 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
0)F_?{vGX}ECB<`na6r^u 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 Nufe3JION9I{{kKI&xim3 diff --git a/database/connection.py b/database/connection.py index e9dbc66..9eb248f 100644 --- a/database/connection.py +++ b/database/connection.py @@ -36,6 +36,15 @@ async def init_db() -> None: except Exception: pass # колонка уже существует + # Добавляем intradesk_task_id в tickets, если ещё нет + try: + await _db.execute( + "ALTER TABLE tickets ADD COLUMN intradesk_task_id INTEGER" + ) + await _db.commit() + except Exception: + pass # колонка уже существует + def get_db() -> aiosqlite.Connection: if _db is None: diff --git a/database/queries/intradesk.py b/database/queries/intradesk.py index ce2be00..18a5a30 100644 --- a/database/queries/intradesk.py +++ b/database/queries/intradesk.py @@ -285,6 +285,88 @@ async def get_last_sync( return dict(row) if row else None +# ── Tasks by local org_id ───────────────────────────────────────────────────── + +_BOT_PAGE_SIZE = 8 + + +async def get_tasks_for_local_org( + db: aiosqlite.Connection, + local_org_id: int, + page: int = 0, + page_size: int = _BOT_PAGE_SIZE, +) -> list[dict]: + """ + Вернуть intradesk_tasks для локальной организации (через organizations.intradesk_id). + """ + async with db.execute( + """ + SELECT it.* + FROM intradesk_tasks it + JOIN organizations o ON o.intradesk_id = it.client_id + WHERE o.id = ? + ORDER BY it.updated_at DESC + LIMIT ? OFFSET ? + """, + (local_org_id, page_size, page * page_size), + ) as cur: + rows = await cur.fetchall() + return [dict(r) for r in rows] + + +async def count_tasks_for_local_org( + db: aiosqlite.Connection, local_org_id: int +) -> int: + async with db.execute( + """ + SELECT COUNT(*) + FROM intradesk_tasks it + JOIN organizations o ON o.intradesk_id = it.client_id + WHERE o.id = ? + """, + (local_org_id,), + ) as cur: + row = await cur.fetchone() + return row[0] if row else 0 + + +async def get_task_for_local_org( + db: aiosqlite.Connection, task_id: int, local_org_id: int +) -> dict | None: + """Получить одну задачу, убедившись что она принадлежит организации пользователя.""" + async with db.execute( + """ + SELECT it.* + FROM intradesk_tasks it + JOIN organizations o ON o.intradesk_id = it.client_id + WHERE it.id = ? AND o.id = ? + """, + (task_id, local_org_id), + ) as cur: + row = await cur.fetchone() + return dict(row) if row else None + + +# ── Contact person lookup ───────────────────────────────────────────────────── + +async def get_intradesk_contact_id_for_phone( + db: aiosqlite.Connection, client_intradesk_id: int, phone: str +) -> int | None: + """ + Найти id контактного лица в Intradesk по клиенту + нормализованному телефону. + phone — только цифры (как в bot_users.phone). + """ + async with db.execute( + "SELECT id, phone FROM intradesk_users WHERE client_id = ?", + (client_intradesk_id,), + ) as cur: + rows = await cur.fetchall() + for row in rows: + if _normalize_phone(row[1] or "") == phone: + return row[0] + return None + + # ── Whitelist sync ──────────────────────────────────────────────────────────── def _normalize_phone(raw: str) -> str: diff --git a/database/queries/tickets.py b/database/queries/tickets.py index 88badcf..e802e2a 100644 --- a/database/queries/tickets.py +++ b/database/queries/tickets.py @@ -81,6 +81,16 @@ async def get_all_tickets( return [dict(r) for r in rows] +async def set_intradesk_task_id(ticket_id: int, intradesk_task_id: int) -> None: + """Сохранить id созданной задачи Intradesk в локальной заявке.""" + db = get_db() + await db.execute( + "UPDATE tickets SET intradesk_task_id = ? WHERE id = ?", + (intradesk_task_id, ticket_id), + ) + await db.commit() + + async def update_ticket_status( ticket_id: int, status: str, last_comment: str | None ) -> None: diff --git a/database/schema.sql b/database/schema.sql index ba23c71..b2703f8 100644 --- a/database/schema.sql +++ b/database/schema.sql @@ -35,14 +35,15 @@ CREATE TABLE IF NOT EXISTS bot_users ( ); CREATE TABLE IF NOT EXISTS tickets ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - org_id INTEGER NOT NULL REFERENCES organizations(id), - created_by INTEGER NOT NULL REFERENCES bot_users(id), - description TEXT NOT NULL, - status TEXT NOT NULL DEFAULT 'open', -- open / in_progress / closed - last_comment TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - updated_at TEXT NOT NULL DEFAULT (datetime('now')) + id INTEGER PRIMARY KEY AUTOINCREMENT, + org_id INTEGER NOT NULL REFERENCES organizations(id), + created_by INTEGER NOT NULL REFERENCES bot_users(id), + description TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'open', -- open / in_progress / closed + last_comment TEXT, + intradesk_task_id INTEGER, -- id задачи в Intradesk (NULL = не создано) + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS ticket_attachments ( diff --git a/external/intradesk_api.py b/external/intradesk_api.py new file mode 100644 index 0000000..4962ca0 --- /dev/null +++ b/external/intradesk_api.py @@ -0,0 +1,149 @@ +""" +Intradesk REST API: создание заявок и чтение истории/комментариев. + +Эндпоинты: + POST /changes/v3/tasks — создать заявку + GET /taskhistory/api/v2.0/lifetime/{id}/full — история (комментарии, смена статуса...) +""" +from __future__ import annotations + +import json +import logging + +import httpx + +import config + +logger = logging.getLogger(__name__) + + +async def get_task_last_comment(task_id: int) -> dict | None: + """ + Получить последний комментарий к заявке из Intradesk. + + Возвращает dict: {text, author, date} или None если нет комментариев. + """ + key = config.INTRADESK_TASKS_API_KEY + if not key: + return None + + url = ( + f"{config.INTRADESK_BASE_URL.rstrip('/')}" + f"/taskhistory/api/v2.0/lifetime/{task_id}/full" + f"?ApiKey={key}&sortDirection=Desc&top=10&events=50" + ) + try: + async with httpx.AsyncClient() as client: + resp = await client.get(url, timeout=15.0) + if resp.status_code != 200: + logger.warning( + "get_task_last_comment task=%s → HTTP %s", task_id, resp.status_code + ) + return None + data = resp.json() + except Exception as exc: + logger.warning("get_task_last_comment error: %s", exc) + return None + + # Ищем последнее событие с blockname="comment" (type=50) + for entry in data.get("data") or []: + for ev in (entry.get("events") or {}).get("data") or []: + if ev.get("blockname") == "comment" and ev.get("type") == 50: + raw_date = entry.get("eventat") or "" + # "2021-03-15T11:54:36.92Z" → "2021-03-15 11:54" + short_date = raw_date[:16].replace("T", " ") + return { + "text": ev.get("stringvalue") or "", + "author": entry.get("username") or "—", + "date": short_date, + } + return None + + +async def create_intradesk_task( + name: str, + description: str, + client_intradesk_id: int | None = None, + contact_person_id: int | None = None, + attachment_urls: list[str] | None = None, +) -> dict | None: + """ + Создать заявку в Intradesk. + + Возвращает ответ API (dict с полями id, tasknumber и др.) или None при ошибке. + Вложения — пока добавляются как ссылки в конец описания (до появления upload API). + В dry-run режиме запрос печатается в консоль и возвращается None. + """ + key = config.INTRADESK_CHANGES_API_KEY + if not key: + logger.warning("create_intradesk_task: INTRADESK_CHANGES_API_KEY не задан") + return None + + # Вложения — ссылки в конце описания + full_desc = description + if attachment_urls: + full_desc += "\n\nВложения:\n" + "\n".join(attachment_urls) + + blocks: dict[str, str] = { + "name": json.dumps({"value": name}, ensure_ascii=False), + "description": json.dumps({"value": full_desc}, ensure_ascii=False), + } + # initiator: вариант 3 из документации — клиент + пользователь клиента. + # clientid заполняется автоматически из initiatorclient, отдельно не нужен. + if client_intradesk_id is not None and contact_person_id is not None: + blocks["initiator"] = json.dumps( + {"value": {"groupid": client_intradesk_id, "userid": contact_person_id}}, + ensure_ascii=False, + ) + elif client_intradesk_id is not None: + # только клиент (нет конкретного контактного лица) + blocks["initiator"] = json.dumps( + {"value": {"groupid": client_intradesk_id}}, ensure_ascii=False + ) + if config.INTRADESK_DEFAULT_SERVICE: + blocks["service"] = json.dumps( + {"value": config.INTRADESK_DEFAULT_SERVICE}, ensure_ascii=False + ) + if config.INTRADESK_DEFAULT_TASK_TYPE: + blocks["tasktype"] = json.dumps( + {"value": int(config.INTRADESK_DEFAULT_TASK_TYPE)}, ensure_ascii=False + ) + + body = {"blocks": blocks, "Channel": "web"} + url = f"{config.INTRADESK_BASE_URL.rstrip('/')}/changes/v3/tasks?ApiKey={key}" + if contact_person_id is not None: + url += f"&userid={contact_person_id}" + + if config.DRY_RUN: + print( + "\n[DRY-RUN] create_intradesk_task — запрос НЕ отправлен\n" + f" POST {url}\n" + f" Body: {json.dumps(body, ensure_ascii=False, indent=2)}\n" + ) + return None + + try: + async with httpx.AsyncClient() as client: + resp = await client.post( + url, + json=body, + headers={"Content-Type": "application/json"}, + timeout=20.0, + ) + if resp.status_code not in (200, 201): + logger.error( + "create_intradesk_task → HTTP %s: %s", + resp.status_code, + resp.text[:500], + ) + return None + result = resp.json() + logger.info( + "create_intradesk_task ok: id=%s number=%s", + result.get("id"), + result.get("tasknumber") or result.get("number"), + ) + return result + except Exception as exc: + logger.exception("create_intradesk_task error: %s", exc) + return None diff --git a/external/intradesk_sync.py b/external/intradesk_sync.py index fce82a4..03fb59a 100644 --- a/external/intradesk_sync.py +++ b/external/intradesk_sync.py @@ -96,6 +96,17 @@ async def _fetch_all( # ── Sync functions ──────────────────────────────────────────────────────────── +def _dry_run_print(label: str, items: list[dict], sample_keys: list[str]) -> None: + """Вывод первых 3 записей в консоль при dry-run.""" + print(f"\n[DRY-RUN] {label} — запись в БД НЕ выполнена, получено {len(items)} записей") + for i, item in enumerate(items[:3]): + preview = {k: item.get(k) for k in sample_keys if k in item} + print(f" [{i}] {preview}") + if len(items) > 3: + print(f" ... и ещё {len(items) - 3}") + print() + + async def sync_clients() -> int: """Синхронизировать клиентов/организации.""" items, _ = await _fetch_all( @@ -103,6 +114,9 @@ async def sync_clients() -> int: config.INTRADESK_API_KEY, {"$filter": "(isarchived eq false)", "$orderby": "name asc"}, ) + if config.DRY_RUN: + _dry_run_print("sync_clients", items, ["id", "name", "clientType", "email"]) + return len(items) db = get_db() for item in items: await upsert_client(db, item) @@ -118,6 +132,9 @@ async def sync_users() -> int: config.INTRADESK_API_KEY, {"$filter": "(isarchived eq false)", "$orderby": "id desc"}, ) + if config.DRY_RUN: + _dry_run_print("sync_users", items, ["id", "name", "email", "phoneNumbers"]) + return len(items) db = get_db() for item in items: await upsert_user(db, item) @@ -128,6 +145,9 @@ async def sync_users() -> int: async def sync_whitelist() -> int: """Обновить organizations + org_users из уже синхронизированных данных Intradesk.""" + if config.DRY_RUN: + print("[DRY-RUN] sync_whitelist — обновление org_users пропущено\n") + return 0 db = get_db() count = await sync_whitelist_from_intradesk(db) logger.info("sync_whitelist: %d org_users затронуто", count) @@ -158,6 +178,12 @@ async def sync_tasks() -> int: config.INTRADESK_TASKS_API_KEY, {"$filter": filter_str, "$orderby": "updatedat desc"}, ) + if config.DRY_RUN: + _dry_run_print( + "sync_tasks", items, + ["id", "tasknumber", "name", "status", "clientid", "updatedat"], + ) + return len(items) db = get_db() for item in items: await upsert_task(db, item, dicts_map) diff --git a/main.py b/main.py index efb069b..60e1cf2 100644 --- a/main.py +++ b/main.py @@ -11,6 +11,15 @@ import config async def main() -> None: + if config.DRY_RUN: + print( + "\n" + "=" * 60 + "\n" + " ⚠️ DRY-RUN режим активен\n" + " Записи в Intradesk API выполняться НЕ будут.\n" + " Payload запросов будет выводиться в консоль.\n" + + "=" * 60 + "\n" + ) + await init_db() bot, dp = create_bot_and_dispatcher() diff --git a/run.bat b/run.bat index f8e0d9e..7fd0139 100644 --- a/run.bat +++ b/run.bat @@ -1,20 +1,3 @@ -@echo off -chcp 65001 >nul -title Support Bot - -:: Переходим в папку скрипта -cd /d "%~dp0" - -:: Активируем venv если есть -if exist "venv\Scripts\activate.bat" ( - call venv\Scripts\activate.bat -) else if exist ".venv\Scripts\activate.bat" ( - call .venv\Scripts\activate.bat -) - -:: Устанавливаем зависимости если нужно (раскомментировать при первом запуске) -:: pip install -r requirements.txt - echo [INFO] Запуск... Admin-панель: http://localhost:8080/admin/ echo.