intrabot/bot/telegram.py
2026-02-24 15:15:23 +03:00

186 lines
7.4 KiB
Python

import logging
import telebot
from telebot import types
from telebot.apihelper import ApiTelegramException
import config
import database
from intradesk import reference
from intradesk import api as intradesk_api
log = logging.getLogger(__name__)
bot = telebot.TeleBot(config.TOKEN, parse_mode="Markdown")
# ── Кнопки под постом ─────────────────────────────────────────────────────────
def make_task_buttons(task_number: str, closed: bool = False) -> types.InlineKeyboardMarkup:
markup = types.InlineKeyboardMarkup(row_width=2)
if not closed:
markup.add(
types.InlineKeyboardButton("📋 Статус", callback_data=f"st:{task_number}"),
types.InlineKeyboardButton("👤 Исполнитель", callback_data=f"ex:{task_number}"),
)
return markup
# ── Отправка / редактирование поста ───────────────────────────────────────────
def send_or_edit(
task_id: int,
task_number: str,
full_text: str,
update_summary: str = None,
closed: bool = False,
):
markup = make_task_buttons(task_number, closed=closed)
msg_id = database.get_message_id(task_id)
if msg_id:
try:
bot.edit_message_text(
full_text,
config.CHANNEL_ID,
msg_id,
parse_mode="Markdown",
disable_web_page_preview=True,
reply_markup=markup if not closed else None,
)
except ApiTelegramException as e:
if "message is not modified" not in str(e):
log.warning(f"edit_message failed for task {task_id}: {e}")
if update_summary:
try:
bot.send_message(
config.CHANNEL_ID,
update_summary,
reply_to_message_id=msg_id,
parse_mode="Markdown",
)
except ApiTelegramException as e:
log.warning(f"send reply failed for task {task_id}: {e}")
else:
try:
msg = bot.send_message(
config.CHANNEL_ID,
full_text,
parse_mode="Markdown",
disable_web_page_preview=True,
reply_markup=markup,
)
database.save_message_id(task_id, msg.message_id, task_number)
except ApiTelegramException as e:
log.error(f"send_message failed for task {task_id}: {e}")
# ── Callbacks: смена статуса ──────────────────────────────────────────────────
@bot.callback_query_handler(func=lambda c: c.data.startswith("st:"))
def cb_show_statuses(call):
"""Показывает список статусов для выбора."""
task_number = call.data[3:]
markup = types.InlineKeyboardMarkup(row_width=1)
for sid, name in reference.STATUSES.items():
markup.add(types.InlineKeyboardButton(name, callback_data=f"ss:{task_number}:{sid}"))
markup.add(types.InlineKeyboardButton("✖ Отмена", callback_data="del"))
try:
bot.send_message(
call.message.chat.id,
f"Заявка *\\#{task_number}* — выберите новый статус:",
parse_mode="Markdown",
reply_markup=markup,
reply_to_message_id=call.message.message_id,
)
bot.answer_callback_query(call.id)
except Exception as e:
bot.answer_callback_query(call.id, str(e), show_alert=True)
@bot.callback_query_handler(func=lambda c: c.data.startswith("ss:"))
def cb_set_status(call):
"""Применяет выбранный статус через API."""
_, task_number, status_id = call.data.split(":")
status_name = reference.STATUSES.get(int(status_id), f"#{status_id}")
success = intradesk_api.change_status(int(task_number), int(status_id))
if success:
bot.answer_callback_query(call.id, f"✅ Статус → {status_name}")
_delete_message(call.message.chat.id, call.message.message_id)
else:
bot.answer_callback_query(call.id, "❌ Ошибка при изменении статуса", show_alert=True)
# ── Callbacks: назначение исполнителя ─────────────────────────────────────────
@bot.callback_query_handler(func=lambda c: c.data.startswith("ex:"))
def cb_show_executors(call):
"""Показывает список сотрудников для назначения исполнителем."""
task_number = call.data[3:]
employees = database.get_all_ref("employees")
if not employees:
bot.answer_callback_query(
call.id,
"Список сотрудников не загружен. Обновите справочники.",
show_alert=True,
)
return
markup = types.InlineKeyboardMarkup(row_width=1)
for emp in employees[:20]: # Telegram ограничивает кол-во кнопок
markup.add(types.InlineKeyboardButton(
emp["name"],
callback_data=f"se:{task_number}:{emp['id']}",
))
markup.add(types.InlineKeyboardButton("✖ Отмена", callback_data="del"))
try:
bot.send_message(
call.message.chat.id,
f"Заявка *\\#{task_number}* — выберите исполнителя:",
parse_mode="Markdown",
reply_markup=markup,
reply_to_message_id=call.message.message_id,
)
bot.answer_callback_query(call.id)
except Exception as e:
bot.answer_callback_query(call.id, str(e), show_alert=True)
@bot.callback_query_handler(func=lambda c: c.data.startswith("se:"))
def cb_set_executor(call):
"""Назначает выбранного сотрудника исполнителем через API."""
_, task_number, emp_id = call.data.split(":")
emp_name = database.resolve("employees", int(emp_id)) or f"#{emp_id}"
success = intradesk_api.assign_executor(int(task_number), user_id=int(emp_id))
if success:
bot.answer_callback_query(call.id, f"✅ Исполнитель → {emp_name}")
_delete_message(call.message.chat.id, call.message.message_id)
else:
bot.answer_callback_query(call.id, "❌ Ошибка при назначении", show_alert=True)
# ── Общий callback: удалить меню ──────────────────────────────────────────────
@bot.callback_query_handler(func=lambda c: c.data == "del")
def cb_delete(call):
bot.answer_callback_query(call.id)
_delete_message(call.message.chat.id, call.message.message_id)
def _delete_message(chat_id, message_id):
try:
bot.delete_message(chat_id, message_id)
except Exception:
pass
# ── Запуск бота ───────────────────────────────────────────────────────────────
def start_bot():
log.info("Telegram bot started (polling)...")
bot.infinity_polling()