правки 1
This commit is contained in:
parent
3e6ffe93d4
commit
96f70cb646
9 changed files with 257 additions and 19 deletions
|
|
@ -8,7 +8,9 @@
|
||||||
"Bash(python -c \"from maxapi import Bot; import inspect; print\\(inspect.signature\\(Bot.edit_message\\)\\)\")",
|
"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\\(''_''\\)]\\)\")",
|
"Bash(python -c \"from maxapi.types.updates.message_callback import MessageCallback; print\\([m for m in dir\\(MessageCallback\\) if not m.startswith\\(''_''\\)]\\)\")",
|
||||||
"Bash(python -c \"from maxapi.types import SendedMessage; print\\(SendedMessage.__fields__ if hasattr\\(SendedMessage, ''__fields__''\\) else dir\\(SendedMessage\\)\\)\")",
|
"Bash(python -c \"from maxapi.types import SendedMessage; print\\(SendedMessage.__fields__ if hasattr\\(SendedMessage, ''__fields__''\\) else dir\\(SendedMessage\\)\\)\")",
|
||||||
"Bash(python -c \"from maxapi.context import State; s = State\\(\\); print\\(type\\(s\\), dir\\(s\\)\\)\")"
|
"Bash(python -c \"from maxapi.context import State; s = State\\(\\); print\\(type\\(s\\), dir\\(s\\)\\)\")",
|
||||||
|
"Bash(python -c \"import maxapi; print\\(maxapi.__file__\\)\")",
|
||||||
|
"Bash(find C:UsersProfessionalAppDataLocalProgramsPythonPython313Libsite-packagesmaxapi -name command.py -o -name *command*)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
.env
1
.env
|
|
@ -23,3 +23,4 @@ INTRADESK_CHANGES_API_KEY=26221B839B6643E68345FD26BF
|
||||||
INTRADESK_DEFAULT_SERVICE=84681/84683/
|
INTRADESK_DEFAULT_SERVICE=84681/84683/
|
||||||
# ID типа заявки (Настройки → Заявки → Типы заявок → ID)
|
# ID типа заявки (Настройки → Заявки → Типы заявок → ID)
|
||||||
INTRADESK_DEFAULT_TASK_TYPE=21026
|
INTRADESK_DEFAULT_TASK_TYPE=21026
|
||||||
|
INTRADESK_CLOSE_STATUS_ID=68045
|
||||||
|
|
@ -18,8 +18,9 @@ from maxapi.dispatcher import Router
|
||||||
from maxapi.filters import F
|
from maxapi.filters import F
|
||||||
from maxapi.types.updates.message_callback import MessageCallback
|
from maxapi.types.updates.message_callback import MessageCallback
|
||||||
|
|
||||||
|
import config
|
||||||
from bot.filters import PayloadStartsWith
|
from bot.filters import PayloadStartsWith
|
||||||
from bot.keyboards import iticket_detail_kb, main_menu_kb, menu_only_kb
|
from bot.keyboards import comment_input_kb, iticket_detail_kb, main_menu_kb, menu_only_kb
|
||||||
from bot.send_helper import bot_reply, bot_send
|
from bot.send_helper import bot_reply, bot_send
|
||||||
from bot.states import CommentStates
|
from bot.states import CommentStates
|
||||||
from database.connection import get_db
|
from database.connection import get_db
|
||||||
|
|
@ -27,7 +28,9 @@ from database.queries.intradesk import get_intradesk_contact_id_for_phone
|
||||||
from database.queries.organizations import get_organization
|
from database.queries.organizations import get_organization
|
||||||
from external.intradesk_api import (
|
from external.intradesk_api import (
|
||||||
add_task_comment,
|
add_task_comment,
|
||||||
|
close_task,
|
||||||
get_client_open_tasks,
|
get_client_open_tasks,
|
||||||
|
get_task_comments,
|
||||||
get_task_last_comment,
|
get_task_last_comment,
|
||||||
invalidate_client_cache,
|
invalidate_client_cache,
|
||||||
invalidate_task_comment,
|
invalidate_task_comment,
|
||||||
|
|
@ -218,6 +221,23 @@ async def cb_iticket_detail(
|
||||||
|
|
||||||
await event.answer(notification="")
|
await event.answer(notification="")
|
||||||
|
|
||||||
|
# Если пришли сюда из комментарийного флоу (кнопка «Назад») —
|
||||||
|
# удаляем промпт-сообщение и сбрасываем state, сохраняя кэш задач.
|
||||||
|
state = await context.get_state()
|
||||||
|
if state == CommentStates.waiting_text:
|
||||||
|
d = await context.get_data()
|
||||||
|
for mid in (d.get("flow_message_ids") or []):
|
||||||
|
try:
|
||||||
|
await event.bot.delete_message(message_id=mid)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
await context.set_state(None)
|
||||||
|
await context.set_data({
|
||||||
|
"tasks": d.get("tasks"),
|
||||||
|
"page": d.get("page"),
|
||||||
|
"total": d.get("total"),
|
||||||
|
})
|
||||||
|
|
||||||
# Ищем задачу в кэше контекста (загружена при показе списка)
|
# Ищем задачу в кэше контекста (загружена при показе списка)
|
||||||
data = await context.get_data()
|
data = await context.get_data()
|
||||||
task = next((t for t in (data.get("tasks") or []) if t.get("id") == task_id), None)
|
task = next((t for t in (data.get("tasks") or []) if t.get("id") == task_id), None)
|
||||||
|
|
@ -244,23 +264,99 @@ async def cb_iticket_detail(
|
||||||
if task.get("name"):
|
if task.get("name"):
|
||||||
lines += ["", f"📄 {task['name']}"]
|
lines += ["", f"📄 {task['name']}"]
|
||||||
|
|
||||||
# Последний комментарий — отдельный запрос к history API
|
if task.get("description"):
|
||||||
comment = await get_task_last_comment(task["id"])
|
desc = task["description"]
|
||||||
if comment and comment.get("text"):
|
if len(desc) > 600:
|
||||||
ctext = comment["text"]
|
desc = desc[:600] + "…"
|
||||||
if len(ctext) > 500:
|
lines += ["", "📝 Описание:", desc]
|
||||||
ctext = ctext[:500] + "…"
|
|
||||||
|
# Последние 10 комментариев из history API
|
||||||
|
comments = await get_task_comments(task["id"], count=10)
|
||||||
|
if comments:
|
||||||
|
lines += ["", f"💬 Комментарии ({len(comments)}):"]
|
||||||
|
for c in comments:
|
||||||
|
ctext = c["text"]
|
||||||
|
if len(ctext) > 300:
|
||||||
|
ctext = ctext[:300] + "…"
|
||||||
lines += [
|
lines += [
|
||||||
"",
|
"",
|
||||||
f"💭 Последний комментарий ({comment['date']}):",
|
f"─" * 16,
|
||||||
f" {ctext}",
|
f"{c['date']} — {c['author']}",
|
||||||
f" — {comment['author']}",
|
ctext,
|
||||||
]
|
]
|
||||||
|
else:
|
||||||
|
lines += ["", "💬 Комментариев нет"]
|
||||||
|
|
||||||
await bot_reply(
|
await bot_reply(
|
||||||
event,
|
event,
|
||||||
text="\n".join(lines),
|
text="\n".join(lines),
|
||||||
attachments=[iticket_detail_kb(task_id=task["id"])],
|
attachments=[iticket_detail_kb(task_id=task["id"], can_close=bool(config.INTRADESK_CLOSE_STATUS_ID))],
|
||||||
|
user_id=bot_user["max_user_id"],
|
||||||
|
chat_id=event.message.recipient.chat_id if event.message else None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ── Закрыть заявку ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@router.message_callback(PayloadStartsWith("iticket_close:"))
|
||||||
|
async def cb_close_ticket(
|
||||||
|
event: MessageCallback, context: BaseContext, bot_user: dict
|
||||||
|
) -> None:
|
||||||
|
try:
|
||||||
|
task_id = int(event.callback.payload.split(":")[1])
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
await event.answer(notification="Ошибка")
|
||||||
|
return
|
||||||
|
|
||||||
|
await event.answer(notification="")
|
||||||
|
|
||||||
|
# Ищем задачу в кэше
|
||||||
|
data = await context.get_data()
|
||||||
|
task = next((t for t in (data.get("tasks") or []) if t.get("id") == task_id), None)
|
||||||
|
task_number = task.get("task_number") if task else None
|
||||||
|
|
||||||
|
if not task_number:
|
||||||
|
await bot_reply(
|
||||||
|
event,
|
||||||
|
text="⚠️ Не удалось определить номер заявки. Вернитесь к списку.",
|
||||||
|
attachments=[iticket_detail_kb()],
|
||||||
|
user_id=bot_user["max_user_id"],
|
||||||
|
chat_id=event.message.recipient.chat_id if event.message else None,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Получаем contact_person_id
|
||||||
|
intradesk_id: int | None = None
|
||||||
|
contact_person_id: int | None = None
|
||||||
|
if bot_user.get("org_id"):
|
||||||
|
org = await get_organization(bot_user["org_id"])
|
||||||
|
intradesk_id = org.get("intradesk_id") if org else 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"]
|
||||||
|
)
|
||||||
|
|
||||||
|
ok = await close_task(task_number=task_number, contact_person_id=contact_person_id)
|
||||||
|
|
||||||
|
if ok:
|
||||||
|
# Сбрасываем кэш — задача теперь закрыта
|
||||||
|
invalidate_task_comment(task_id)
|
||||||
|
if intradesk_id:
|
||||||
|
invalidate_client_cache(intradesk_id)
|
||||||
|
num_str = f" #{task_number}" if task_number else ""
|
||||||
|
await bot_reply(
|
||||||
|
event,
|
||||||
|
text=f"✅ Заявка{num_str} закрыта.",
|
||||||
|
attachments=[menu_only_kb()],
|
||||||
|
user_id=bot_user["max_user_id"],
|
||||||
|
chat_id=event.message.recipient.chat_id if event.message else None,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await bot_reply(
|
||||||
|
event,
|
||||||
|
text="⚠️ Не удалось закрыть заявку. Попробуйте позже.",
|
||||||
|
attachments=[iticket_detail_kb(task_id=task_id, can_close=bool(config.INTRADESK_CLOSE_STATUS_ID))],
|
||||||
user_id=bot_user["max_user_id"],
|
user_id=bot_user["max_user_id"],
|
||||||
chat_id=event.message.recipient.chat_id if event.message else None,
|
chat_id=event.message.recipient.chat_id if event.message else None,
|
||||||
)
|
)
|
||||||
|
|
@ -280,7 +376,7 @@ async def cb_add_comment(
|
||||||
|
|
||||||
await event.answer(notification="")
|
await event.answer(notification="")
|
||||||
|
|
||||||
# Ищем task_number в кэше контекста
|
# Ищем task_number в кэше контекста; сохраняем кэш задач для кнопки «Назад»
|
||||||
data = await context.get_data()
|
data = await context.get_data()
|
||||||
task = next((t for t in (data.get("tasks") or []) if t.get("id") == task_id), None)
|
task = next((t for t in (data.get("tasks") or []) if t.get("id") == task_id), None)
|
||||||
task_number = task.get("task_number") if task else None
|
task_number = task.get("task_number") if task else None
|
||||||
|
|
@ -290,6 +386,10 @@ async def cb_add_comment(
|
||||||
"comment_task_id": task_id,
|
"comment_task_id": task_id,
|
||||||
"comment_task_number": task_number,
|
"comment_task_number": task_number,
|
||||||
"flow_message_ids": [],
|
"flow_message_ids": [],
|
||||||
|
# Сохраняем кэш списка чтобы кнопка «Назад» нашла задачу
|
||||||
|
"tasks": data.get("tasks"),
|
||||||
|
"page": data.get("page"),
|
||||||
|
"total": data.get("total"),
|
||||||
})
|
})
|
||||||
|
|
||||||
chat_id = event.message.recipient.chat_id if event.message else None
|
chat_id = event.message.recipient.chat_id if event.message else None
|
||||||
|
|
@ -303,9 +403,9 @@ async def cb_add_comment(
|
||||||
user_id=bot_user["max_user_id"],
|
user_id=bot_user["max_user_id"],
|
||||||
text=(
|
text=(
|
||||||
f"💬 Напишите комментарий{num_str}:\n\n"
|
f"💬 Напишите комментарий{num_str}:\n\n"
|
||||||
"Для отмены нажмите «🏠 Главное меню»."
|
"Отправьте текстовое сообщение или нажмите «← Назад к заявке» для отмены."
|
||||||
),
|
),
|
||||||
attachments=[menu_only_kb()],
|
attachments=[comment_input_kb(task_id)],
|
||||||
)
|
)
|
||||||
# Сохраняем mid для возможной очистки
|
# Сохраняем mid для возможной очистки
|
||||||
if mid:
|
if mid:
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ def intradesk_tasks_list_kb(
|
||||||
return builder.as_markup()
|
return builder.as_markup()
|
||||||
|
|
||||||
|
|
||||||
def iticket_detail_kb(task_id: int | None = None) -> Attachment:
|
def iticket_detail_kb(task_id: int | None = None, can_close: bool = False) -> Attachment:
|
||||||
"""Клавиатура внутри карточки Intradesk-задачи."""
|
"""Клавиатура внутри карточки Intradesk-задачи."""
|
||||||
builder = InlineKeyboardBuilder()
|
builder = InlineKeyboardBuilder()
|
||||||
if task_id is not None:
|
if task_id is not None:
|
||||||
|
|
@ -126,11 +126,26 @@ def iticket_detail_kb(task_id: int | None = None) -> Attachment:
|
||||||
text="💬 Оставить комментарий",
|
text="💬 Оставить комментарий",
|
||||||
payload=f"iticket_comment:{task_id}",
|
payload=f"iticket_comment:{task_id}",
|
||||||
))
|
))
|
||||||
|
if task_id is not None and can_close:
|
||||||
|
builder.row(CallbackButton(
|
||||||
|
text="🔒 Закрыть заявку",
|
||||||
|
payload=f"iticket_close:{task_id}",
|
||||||
|
))
|
||||||
builder.row(CallbackButton(text="← К списку заявок", payload="itickets_page:0"))
|
builder.row(CallbackButton(text="← К списку заявок", payload="itickets_page:0"))
|
||||||
builder.row(CallbackButton(text="🏠 Главное меню", payload="main_menu"))
|
builder.row(CallbackButton(text="🏠 Главное меню", payload="main_menu"))
|
||||||
return builder.as_markup()
|
return builder.as_markup()
|
||||||
|
|
||||||
|
|
||||||
|
def comment_input_kb(task_id: int) -> Attachment:
|
||||||
|
"""Клавиатура при вводе комментария — кнопка назад и меню."""
|
||||||
|
return (
|
||||||
|
InlineKeyboardBuilder()
|
||||||
|
.row(CallbackButton(text="← Назад к заявке", payload=f"iticket_detail:{task_id}"))
|
||||||
|
.row(CallbackButton(text="🏠 Главное меню", payload="main_menu"))
|
||||||
|
.as_markup()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def ticket_detail_kb(page: int = 0) -> Attachment:
|
def ticket_detail_kb(page: int = 0) -> Attachment:
|
||||||
return (
|
return (
|
||||||
InlineKeyboardBuilder()
|
InlineKeyboardBuilder()
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ INTRADESK_CHANGES_API_KEY: str = os.environ.get("INTRADESK_CHANGES_API_KEY", "")
|
||||||
INTRADESK_DEFAULT_SERVICE: str = os.environ.get("INTRADESK_DEFAULT_SERVICE", "")
|
INTRADESK_DEFAULT_SERVICE: str = os.environ.get("INTRADESK_DEFAULT_SERVICE", "")
|
||||||
# ID типа заявки по умолчанию (Настройки → Заявки → Типы заявок → ID)
|
# ID типа заявки по умолчанию (Настройки → Заявки → Типы заявок → ID)
|
||||||
INTRADESK_DEFAULT_TASK_TYPE: str = os.environ.get("INTRADESK_DEFAULT_TASK_TYPE", "")
|
INTRADESK_DEFAULT_TASK_TYPE: str = os.environ.get("INTRADESK_DEFAULT_TASK_TYPE", "")
|
||||||
|
# ID статуса «Выполнена» для кнопки «Закрыть заявку» (Настройки → Справочники → Статусы → ID)
|
||||||
|
INTRADESK_CLOSE_STATUS_ID: str = os.environ.get("INTRADESK_CLOSE_STATUS_ID", "")
|
||||||
# ID группы исполнителей для фильтрации заявок
|
# ID группы исполнителей для фильтрации заявок
|
||||||
INTRADESK_EXECUTOR_GROUP: str = os.environ.get("INTRADESK_EXECUTOR_GROUP", "")
|
INTRADESK_EXECUTOR_GROUP: str = os.environ.get("INTRADESK_EXECUTOR_GROUP", "")
|
||||||
# Список ID исполнителей через запятую (доп. фильтр)
|
# Список ID исполнителей через запятую (доп. фильтр)
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
105
external/intradesk_api.py
vendored
105
external/intradesk_api.py
vendored
|
|
@ -87,6 +87,7 @@ def invalidate_client_cache(client_id: int) -> None:
|
||||||
def invalidate_task_comment(task_id: int) -> None:
|
def invalidate_task_comment(task_id: int) -> None:
|
||||||
"""Сбросить кэш истории конкретной задачи после добавления комментария."""
|
"""Сбросить кэш истории конкретной задачи после добавления комментария."""
|
||||||
_cache.pop(f"task_comment:{task_id}", None)
|
_cache.pop(f"task_comment:{task_id}", None)
|
||||||
|
_cache_delete_prefix(f"task_comments:{task_id}:")
|
||||||
logger.debug("cache invalidated for task_id=%s", task_id)
|
logger.debug("cache invalidated for task_id=%s", task_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -144,6 +145,56 @@ async def get_task_last_comment(task_id: int) -> dict | None:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
async def get_task_comments(task_id: int, count: int = 10) -> list[dict]:
|
||||||
|
"""
|
||||||
|
Получить последние комментарии к заявке.
|
||||||
|
|
||||||
|
Возвращает список {text, author, date}, до count штук, новые сначала.
|
||||||
|
Результат кэшируется на _TTL_TASK_COMMENT секунд.
|
||||||
|
"""
|
||||||
|
ck = f"task_comments:{task_id}:{count}"
|
||||||
|
hit, cached = _cache_get(ck)
|
||||||
|
if hit:
|
||||||
|
return cached
|
||||||
|
|
||||||
|
api_key = config.INTRADESK_TASKS_API_KEY
|
||||||
|
if not api_key:
|
||||||
|
return []
|
||||||
|
|
||||||
|
url = (
|
||||||
|
f"{config.INTRADESK_BASE_URL.rstrip('/')}"
|
||||||
|
f"/taskhistory/api/v2.0/lifetime/{task_id}/full"
|
||||||
|
f"?ApiKey={api_key}&sortDirection=Desc&top={count * 3}&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_comments task=%s → HTTP %s", task_id, resp.status_code)
|
||||||
|
return []
|
||||||
|
data = resp.json()
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("get_task_comments error: %s", exc)
|
||||||
|
return []
|
||||||
|
|
||||||
|
result: list[dict] = []
|
||||||
|
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 ""
|
||||||
|
result.append({
|
||||||
|
"text": _strip_html(ev.get("stringvalue") or ""),
|
||||||
|
"author": entry.get("username") or "—",
|
||||||
|
"date": raw_date[:16].replace("T", " "),
|
||||||
|
})
|
||||||
|
break # одна запись = одно событие комментария
|
||||||
|
if len(result) >= count:
|
||||||
|
break
|
||||||
|
|
||||||
|
_cache_set(ck, result, _TTL_TASK_COMMENT)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
async def count_client_open_tasks(client_id: int) -> int:
|
async def count_client_open_tasks(client_id: int) -> int:
|
||||||
"""
|
"""
|
||||||
Количество незакрытых заявок клиента.
|
Количество незакрытых заявок клиента.
|
||||||
|
|
@ -297,6 +348,7 @@ async def get_client_open_tasks(
|
||||||
"id": t.get("id"),
|
"id": t.get("id"),
|
||||||
"task_number": t.get("tasknumber"),
|
"task_number": t.get("tasknumber"),
|
||||||
"name": t.get("name") or "",
|
"name": t.get("name") or "",
|
||||||
|
"description": _strip_html(t.get("description") or ""),
|
||||||
"status_name": dicts_map.get(status_id, "") if status_id else "",
|
"status_name": dicts_map.get(status_id, "") if status_id else "",
|
||||||
"created_at": t.get("createdat") or "",
|
"created_at": t.get("createdat") or "",
|
||||||
"updated_at": t.get("updatedat") or "",
|
"updated_at": t.get("updatedat") or "",
|
||||||
|
|
@ -389,6 +441,59 @@ async def add_task_comment(
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
async def close_task(
|
||||||
|
task_number: int,
|
||||||
|
contact_person_id: int | None = None,
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Закрыть заявку — установить статус INTRADESK_CLOSE_STATUS_ID (PUT /changes/v3/tasks).
|
||||||
|
|
||||||
|
Возвращает True при успехе, False при ошибке или незаданном статусе.
|
||||||
|
"""
|
||||||
|
status_id = config.INTRADESK_CLOSE_STATUS_ID
|
||||||
|
if not status_id:
|
||||||
|
logger.warning("close_task: INTRADESK_CLOSE_STATUS_ID не задан")
|
||||||
|
return False
|
||||||
|
|
||||||
|
key = config.INTRADESK_CHANGES_API_KEY
|
||||||
|
if not key:
|
||||||
|
logger.warning("close_task: INTRADESK_CHANGES_API_KEY не задан")
|
||||||
|
return False
|
||||||
|
|
||||||
|
blocks: dict[str, str] = {
|
||||||
|
"status": json.dumps({"value": int(status_id)}, ensure_ascii=False),
|
||||||
|
}
|
||||||
|
body = {"number": task_number, "blocks": blocks, "Channel": "api"}
|
||||||
|
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] close_task — запрос НЕ отправлен\n"
|
||||||
|
f" PUT {url}\n"
|
||||||
|
f" Body: {json.dumps(body, ensure_ascii=False, indent=2)}\n"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
resp = await client.put(
|
||||||
|
url,
|
||||||
|
json=body,
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
timeout=20.0,
|
||||||
|
)
|
||||||
|
if resp.status_code not in (200, 201):
|
||||||
|
logger.error("close_task → HTTP %s: %s", resp.status_code, resp.text[:500])
|
||||||
|
return False
|
||||||
|
logger.info("close_task ok: task_number=%s", task_number)
|
||||||
|
return True
|
||||||
|
except Exception as exc:
|
||||||
|
logger.exception("close_task error: %s", exc)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def create_intradesk_task(
|
async def create_intradesk_task(
|
||||||
name: str,
|
name: str,
|
||||||
description: str,
|
description: str,
|
||||||
|
|
|
||||||
13
main.py
13
main.py
|
|
@ -7,6 +7,7 @@ setup_logging() # до всех остальных импортов
|
||||||
from database.connection import init_db
|
from database.connection import init_db
|
||||||
from bot.setup import create_bot_and_dispatcher
|
from bot.setup import create_bot_and_dispatcher
|
||||||
from admin.app import create_admin_app
|
from admin.app import create_admin_app
|
||||||
|
from maxapi.types.command import BotCommand
|
||||||
import config
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -23,6 +24,18 @@ async def main() -> None:
|
||||||
await init_db()
|
await init_db()
|
||||||
|
|
||||||
bot, dp = create_bot_and_dispatcher()
|
bot, dp = create_bot_and_dispatcher()
|
||||||
|
|
||||||
|
# Регистрируем команды — появляются в меню кнопки "/" в поле ввода
|
||||||
|
try:
|
||||||
|
await bot.set_my_commands(
|
||||||
|
BotCommand(name="start", description="Главное меню"),
|
||||||
|
BotCommand(name="menu", description="Показать меню"),
|
||||||
|
BotCommand(name="cancel", description="Отменить текущее действие"),
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
import logging
|
||||||
|
logging.getLogger(__name__).warning("set_my_commands failed: %s", e)
|
||||||
|
|
||||||
admin_app = create_admin_app()
|
admin_app = create_admin_app()
|
||||||
|
|
||||||
server = uvicorn.Server(
|
server = uvicorn.Server(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue