- PVSM.RU - https://www.pvsm.ru -
Прошел тот период, когда каждая вторая статья на Habrahabr была посвящена написанию своего телеграмм-бота. Также прошел период времени, когда бота без трудностей можно было разместить на своем компьютере или
Чтобы получить доступ к облаку необходимо создать аккаунт Wolfram. Для этого нужно перейти по адресу https://account.wolfram.com [2] и следовать инструкциям после нажатия на кнопку Create One.
После всех проделанных манипуляций на главной странице облака по адресу https://www.wolframcloud.com [3] будут отображаться все продукты и их планы использования. Необходимо выбрать Development Platform и создать новый блокнот.
Весь код приведенный в дальнейшем, будет выполняться именно в этом облачном блокноте.
Существует огромное множество статей посвященных им. Здесь всего лишь надо сказать, что перед тем как выполнять все дальнейшие действия, бота надо создать стандартным способом. То есть просто начать чат с ботом @BotFather и отправить ему команду:
/newbot
Дальше просто необходимо следовать инструкциям и ввести имя и логин. Пусть его имя будет Wolfram Cloud Bot и логин @WolframCloud5973827Bot.
Воспользуемся рекомендациями @BotFather и бегло осмотрим HTTP API телеграм-ботов. Задачи по реализации всего API целиком пока не стоит. Для написания бота достаточно только небольшой части. Проверим, что API доступно и бот с указанным выше токеном существует. Для этого достаточно выполнить всего одну строчку:
URLExecute["https://api.telegram.org/bot753681357:AAFqdRFN_QoODJxsBy3VN2sVwWTPKJEqteY/getMe"]
{"ok" -> True,
"result" -> {"id" -> 753681357, "is_bot" -> True,
"first_name" -> "Wolfram Cloud Bot",
"username" -> "WolframCloud5973827Bot"}}
Команда выше — самый простой способ выполнить HTTP запрос из Wolfram Language. Но немного усложним его, чтобы было легко реализовать все остальные методы API. Создадим общий метод выполнения запроса к API:
TelegramBot::usage = "TelegramBot[token]";
$telegramAPI = "https://api.telegram.org";
telegramExecute[
TelegramBot[token_String], method_String,
parameters: {(_String -> _)...}: {}
] := Module[{
request, requestURL, requestRules, requestBody,
response, responseBody
},
requestURL = URLBuild[{$telegramAPI, "bot" <> token, method}];
requestRules = DeleteCases[parameters, _[_String, Automatic | Null | None]];
requestBody = ImportString[ExportString[requestRules, "JSON"], "Text"];
request = HTTPRequest[requestURL, <|
Method -> "POST",
"ContentType" -> "application/json; charset=utf-8",
"Body" -> requestBody
|>];
response = URLRead[request];
responseBody = response["Body"];
Return[ImportString[responseBody, "RawJSON"]]
]
Проверим работает ли это на уже протестированном выше методе:
token = "753681357:AAFqdRFN_QoODJxsBy3VN2sVwWTPKJEqteY";
bot = TelegramBot[token];
telegramExecute[bot, "getMe"]
<|"ok" -> True,
"result" -> <|"id" -> 753681357, "is_bot" -> True,
"first_name" -> "Wolfram Cloud Bot",
"username" -> "WolframCloud5973827Bot"|>|>
Отлично. Создадим отдельно функцию для выполнения проверки бота:
getMe::usage="getMe[bot]";
TelegramBot /:
getMe[bot_TelegramBot] :=
telegramExecute[bot, "getMe"]
getMe[bot]
<|"ok" -> True,
"result" -> <|"id" -> 753681357, "is_bot" -> True,
"first_name" -> "Wolfram Cloud Bot",
"username" -> "WolframCloud5973827Bot"|>|>
Теперь подобным образом осталось добавить основные методы, которые необходимы для создания бота в облаке:
getUpdates::usage = "getUpdates[bot, opts]";
Options[getUpdates] = {
"offset" -> Automatic,
"limit" -> Automatic,
"timeout" -> Automatic,
"allowed_updates" -> Automatic
};
TelegramBot /:
getUpdates[bot_TelegramBot, opts: OptionsPattern[getUpdates]] :=
telegramExecute[bot, "getUpdates", Flatten[{opts}]]
setWebhook::usage = "setWebhook[bot, url, opts]";
Options[setWebhook] = {
"certificate" -> Automatic,
"max_connections" -> Automatic,
"allowed_updates" -> Automatic
};
TelegramBot /:
setWebhook[bot_TelegramBot, url_String, opts: OptionsPattern[setWebhook]] :=
telegramExecute[bot, "setWebhook", Join[{"url" -> url}, Flatten[{opts}]]]
deleteWebhook::usage = "deleteWebhook[bot]";
TelegramBot /:
deleteWebhook[bot_TelegramBot] :=
telegramExecute[bot, "deleteWebhook"]
getWebhookInfo::usage = "getWebhookInfo[bot]";
TelegramBot /:
getWebhookInfo[bot_TelegramBot] :=
telegramExecute[bot, "getWebhookInfo"]
sendMessage::usage = "sendMessage[bot, chat, text]";
Options[sendMessage] = {
"parse_mode" -> Automatic,
"disable_web_page_preview" -> Automatic,
"disable_notification" -> Automatic,
"reply_to_message_id" -> Automatic,
"reply_markup" -> Automatic
};
TelegramBot /:
sendMessage[bot_TelegramBot, chat_Integer, text_String,
opts: OptionsPattern[sendMessage]] :=
telegramExecute[
bot, "sendMessage",
Join[{"chat_id" -> chat, "text" -> text}, Flatten[{opts}]]
]
Минимальная версия API готова. Проверим как работает отправка сообщение и получение обновлений. Для этого создадим чат с нашим ботом. При создании боту отправится первое сообщение с тектом /start. Посморим — попало ли оно в список обновлений:
updates = getUpdates[bot]
<|"ok" -> True,
"result" -> {<|"update_id" -> 570790461,
"message" -> <|"message_id" -> 1,
"from" -> <|"id" -> 490138492, "is_bot" -> False,
"first_name" -> "Kirill", "last_name" -> "Belov",
"username" -> "KirillBelovTest"|>,
"chat" -> <|"id" -> 490138492, "first_name" -> "Kirill",
"last_name" -> "Belov", "username" -> "KirillBelovTest",
"type" -> "private"|>, "date" -> 1542182547,
"text" -> "/start",
"entities" -> {<|"offset" -> 0, "length" -> 6,
"type" -> "bot_command"|>}|>|>}|>
Получить из списка обновлений данные последнего обновления можно так:
lastUpdate = updates["result"][[-1]]
<|"update_id" -> 570790461,
"message" -> <|"message_id" -> 1,
"from" -> <|"id" -> 490138492, "is_bot" -> False,
"first_name" -> "Kirill", "last_name" -> "Belov",
"username" -> "KirillBelovTest"|>,
"chat" -> <|"id" -> 490138492, "first_name" -> "Kirill",
"last_name" -> "Belov", "username" -> "KirillBelovTest",
"type" -> "private"|>, "date" -> 1542182547, "text" -> "/start",
"entities" -> {<|"offset" -> 0, "length" -> 6,
"type" -> "bot_command"|>}|>|>
А вот так можно получить чат, из которого пришло сообщение и сам текст сообщения:
chat = lastUpdate["message", "chat", "id"]
text = lastUpdate["message", "text"]
490138492
/start
Как видно из результат выполнения — все на месте. Теперь отправим сообщение от имени бота используя sendMessage.
sendMessage[bot, chat, "hello"]
<|"ok" -> True,
"result" -> <|"message_id" -> 2,
"from" -> <|"id" -> 753681357, "is_bot" -> True,
"first_name" -> "Wolfram Cloud Bot",
"username" -> "WolframCloud5973827Bot"|>,
"chat" -> <|"id" -> 490138492, "first_name" -> "Kirill",
"last_name" -> "Belov", "username" -> "KirillBelovTest",
"type" -> "private"|>, "date" -> 1542182601, "text" -> "hello"|>|
>
В общем-то этого набора функций уже достаточно. Однако, использовать метод getUpdates не очень удобно. Нужно придумать способ, как обрабатывать сообщения с помощью webhook.
В Wolram Langauge есть специальный вид функций, которые создаются с помощью APIFunction. Вот пример одной из таких:
apiFunc = APIFunction[{"n" -> "Integer"}, Plot[Sin[#n * x], {x, -2Pi, 2Pi}]&, "PNG"];
apiFunc[{"n"->3}]
Такие функции предназначены специально для развертывания в облаке. Данная функция будет принимать на вход один параметр запроса. Чтобы развернуть ее в облаке достаточно передать саму функцию в CloudDeploy.
apiObject = CloudDeploy[apiFunc, "Deploy/apiObject"]
CloudObject[https://www.wolframcloud.com/objects/kirillbelovtest/apiObject]
Затем можно перейти по полученной ссылке в браузере и добавить параметр запроса:
Функция выше обрабатывала параменты запроса. Значит нужно создать такую же функцию для обработки тела HTTP запроса, прирходящего от телеграм-бота в виде объекта Update. Для генерации адреса используем токен, чтобы получить доступ к облачному объекту было сложнее. Также необходимо указать, что объект имеет публичный доступ, иначе телеграм не сможет попасть на webhook.
deployWebhook[bot_TelegramBot, handler_] :=
CloudDeploy[APIFunction[{}, handler[HTTPRequestData["Body"]] &],
"Deploy/Webhooks/" <> Hash[bot, "SHA", "HexString"],
Permissions -> "Public"
]
handler — другая функция обработчик. Пусть обработчик превращает строку тела запроса в ассоциацию, получает оттуда идентификатор чата и высылает обратно слово "hello".
handlerHello[bot_TelegramBot][body_String] :=
Block[{json = ImportString[body, "RawJSON"], chat},
chat = json["message", "chat", "id"];
sendMessage[bot, chat, "hello"];
]
Теперь развернем фунцкию в облаке.
webhookObject = deployWebhook[bot, handlerHello[bot]]
CloudObject[https://www.wolframcloud.com/objects/kirillbelovtest/Deploy/Webhooks/b9bd74f89348faecd6b683ba02637dd4d4028a28]
И последний шаг — передадим адрес этого объекта телеграм-боту.
setWebhook[bot, webhookObject[[1]]]
<|"ok" -> True, "result" -> True, "description" -> "Webhook was set"|>
Теперь напишем что-нибуд боту и посмотрим что он ответит:
Диалог можно считать состоявшимся. Для того чтобы изменить логику работы уже существующего обработчика — достаточно повторно выполнить развертывание облачного объекта. При этом выполнять установку webhook для бота уже не потребуется.
Это будет последняя часть в процессе создания бота в облаке Wolfram. Дальше таким же образом можно усложнять логику и добавлять новые методы API. Теперь о самом диалоге. Пусть, после отправки команды /start бот возвращает ответ "Привет" и меняет клавиатуру пользователя. В клавиатуре остается всего две кнопки: "Привет" и "Кто ты?". Реализуем диалог в виде ассоциации. Ключами будут команды, которые высылает пользователь боту. Значения ключей — сам ответ бота и новая клавиатура. При этом множество ключей и кнопок должны полностью совпадать. Иначе может появиться ситуациция, когда бот не знает что ответить. В таких случаях, конечно, можно добавить ответ по умолчанию.
keyboard[buttons : {__String}] :=
{"keyboard" -> {Table[{"text" -> button}, {button, buttons}]},
"resize_keyboard" -> True}
$answers = <|
(*user_text-><|"answer"->bot_text,"keyboard"->next_text|>*)
"/start"-><|"answer"->"Привет","keyboard"->
keyboard[{"Привет","Кто ты?"}]|>,
"Привет"-><|"answer"->"Как дела?",
"keyboard" -> keyboard[{"А твои?"}]|> ,
"А твои?"-><|"answer"->"Нормально",
"keyboard" -> keyboard[{"Назад"}]|> ,
"Кто ты?"-><|"answer"->"Бот написанный на Wolfram Language специально для статьи",
"keyboard"->keyboard[{"Какая статья?","Кто автор?"}]|> ,
"Какая статья?"-><|"answer"->"вот ссылка на нее:nhttps://habr.com/post/422517/",
"keyboard"->keyboard[{"Назад","Кто автор?"}]|> ,
"Кто автор?"-><|"answer"->"Вот этот пользователь:n@KirillBelovTest",
"keyboard"->keyboard[{"Какая статья?","Назад"}]|> ,
"Назад"-><|"answer"->"Привет",
"keyboard"->keyboard[{"Привет","Кто ты?"}]|>
|>;
answer[text_String] /; KeyExistsQ[$answers, text] := $answers[text]
Теперь создадим обработчик:
handlerAbout[bot_TelegramBot][body_String] :=
Block[{json = ImportString[body, "RawJSON"], chat, text},
chat = json["message", "chat", "id"];
text = json["message", "text"];
sendMessage[bot, chat, answer[text]["answer"],
"reply_markup" -> answer[text]["keyboard"]];
]
И выполним повторно развертывание облачного объекта:
deployWebhook[bot, handlerAbout[bot]];
Приверим, что получилось в чате с ботом. Но для начала очистим историю сообщений:
Пока что принципиальных отличий от огромного множества уже существующих ботов нет. Может и смысла в его напиании тоже нет? Смысл всей проделанной выше работы будет, если понять в чем собственно преимущества такого бота! Ведь он может использовать все возможности Wolfram Language и Wolrfam Cloud. Необходимо, чтобы робот умел решать уравнения? Это очень легко! Надо всего-лишь доопределить ответ!
answer[text_String]["keyboard"] /;
StringContainsQ[text, " найти "] := Automatic
answer[text_String]["answer"] /; StringContainsQ[text, " найти "] :=
ToString[Flatten[Block[{args = StringSplit[text, " найти "]},
Solve[ToExpression[args[[1]]], ToExpression[args[[2]]]]
]]]
deployWebhook[bot, handlerAbout[bot]];
Если у кого-то дополнительно появится интерес к возможностям облака — то хорошее описание его функциональности есть здесь [10].
Wolfram Cloud — платформа, которая позволяет использовать язык Wolfram бесплатно, в то время как основной продукт компании Wolfram Research — Mathematica стоит денег. Соответственно на использование есть ограничения и на мой взгяд они очень сильные. При использовании бесплатной версии Development Platform пользователю в месяц выдается 1000 облачных кредитов. Каждый облачный кредит дает время на вычисление различного типа. Так как в статье говоиртся про CloudDeploy + APIFunction — то такие объекты, хранящиеся в облаке тратят 1 кредит за 0.1 секунды вычислительного времени. Несложно подсчитать, что пользователю бесплатно выдается всего 1 минута и 40 секунд серверного времени на работу своего приложения (в данном случае бота). Мне здесь нечего добавить — это очень и очень мало. Основной упор на пользователей, которые работают в Development Platform самостоятельно с помощью браузера. Ведь в таком режиме нет никаких ограничений по времени, а только по длительности сессии и выделяемым ресурсам. При таком использовании Development Platform — это почти полноценная Mathematica, но не требующая установки и лицензии.
→ Статья в Wolfram Cloud [11]
Автор: KirillBelovTest
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/api/299147
Ссылки в тексте:
[1] хостинге: https://www.reg.ru/?rlink=reflink-717
[2] https://account.wolfram.com: https://account.wolfram.com
[3] https://www.wolframcloud.com: https://www.wolframcloud.com
[4] getMe: https://core.telegram.org/bots/api#getme
[5] getUpdates: https://core.telegram.org/bots/api#getupdates
[6] setWebhook: https://core.telegram.org/bots/api#setwebhook
[7] deleteWebhook: https://core.telegram.org/bots/api#deletewebhook
[8] getWebhookInfo: https://core.telegram.org/bots/api#getwebhookinfo
[9] sendMessage: https://core.telegram.org/bots/api#sendmessage
[10] здесь: https://www.youtube.com/watch?v=Xv8vugvu32c
[11] Статья в Wolfram Cloud: https://www.wolframcloud.com/objects/kirillbelovtest/Published/TelegramBotInWolframCloudArticle.nb
[12] Источник: https://habr.com/post/422517/?utm_campaign=422517
Нажмите здесь для печати.