Criação do Whatsapp bot em C#. Guia completo

Repositório com fonte de bot

Introdução

Neste guia, explicaremos como criar um bot do WhatsApp em C# usando nosso gateway de API do WhatsApp

Este bot responderá aos comandos recebidos na forma de mensagens regulares no WhatsApp e responderá a eles. A funcionalidade de teste do bot será limitado pelas seguintes funções:

  • Reação com um texto de boas-vindas a uma mensagem cujo bot não possui um comando. Exibição do menu Bot
  • Exibição do ID de bate-papo atual (no chat privado ou na conversa)
  • Envio de arquivos de vários formatos (pdf, jpg, doc, mp3, etc.)
  • Envio de mensagens de voz (arquivos *.ogg)
  • Envio de geolocalização
  • Criação de um grupo privado com interlocutor e o bot

Nós escreveremos nosso bot usando a tecnologia ASP.Net para criar um servidor que processará e responderá às solicitações do usuário.

Obter acesso gratuito à WhatsApp API

Capítulo 1. Criação de um projeto ASP.Net

Abrimos o Visual Studio e criamos o projeto "Aplicação Web ASP.NET Core".

Em seguida, selecionamos um modelo com um projeto vazio (você também pode selecionar um modelo de API que incluirá os controladores necessários, que só precisará ser editado. Nós, porém, para simplificar, criaremos tudo a partir do zero)

Abrimos o arquivo Startup.cs e digitamos este código no método Configure:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
         if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

Isso nos permitirá configurar a navegação usando o controlador. Agora vamos começar a escrever o próprio controlador.

Para isso, criamos no projecto uma pasta chamado Controllers, na qual criaremos nossa classe de controlador WebHookController

Nosso controlador deve herdar a classe ControllerBase e ser assinalado com atributos

using Microsoft.AspNetCore.Mvc;
namespace WaBot.Controllers
    {
        [ApiController]
        [Route("/")]
        public class WebHookController : ControllerBase
            {

            }
    }

O atributo Route será responsável pelo endereço em que este controlador irá operar. Especificamos o caminho base de domínio.

Nesta fase, nosso controlador está quase pronto. Agora precisamos adicionar métodos para trabalhar com a API WA e outras classes auxiliares que serão úteis para nós no nosso trabalho.


Autorização do Telefone

Associamos o whatsapp ao nosso script, para que, enquanto digitar o código, verificar seu funcionamento. Para isso, vá para a sua conta pessoal e obtenha um código QR lá. A seguir, abra o WhatsApp no seu celular, vá para Configurações -> WhatsApp Web -> Digitalize o código QR.


Capítulo 2. Classe da API

Neste capítulo, consideramos a escrita de uma classe que será responsável por interagir com nosso gateway de API. A documentação pode ser lida aqui. Criamos uma classe* WaApi*:

public class WaApi
    {
        private string APIUrl = "";
        private string token = "";

        public WaApi(string aPIUrl, string token)
            {
                APIUrl = aPIUrl;
                this.token = token;
            }
    }

Essa classe armazenará os campos APIUrl e token, que são necessários para trabalhar com a API. Você pode obtê-los na sua conta pessoal.

O mesmo acontece com um construtor que atribui valores aos campos. Graças a isso, podemos ter vários objetos que podem representar diferentes bots, caso seja necessário configurar o trabalho de vários bots ao mesmo tempo.

Método para o envio de solicitações

Adicionamos um método assíncrono a essa classe que enviará solicitações POST:

public async Task<string> SendRequest(string method, string data)
    {
        string url = $"{APIUrl}{method}?token={token}";

        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri(url);
            var content = new StringContent(data, Encoding.UTF8, "application/json");
            var result = await client.PostAsync("", content);
            return await result.Content.ReadAsStringAsync();
        }
    }

Este método aceita dois argumentos.

  •   method - o nome do método desejado de acordo com documentação
  •   data - json string para o envio

No método, formamos a string de url para a qual a solicitação será enviada. E então fazemos uma solicitação POST para esse endereço usando a classe System.Net.Http.HttpClient. Retornamos a resposta do servidor.

Com base nesse método, podemos criar a funcionalidade necessária para o bot funcionar.

Envio de mensagens

public async Task<string> SendMessage(string chatID, string text)
    {
        var data = new Dictionary<string, string>()
        {
            {"chatId",chatID },
            { "body", text }
        };
        return await SendRequest("sendMessage", JsonConvert.SerializeObject(data));
    }

Este método tem como parâmetros:

  • chatId - ID do chat para onde enviar a mensagem
  • text - o texto da mensagem a ser enviada.

Para formar uma string Json, usaremos a biblioteca apropriada Newtonsoft.Json. Criaremos um dicionário cuja chave é uma string com o campo Json necessário, de acordo com documentação, e o valor são: nossos parâmetros. Basta chamar o método JsonConvert.SerializeObject e passar nosso dicionário para ele formar uma string Json. Chamamos o método SendRequest, passando nele o nome do método para o envio de mensagens e nossa string json.

Desta forma, nosso bot responderá aos usuários.

Envio de uma mensagem de voz

public async Task<string> SendOgg(string chatID)
    {
        string ogg = "https://firebasestorage.googleapis.com/v0/b/chat-api-com.appspot.com/o/audio_2019-02-02_00-50-42.ogg?alt=media&token=a563a0f7-116b-4606-9d7d-172426ede6d1";
        var data = new Dictionary<string, string>
        {
            {"audio", ogg },
            {"chatId", chatID }
        };

        return await SendRequest("sendAudio", JsonConvert.SerializeObject(data));
    }

Em seguida, a lógica da construção de métodos é semelhante. Vemos em documentação e enviamos os dados necessários para o servidor, chamando os métodos necessários.

Para o envio de uma mensagem de voz, utilizamos o link no arquivo formato .ogg e o método sendAudio

Método para o envio de geolocalização

public async Task<string> SendGeo(string chatID)
    {
        var data = new Dictionary<string, string>()
        {
            { "lat", "55.756693" },
            { "lng", "37.621578" },
            { "address", "Your address" },
            { "chatId", chatID}
        };
        return await SendRequest("sendLocation", JsonConvert.SerializeObject(data));
    }

Criação de grupos

Crie uma conferência na qual estará você e o bot

public async Task<string> CreateGroup(string author)
    {
        var phone = author.Replace("@c.us", "");
        var data = new Dictionary<string, string>()
        {
            { "groupName", "Group C#"},
            { "phones", phone },
            { "messageText", "This is your group." }
        };
        return await SendRequest("group", JsonConvert.SerializeObject(data));
    }

Método para o envio de arquivos

Debatemos alguns pontos relacionados a esse método. Ele é admitido em parâmetros:

  •   chatID - ID do chat
  •   format - formato de arquivo que você deseja enviar.

Para enviar um arquivo, a documentação fornece várias formas:

  •   Link para o arquivo que deseja enviar
  •   String que é um arquivo codificado usando o método Base64.

É recomendável enviar arquivos usando o segundo método, codificando os arquivos no formato Base64. No Capítulo 4 falaremos sobre isso com mais detalhes. E agora, vale a pena saber que nós descrevemos uma classe estática Base64String, na qual escrevemos as propriedades gravando todos os arquivos de teste dos formatos desejados. No método, simplesmente chamamos a propriedade do formato desejado e transferimos a string Base64 fornecida para o servidor.

public async Task<string> SendFile(string chatID, string format)
    {
        var availableFormat = new Dictionary<string, string>()
        {
            {"doc", Base64String.Doc },
            {"gif",Base64String.Gif },

            { "jpg",Base64String.Jpg },
            { "png", Base64String.Png },
            { "pdf", Base64String.Pdf },
            { "mp4",Base64String.Mp4 },
            { "mp3", Base64String.Mp3}
        };

        if (availableFormat.ContainsKey(format))
        {
            var data = new Dictionary<string, string>(){
                { "chatId", chatID },
                { "body", availableFormat[format] },
                { "filename", "yourfile" },
                { "caption", $"My file!" }
            };

            return await SendRequest("sendFile", JsonConvert.SerializeObject(data));
        }

        return await SendMessage(chatID, "No file with this format");
    }

Isto descreve a funcionalidade básica de nossa classe API. Agora conectamos nosso controlador do Capítulo 1 e API.

Capítulo 3. Processamento de solicitações.

Voltemos ao controlador do primeiro capítulo. Descrevemos um método dentro do controlador que vai lidar com as solicitações post que chegam ao nosso servidor do chat-api.com. Chamaremos o método Post (o nome pode ser qualquer) e o marcamos com o atributo [HttpPost], o que significaria responder às solicitações Post:

[HttpPost]
public async Task<string> Post(Answer data)
    {
        return "";
    }

Nosso método será reconhecido pela classe Answer, que é um objeto desserializado da string json. Para descrever a classe Answer, precisamos descobrir qual json aparecerá.

Para isso, você pode usar a confortável seção "Testes" - "Simulação Webhooka" na sua conta pessoal.

À direita podemos ver o corpo json que aparecerá.

Usaremos o Serviço converter json em C#. Ou descrevemos a classe nós mesmos, usando os atributos da biblioteca Newtonsoft.Json:

public partial class Answer
    {
        [JsonProperty("instanceId")]
        public string InstanceId { get; set; }

        [JsonProperty("messages")]
        public Message[] Messages { get; set; }
    }

public partial class Message
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("body")]
        public string Body { get; set; }

        [JsonProperty("type")]
        public string Type { get; set; }

        [JsonProperty("senderName")]
        public string SenderName { get; set; }

        [JsonProperty("fromMe")]
        public bool FromMe { get; set; }

        [JsonProperty("author")]
        public string Author { get; set; }

        [JsonProperty("time")]
        public long Time { get; set; }

        [JsonProperty("chatId")]
        public string ChatId { get; set; }

        [JsonProperty("messageNumber")]
        public long MessageNumber { get; set; }
    }

Agora que temos uma representação de objeto da solicitação de entrada, iremos processá-la no controlador.

Dentro do controlador, criaremos um campo estático, que será a nossa API referência e token:

private static readonly WaApi api = new WaApi("https://eu115.chat-api.com/instance12345/", "123456789token");

No loop do método, revisamos todas as mensagens que nos têm chegado e verificamos se a mensagem que está sendo processada não é nossa. Isso é necessário para que o bot não entre em ciclos por si mesmo. Se, no entanto, a mensagem é de si mesmo, saltamos:

[HttpPost]
public async Task<string> Post(Answer data)
    {
        foreach (var message in data.Messages)
        {
            if (message.FromMe)
                continue;
        }
    }

A seguir, descrevemos o switch para o qual transmitiremos os comandos recebidos. Aplique à propriedade Body o método Split(), para dividir a mensagem por espaços. Transferimos no switch o primeiro comando, chamando o método ToLower() para que o estilo da escrita do comando não desempenhe um papel e seja processado da mesma maneira:

[HttpPost]
public async Task<string> Post(Answer data)
    {
        foreach (var message in data.Messages)
        {
            if (message.FromMe)
                continue;

            switch (message.Body.Split()[0].ToLower())
            {
                case "chatid":
                    return await api.SendMessage(message.ChatId, $"Your ID: {message.ChatId}");
                case "file":
                    var texts = message.Body.Split();
                    if (texts.Length > 1)
                        return await api.SendFile(message.ChatId, texts[1]);
                    break;
                case "ogg":
                    return await api.SendOgg(message.ChatId);
                case "geo":
                    return await api.SendGeo(message.ChatId);
                case "group":
                    return await api.CreateGroup(message.Author);
                default:
                    return await api.SendMessage(message.ChatId, welcomeMessage);
            }
        }
        return "";
    }

No case escreveremos todos os comandos que precisamos e chamaremos os métodos do nosso objeto API que os implementa.  E default processará comandos que não existem. Para isso, basta enviar uma mensagem do menu bot para o usuário.

Whatsapp bot em C#

Guia detalhado para desenvolvimento de bot usando Csharp

Nosso bot está pronto. Ele já pode responder e processar os comandos do usuário, resta apenas adicioná-lo à hospedagem e especificar o domínio como webhook na conta de usuário do chat-api.com.

As fontes do bot estão disponíveis no link da github https://github.com/chatapi/whatsapp-csharp-bot-en. Não se esqueça de substituir seu token da sua conta pessoal e número da instância.

E nos próximos capítulos, falaremos sobre base64 e os possíveis erros que você pode encontrar.

Get API key

Capítulo 4. Base64

Base64 é um padrão de codificação de dados. Com esse padrão, podemos codificar arquivos em uma string e transferi-los desta forma. Na sua conta pessoal está disponível serviço, o que ajudará a gerar uma string desse formato. Para escrever este bot, foi necessário fornecer alguns dados estáticos para o teste, por isso inserimos as strings recebidas na classe auxiliar e acedemos a elas a partir do código.

Pode ser mais útil para você codificar arquivos durante o progresso "on the fly".

Para a geração de tais strings também pode usar ferramentas internas da linguagem C#.

Capítulo 5. Publicação do Servidor.

Para instalar o servidor como weebhook, é necessário carregar deste mesmo servidor na Internet. Para tal, você poderá usar os serviços de prestação de serviços de hospedagem, servidores vps ou vds.

É necessário escolher e pagar por um serviço que ofereça suporte a tecnologia ASP.Net.

Possíveis problemas que podem ocorrer

  •   Se você não pode se conectar com o servidor de hospedagem para publicar o seu servidor. Solução: entre em contato com o suporte técnico e solicite habilitar o web Deploy para o seu serviço.

  •   HTTP ERROR 500.0 a solução está aqui