Боты на .Net Core для Telegram, Slack и Facebook

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

-bwy80wne-voimxznfmoswfmm-q.png

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

Боты могут использоваться для информирования, для автоматизации процессов (например, автоматической генерации задачи в TFS на основе письма пользователя) и для многих других целей, но т.к. одной статьи не хватит, чтобы рассмотреть все варианты, далее пойдёт рассказ лишь о том, как создать бота для обработки команд.

Бот для Telegram


Самая лёгкая платформа для разработки — это Telegram. Процесс начинается с вызова специального бота BotFather.

zuvohhl6cnyir6em4cxipxjm8m8.png

Всё очень просто, в строке поиска вбиваете 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 .


После запуска появится окно со сгенерированными адресами. Также сюда будет выводиться информациях обо всех событиях туннелирования.

cvagzhmoxrgcd_sbuhmsgfx-2co.png

HTTPS-адрес, который вернёт Ngrok, будет меняться при каждом запуске туннелирования, но вам не нужно делать это всё время при отладке приложения. Настройте туннелирование для конкретного порта один раз и останавливайте (запускайте) ваш сервис сколько угодно раз.

У Ngrok есть административная страница, доступная по адресу 127.0.0.1:4040, на которой можно посмотреть параметры запросов пользователя.

ztodvlzu7ut3qfelzucpgxpbauq.png

Вернёмся к нашему предыдущему примеру с повторной отправкой сообщения и создадим 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 — назначение команды

cigtdts-k_febslf3hspd-t_3ka.png

На рисунке пример, как в чате выглядит вызов команды getmoney бота AlfaTestNdo.

dwjnczjpllmm1ttqt1n3l5_tzfe.png

После выбора команды 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

txeqpwrnnxgmjv-1tad9kzznxze.png

В завершении темы Slack добавлю, что как и в Telegram вы можете создавать не только текстовые сообщения, но и различные интерактивные компоненты: кнопки, меню и так далее.

Бот для Facebook


Создание бота в Facebook, на мой взгляд сложнее, чем в Telegram и Slack, но в целом почти всё то же самое. Вам нужно зайти на страницу developers.facebook.com/apps, нажать кнопку «Добавить новое приложение» и ввести название, а также адрес почты для обратной связи.

uf_si7bj5hunupl3ue_yq9uhkam.png

После нажатия кнопки «Создайте ID приложения» откроется страница настройки, где нужно указать персональную страницу бота (потребуется создать её), чтобы получить ключ доступа к методам API.

9xnithoclartuv8cxrrrdnotsti.png

В разделе Webhook потребуется указать https-адрес вашего сервиса, придумать проверочный ключи и ввести его в поле «Подтвердить маркер», а также выбрать информацию, на которую вы подписываетесь.

gclacmg7s0esnhgloke6xbxq0te.png

Тут важно знать следующее, при нажатии кнопки «Подтвердить и сохранить» 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 отправит сообщение следующего вида.

upless6mmwsexxgw6m88je9ifdc.png

Теперь как выглядит код обработки входящих сообщений. В примере сообщение повторно отправляется обратно пользователю.

  [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 и пароль приложения.

myltn5ydrzgjwhoa-lwptdesvw8.png

Есть, правда, и ряд неприятных моментов. В частности, нет поддержки .Net Core. Об этом подробнее написано здесь.
github.com/Microsoft/BotBuilder/issues/572
designprincipia.com/microsoft-bot-framework-on-asp-net-core

Вернёмся к нашему примеру с повторной отправкой сообщения. Вам потребуется установить шаблон Bot Framework.

rj7oz6vmpqbdhatqn5_qc7mgjge.png

В данном примере в ответ на текстовое сообщение от пользователя (тип ActivityTypes.Message) создаётся экземпляр класса, поддерживающего интерфейс IDialog.

 [Route("api/[controller]")]
    [BotAuthentication]
    public class MessagesController : Controller
    {
        [HttpPost]
        public async Task 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
{
	public Task StartAsync(IDialogContext context)
	{
		context.Wait(MessageReceivedAsync);
		return Task.CompletedTask;
	}
	private async Task MessageReceivedAsync(IDialogContext context, IAwaitable 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#


Дополнительные инструкции


  • Статья «Пишем бота Telegram на C#»
  • Статья «Как легко написать бота для Telegram на C#»
  • Цикл видео по Microsoft Bot Framework
  • Статья «Разработка чат-бота для Facebook Messenger»
  • Статья «Facebook Chatbot in ASP.NET»

© Habrahabr.ru