Здравствуй, Хабр. Как-то появилось необходимость сделать бота-помощника, но с двумя условиями — использовать WCF сервис и Телеграмм. Так как пришлось потратить достаточное количество времени на реализацию, решил написать статью, возможно кому-то будет полезно.

Создать собственного бота очень просто — заходим в телеграмм, находим специального бота @BotFather, пишем ему команду /newbot и следуем его инструкциям, в результате получаем токен нашего бота.

image

Теперь надо определиться, каким образом мы будем получать обновления от бота. Telegram предусматривает два способа — тянуть самим с помощью метода getUpdates, либо использовать Webhook. С первым способом все понятно, нам более интересен второй вариант. Используя метод setWebhook можно задать URL вебхука, тем самым вы как бы сообщаете серверам Telegram, куда они должны отправлять все обновления. В роли вебхука как раз будет выступать наш WCF сервис

Создадим новый проект «приложение службы WCF» и назовем его «WcfBot». В документации Telegram сказано, что в обновлениях мы получим объект Update, сериализованный в JSON, поэтому создадим класс этого объекта в новом файле. В этом нам помог json2charp.

Update.cs
namespace WcfBot
{
    public class From
    {
        public long id { get; set; }
        public string first_name { get; set; }
        public string last_name { get; set; }
        public string username { get; set; }
    }

    public class Chat
    {
        public long id { get; set; }
        public string first_name { get; set; }
        public string last_name { get; set; }
        public string username { get; set; }
    }

    public class ForwardFrom
    {
        public long id { get; set; }
        public string first_name { get; set; }
        public string last_name { get; set; }
        public string username { get; set; }
    }

    public class Audio
    {
        public string file_id { get; set; }
        public long duration { get; set; }
        public string performer { get; set; }
        public string title { get; set; }
        public string mime_type { get; set; }
        public long file_size { get; set; }
    }

    public class Thumb
    {
        public string file_id { get; set; }
        public long width { get; set; }
        public long height { get; set; }
        public long file_size { get; set; }
    }

    public class Document
    {
        public string file_id { get; set; }
        public Thumb thumb { get; set; }
        public string file_name { get; set; }
        public string mime_type { get; set; }
        public long file_size { get; set; }
    }

    public class Photo
    {
        public string file_id { get; set; }
        public long width { get; set; }
        public long height { get; set; }
        public long file_size { get; set; }
    }

    public class Sticker
    {
        public string file_id { get; set; }
        public string width { get; set; }
        public string height { get; set; }
        public Thumb thumb { get; set; }
        public long file_size { get; set; }
    }

    public class Video
    {
        public string file_id { get; set; }
        public long width { get; set; }
        public long height { get; set; }
        public long duration { get; set; }
        public Thumb thumb { get; set; }
        public string mime_type { get; set; }
        public long file_size { get; set; }
    }

    public class Voice
    {
        public string file_id { get; set; }
        public long duration { get; set; }
        public string mime_type { get; set; }
        public long file_size { get; set; }
    }

    public class Contact
    {
        public string phone_number { get; set; }
        public string first_name { get; set; }
        public string last_name { get; set; }
        public long user_id { get; set; }
    }

    public class Location
    {
        public double longitude { get; set; }
        public double latitude { get; set; }
    }

    public class NewChatParticipant
    {
        public long id { get; set; }
        public string first_name { get; set; }
        public string last_name { get; set; }
        public string username { get; set; }
    }

    public class LeftChatParticipant
    {
        public long id { get; set; }
        public string first_name { get; set; }
        public string last_name { get; set; }
        public string username { get; set; }
    }

    public class NewChatPhoto
    {
        public string file_id { get; set; }
        public long width { get; set; }
        public long height { get; set; }
        public long file_size { get; set; }
    }

    public class ReplyToMessage
    {
        public long message_id { get; set; }
        public From from { get; set; }
        public long date { get; set; }
        public Chat chat { get; set; }
        public ForwardFrom forward_from { get; set; }
        public long forward_date { get; set; }
        public object reply_to_message { get; set; }
        public string text { get; set; }
        public Audio audio { get; set; }
        public Document document { get; set; }
        public IList<Photo> photo { get; set; }
        public Sticker sticker { get; set; }
        public Video video { get; set; }
        public Voice voice { get; set; }
        public string caption { get; set; }
        public Contact contact { get; set; }
        public Location location { get; set; }
        public NewChatParticipant new_chat_participant { get; set; }
        public LeftChatParticipant left_chat_participant { get; set; }
        public string new_chat_title { get; set; }
        public IList<NewChatPhoto> new_chat_photo { get; set; }
        public bool delete_chat_photo { get; set; }
        public bool group_chat_created { get; set; }
    }

    public class Message
    {
        public long message_id { get; set; }
        public From from { get; set; }
        public long date { get; set; }
        public Chat chat { get; set; }
        public ForwardFrom forward_from { get; set; }
        public long forward_date { get; set; }
        public ReplyToMessage reply_to_message { get; set; }
        public string text { get; set; }
        public Audio audio { get; set; }
        public Document document { get; set; }
        public IList<Photo> photo { get; set; }
        public Sticker sticker { get; set; }
        public Video video { get; set; }
        public Voice voice { get; set; }
        public string caption { get; set; }
        public Contact contact { get; set; }
        public Location location { get; set; }
        public NewChatParticipant new_chat_participant { get; set; }
        public LeftChatParticipant left_chat_participant { get; set; }
        public string new_chat_title { get; set; }
        public IList<NewChatPhoto> new_chat_photo { get; set; }
        public bool delete_chat_photo { get; set; }
        public bool group_chat_created { get; set; }
    }

    public class Update
    {
        public long update_id { get; set; }
        public Message message { get; set; }
    }
}


Теперь откроем файл IService1.cs, в котором находится интерфейс IService1 с атрибутом ServiceContract — это есть контракт службы. Добавим в него операцию службы и к этой операции допишем атрибут WebInvoke, который определяет, на какой HTTP метод реагирует операция службы, какого формата данные получает и по какому URL обращаться к ней.

IService1.cs
namespace WcfBot
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = @"/Update")]
        void GetUpdate(Update update);
    }
}

Далее откроем файл Service1.svc.cs и допишем реализацию нашей операции в классе Service1.

Service1.svc.cs
namespace WcfBot
{
    public class Service1 : IService1
    {
        public void GetUpdate(Update update)
        {
            
        }
    }
}

Теперь нам нужно отправлять ответ на сообщение от лица бота. В этом нам поможет библиотека Telegram.Bot. Чтобы её установить, заходим в консоль диспетчера пакетов (Сервис -> Диспетчер пакетов NuGet -> Консоль диспетчера пакетов) и пишем:

Install-Package Telegram.Bot

Теперь в методе инициализируем объект Telegram.Bot.Api, используя наш token, который нам выдали при создании бота в телеграмме, и реализуем ответ на сообщение “Привет”.

Service1.svc.cs
namespace WcfBot
{
    public class Service1 : IService1
    {
        public void GetUpdate(Update update)
        {
            var Bot = new Telegram.Bot.Api("<token>");
            if(update.message.text == "Привет")
            {
                Bot.SendTextMessage(update.message.chat.id, "Привет," + update.message.from.first_name);
            }
        }
    }
}

Осталось подредактировать файл конфигурации Web.config, добавив в behaviors:
<endpointBehaviors>
  <behavior name="web">
    <webHttp />
  </behavior>
</endpointBehaviors>

и в system.serviceModel:
<services>
  <service name="WcfBot.Service1">
    <endpoint binding="webHttpBinding" contract="WcfBot.IService1" behaviorConfiguration="web"></endpoint>
  </service>
</services>

Теперь можно тестировать. Запускаем сервис, заходим в трей, находим IIS Express, щелкаем правой кнопкой мыши, кликаем «Показать все приложения», находим свой сервис, смотрим по какому порту он включился
image

Для того, чтобы сервера телеграмм имели доступ к вашему сервису, нужно расшарить localhost. Для этих целей подойдет ngrok.

Скачиваем ngrok, распаковываем, запускаем ngrok.exe и вводим команду:

ngrok http 1234 -host-header=”localhost:1234

Вместо 1234 — порт нашего сервиса.

В результате ngrok выдаст нам URL на наш сервис.

image

Важно скопировать https-версию ссылки, так как Telegram поддерживает только защищенный протокол HTTPS. Теперь осталось лишь сказать нашему боту адрес, куда высылать сообщения. Для этого просто в адресной строке браузера вбиваем ссылку:

https://api.telegram.org/bot<token>/setWebhook?url=<url>/Service1.svc/Update

Всё, теперь можно написать боту и убедиться, что он действительно отвечает.

image

[ Исходники ]
Поделиться с друзьями
-->

Комментарии (9)


  1. fcoder
    06.07.2016 09:25

    WCF — замечательная и очень гибкая технология, но для подобной задачи мне кажется слишком уж излишней. Гораздо более подходящей здесь мне кажется будет смотреться asp.net стек.


    1. Atreides07
      06.07.2016 10:08

      "… файл конфигурации Web.config, добавив в behaviors..." — Автор и использовал ASP.NET. Могли бы уточнить что именно из ASP.NET использовали бы Вы для этой задачи?


      1. Razaz
        06.07.2016 10:57
        +1

        WebApi. WebInvoke в 2016 году смотрится дико.


  1. ZOXEXIVO
    06.07.2016 11:15
    -2

    Автор вообще понимает, зачем и для чего нужен WCF? Если не понимает, то зачем пишет такие статьи, засоряющие Хабр?


  1. aquamakc
    06.07.2016 11:54
    -2

    Смешались в кучу кони, люди asp, wcf. Зачем? Есть же замечательный WebClient.


  1. mihmig
    06.07.2016 13:04

    А вот какая у ngrok «пропускная способность»?


    1. timiskhakov
      06.07.2016 18:32

      Пишут, что:

      1 online ngrok process
      4 tunnels per ngrok process
      40 connections / minute

      в бесплатном плане. Есть другие.


  1. lair
    06.07.2016 13:14

    Как-то появилось необходимость сделать бота-помощника, но с двумя условиями — использовать WCF сервис и Телеграмм.

    А откуда у вас требование на использование WCF? Чем оно обосновано?


    (статья представляет собой тривиальный туториал "как собрать бота из двух готовых компонентов")


  1. BlackMetal
    06.07.2016 20:59

    Подписываюсь под словами комментаторов выше. Чем обосновано использование таких костылей и подпорок в 2016?