/
koskv
/
TeamSync-Bot
Обзор
Документация
Войти
/
koskv
/
TeamSync-Bot
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/main/java/org/example/teamsyncbot/service/TelegramBot.java
252 строки
10 KB
Koskv
Add: Get All Users
13 дек 2025, 23:43
13 дек 2025, 23:43
6966893
Код
Авторство
О чём код?
package org.example.teamsyncbot.service; import org.example.teamsyncbot.client.auth.AuthService; import org.example.teamsyncbot.client.task.TeamSyncTaskClient; import org.example.teamsyncbot.client.user.TeamSyncUserClient; import org.example.teamsyncbot.config.BotProperties; import org.example.teamsyncbot.dto.user.UserSummaryDto; import org.example.teamsyncbot.exception.TeamSyncClientException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.telegram.telegrambots.bots.TelegramLongPollingBot; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.objects.CallbackQuery; import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.api.objects.MaybeInaccessibleMessage; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup; import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import java.util.List; import java.util.Optional; import java.util.Set; /** * Telegram-бот для администрирования TeamSync. * Отвечает ТОЛЬКО за взаимодействие с Telegram API: * - обработка команд и callback-ов, * - формирование клавиатур и сообщений, * - контроль доступа по chatId. * <p> * Вся логика интеграции с TeamSync вынесена в отдельные клиенты: * - {@link TeamSyncUserClient} — управление пользователями, * - {@link TeamSyncTaskClient} — управление задачами, * - {@link AuthService} — управление сессией. */ @Component public class TelegramBot extends TelegramLongPollingBot { private static final Logger LOGGER = LoggerFactory.getLogger(TelegramBot.class); private final BotProperties botProperties; private final Set<Long> allowedChatIds; // 🔹 Зависимости — только клиенты, а не restTemplate/authService напрямую private final TeamSyncUserClient userClient; private final TeamSyncTaskClient taskClient; // будет использоваться позже public TelegramBot( BotProperties botProperties, TeamSyncUserClient userClient, TeamSyncTaskClient taskClient ) { this.botProperties = botProperties; this.userClient = userClient; this.taskClient = taskClient; this.allowedChatIds = Set.copyOf(botProperties.getAllowedChatIds()); } @Override public String getBotUsername() { return botProperties.getUsername(); } @Override public String getBotToken() { return botProperties.getToken(); } /** * Основной обработчик входящих обновлений от Telegram. * Поддерживает: * - команды: /start, /users * - inline-кнопки: list_users * <p> * Валидация доступа происходит ДО любой бизнес-логики. */ @Override public void onUpdateReceived(Update update) { Long chatId = extractChatId(update); if (!isAuthorized(chatId)) { handleUnauthorized(update, chatId); return; } if (update.hasMessage() && update.getMessage().hasText()) { handleTextMessage(update.getMessage(), chatId); } else if (update.hasCallbackQuery()) { handleCallbackQuery(update.getCallbackQuery(), chatId); } } // ——— ВНУТРЕННИЕ МЕТОДЫ ——— private boolean isAuthorized(Long chatId) { return chatId != null && allowedChatIds.contains(chatId); } private void handleTextMessage(Message message, Long chatId) { String text = message.getText().trim().toLowerCase(); switch (text) { case "/start" -> handleStart(chatId); case "/users", "/list" -> sendUsersList(chatId); default -> safeExecute(chatId, "❓ Неизвестная команда. Напишите /start."); } } private void handleCallbackQuery(CallbackQuery callback, Long chatId) { String data = callback.getData(); switch (data) { case "list_users" -> sendUsersList(chatId); default -> safeExecute(chatId, "⚠️ Неизвестное действие."); } } private void handleStart(Long chatId) { String text = "👋 Привет! Это админ-бот TeamSync.\nВыберите действие:"; InlineKeyboardMarkup keyboard = new InlineKeyboardMarkup( List.of( List.of( InlineKeyboardButton.builder() .text("👥 Пользователи") .callbackData("list_users") .build() ), List.of( InlineKeyboardButton.builder() .text("✅ Задачи") .callbackData("list_tasks") .build() ) ) ); safeExecute(SendMessage.builder() .chatId(String.valueOf(chatId)) .text(text) .replyMarkup(keyboard) .parseMode("HTML") .build()); } private void sendUsersList(Long chatId) { try { List<UserSummaryDto> users = userClient.getAllUsers(); String message = formatUsersList(users); safeExecute(chatId, message); } catch (TeamSyncClientException e) { LOGGER.error("Ошибка при получении списка пользователей", e); safeExecute(chatId, "❌ " + e.getMessage()); } } // Позже: private void sendTasksList(Long chatId) { ... } /** * Формирует HTML-сообщение со списком пользователей. * Безопасно экранирует username. */ private String formatUsersList(List<UserSummaryDto> users) { if (users.isEmpty()) { return "📭 Пользователей не найдено."; } StringBuilder sb = new StringBuilder("👥 <b>Список пользователей</b>:\n\n"); for (UserSummaryDto u : users) { String status = u.enabled() ? "🟢" : "🔴"; sb.append(String.format( "%s <b>%s</b> — <code>%s</code>\n", status, escapeHtml(u.username()), u.role() )); } return sb.toString(); } private String escapeHtml(String s) { if (s == null) return ""; return s.replace("&", "&") .replace("<", "<") .replace(">", ">"); } private void safeExecute(Long chatId, String text) { safeExecute(SendMessage.builder() .chatId(String.valueOf(chatId)) .text(text) .parseMode("HTML") .build()); } private void safeExecute(SendMessage message) { try { execute(message); } catch (TelegramApiException e) { LOGGER.error("Ошибка отправки сообщения в Telegram", e); } } private void handleUnauthorized(Update update, Long chatId) { Long targetId = chatId; // Если chatId не задан, пробуем извлечь из update if (targetId == null && update.hasMessage()) { targetId = update.getMessage().getChatId(); } if (targetId == null && update.hasCallbackQuery()) { MaybeInaccessibleMessage maybeMsg = update.getCallbackQuery().getMessage(); if (maybeMsg instanceof Message msg) { targetId = msg.getChatId(); } else { LOGGER.warn("Невозможно получить chatId: сообщение недоступно (InaccessibleMessage)"); } } if (targetId != null) { try { execute(SendMessage.builder() .chatId(String.valueOf(targetId)) .text("🔒 Доступ запрещён.\nЭтот бот доступен только авторизованным пользователям.") .build()); } catch (TelegramApiException e) { LOGGER.warn("Не удалось отправить уведомление о запрете для chatId={}", targetId, e); } } LOGGER.warn("⚠️ Попытка доступа от chatId={}", chatId); } private Long extractChatId(Update update) { if (update.hasMessage()) { return update.getMessage().getChatId(); } if (update.hasCallbackQuery()) { MaybeInaccessibleMessage maybeMsg = update.getCallbackQuery().getMessage(); // Проверяем, что сообщение доступно if (maybeMsg instanceof Message message) { return message.getChatId(); } // Если InaccessibleMessage — chatId недоступен LOGGER.warn("Получен callback от недоступного сообщения: {}", maybeMsg); return null; } return null; } }