Практически все, кто пользуется современными средствами обмена сообщениями, сталкивались с ботами. Одно из определений бота – это программа, выполняющая автоматически и/или по заданному расписанию какие-либо действия через интерфейсы, предназначенные для людей.
Боты могут использоваться для информирования, для автоматизации процессов (например, автоматической генерации задачи в TFS на основе письма пользователя) и для многих других целей, но т.к. одной статьи не хватит, чтобы рассмотреть все варианты, далее пойдёт рассказ лишь о том, как создать бота для обработки команд.
Бот для Telegram
Самая лёгкая платформа для разработки – это Telegram. Процесс начинается с вызова специального бота BotFather.
Всё очень просто, в строке поиска вбиваете BotFather, нажимаете кнопку start, выбираете команду \newbot, после чего, последовательно отвечая на вопросы, указываете имя бота и его пользовательское имя. В финале Telegram сообщит, что бот успешно создан, предоставит его ключ и предложит указать дополнительное описание и/или ввести названия команд.
Технически у вас два способа, чтобы обработать сообщения и команды бота. Первый заключается, в том, что ваш сервис периодически опрашивает сервер Telegram на наличие изменений. Для демонстрации воспользуемся API Telegram.Bot (пакет доступен через nuget).
using Telegram.Bot;
using Telegram.Bot.Args;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
class Program
{
private static TelegramBotClient client;
static void Main(string[] args)
{
// token, который вернул BotFather
client = new TelegramBotClient(token);
client.OnMessage += BotOnMessageReceived;
client.OnMessageEdited += BotOnMessageReceived;
client.StartReceiving();
Console.ReadLine();
client.StopReceiving();
}
private async void BotOnMessageReceived(object sender, MessageEventArgs messageEventArgs)
{
var message = messageEventArgs.Message;
if (message?.Type == MessageType.TextMessage)
{
await client.SendTextMessageAsync(message.Chat.Id, message.Text);
}
}
}
В примере выше каждое входящее сообщение повторно отправляется пользователю. Сразу отмечу, что в Telegram команда – это тоже сообщение, только со знаком “/”.
Второй способ требует настройки webhook, т.е. сервиса, размещённого по https-адресу, который будет обрабатывать изменения. Этот способ меньше нагружает сервера Telegram, но требует наличия сертификата. Впрочем, проблема с сертификатом легко решается благодаря сервису Ngrok (https://ngrok.com/), который может осуществлять туннелирование запросов с https-адреса на адрес приложения на вашей машине.
Чтобы воспользоваться Ngrok, необходимо:
- зарегистрироваться на сайте и получить персональный ключ;
- установить ngrok.exe и в командной строке ввести ngrok authtoken ключ Ngrok;
- выполнить команду ngrok http портсервиса;
- в Visual Studio можно поставить плагин Ngrok и воспользоваться командой из меню Tools -> Start Ngrok tunnel .
После запуска появится окно со сгенерированными адресами. Также сюда будет выводиться информациях обо всех событиях туннелирования.
HTTPS-адрес, который вернёт Ngrok, будет меняться при каждом запуске туннелирования, но вам не нужно делать это всё время при отладке приложения. Настройте туннелирование для конкретного порта один раз и останавливайте (запускайте) ваш сервис сколько угодно раз.
У Ngrok есть административная страница, доступная по адресу 127.0.0.1:4040, на которой можно посмотреть параметры запросов пользователя.
Вернёмся к нашему предыдущему примеру с повторной отправкой сообщения и создадим ASP.NET Core проект со следующим WebAPI-контроллером.
[Route("bot")]
public class BotController : Controller
{
// token, который вернул BotFather
private readonly TelegramBotClient client = new TelegramBotClient(token);
[HttpPost]
public async void Post([FromBody]Update update)
{
if (update == null) return;
var message = update.Message;
if (message?.Type == MessageType.TextMessage)
{
await client.SendTextMessageAsync(message.Chat.Id, message.Text);
}
}
}
Для того чтобы сообщить Telegram, где находится сервис, можно написать следующее простое консольное приложение.
using Telegram.Bot;
using Telegram.Bot.Args;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
class Program
{
static void Main(string[] args)
{
// token, который вернул BotFather
var client = new TelegramBotClient(token);
client.SetWebhookAsync("https адрес от Ngrok сервиса").Wait();
Console.ReadLine();
client.SetWebhookAsync().Wait();
}
}
Бот для Slack
Рассмотрим другой популярный сервис обмена сообщениями. Создание бота в Slack начинается с простой формы (https://api.slack.com/apps/new), где нужно заполнить название приложения и указать рабочее пространство (workspace).
После нажатия кнопки Create App появится страница настройки, где в подразделе Add features and functionality вам нужно выбрать пункт Slash Commands, чтобы перейти к созданию команд бота. На появившейся форме нужно будет ввести:
- В поле Command — название команды
- В поле Request URL – https-адрес нашего сервиса
- В поле Short Description – назначение команды
На рисунке пример, как в чате выглядит вызов команды getmoney бота AlfaTestNdo.
После выбора команды Slack по указанному https-адресу отправляет следующее сообщение
public class Message
{
public string channel_id { get; set; }
public string channel_name { get; set; }
public string command { get; set; }
public string response_url { get; set; }
public string team_domain { get; set; }
public string team_id { get; set; }
public string text { get; set; }
public string token { get; set; }
public string trigger_id { get; set; }
public string user_id { get; set; }
public string user_name { get; set; }
}
Посмотрим теперь, как будет выглядеть код нашего сервиса, отправляющего в ответ сообщение “hello”.
[Route("bot")]
public class BotController : Controller
{
[HttpPost]
public async void Post(Message message)
{
var uri = new Uri("https://slack.com/api/chat.postMessage?token="
+ token + "&channel=“ + message.channel_id + "&text=hello");
var httpClient = new HttpClient();
await httpClient.GetAsync(uri).ConfigureAwait(false);
}
}
В сервисе вызывается метод chat.postMessage с ключом token и идентификатором канала message.channel_id.
Slack определяет следующие типы ключей:
- User token — ключ пользователя, установившего приложение, при этом применяется авторизация через Oauth. Имеет префикс xoxp. Подробнее об этом здесь https://api.slack.com/docs/oauth
- Bot user token – ключ, технического виртуального пользователя. Имеет префикс xoxb. Подробнее о них написано здесь https://api.slack.com/bot-users.
- Workspace token – ключ рабочей области. Имеет префикс xoxa.
- Legacy token – ключ авторизованного пользователя. Имеет префикс xoxp. Подробнее о них здесь https://api.slack.com/custom-integrations/legacy-tokens. Не рекомендуется их использовать.
- Verification token – этот ключ slack отправляет при обращению к вашему сервису. С помощью него можно проверить, что именно ваше приложение обращается к сервису. Никогда не применяется для вызовов методов API.
Все методы API Slack хорошо документированы, содержат информацию о поддерживаемых ключах. На странице описания метода также есть вкладка Tester для тестирования.
https://api.slack.com/methods/chat.postMessage
В завершении темы Slack добавлю, что как и в Telegram вы можете создавать не только текстовые сообщения, но и различные интерактивные компоненты: кнопки, меню и так далее.
Бот для Facebook
Создание бота в Facebook, на мой взгляд сложнее, чем в Telegram и Slack, но в целом почти всё то же самое. Вам нужно зайти на страницу developers.facebook.com/apps, нажать кнопку “Добавить новое приложение” и ввести название, а также адрес почты для обратной связи.
После нажатия кнопки “Создайте ID приложения” откроется страница настройки, где нужно указать персональную страницу бота (потребуется создать её), чтобы получить ключ доступа к методам API.
В разделе Webhook потребуется указать https-адрес вашего сервиса, придумать проверочный ключи и ввести его в поле “Подтвердить маркер”, а также выбрать информацию, на которую вы подписываетесь.
Тут важно знать следующее, при нажатии кнопки “Подтвердить и сохранить” Facebook отправит проверочное сообщение с полями hub.mode, hub.challenge плюс hub.verify_token, в котором будет ваш персональный ключ. Ваш сервис должен суметь принять это сообщение и вернуть hub.challenge в качестве успеха, если в поле hub.verify_token был действительно передан ваш ключ.
[Route("bot")]
public class BotController : Controller
{
[HttpGet]
public string Verify()
{
var mode = Request.Query["hub.mode"].FirstOrDefault();
var challenge = Request.Query["hub.challenge"].FirstOrDefault();
var token = Request.Query["hub.verify_token"].FirstOrDefault();
return challenge ?? string.Empty;
}
}
Допустим, вы подписались только на messages и отправили боту сообщение “hello”. В этом случае на указанный вами https-адрес Facebook отправит сообщение следующего вида.
Теперь как выглядит код обработки входящих сообщений. В примере сообщение повторно отправляется обратно пользователю.
[Route("bot")]
public class BotController : Controller
{
[HttpPost]
public void Post([FromBody] Letter letter)
{
var content = letter.entry[0].messaging[0];
const string token = “сгенерированный ключ, когда вы указали страницу бота";
var uri = new Uri("https://graph.facebook.com/v2.6/me/messages?access_token=" + token);
var request = (HttpWebRequest)WebRequest.Create(uri);
request.ContentType = "application/json";
request.Method = "POST";
using (var requestWriter = new StreamWriter(request.GetRequestStream()))
{
requestWriter.Write($@" {{recipient: {{ id: {content.sender.id}}},message: {{text: ""{content.message.text}"" }}}}");
}
var response = (HttpWebResponse)request.GetResponse();
}
}
Bot Framework
Наш обзор будет неполным, если мы не коснёмся такого замечательного инструмента, как Bot Framework от компании Microsoft.
Во-первых, Bot Framework предоставляет унифицированное API для работы с разными каналами: Bing, Cortana, Email, Facebook, GroupMe, Kik, Skype, Skype for Business, Slack, SMS, Microsoft Teams.
Во-вторых, вы получаете эмулятор (https://github.com/Microsoft/BotFramework-Emulator) для тестирования вашего сервиса, который не требует настройки https-адреса. Эмулятор в том числе позволяет проверить уже размещённый сервис по адресу dev.botframework.com/bots, хотя в этом случае потребуется указать ID и пароль приложения.
Есть, правда, и ряд неприятных моментов. В частности, нет поддержки .Net Core. Об этом подробнее написано здесь.
github.com/Microsoft/BotBuilder/issues/572
designprincipia.com/microsoft-bot-framework-on-asp-net-core
Вернёмся к нашему примеру с повторной отправкой сообщения. Вам потребуется установить шаблон Bot Framework.
В данном примере в ответ на текстовое сообщение от пользователя (тип ActivityTypes.Message) создаётся экземпляр класса, поддерживающего интерфейс IDialog.
[Route("api/[controller]")]
[BotAuthentication]
public class MessagesController : Controller
{
[HttpPost]
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity?.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
В нашем случае это класс RootDialog с методом StartAsync, который организует задачу по отправке входящего сообщения.
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
await context.PostAsync(activity.Text);
context.Wait(MessageReceivedAsync);
}
}
На мой взгляд, самой интересной фишкой Bot Framework является Connector. Это API, позволяющее организовать связь между разными каналами. Более подробно можно почитать здесь: docs.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-connector
Заключение
Если сравнивать процессы разработки бота и web-приложения, можно отметить следующее: в обоих случаях одинаково требуется middle-слой обработки запросов, но что касается front-части – различие существенное. Когда вы делаете бота, вам не нужно искать ресурсы, чтобы разработать интерфейс, способный работать в разных браузерах и операционных системах, т.к. за интерфейс отвечает разработчик сервиса обмена сообщений. И хотя вы никак не контролируете API, во многих случаях реализация взаимодействия с пользователем через бот вместо сайта может оказаться перспективнее и сэкономить много ресурсов.
Полезные ссылки
Официальные инструкции
- Информация по созданию ботов в Telegram
- Информация по ботам в Slack
- Инструкция по созданию бота в Facebook
- Документация о Bot Framework
- Информация по ботам Viber
- vk.com/dev/bots и vk.com/dev/bots_docs" — информация по ботам ВКонтакте
SDK
- API для Telegram на .Net (nuget-пакет Telegram.Bot)
- Примеры использования API telegram.bot
- Официальная страница на реализации API на различных языках, в том числе и на C#
Дополнительные инструкции
Комментарии (12)
BosonBeard
04.12.2017 21:25+1Хорошая статья, ботами никогда не интересовался, но бегло пролистал и вроде как концептуально все понятно, даже захотелось когда-нибудь опробовать, продолжайте в том же духе!
dmitry_dvm
04.12.2017 23:56Вместо огорода с хттпс-костылями можно просто захостить бота в эйжуре, тем более что он на дотнете. Бесплатно, хттпс искаропки и отладка в студии «на живую».
wrmax Автор
05.12.2017 00:50+1В целевом варианте да. Сервис в любом случае надо будет размещать на каком-либо хосте, хоть azure. Ngrok приведён в статье, потому что на мой взгляд он проще для начала работы.
dmitry_dvm
05.12.2017 10:55+1Не очень проще, учитывая, что в эйжур в 3 клика прям из студии всё отправляется. В любом случае, спасибо за статью.
Ti_Fix
05.12.2017 11:54Разве хостинг в Azure бесплатен (не считая студентов)?
bluetooth
05.12.2017 15:52Не только студентов. Все, у кого есть подписка MSDN (или как она сейчас называется). Да и остальные могут подписаться на бесплатные 30 дней.
NeuroHunter
05.12.2017 16:13Плюс — для бота можно взять бесплатную подписку с определенным количеством API calls. Конечно для промышленного внедрения это не прокатит, но для себя любимого — вполне
Oxoron
05.12.2017 20:18Можно ссылку?
NeuroHunter
05.12.2017 20:53azure.microsoft.com/en-us/pricing/details/bot-service
Для Standard Web App есть бесплатный вариант.
Раньше был бесплатный вариант и для Serverless, но теперь, как я погляжу, его убрали.
dmitry_dvm
05.12.2017 21:57У меня бот живет там как веб-приложение с оплатой по мере использования. Трафик не ахти какой, в среднем 10к запросов в месяц, до минимального платного порога не дотягивает. С момента анонса телеграм-ботов крутится бесплатно.
stDistarik
05.12.2017 10:52+1Можно самопаканый сертификат прикрутить, а можно nginx + Let’s Encrypt использовать в качестве прокси для любого количества ботов.
Вот здесь писал своего бота на СИ.
Tantrido