Introduccion
El período ha pasado cuando cada segundo artículo sobre Habrahabr se dedicó a escribir su bot de telegramas. Además, pasó un período de tiempo en el que el bot sin dificultades se pudo colocar en su computadora o en un servidor en Rusia. Hace seis meses, mi bot recién comenzó en una computadora portátil y no experimentó ningún problema al conectarse a la API. Pero ahora, cuando pensé en llevarlo de vuelta al trabajo, me di cuenta de que no sería tan fácil. No quería buscar y configurar un servidor proxy, y más aún en el extranjero. Además, antes de eso escribí el bot en Wolfram Language y no tenía idea de cómo funciona el lenguaje con los servidores proxy, ya que todavía no los he usado. ¡Y entonces surgió una gran idea! Utiliza Wolfram Cloud. En este artículo quiero mostrar cuán simple con el registro, pero sin SMS puede iniciar su bot de telegramas simple escrito en Wolfram Language. De las herramientas solo necesitas un navegador.
Un poco sobre la nube de Wolfram
Para acceder a la nube, debe crear una cuenta Wolfram. Para hacer esto, vaya a https://account.wolfram.com y siga las instrucciones después de hacer clic en el botón Crear uno.

Después de todas las manipulaciones realizadas, en la página principal de la nube en https://www.wolframcloud.com se mostrarán todos los productos y sus planes de uso. Debe seleccionar la Plataforma de desarrollo y crear un nuevo cuaderno.

Todo el código proporcionado más adelante se ejecutará en este cuaderno en la nube en particular.
Bastante sobre los bots de telegramas
Hay muchos artículos dedicados a ellos. Aquí solo es necesario decir que antes de realizar todas las acciones adicionales, el bot debe crearse de la manera estándar. Es decir, simplemente inicie una conversación con el bot @BotFather y envíele el comando:
/newbot
Luego solo tiene que seguir las instrucciones e ingresar el nombre e iniciar sesión. Deje que su nombre sea Wolfram Cloud Bot e inicie sesión en @ WolframCloud5973827Bot.

Implementación API
Aprovechamos las recomendaciones de @BotFather y examinamos brevemente la API HTTP de los bots de telegramas. La tarea de implementar toda la API aún no merece la pena. Para escribir un bot, solo una pequeña parte es suficiente. Compruebe que la API sea accesible y que el bot con el token especificado anteriormente exista. Para hacer esto, solo haz una línea:
URLExecute["https://api.telegram.org/bot753681357:AAFqdRFN_QoODJxsBy3VN2sVwWTPKJEqteY/getMe"]
Fuera [..]: = ... {"ok" -> True, "result" -> {"id" -> 753681357, "is_bot" -> True, "first_name" -> "Wolfram Cloud Bot", "username" -> "WolframCloud5973827Bot"}}
El comando anterior es la forma más fácil de realizar una solicitud HTTP desde Wolfram Language. Pero vamos a complicarlo un poco para que sea fácil implementar todos los demás métodos API. Creemos un método general para ejecutar una solicitud de 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"]] ]
Compruebe si esto funciona en el método ya probado anteriormente:
token = "753681357:AAFqdRFN_QoODJxsBy3VN2sVwWTPKJEqteY"; bot = TelegramBot[token]; telegramExecute[bot, "getMe"]
Fuera [..]: = ... <|"ok" -> True, "result" -> <|"id" -> 753681357, "is_bot" -> True, "first_name" -> "Wolfram Cloud Bot", "username" -> "WolframCloud5973827Bot"|>|>
Genial Creemos una función separada para realizar una verificación de bot:
- getMe - información del bot
getMe::usage="getMe[bot]"; TelegramBot /: getMe[bot_TelegramBot] := telegramExecute[bot, "getMe"] getMe[bot]
Fuera [..]: = ... <|"ok" -> True, "result" -> <|"id" -> 753681357, "is_bot" -> True, "first_name" -> "Wolfram Cloud Bot", "username" -> "WolframCloud5973827Bot"|>|>
Ahora, de manera similar, queda por agregar los métodos básicos que son necesarios para crear un bot en la nube:
- getUpdates : obtiene todos los últimos mensajes escritos en el bot
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 : establece la dirección del servidor para procesar actualizaciones
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}]] ]
La versión mínima de la API está lista. Veamos cómo funciona enviar un mensaje y recibir actualizaciones. Para hacer esto, crea un chat con nuestro bot. Cuando se crea el bot, se enviará el primer mensaje con el texto / inicio. Veamos si entró en la lista de actualizaciones:
updates = getUpdates[bot]
Fuera [..]: = ... <|"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"|>}|>|>}|>
Puede obtener los últimos datos de actualización de la lista de actualizaciones de la siguiente manera:
lastUpdate = updates["result"][[-1]]
Fuera [..]: = ... <|"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"|>}|>|>
Y así es como puede obtener el chat del que vino el mensaje y el texto del mensaje en sí:
chat = lastUpdate["message", "chat", "id"] text = lastUpdate["message", "text"]
Como puede ver en el resultado, todo está en su lugar. Ahora enviaremos un mensaje en nombre del bot usando sendMessage.
sendMessage[bot, chat, "hello"]
Fuera [..]: = ... <|"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"|>| >

En general, este conjunto de funciones ya es suficiente. Sin embargo, usar el método getUpdates no es muy conveniente. Debe encontrar una manera de manejar los mensajes usando webhook.
Creando webhook
Wolram Langauge tiene un tipo especial de función que se crea usando APIFunction. Aquí hay un ejemplo de uno de estos:
apiFunc = APIFunction[{"n" -> "Integer"}, Plot[Sin[#n * x], {x, -2Pi, 2Pi}]&, "PNG"]; apiFunc[{"n"->3}]
Estas características están específicamente diseñadas para su implementación en la nube. Esta función aceptará un parámetro de solicitud como entrada. Para implementarlo en la nube, simplemente transfiera la función a CloudDeploy.
apiObject = CloudDeploy[apiFunc, "Deploy/apiObject"]
Fuera [..]: = ... CloudObject[https://www.wolframcloud.com/objects/kirillbelovtest/apiObject]
Luego puede seguir el enlace en el navegador y agregar un parámetro de consulta:

La función anterior maneja los parámetros de solicitud. Por lo tanto, debe crear la misma función para procesar el cuerpo de la solicitud HTTP que proviene del bot de telegramas en forma de un objeto Actualizar. Para generar la dirección, utilizamos un token para obtener acceso al objeto en la nube fue más difícil. También debe indicar que el objeto tiene acceso público, de lo contrario, los telegramas no podrán acceder al webhook.
deployWebhook[bot_TelegramBot, handler_] := CloudDeploy[APIFunction[{}, handler[HTTPRequestData["Body"]] &], "Deploy/Webhooks/" <> Hash[bot, "SHA", "HexString"], Permissions -> "Public" ]
manejador es otro manejador de funciones. Deje que el controlador convierta la cadena del cuerpo de la solicitud en una asociación, obtenga el identificador de chat desde allí y envíe la palabra "hola".
handlerHello[bot_TelegramBot][body_String] := Block[{json = ImportString[body, "RawJSON"], chat}, chat = json["message", "chat", "id"]; sendMessage[bot, chat, "hello"]; ]
Ahora despliega el funky en la nube.
webhookObject = deployWebhook[bot, handlerHello[bot]]
Fuera [..]: = ... CloudObject[https://www.wolframcloud.com/objects/kirillbelovtest/Deploy/Webhooks/b9bd74f89348faecd6b683ba02637dd4d4028a28]
Y el último paso es transferir la dirección de este objeto al bot de telegramas.
setWebhook[bot, webhookObject[[1]]]
Fuera [..]: = ... <|"ok" -> True, "result" -> True, "description" -> "Webhook was set"|>
Ahora escribiremos algo para el bot y veremos qué dice:

El diálogo puede considerarse válido. Para cambiar la lógica de un controlador existente, simplemente vuelva a implementar el objeto en la nube. Al mismo tiempo, ya no necesitará instalar webhook para el bot.
Lógica de respuestas
Esta será la última parte en el proceso de creación de un bot en la nube de Wolfram. Además, de la misma manera, puede complicar la lógica y agregar nuevos métodos API. Ahora sobre el diálogo en sí. Supongamos que después de enviar el comando / start, el bot devuelve una respuesta "Hola" y cambia el teclado del usuario. Solo quedan dos botones en el teclado: "Hola" y "¿Quién eres?" Realizamos el diálogo en forma de asociación. Las teclas serán los comandos que el usuario envía al bot. Los valores clave son la respuesta del bot en sí y el nuevo teclado. En este caso, muchas teclas y botones deben coincidir exactamente. De lo contrario, puede surgir una situación cuando el bot no sabe qué responder. En tales casos, por supuesto, puede agregar una respuesta predeterminada.
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]
Ahora cree un controlador:
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"]]; ]
Y vuelva a implementar el objeto en la nube:
deployWebhook[bot, handlerAbout[bot]];
Verifiquemos qué sucedió en el chat con el bot. Pero primero, borremos el historial de mensajes:

Extensión de funcionalidad
Hasta ahora, no hay diferencias fundamentales con respecto a la gran cantidad de bots existentes. ¿Quizás tampoco tiene sentido beberlo? ¡El significado de todo el trabajo realizado anteriormente será, si comprende cuáles son las ventajas de tal bot! Después de todo, puede usar todas las características de Wolfram Language y Wolrfam Cloud. ¿Es necesario que el robot resuelva ecuaciones? Es muy facil! ¡Solo necesita redefinir la respuesta!
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]];

Si alguien también tiene interés en las capacidades de la nube, aquí encontrará una buena descripción de su funcionalidad.
Limitaciones
Wolfram Cloud es una plataforma que le permite utilizar el lenguaje Wolfram de forma gratuita, mientras que el producto principal de Wolfram Research, Mathematica, cuesta dinero. En consecuencia, existen restricciones en el uso y, en mi opinión, son muy fuertes. Al usar la versión gratuita de la Plataforma de desarrollo, un usuario recibe 1000 créditos en la nube por mes. Cada préstamo en la nube da tiempo para calcular un tipo diferente. Como el artículo habla sobre CloudDeploy + APIFunction, tales objetos almacenados en la nube gastan 1 crédito en 0.1 segundos de tiempo de computación. Es fácil calcular que el usuario recibe de forma gratuita solo 1 minuto y 40 segundos de tiempo de servidor para el funcionamiento de su aplicación (en este caso, el bot). No tengo nada que agregar aquí, es muy, muy pequeño. El énfasis principal está en los usuarios que trabajan de forma independiente en la Plataforma de Desarrollo utilizando un navegador. De hecho, en este modo no hay límites de tiempo, sino solo de acuerdo con la duración de la sesión y los recursos asignados. Con este uso, la plataforma de desarrollo es casi un Mathematica completo, pero no requiere instalación ni licencia.
→ Artículo y código en Wolfram Cloud