Делаем телеграм-бот для сохранения сообщений в блокчейн

963d1f87c634f7fd44478378285f951b

Привет, я Дмитрий и хочу поделиться с вами как я делал свой телеграм-бот для сохранения сообщений из чатов в блокчейн на языке java. Вообще идея была в том, чтобы научиться быстро и удобно увековечивать некоторые особо важные сообщения, так чтобы они оставались без изменений и вне зависимости от того, что будет с чатом где они были размещены или даже с самим Телеграмом.

Чему мы научимся:

  1. (1-я часть) писать предупреждающее сообщение когда бот добавляют в чат

  2. посылать в чат приветственное сообщение когда боту дают права админа и он начинает работать

  3. читать сообщения из чатов или приватных бесед

  4. (2-я часть) обрабатывать сообщения из чатов, к которым он подключен или из приватных бесед

  5. обрабатывать команды

  6. (3-я часть) делать транзакцию для блокчейн и писать ответ на сообщение, что транзакция создана

  7. проверять подтверждение транзакции в блокчейн и изменять ранее посланный ответ на новое сообщение

Во-первых, что такое блокчейн? Представьте что есть такая волшебная книга-летопись, и волшебник летописец, который может вносить в нее записи. И что таких летописей множество и у каждой свой летописец. И волшебство в том, что если в одну из них внесена запись, то она магическим образом появляется во всех летописях и ее оттуда уже нельзя удалить или изменить. И все видят эту запись и всегда могут найти ее на заданной странице. К примеру, вы приходите к летописцу и говорите — «у меня есть мое стихотворение, которое я хочу увековечить и подарить миру, но так, чтобы мое авторство было учтено!» Волшебник, говорит — «добро, а сколько строк в твоем стихотворении?» — »100!» — отвечаете вы. «Тогда это будет стоить 5 волшебных монет», — говорит он. «По рукам!». Вы отдаете ему монетки, и волшебник вносит ваше стихотворения в волшебную летопись и она сразу появляется во всех волшебных летописях разом на текущей странице — с подписью внесшего её волшебника и с вашей подписью как автора.

Во-вторых, зачем нужно сообщения из чата сохранять в блокчейне? В первую очередь чтобы не зависеть от изменений в чате телеграм — сообщение уже записанное в блокчейн нельзя удалить или изменить. Например (кейс 1), как вы знаете, накануне задержали Павла Дурова во Франции с целью получить доступы к чатам телеграм. А что если к вашему чату будет получен доступ извне и все сообщения будут удалены или искажены? В свою защиту, Вы всегда сможете получить изначальные сообщения, если они сохранялись в блокчейн. Второй момент (кейс 2) — чтобы сохранить для истории, чтобы можно было через года знать что данные остались неизменны.

С блокчейном разобрались, идем дальше…

Сперва создаем учетную запись для своего бота, в которой задаем его имена и получаем ключи доступа. Это можно сделать вызвав официальный менеджер ботов Телеграм — @BotFather.

Теперь начинаем писать код

Для работы с ботом я использовал библиотеку com.pengrad.telegrambothttps://github.com/pengrad/java-telegram-bot-api? tab=readme-ov-file#creating-your-bot

Создаем экземпляр бота так:

import com.pengrad.telegrambot.TelegramBot;
…
       TelegramBot bot = new TelegramBot(botToken);
       // Спросить бота о его настройках:
       System.out.println(bot.execute(new GetMe()));

где botToken — это ключ доступа, полученный от @BotFather.

Пересылка сообщений в чат

Бот может отсылать сообщения в чат по его уникальному номеру, как простые текстовые, так и красиво отформатированные (например по Markdown). Код такой:

    protected BaseResponse sendSimpleText(long chatId, String textToSend) {
        SendMessage message = new SendMessage(chatId, textToSend);
        return sendMessage(message);
    }

    protected BaseResponse sendMarkdown(long chatId, String textToSend) {
        SendMessage message = new SendMessage(chatId, textToSend);
        message.parseMode(ParseMode.Markdown);
        return sendMessage(message);
    }

Получение событий из чата

Для того чтобы не заморачиваться с доменным именем, мы будем использовать режим опроса телеграм-сервиса, получая с него все обновления и события из мира телеграм-чатов:

       // Подписка на обновления
        bot.setUpdatesListener(updates -> {
            // Обработка обновлений
            try {
                updates.forEach(update -> onUpdateReceived(update));
                // return id of last processed update or confirm them all
                // Создание Обработчика ошибок
            } catch (Exception e) {
                log.error("On TELEGRAM Updates error {}", e.toString());
            }
            return UpdatesListener.CONFIRMED_UPDATES_ALL;
        }, e -> {
            if (e.response() != null) {
                // Ошибка из Телеграма
                log.warn("TELEGRAM ERR: " + e.response().errorCode() + " - " + e.response().description());
            } else {
                // Как видно проблема сети
                e.printStackTrace();
            }
        });

Теперь нам нужно задать обработку в функции onUpdateReceived и дело в шляпе!

Давайте сделаем простой обработчик вида:

   protected void onUpdateReceived(Update update) {

        try {

            Chat chat;
            Chat origMessageChat;
            User from;
            User user;
            Long chatId;
            Long userId;
            String text;

            Integer updateId = update.updateId();
            String lang = "ru";

            ////////// MESSAGE
            Message message = update.message();
            String userName;
           if (message != null) {
                // сюда приходит если:
                // или прямой чат пользователя с ботом
                // или в чате группы/супергруппы (не канал) бот добавлен администратором

                chat = message.chat();
                chatId = chat.id();
                chatErrorId = chatId;
                System.out.println(message.text());

…
           }
        }
    }

Теперь, для того чтобы бот смог читать сообщения в каком-либо чате — его нужно пригласить в этот чат и сделать администратором — только тогда он сможет получать уведомления о событиях и сообщениях в данном чате. Также можно писать команды боту напрямую через приватный чат с этим ботом.

Если мы запустим данную программу и пригласим бота в какой-либо чат и сделаем его админом, то все сообщения из этого чата будут печататься и в системный лог.

Кроме сообщений из чата, вы сможете получать события, такие как наделение правами пользователя, приглашения в чат и т.д.

Давайте теперь научим нашего бота определять — пригласили ли его в чат и наделили ли правами администратора. Для этого в код обработчика вставим:

            // приглашения и удаления из групп
            ChatMemberUpdated myChatMember = update.myChatMember();
            if (myChatMember != null) {
                chat = myChatMember.chat(); // в каком чате действие произошло
                chatId = chat.id();
                chatErrorId = chat.id();
                from = myChatMember.from(); // кто это сделал

                log.warn("User from: " + GSON.toJson(from).toString());

                ChatMember newChatMember = myChatMember.newChatMember();
                user = newChatMember.user();
                ChatMember.Status oldStatus = myChatMember.oldChatMember().status();
                ChatMember.Status newStatus = newChatMember.status();
                if (oldStatus.equals(newStatus))
                    return;

                if (user.username().equals(botUserName)) {

                    if (newStatus.equals(ChatMember.Status.kicked) || newStatus.equals(ChatMember.Status.left)) {
                        // нас удалили из группы
                        Integer untilDate = newChatMember.untilDate(); // 0 - просто удалили
                        log.error("kicked from " + chat.title() + " by User " + from.username());

                        // Конкретный пользователь известен только когда удаляют администратора! Иначе бот кикает и не понятно кто тебя кикнул
                        if (!from.isBot())
                            sendSimpleText(from.id(), from.username() + ", спасибо за использование наших услуг в чате \"" + chat.title() + "\". Надеемся на продолжение сотрудничества в будущем.");

                    } else if (newStatus.equals(ChatMember.Status.administrator)) {
                        // нас сделали администратором
                        // Тут известен конкретный пользователь кто пригласил
                        if (!from.isBot())
                            sendSimpleText(from.id(), from.username() + ", спасибо за допуск меня к работе в чате \"" + chat.title() + "\". Надеюсь быть полезным...");

                    } else if (newStatus.equals(ChatMember.Status.member)) {
                        // нас внесли в группу или понизили из админа до обычного пользователя
                        if (!from.isBot())
                            sendSimpleText(from.id(), from.username() + ", моя работа в чате \"" + chat.title() + "\" прекращена. Жду назначения администраторам снова...");

                    }

                    log.info("status:" +  newStatus.name());

                } else {
                    // Это не про меня
                }

            }

Приватный чат с ботом

Так же можно понять что кто-то общается с ботом в приватной беседе (когда кто-то общается напрямую с ботом). Приватный чат можно определить так:

if (chat.type().equals(Chat.Type.Private))

Обработка пересланных сообщений из каналов

Интересный режим, когда связаны чат для обсуждения и новостной канал. Тогда сообщения из канала автоматически падают в чат обсуждений. Поймать их можно так:

                if (Boolean.TRUE.equals(message.isAutomaticForward())) {
                    // это сообщение пришло из канала к которому привязан данный чат (супергруппа)
                    // это основной чат откуда пришло сообщение - с именем и правильным UserName
                    origMessageChat = message.senderChat();

Итак, в первой части статьи мы разобрались как посылать сообщения в чаты, как их читать и как определять что бот добавили в чат и наделили правами администратора.

Полный код телеграм-бота (супер-класс и подкласс, реализующий посылку транзакций в блокчейн) расположен в Gillab репозитории проекта Erachain — внутри ноды блокчейн. Вызвать бот в телеграм-мессенджере можно по имени @blockchain_storage_bot — вы можете посмотреть как он работает, чтобы лучше понять код бота.

В комментах пишите вопросы — будем делать вторую часть статьи

© Habrahabr.ru