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()