
Практически все, кто пользуется современными средствами обмена сообщениями, сталкивались с ботами. Одно из определений бота – это программа, выполняющая автоматически и/или по заданному расписанию какие-либо действия через интерфейсы, предназначенные для людей.
Боты могут использоваться для информирования, для автоматизации процессов (например, автоматической генерации задачи в 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