Como criar bots para Telegram em MQL5

4 agosto 2016, 17:24
Andrey Voytenko
4
2 625

Introdução

Durante a conferência F8, realizada em San Francisco em 12 de abril de 2016, o Facebook anunciou a integração da API para bots dentro do Messenger. No mesmo dia, uma grande atualização foi realizada na plataforma Bot do Telegram. A versão 2.0 foi uma surpresa agradável com a sua funcionalidade. Parece que os bots que costumavam ser populares na era ICQ estão retornando agora. Na nova fase de desenvolvimento, os bots receberam uma funcionalidade interativa, a interface aberta para programação e suporte multimídia. Basicamente, eles têm todas as condições de se tornarem insubstituíveis quando você deseja encontrar, ver ou comprar algo.

Este artigo é um manual passo-a-passo para a criação de bots para o Telegram em MQL5. Então, o que é um "bot"? Um bot (abreviação de "robot") é uma conta especial no Telegram para troca de mensagens. Bots operam no seu lado (cliente) e interagem com o servidor Telegram, usando um conjunto especial de comandos que são parte da API Bot. Antes de prosseguir com a criação de um bot, por favor faça o download do Telegram e conecte-se a ele. O registro está ligado ao número de telefone, mas você também pode procurar pelo nome de usuário (@username). Agora está na hora de inteirar-se com uma pasta de todos os bots.

Registo de um novo bot

Um bot especial chamado @BotFather é responsável pelo registro e criação dos bots. Vamos encontrá-lo através do motor de busca. Depois de adicioná-lo à lista de contatos, vamos começar a nos comunicar com ele usando o comando /start. Como resposta ele irá enviar-lhe uma lista de todos os comandos disponíveis, como mostrado na Fig. 1.


Lista dos comandos @BotFather.

Fig.1. Lista dos comandos @BotFather.

Com o comando /newbot começamos o registro de um novo bot. Precisamos chegar com dois nomes. O primeiro é um nome de bot que pode ser definido na sua língua nativa. O segundo é um nome de usuário em latim do bot que termina com um prefixo "bot". Como resultado, obtém-se um símbolo - a chave de acesso via API para operar um bot. O exemplo de registo é mostrado na Fig. 2.


Registo de um novo bot

Fig.2.  Registo de um novo bot.

Se desejar, alguns parâmetros podem ser alterados. Sugiro que mantenha as configurações no modo em linha. Caso contrário, os bots não trabalharão com ele. Eu recomendaria definir apenas as qualidades estéticas:
  • /setcomands - definir a lista dos comandos suportados. Esta lista aparecerá aos usuários como uma dica quando entrar o símbolo "/" na janela do chat.
  • /setuserpic – configurando a imagem no perfil Sem este símbolo, o bot não é o bastante para ser apresentável.
  • /SetDescription - um texto exibido como uma saudação quando um bot é adicionado ao Messenger. Normalmente, existem algumas frases para descrever a finalidade de um bot.

Assim, um novo bot é registrado. Vamos agora discutir os modos que podem ser utilizados.

Modo de operação para bots

O Telegram possui três esquemas de interação entre bots e usuários. Primeiro - conversas privadas. Cada usuário comunica com um bot de forma independente do outro, como mostra a Fig 3, ao fazer solicitações e receber respostas.


Bot e conversas privadas

Fig.3.  Bot e conversas privadas.


Os usuários enviam mensagens a um bot. Eles são armazenados no servidor não mais do que 24 horas, depois são removidos. Um bot tem tempo para solicitar e responder essas mensagens. Este é o principal modo o qual os nossos bots estarão operando.

O segundo modo envolve um grupo de chats. Neste caso, a mensagem enviada por qualquer membro de um grupo é visto por todo o grupo (Fig. 4).

Bot e um chat de grupo

Fig.4. Bot e um chat de grupo.

No que diz respeito aos bots, você pode deixá-los participar de grupos usando o comando /setjoingroups. Se um bot é adicionado a um grupo, então usando o comando /setprivacy, você pode definir a opção de receber todas as mensagens ou apenas aqueles que começam com um sinal de símbolo "/". Para ser honesto, eu somente consegui pensar num bot desta forma - estatísticas de mensagens para uma análise posterior.

O terceiro modo de operação incide sobre um canal. Canais do Telegram são contas para transmitir mensagens a um grande público que suportam um número ilimitado de assinantes. A característica importante dos canais é que os usuários não podem deixar comentários e likes no feed de notícias (conexão one-way). Apenas os administradores do canal podem criar mensagens lá (Fig. 5).


Bot como administrador do canal

Fig.5. Bot como administrador de canal.

Bots também podem ser adicionados à lista dos administradores. Isso faz do canal uma ferramenta ideal para fornecer sinais de negociação. Mais adiante, vamos escrever um bot simples que publica os sinais do indicador padrão MACD. Um novo canal público pode ser criado através do menu "Novo Canal" do messenger. Não se esqueça de adicionar o seu bot à lista de canais dos administradores. Isso é conseguido através da janela de propriedade do canal. Todos os preparativos foram concluídas para prosseguirmos com a programação.


Manipulação de um fluxo de mensagens

Enquanto escrevia este artigo, eu tinha a meta de criar uma classe que realizaria a rotina de tratamento das mensagens e me permitia focar na lógica do bot. Como resultado, a classe CCustomBot implementa uma funcionalidade mínima para o trabalho já escrito.

A comunicação com um servidor ocorre através de solicitações POST, usando a função WebRequest. Cada comando tem a sua própria URL:

https://api.telegram.org/bot< TOKEN >/ METHOD_NAME

Onde o TOKEN é um sinal de um bot registrado; METHOD_NAME - uma lista dos métodos suportados.

Respostas do servidor chegam no formato JSON, portanto, um bom analisador JSON foi exigido. Eu apliquei um analisador nativo JSON de serialização e desserialização. Eu gostaria de agradecer Alexey (sergeev) pelo trabalho que realizou. Além disso, o painel para a exibição de alguns parâmetros também foi aplicado. A classe CComment retirado do Codebase era adequado para esta tarefa. Nomes de métodos públicos da classe foram copiados da documentação para a API Bot alcançar a universalidade. Os métodos que foram implementados a classe estão listados abaixo:

A fim de entender como usar essas funções, estamos chegando mais fundo na programação.


GetMe

Uma vez que durante cada pedido um sinal é enviado, assim acima de tudo, a função GetMe que verifica sua credibilidade é implementada. É aconselhável realizar esta verificação no início do EA e notificar o usuário em caso de falha.

int GetMe()
 Valor retornado  código de erro

Se for bem sucedido, o GetMe retorna 0 e você pode descobrir o nome de usuário bot através do método Name(). Este nome não é utilizado na operação. No entanto, será mostrado no painel para fins de informação. O endereço telegram.me/<botname> permite a utilização de uma web-versão do Messenger e servirá como um link para anunciar o seu bot. O EA que verifica o sinal em OnInit ficaria da seguinte forma:

//+------------------------------------------------------------------+
//|                                               Telegram_GetMe.mq5 |
//|                        Copyright 2014, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#include <Telegram.mqh>

input string InpToken="177791741:AAH0yB3YV7ywm80af_-AGqb7hzTR_Ud9DhQ";//Token

CCustomBot bot;
int getme_result;
//+------------------------------------------------------------------+
//|   OnInit                                                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- configuração token
   bot.Token(InpToken);
//--- checagem token
   getme_result=bot.GetMe();
//--- executando o timer
   EventSetTimer(3);
   OnTimer();
//--- feito
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|   OnDeinit                                                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Comment("");
  }
//+------------------------------------------------------------------+
//|   OnTimer                                                        |
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- mostrar mensagem de erro 
   if(getme_result!=0)
     {
      Comment("Error: ",GetErrorDescription(getme_result));
      return;
     }
//--- mostrar Nome do bot
   Comment("Bot name: ",bot.Name());

//---{ insert your code here }
  }
//+------------------------------------------------------------------+


GetUpdates

A principal função GetUpdates lê as mensagens armazenados no servidor. Ele precisa ser chamado pelo timer. Um período para atualizar o timer não deve ser ajustado abaixo de 1 segundo, a fim de evitar a sobrecarga do servidor.

int GetUpdate()
 Valor retornado  código de erro

Vamos dar uma olhada no interior desta função. Quando chamada, é realizado a leitura e análise de todas as mensagens não lidas recebidos dos usuários. Abaixo, o exemplo de uma dessas mensagens:

{ 
   "ok":true,
   "result":[ 
      { 
         "update_id":349778698,
         "message":{ 
            "message_id":2,
            "from":{ 
               "id":198289825,
               "first_name":"Andriy",
               "last_name":"Voitenko",
               "username":"avaticks"
            },
            "chat":{ 
               "id":198289825,
               "first_name":"Andriy",
               "last_name":"Voitenko",
               "username":"avaticks",
               "type":"private"
            },
            "date":1459775817,
            "text":"\/start"
         }
      }
   ]
}


Um usuário com o nome de Avaticks enviou o comando /start para o bot. O ponto é salvar essas mensagens e respondê-las no futuro. O número chat[id] é um identificador exclusivo. O mesmo usuário que se comunica com um bot através de vários dispositivos, tem diferentes identificadores do chat. Este parâmetro é adequado como uma chave única para a construção de um chat. Durante a operação, os bots vão acumular os arrays do chat e atualizar a última mensagem recebida em cada um deles. Se nós respondemos a ele, então esta mensagem será manipulada e podemos definir a flag feita a ele. O tipo de chat também é conhecido. Pode ser privado ou um grupo.

Para escrever seu próprio bot, é obrigatório herdar do CCustomBot e redeterminar a função virtual da classe ProcessMessage, que está prevista na execução da lógica de operação. Um bot completo, de acordo com a documentação do Telegram, precisa saber como responder a dois comandos: "/start" e "/help". Vamos escrever o primeiro bot que irá responder a eles.

//+------------------------------------------------------------------+
//|                                          Telegram_GetUpdates.mq5 |
//|                        Copyright 2014, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#include <Telegram.mqh>
//+------------------------------------------------------------------+
//|   CMyBot                                                         |
//+------------------------------------------------------------------+
class CMyBot: public CCustomBot
  {
public:
   void ProcessMessages(void)
     {
      for(int i=0; i<m_chats.Total(); i++)
        {
         CCustomChat *chat=m_chats.GetNodeAtIndex(i);
         //--- se a mensagem não foi processada
         if(!chat.m_new_one.done)
           {
            chat.m_new_one.done=true;
            string text=chat.m_new_one.message_text;

            //--- início
            if(text=="/start")
               SendMessage(chat.m_id,"Hello, world! I am bot. \xF680");

            //--- ajuda
            if(text=="/help")
               SendMessage(chat.m_id,"My commands list: \n/start-start chatting with me \n/help-get help");
           }
        }
     }
  };

//---
input string InpToken="177791741:AAH0yB3YV7ywm80af_-AGqb7hzTR_Ud9DhQ";//Token
//---
CMyBot bot;
int getme_result;
//+------------------------------------------------------------------+
//|   OnInit                                                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- configuração token
   bot.Token(InpToken);
//--- checagem token
   getme_result=bot.GetMe();
//--- executando o timer
   EventSetTimer(3);
   OnTimer();
//--- feito
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|   OnDeinit                                                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Comment("");
  }
//+------------------------------------------------------------------+
//|   OnTimer                                                        |
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- mostrar mensagem de erro 
   if(getme_result!=0)
     {
      Comment("Error: ",GetErrorDescription(getme_result));
      return;
     }
//--- mostrar Nome do bot
   Comment("Bot name: ",bot.Name());
//--- mensagens de leitura
   bot.GetUpdates();
//--- processando mensagem
   bot.ProcessMessages();
  }
//+------------------------------------------------------------------+


O resultado obtido é mostrado na Figura 6.


Bot com um conjunto mínimo de comandos

Fig.6. Bot com um conjunto mínimo de comandos.

Trabalhando com um teclado

Para a comunicação interativa dos usuários com os bots, os desenvolvedores trouxeram a ideia de um "teclado". Ao enviar mensagens para cada chat, um "teclado" com um conjunto pré-selecionado de chaves pode ser exibido. Ao pressionar uma tecla, um usuário envia uma mensagem com um texto indicado nele. Desta forma, a interação entre um bot e um usuário é significativamente simplificada.

A classe tem três funções para trabalhar com o teclado. A primeira função cria o objeto do teclado.

string ReplyKeyboardMarkup(const string keyboard,
                           const bool resize,
                           const bool one_time)

 keyboard
 string que define a localização das chaves
 resize  permissão para redimensionar o tamanho do teclado
 one_time  para mostrar o teclado apenas uma vez. O teclado desaparece após pressionar a tecla.
 Valor retornado A string (objeto JSON) precisa ser transmitida como o parâmetro reply_markup ao enviar uma mensagem com SendMessage

O teclado desaparece após pressionar a tecla.

string ReplyKeyboardHide()
 Valor retornado A string (objeto JSON) precisa ser transmitida como o parâmetro reply_markup ao enviar uma mensagem com SendMessage

A terceira função permite enviar um pequeno painel cujo tipo indica que um bot espera uma resposta de você na forma de texto (o teclado não é exibido).

string ForceReply()
 Valor retornado A string (objeto JSON) precisa ser transmitida como o parâmetro reply_markup ao enviar uma mensagem com SendMessage

Agora passamos a analisar como essas funções são usadas.

SendMessage

O teclado não pode ser exibido ou ocultado por si só. A ação é enviada com uma mensagem A função SendMessage envia as seguintes mensagens ao chat:

int SendMessage(const long chat_id,
                const string text,
                const string reply_markup=NULL)

 chat_id
 número do chat
 text  message text
 reply markup  keyboard (JSON object)
 Valor retornado  código de erro

O teclado é opcional no presente caso. Nós podemos enviar mensagens simples de texto dos nossos programas MQL. Na minha opinião, esta função é mais interessante que a nativa SendNotification. Em primeiro lugar, podemos enviar mensagens com mais frequência (aproximadamente uma vez por segundo). Em segundo, o formato HTML é suportado. Além disso, a capacidade de enviar Emoji é um bonus.

O Тelegram suporta um número de caracteres Emoji que pode ser visto aqui. Como você pode ver, a maioria dos códigos Emoji estão na faixa de 1F300 - 1F700. Seus bitness vão além do código de dois bytes de strings aceitáveis em MQL5. Se você remover dígitos mais altos, apenas um número de dois bytes permanece, então o intervalo obtido (F300 - F700) cai na área (E000- f8ff), que na tabela de Unicode é reservado para um determinado uso. Desta forma, nada nos impede de usar 2 bits mais baixos para o envio de Emoji. A mensagem string com um Emoji clássico, com um código de U+1F642 é o seguinte:

string text="Have a nice day.\xF642";//mensagem de texto com o Emoji U+1F642

Na verdade, isso também serve para as chaves que são um texto. Nada nos impede de usar Emoji em chaves. Vamos escrever um exemplo para a exibição de três teclas com o handler de eventos.

//+------------------------------------------------------------------+
//|                                         Telegram_SendMessage.mq5 |
//|                        Copyright 2014, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#include <Telegram.mqh>
//+------------------------------------------------------------------+
//|   CMyBot                                                         |
//+------------------------------------------------------------------+
class CMyBot: public CCustomBot
  {
private:
   string            m_button[3];
public:
   //+------------------------------------------------------------------+
   void CMyBot::CMyBot(void)
     {
      m_button[0]="Button #1";
      m_button[1]="Button #2";
      m_button[2]="Button #3";
     }

   //+------------------------------------------------------------------+
   string GetKeyboard()
     {
      return("[[\""+m_button[0]+"\"],[\""+m_button[1]+"\"],[\""+m_button[2]+"\"]]");
     }

   //+------------------------------------------------------------------+
   void ProcessMessages(void)
     {
      for(int i=0;i<m_chats.Total();i++)
        {
         CCustomChat *chat=m_chats.GetNodeAtIndex(i);
         if(!chat.m_new_one.done)
           {
            chat.m_new_one.done=true;
            string text=chat.m_new_one.message_text;

            //--- Comandos de início ou de ajuda
            if(text=="/start" || text=="/help")
               bot.SendMessage(chat.m_id,"Click on the buttons",bot.ReplyKeyboardMarkup(GetKeyboard(),false,false));

            //--- evento on click
            int total=ArraySize(m_button);
            for(int k=0;k<total;k++)
              {
               if(text==m_button[k])
                  bot.SendMessage(chat.m_id,m_button[k],bot.ReplyKeyboardMarkup(GetKeyboard(),false,false));
              }
           }
        }
     }
  };

input string InpToken="177791741:AAH0yB3YV7ywm80af_-AGqb7hzTR_Ud9DhQ";//Token

CMyBot bot;
int getme_result;
//+------------------------------------------------------------------+
//|   OnInit                                                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- configuração token
   bot.Token(InpToken);
//--- checagem token
   getme_result=bot.GetMe();
//--- executando o timer
   EventSetTimer(1);
   OnTimer();
//--- feito
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|   OnDeinit                                                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Comment("");
  }
//+------------------------------------------------------------------+
//|   OnTimer                                                        |
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- mostrar mensagem de erro 
   if(getme_result!=0)
     {
      Comment("Error: ",GetErrorDescription(getme_result));
      return;
     }
//--- mostrar Nome do bot
   Comment("Bot name: ",bot.Name());
//--- mensagens de leitura
   bot.GetUpdates();
//--- processando mensagem
   bot.ProcessMessages();
  }
//+------------------------------------------------------------------+


Como resultado, receberemos uma mensagem com o teclado como mostrado na Fig. 7.

Mensagem com teclado

Fig.7. Mensagem com teclado.

Agora, vamos tentar implementar o análogo de controles RadioButton e CheckBox. Por exemplo, temos que escolher uma das três opções e também ativar ou desativar uma determinada opção. Mudanças afetarão apenas nossa classe única, portanto o código restante do exemplo anterior do EA permanecerá o mesmo.
//+------------------------------------------------------------------+
//|                                         Telegram_SendMessage.mq5 |
//|                        Copyright 2014, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#include <Telegram.mqh>

#define MUTE_TEXT       "Mute"
#define UNMUTE_TEXT     "Unmute"

#define LOCK_TEXT       "Lock"
#define UNLOCK_TEXT     "Unlock"

#define RADIO_SELECT    "\xF518"
#define RADIO_EMPTY     "\x26AA"

#define MUTE_CODE       "\xF515"
#define UNMUTE_CODE     "\xF514"

#define LOCK_CODE       "\xF512"
#define UNLOCK_CODE     "\xF513"
//+------------------------------------------------------------------+
//|   CMyBot                                                         |
//+------------------------------------------------------------------+
class CMyBot: public CCustomBot
  {
private:
   string            m_radio_button[3];
   int               m_radio_index;
   bool              m_lock_state;
   bool              m_mute_state;

public:
   //+------------------------------------------------------------------+
   void CMyBot::CMyBot(void)
     {
      m_radio_button[0]="Radio Button #1";
      m_radio_button[1]="Radio Button #2";
      m_radio_button[2]="Radio Button #3";
      m_radio_index=0;
      m_lock_state=false;
      m_mute_state=true;
     }

   //+------------------------------------------------------------------+
   string GetKeyboard()
     {
      //---
      string radio_code[3]={RADIO_EMPTY,RADIO_EMPTY,RADIO_EMPTY};
      if(m_radio_index>=0 && m_radio_index<=2)
         radio_code[m_radio_index]=RADIO_SELECT;
      //---
      string mute_text=UNMUTE_TEXT;
      string mute_code=UNMUTE_CODE;
      if(m_mute_state)
        {
         mute_text=MUTE_TEXT;
         mute_code=MUTE_CODE;
        }
      //---
      string lock_text=UNLOCK_TEXT;
      string lock_code=UNLOCK_CODE;
      if(m_lock_state)
        {
         lock_text=LOCK_TEXT;
         lock_code=LOCK_CODE;
        }
      //---
      //Print(m_lock.GetKey());
      return(StringFormat("[[\"%s %s\"],[\"%s %s\"],[\"%s %s\"],[\"%s %s\",\"%s %s\"]]",
             radio_code[0],m_radio_button[0],
             radio_code[1],m_radio_button[1],
             radio_code[2],m_radio_button[2],
             lock_code,lock_text,
             mute_code,mute_text));
     }

   //+------------------------------------------------------------------+
   void ProcessMessages(void)
     {
      for(int i=0;i<m_chats.Total();i++)
        {
         CCustomChat *chat=m_chats.GetNodeAtIndex(i);
         if(!chat.m_new_one.done)
           {
            chat.m_new_one.done=true;
            string text=chat.m_new_one.message_text;

            //--- início
            if(text=="/start" || text=="/help")
              {
               bot.SendMessage(chat.m_id,"Click on the buttons",bot.ReplyKeyboardMarkup(GetKeyboard(),false,false));
              }

            //--- Clique num botão de opção
            int total=ArraySize(m_radio_button);
            for(int k=0;k<total;k++)
              {
               if(text==RADIO_EMPTY+" "+m_radio_button[k])
                 {
                  m_radio_index=k;
                  bot.SendMessage(chat.m_id,m_radio_button[k],bot.ReplyKeyboardMarkup(GetKeyboard(),false,false));
                 }
              }

            //--- Desbloquear
            if(text==LOCK_CODE+" "+LOCK_TEXT)
              {
               m_lock_state=false;
               bot.SendMessage(chat.m_id,UNLOCK_TEXT,bot.ReplyKeyboardMarkup(GetKeyboard(),false,false));
              }

            //--- Bloquear
            if(text==UNLOCK_CODE+" "+UNLOCK_TEXT)
              {
               m_lock_state=true;
               bot.SendMessage(chat.m_id,LOCK_TEXT,bot.ReplyKeyboardMarkup(GetKeyboard(),false,false));
              }

            //--- Ativar o som
            if(text==MUTE_CODE+" "+MUTE_TEXT)
              {
               m_mute_state=false;
               bot.SendMessage(chat.m_id,UNMUTE_TEXT,bot.ReplyKeyboardMarkup(GetKeyboard(),false,false));
              }

            //--- Mudo
            if(text==UNMUTE_CODE+" "+UNMUTE_TEXT)
              {
               m_mute_state=true;
               bot.SendMessage(chat.m_id,MUTE_TEXT,bot.ReplyKeyboardMarkup(GetKeyboard(),false,false));
              }
           }
        }
     }
  };

Como resultado obtemos a seguinte janela (Figura 8).

Fig.8. Controles RadioButton e CheckBox

Podemos ver aqui que o Emoji usado fornece configurações com uma melhor visibilidade. Além destes controles, poderemos implementar facilmente um menu hierárquico com navegação em cada sub-menu. Tudo vai depender da funcionalidade que você vai usar e decidir implementar.

No caso de decidirmos publicar mensagens no canal, há uma segunda opção - SendMessage.

int SendMessage(const string channel_name,
                const string text)

 channel_name
 channel name as @name
 text  message text. Tags HTML são suportadas.
 Valor retornado  código de erro

O resultado desta função é apresentada abaixo na Figura 9.

Trabalhando com multimídia

Bots podem trocar fotos, arquivos de áudio e vídeo, mensagens de voz, etiquetas e coordenadas de localização. Até o momento em que escrevo este artigo, foi lançado o Bot API 2.0 com uma função de troca de dados de contatos e convites. De toda a lista fornecida, apenas a opção de trocar fotos possui relevância para nós.

SendPhoto

A classe tem implementado a oportunidade de enviar fotos com duas formas de aplicação.

int SendPhoto(const long   chat_id,
              const string local_path,
              string       &photo_id,
              const string caption=NULL,
              const bool   common_flag=false,
              const int    timeout=10000)

 chat_id  número do chat
 local_path  local path to the folder in <data folder>\MQL5\Files
 photo_id  identificador da foto enviada o servidor
 caption  texto da assinatura abaixo da foto
 common_flag  flag de localização do arquivo na pasta comum de todos os terminais do cliente: \Terminal\Common\Files
 timeout  tempo limite de operação em milissegundos

Exemplo do código que envia foto:

CCustomBot bot;

string token = "208375865:AAFnuOjlZ3Wsdan6PAjeqqUtBybe0Di1or8";

bot.Token(token);

string photo_id;
int result=bot.SendPhoto(198289825,"EURUSD1.gif",photo_id,"screenshot");
if(result==0)
   Print("Photo ID: ",photo_id);
else
   Print("Error: ",GetErrorDescription(result));


Eu acredito que você terá casos onde será necessário enviar uma foto para vários usuários ou enviar a mesma foto diversas vezes. Neste caso, é mais racional fazer apenas uma vez o upload de uma foto, aplicando o identificador photo_id juntamente com a segunda opção da função SendPhoto, no reenvio de uma foto:

int SendPhoto(const long chat_id,
              const string photo_id,
              const string caption=NULL)

 chat_id  número do chat
 photo_id  identificador da foto enviada o servidor
 caption  texto da assinatura abaixo da foto

SendChartAction

Imagine que você lida com a resposta de um usuário e está quase pronto para fornecer-lhe um resultado. Como pode demorar alguns segundos para criar uma resposta, seria educado notificar usuário que já está sendo processada. É para isso que os eventos são usados. Por exemplo, enquanto a imagem do gráfico é formada para ser enviada a um usuário, você pode enviar o evento "envio de fotos" no mesmo período. Isto é conseguido através do SendChatAction.

int SendChatAction(const long chat_id,
                   const ENUM_CHAT_ACTION actiona)

 chat_id  número do chat
 ação  identificador de evento
Todas as funções descritas anteriormente foram implementados em três bots de demonstração que vamos falar mais a frente.

Exemplos de bots

O primeiro bot Telegram_Bot_EA permite a obtenção de informações sobre o saldo da conta, cotações e imagens do gráfico. O funcionamento é mostrado neste vídeo.


O segundo bot Telegram_Search_EA envia resultados de pesquisa ao site MQL5.com. Veja o vídeo abaixo e saiba como ele realmente funciona.


O terceiro bot Telegram_Signal_EA publica sinais do indicador padrão MACD no canal. Eu acho que será fácil mudar o MACD pelo seu indicador favorito e usar esse código nos seus propósitos.

//+------------------------------------------------------------------+
//|                                        Telegram_Signal_EA_v1.mq4 |
//|                        Copyright 2014, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

//+------------------------------------------------------------------+
//|   inclusão                                                       |
//+------------------------------------------------------------------+
#include <Telegram.mqh>

//--- Parâmetros de entrada
input string InpChannelName="@forexsignalchannel";//Nome do Canal
input string InpToken="177791741:AAH0yB3YV7ywm80af_-AGqb7hzTR_Ud9DhQ";//Token

//--- Variáveis globais
CCustomBot bot;
int macd_handle;
datetime time_signal=0;
//+------------------------------------------------------------------+
//| Função de inicialização do Expert                                |
//+------------------------------------------------------------------+
int OnInit()
  {
   time_signal=0;

//--- configuração token
   bot.Token(InpToken);

//--- obter um handle de indicador
   macd_handle=iMACD(NULL,0,12,26,9,PRICE_CLOSE);
   if(macd_handle==INVALID_HANDLE)
      return(INIT_FAILED);

//--- feito
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função tick do Expert                                            |
//+------------------------------------------------------------------+
void OnTick()
  {

//--- obtenção do tempo
   datetime time[1];
   if(CopyTime(NULL,0,0,1,time)!=1)
      return;

//--- Verifica o sinal em cada barra
   if(time_signal!=time[0])
     {
      //--- primeiro cálculo
      if(time_signal==0)
        {
         time_signal=time[0];
         return;
        }

      double macd[2]={0.0};
      double signal[2]={0.0};

      if(CopyBuffer(macd_handle,0,0,2,macd)!=2)
         return;
      if(CopyBuffer(macd_handle,1,0,2,signal)!=2)
         return;

      time_signal=time[0];

      //--- Enviar sinal BUY
      if(macd[1]>signal[1] && 
         macd[0]<=signal[0])
        {
         string msg=StringFormat("Name: MACD Signal\nSymbol: %s\nTimeframe: %s\nType: Buy\nPrice: %s\nTime: %s",
                                 _Symbol,
                                 StringSubstr(EnumToString(_Period),7),
                                 DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits),
                                 TimeToString(time[0]));
         int res=bot.SendMessage(InpChannelName,msg);
         if(res!=0)
            Print("Error: ",GetErrorDescription(res));
        }

      //--- Enviar sinal SELL
      if(macd[1]<signal[1] && 
         macd[0]>=signal[0])
        {
         string msg=StringFormat("Name: MACD Signal\nSymbol: %s\nTimeframe: %s\nType: Sell\nPrice: %s\nTime: %s",
                                 _Symbol,
                                 StringSubstr(EnumToString(_Period),7),
                                 DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits),
                                 TimeToString(time[0]));
         int res=bot.SendMessage(InpChannelName,msg);
         if(res!=0)
            Print("Error: ",GetErrorDescription(res));
        }
     }
  }
//+------------------------------------------------------------------+


Como resultado, você receberá mensagens mostradas na Fig. 9.

Sinais do indicador MACD

Fig.9. Sinais do indicador MACD.

Conclusão

Aqueles que desejam se conectar analiticamente à base Yandex.AppMetrica para seus bots, podem usar a fonte Botan . A idéia do serviço é enviar mensagens recebidas de usuários e solicitar indicadores como segmentação, acompanhamento, análise de corte, etc. Não há necessidade de sair do Messenger, porque as estatísticas serão enviadas pelo bot especial na forma de gráficos e um relatório mais detalhado estará disponível no site.

Espero que este artigo tenha inspirado você a usar o Telegram na negociação. Não era o meu objetivo cobrir todos os detalhes, porque eles já foram fornecidos na documentação para Bot API. Os códigos anexados a este artigo são adaptados à negociação em ambas as plataformas - MetaTrader 4 e MetaTrader 5.

Traduzido do russo pela MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/2355

Arquivos anexados |
telegram.zip (31.56 KB)
Últimos Comentários | Ir para discussão (4)
Joao Luiz Sa Marchioro
Joao Luiz Sa Marchioro | 8 jun 2018 em 06:19
Your post on how to use Telegram in MQL5 is incredible. I will study and add to my EAs, so it is easy to know how they are working.
You are to be congratulated.
willhenrique
willhenrique | 27 jul 2018 em 03:37

Andrey, I got this problem to use Telegram. The error message is: "Error: Unknown error 1001".

Could you help me?

Bilal Said
Bilal Said | 20 nov 2018 em 21:19
Thank you ! this article is awesome and helpful !
Paladino Vita
Paladino Vita | 5 mar 2019 em 12:29

Perfect. Thanks. 

I used to send me information about many accounts. 

Expert Advisor Universal: Um Trailing Stop Customizado (Parte 6) Expert Advisor Universal: Um Trailing Stop Customizado (Parte 6)

A sexta parte do artigo sobre o Expert Advisor universal descreve o uso do recurso "Trailing Stop". O artigo irá guiá-lo através de como criar um módulo "Trailing Stop" personalizado com regras unificadas, bem como adicioná-lo ao motor de negociação para gerir automaticamente as posições.

Interfaces gráficas I: Biblioteca de Testes em Programas de Diferentes Tipos e no Terminal MetaTrader 4 (Capítulo 5) Interfaces gráficas I: Biblioteca de Testes em Programas de Diferentes Tipos e no Terminal MetaTrader 4 (Capítulo 5)

No capítulo anterior da primeira parte da série sobre interfaces gráficas, a classe de formulário foi enriquecida por métodos que permitiram gerir o formulário através dos cliques em seus controles. Neste artigo, nós vamos testar nosso trabalho em diferentes tipos de programa MQL, como indicadores e scripts. Já que a biblioteca foi concebida para ser multi-plataforma para que ela pudesse ser utilizada em todas as plataformas MetaTrader, nós também vamos testá-la no MetaTrader 4.

Interfaces Gráficas II: O Elemento de Menu (Capítulo 1) Interfaces Gráficas II: O Elemento de Menu (Capítulo 1)

Na segunda parte da série, nós vamos mostrar em detalhes o desenvolvimento de tais elementos de interface como o menu principal e o menu de contexto. Nós também vamos mencionar os elementos de desenho e criar uma classe especial para ele. Nós vamos discutir detalhadamente tais questões como a gestão de eventos do programa, incluindo aquelas que são personalizadas.

Interfaces Gráficas II: Os Elementos Linha de Separação e o Menu de Contexto (Capítulo 2) Interfaces Gráficas II: Os Elementos Linha de Separação e o Menu de Contexto (Capítulo 2)

Neste artigo, nós vamos criar o elemento linha de separação. Será possível usá-lo não só como um elemento de interface independentes, mas também como uma parte de diversos outros elementos. Depois disso, nós teremos todos os recursos necessários para o desenvolvimento da classe do menu de contexto, que também serão considerados neste artigo em detalhe. Acrescentando, nós vamos apresentar todos os incrementos necessários à classe, que é a base para armazenar os ponteiros para todos os elementos da interface gráfica da aplicação.