Делаем телеграм-бот для сохранения сообщений в блокчейн
Привет, я Дмитрий и хочу поделиться с вами как я делал свой телеграм-бот для сохранения сообщений из чатов в блокчейн на языке java. Вообще идея была в том, чтобы научиться быстро и удобно увековечивать некоторые особо важные сообщения, так чтобы они оставались без изменений и вне зависимости от того, что будет с чатом где они были размещены или даже с самим Телеграмом.
Чему мы научимся:
(1-я часть) писать предупреждающее сообщение когда бот добавляют в чат
посылать в чат приветственное сообщение когда боту дают права админа и он начинает работать
читать сообщения из чатов или приватных бесед
(2-я часть) обрабатывать сообщения из чатов, к которым он подключен или из приватных бесед
обрабатывать команды
(3-я часть) делать транзакцию для блокчейн и писать ответ на сообщение, что транзакция создана
проверять подтверждение транзакции в блокчейн и изменять ранее посланный ответ на новое сообщение
Во-первых, что такое блокчейн? Представьте что есть такая волшебная книга-летопись, и волшебник летописец, который может вносить в нее записи. И что таких летописей множество и у каждой свой летописец. И волшебство в том, что если в одну из них внесена запись, то она магическим образом появляется во всех летописях и ее оттуда уже нельзя удалить или изменить. И все видят эту запись и всегда могут найти ее на заданной странице. К примеру, вы приходите к летописцу и говорите — «у меня есть мое стихотворение, которое я хочу увековечить и подарить миру, но так, чтобы мое авторство было учтено!» Волшебник, говорит — «добро, а сколько строк в твоем стихотворении?» — »100!» — отвечаете вы. «Тогда это будет стоить 5 волшебных монет», — говорит он. «По рукам!». Вы отдаете ему монетки, и волшебник вносит ваше стихотворения в волшебную летопись и она сразу появляется во всех волшебных летописях разом на текущей странице — с подписью внесшего её волшебника и с вашей подписью как автора.
Во-вторых, зачем нужно сообщения из чата сохранять в блокчейне? В первую очередь чтобы не зависеть от изменений в чате телеграм — сообщение уже записанное в блокчейн нельзя удалить или изменить. Например (кейс 1), как вы знаете, накануне задержали Павла Дурова во Франции с целью получить доступы к чатам телеграм. А что если к вашему чату будет получен доступ извне и все сообщения будут удалены или искажены? В свою защиту, Вы всегда сможете получить изначальные сообщения, если они сохранялись в блокчейн. Второй момент (кейс 2) — чтобы сохранить для истории, чтобы можно было через года знать что данные остались неизменны.
С блокчейном разобрались, идем дальше…
Сперва создаем учетную запись для своего бота, в которой задаем его имена и получаем ключи доступа. Это можно сделать вызвав официальный менеджер ботов Телеграм — @BotFather.
Теперь начинаем писать код
Для работы с ботом я использовал библиотеку com.pengrad.telegrambot
https://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 — вы можете посмотреть как он работает, чтобы лучше понять код бота.
В комментах пишите вопросы — будем делать вторую часть статьи