186 lines
7.4 KiB
Python
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()
|