599 lines
17 KiB
MQL5
599 lines
17 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Telegram.mqh |
|
|
//| Copyright 2014, MetaQuotes Software Corp. |
|
|
//| http://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2014, MetaQuotes Software Corp."
|
|
#property link "http://www.mql5.com"
|
|
#property strict
|
|
|
|
#ifndef TELEGRAMBYLEO_TELEGRAM_MQH
|
|
#define TELEGRAMBYLEO_TELEGRAM_MQH
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Include |
|
|
//+------------------------------------------------------------------+
|
|
#include <Arrays\List.mqh>
|
|
#include <Arrays\ArrayLong.mqh>
|
|
#include "Common.mqh"
|
|
#include "..\\JsonParserByLeo\\Src\\JsonNode.mqh"
|
|
#include <TSN\\MQLArticles\\Utils\\FA\\Logger.mqh>
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| CCustomChat |
|
|
//+------------------------------------------------------------------+
|
|
class CCustomChat : public CObject
|
|
{
|
|
public:
|
|
long m_id;
|
|
CCustomMessage m_last;
|
|
CCustomMessage m_new_one;
|
|
int m_state;
|
|
datetime m_time;
|
|
};
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| ChatActionToString |
|
|
//+------------------------------------------------------------------+
|
|
string ChatActionToString(const ENUM_CHAT_ACTION _action)
|
|
{
|
|
string result = EnumToString(_action);
|
|
result = StringSubstr(result, 7);
|
|
StringToLower(result);
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
struct CCustomMessage
|
|
{
|
|
//---
|
|
string chat_first_name;
|
|
string chat_last_name;
|
|
string chat_username;
|
|
string from_first_name;
|
|
string from_last_name;
|
|
string from_username;
|
|
string message_text;
|
|
string chat_type;
|
|
|
|
//---
|
|
long update_id;
|
|
long message_id;
|
|
long from_id;
|
|
long chat_id;
|
|
datetime message_date;
|
|
|
|
//---
|
|
bool done;
|
|
|
|
|
|
//---
|
|
CCustomMessage()
|
|
{
|
|
done = false;
|
|
update_id = 0;
|
|
message_id = 0;
|
|
from_id = 0;
|
|
from_first_name = NULL;
|
|
from_last_name = NULL;
|
|
from_username = NULL;
|
|
chat_id = 0;
|
|
chat_first_name = NULL;
|
|
chat_last_name = NULL;
|
|
chat_username = NULL;
|
|
chat_type = NULL;
|
|
message_date = 0;
|
|
message_text = NULL;
|
|
from_id = 0;
|
|
from_first_name = NULL;
|
|
from_last_name = NULL;
|
|
from_username = NULL;
|
|
chat_id = 0;
|
|
chat_first_name = NULL;
|
|
chat_last_name = NULL;
|
|
chat_username = NULL;
|
|
chat_type = NULL;
|
|
message_date = 0;
|
|
message_text = NULL;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Clase para poder enviar mensajes |
|
|
//+------------------------------------------------------------------+
|
|
class CTlgBotMessageSender : public CCustomBot
|
|
{
|
|
protected:
|
|
long m_chat_id; // ChatId
|
|
TSN::CJsonParser m_json;
|
|
|
|
public:
|
|
|
|
//--- Basic setters \ getters
|
|
// ChatId
|
|
void ChatId(const long _chat_id) { this.m_chat_id = _chat_id; }
|
|
inline long ChatId() const { return m_chat_id; }
|
|
|
|
|
|
//--- Enviar a telegram
|
|
// Enviar mensajes
|
|
int SendMessage(const string _text, const string _reply_markup = NULL, const ENUM_TELEGRAM_PARSE_MODE _parse_mode = WRONG_VALUE, const bool _silently = false);
|
|
|
|
// Enviar fotos
|
|
int SendPhoto(string &_photo_id, const string _local_path, const string _caption = NULL, const bool _common_flag = false, const int _timeout = 10000);
|
|
|
|
// Enviar una foto por id
|
|
int SendPhoto(const string _photo_id, const string _caption = NULL);
|
|
|
|
// Chat action
|
|
int SendChatAction(const ENUM_CHAT_ACTION _action);
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
int CTlgBotMessageSender::SendMessage(
|
|
const string _text,
|
|
const string _reply_markup = NULL,
|
|
const ENUM_TELEGRAM_PARSE_MODE _parse_mode = WRONG_VALUE,
|
|
const bool _silently = false)
|
|
{
|
|
//--- check token
|
|
if(m_token == NULL)
|
|
return(ERR_TOKEN_ISEMPTY);
|
|
|
|
//---
|
|
//string out;
|
|
//---
|
|
string params = StringFormat("chat_id=%lld&text=%s", m_chat_id, UrlEncode(_text));
|
|
if(_reply_markup != NULL)
|
|
params += "&reply_markup=" + _reply_markup;
|
|
if(_parse_mode != WRONG_VALUE)
|
|
params += "&parse_mode=" + g_telegram_parse_mode_str[_parse_mode];
|
|
if(_silently)
|
|
params += "&disable_notification=true";
|
|
|
|
//---
|
|
return (PostRequest(StringFormat("%s/bot%s/sendMessage", TELEGRAM_BASE_URL, m_token),
|
|
params,
|
|
WEB_TIMEOUT));
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
int CTlgBotMessageSender::SendPhoto(
|
|
const string _photo_id,
|
|
const string _caption = NULL)
|
|
{
|
|
if(m_token == NULL)
|
|
return(ERR_TOKEN_ISEMPTY);
|
|
|
|
//---
|
|
string url = StringFormat("%s/bot%s/sendPhoto", TELEGRAM_BASE_URL, m_token);
|
|
string params = StringFormat("chat_id=%lld&photo=%s", m_chat_id, _photo_id);
|
|
if(_caption != NULL)
|
|
params += "&caption=" + UrlEncode(_caption);
|
|
|
|
//---
|
|
const int res = PostRequest(m_json.m_raw, m_json.m_offset, url, params, WEB_TIMEOUT);
|
|
if(res != 0)
|
|
{
|
|
//--- parse result
|
|
m_json.CalcLen(); // Del json dado calculamos el len
|
|
m_json.CorrectPadding();
|
|
bool done = m_json.Parse();
|
|
if(!done)
|
|
return(ERR_JSON_PARSING);
|
|
|
|
//---
|
|
TSN::CJsonNode root = m_json.GetRoot();
|
|
|
|
//--- get error description
|
|
bool ok = root["ok"].ToBool(false); // por defecto false
|
|
long err_code = root["error_code"].ToInt(0);
|
|
string err_desc = root["description"].ToString();
|
|
|
|
//---
|
|
LogError(StringFormat("Ok = %d, err code = %d, err desc = %s", ok, err_code, err_desc), FUNCION_ACTUAL);
|
|
}
|
|
//--- done
|
|
return(res);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
int CTlgBotMessageSender::SendPhoto(string &_photo_id,
|
|
const string _local_path,
|
|
const string _caption = NULL,
|
|
const bool _common_flag = false,
|
|
const int _timeout = 10000)
|
|
{
|
|
if(m_token == NULL)
|
|
return(ERR_TOKEN_ISEMPTY);
|
|
|
|
ResetLastError();
|
|
//--- copy file to memory buffer
|
|
if(!FileIsExist(_local_path, _common_flag))
|
|
return(ERR_FILE_NOT_EXIST);
|
|
|
|
//---
|
|
int flags = FILE_READ | FILE_BIN | FILE_SHARE_WRITE | FILE_SHARE_READ;
|
|
if(_common_flag)
|
|
flags |= FILE_COMMON;
|
|
|
|
//---
|
|
int file = FileOpen(_local_path, flags);
|
|
if(file < 0)
|
|
return(_LastError);
|
|
|
|
//---
|
|
int file_size = (int)FileSize(file);
|
|
uchar photo[];
|
|
ArrayResize(photo, file_size);
|
|
FileReadArray(file, photo, 0, file_size);
|
|
FileClose(file);
|
|
|
|
//--- create boundary: (data -> base64 -> 1024 bytes -> md5)
|
|
uchar base64[];
|
|
uchar key[];
|
|
CryptEncode(CRYPT_BASE64, photo, key, base64);
|
|
//---
|
|
uchar temp[1024] = {0};
|
|
ArrayCopy(temp, base64, 0, 0, 1024);
|
|
//---
|
|
uchar md5[];
|
|
CryptEncode(CRYPT_HASH_MD5, temp, key, md5);
|
|
//---
|
|
string hash = NULL;
|
|
int total = ArraySize(md5);
|
|
for(int i = 0; i < total; i++)
|
|
hash += StringFormat("%02X", md5[i]);
|
|
hash = StringSubstr(hash, 0, 16);
|
|
|
|
//--- WebRequest
|
|
string result_headers;
|
|
|
|
string url = StringFormat("%s/bot%s/sendPhoto", TELEGRAM_BASE_URL, m_token);
|
|
|
|
//--- 1
|
|
char data[];
|
|
|
|
//--- add chart_id
|
|
ArrayAdd(data, "\r\n");
|
|
ArrayAdd(data, "--" + hash + "\r\n");
|
|
ArrayAdd(data, "Content-Disposition: form-data; name=\"chat_id\"\r\n");
|
|
ArrayAdd(data, "\r\n");
|
|
ArrayAdd(data, IntegerToString(m_chat_id));
|
|
ArrayAdd(data, "\r\n");
|
|
|
|
if(StringLen(_caption) > 0)
|
|
{
|
|
ArrayAdd(data, "--" + hash + "\r\n");
|
|
ArrayAdd(data, "Content-Disposition: form-data; name=\"caption\"\r\n");
|
|
ArrayAdd(data, "\r\n");
|
|
ArrayAdd(data, _caption);
|
|
ArrayAdd(data, "\r\n");
|
|
}
|
|
|
|
ArrayAdd(data, "--" + hash + "\r\n");
|
|
ArrayAdd(data, "Content-Disposition: form-data; name=\"photo\"; filename=\"lampash.gif\"\r\n");
|
|
ArrayAdd(data, "\r\n");
|
|
|
|
// Convert uchar photo[] to char[] before adding
|
|
char photo_char[];
|
|
ArrayResize(photo_char, file_size);
|
|
for(int i = 0; i < file_size; i++)
|
|
{
|
|
photo_char[i] = (char)photo[i];
|
|
}
|
|
ArrayAdd(data, photo_char);
|
|
|
|
ArrayAdd(data, "\r\n");
|
|
ArrayAdd(data, "--" + hash + "--\r\n");
|
|
|
|
// SaveToFile("debug.txt",data);
|
|
|
|
//---
|
|
const string headers = "Content-Type: multipart/form-data; boundary=" + hash + "\r\n";
|
|
const int res = WebRequest("POST", url, headers, _timeout, data, m_json.m_raw, result_headers);
|
|
if(res == 200) //OK
|
|
{
|
|
//--- delete BOM
|
|
m_json.m_offset = 0;
|
|
int size = ArraySize(m_json.m_raw);
|
|
for(int i = 0; i < fmin(size, 8); i++)
|
|
{
|
|
// Cast to uchar for proper BOM comparison
|
|
const uchar byte_val = (uchar)m_json.m_raw[i];
|
|
if(byte_val == 0xef || byte_val == 0xbb || byte_val == 0xbf)
|
|
m_json.m_offset = i + 1;
|
|
else
|
|
break;
|
|
}
|
|
|
|
//--- parse result
|
|
m_json.CalcLen();
|
|
m_json.CorrectPadding();
|
|
const bool done = m_json.Parse();
|
|
if(!done)
|
|
return(ERR_JSON_PARSING);
|
|
TSN::CJsonNode root = m_json.GetRoot();
|
|
|
|
//--- get error description
|
|
const bool ok = root["ok"].ToBool(false);
|
|
if(!ok)
|
|
return(ERR_JSON_NOT_OK);
|
|
|
|
//---
|
|
CJsonIteratorArray it = root["result"]["photo"].BeginArr();
|
|
|
|
//---
|
|
while(it.IsValid())
|
|
{
|
|
TSN::CJsonNode image = it.Val();
|
|
const long image_size = image["file_size"].ToInt(0);
|
|
if(image_size <= file_size)
|
|
_photo_id = image["file_id"].ToString();
|
|
|
|
it.Next();
|
|
}
|
|
|
|
//---
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
if(res == -1)
|
|
{
|
|
return(_LastError);
|
|
}
|
|
else
|
|
{
|
|
if(res >= 100 && res <= 511)
|
|
{
|
|
LogError(CharArrayToString(m_json.m_raw, 0, WHOLE_ARRAY, CP_UTF8), FUNCION_ACTUAL);
|
|
return(ERR_HTTP_ERROR_FIRST + res);
|
|
}
|
|
return(res);
|
|
}
|
|
}
|
|
//---
|
|
return(0);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
int CTlgBotMessageSender::SendChatAction(const ENUM_CHAT_ACTION _action)
|
|
{
|
|
if(m_token == NULL)
|
|
return(ERR_TOKEN_ISEMPTY);
|
|
// string out;
|
|
string url = StringFormat("%s/bot%s/sendChatAction", TELEGRAM_BASE_URL, m_token);
|
|
string params = StringFormat("chat_id=%lld&action=%s", m_chat_id, ChatActionToString(_action));
|
|
int res = PostRequest(url, params, WEB_TIMEOUT);
|
|
return(res);
|
|
}
|
|
|
|
/*
|
|
"<u>underlined</u>\n"
|
|
"<b>bold</b>\n"
|
|
"<i>italic</i>\n"
|
|
"<s>strikethrough</s>\n"
|
|
"<a href=\"http://www.example.com/\">URL</a>\n"
|
|
"<code>inline fixed-width code</code>\n"
|
|
"<pre>pre-formatted monospace</pre></font>\n"
|
|
"<pre><code class=\"language-MQL5\">pre-formatted fixed-width code block written in the MQL5 programming language</code></pre>\n"
|
|
"<blockquote>Block quotation started\nBlock quotation continued\nThe last line of the block quotation</blockquote>"
|
|
"<tg-spoiler>spoiler</tg-spoiler>\n"
|
|
*/
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Clase de bot de telegram full (obtiene mensajes) |
|
|
//+------------------------------------------------------------------+
|
|
class CTlgFullBot : public CTlgBotMessageSender
|
|
{
|
|
protected:
|
|
CList m_chats;
|
|
string m_name;
|
|
long m_update_id;
|
|
CArrayLong m_chats_id_permitidos;
|
|
bool m_first_remove;
|
|
|
|
public:
|
|
CTlgFullBot(void);
|
|
~CTlgFullBot(void) {}
|
|
|
|
|
|
//---
|
|
__forceinline int ChatsTotal() const { return(m_chats.Total()); }
|
|
__forceinline string Name() const { return(m_name); }
|
|
__forceinline CArrayLong* PermitedChats() { return &m_chats_id_permitidos; }
|
|
|
|
//---
|
|
int GetMe();
|
|
int GetUpdates();
|
|
|
|
//---
|
|
virtual void ProcessMessages(void) {};
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
CTlgFullBot::CTlgFullBot(void)
|
|
{
|
|
m_name = NULL;
|
|
m_update_id = 0;
|
|
m_first_remove = true;
|
|
m_chats.Clear();
|
|
m_chats_id_permitidos.Clear();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int CTlgFullBot::GetMe()
|
|
{
|
|
if(m_token == NULL)
|
|
return(ERR_TOKEN_ISEMPTY);
|
|
//---
|
|
string url = StringFormat("%s/bot%s/getMe", TELEGRAM_BASE_URL, m_token);
|
|
string params = "";
|
|
int res = PostRequest(m_json.m_raw, m_json.m_offset, url, params, WEB_TIMEOUT);
|
|
if(res == 0)
|
|
{
|
|
m_json.CalcLen();
|
|
m_json.CorrectPadding();
|
|
if(!m_json.Parse())
|
|
return(ERR_JSON_PARSING);
|
|
|
|
//---
|
|
TSN::CJsonNode root = m_json.GetRoot();
|
|
const bool ok = root["ok"].ToBool(false);
|
|
if(!ok)
|
|
{
|
|
long err_code = root["error_code"].ToInt(0);
|
|
string err_desc = root["description"].ToString();
|
|
LogError(StringFormat("Ok = %d, err code = %d, err desc = %s", ok, err_code, err_desc), FUNCION_ACTUAL);
|
|
return(ERR_JSON_NOT_OK);
|
|
}
|
|
|
|
//---
|
|
if(m_name == NULL)
|
|
m_name = root["result"]["username"].ToString();
|
|
}
|
|
//---
|
|
return(res);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
int CTlgFullBot::GetUpdates()
|
|
{
|
|
if(m_token == NULL)
|
|
return(ERR_TOKEN_ISEMPTY);
|
|
|
|
//---
|
|
string url = StringFormat("%s/bot%s/getUpdates", TELEGRAM_BASE_URL, m_token);
|
|
string params = StringFormat("offset=%d", m_update_id);
|
|
|
|
//---
|
|
const int res = PostRequest(m_json.m_raw, m_json.m_offset, url, params, WEB_TIMEOUT);
|
|
if(res == 0)
|
|
{
|
|
//---
|
|
m_json.CalcLen();
|
|
m_json.CorrectPadding();
|
|
if(!m_json.Parse())
|
|
return(ERR_JSON_PARSING);
|
|
|
|
//---
|
|
TSN::CJsonNode root = m_json.GetRoot();
|
|
const bool ok = root["ok"].ToBool(false);
|
|
if(!ok)
|
|
{
|
|
long err_code = root["error_code"].ToInt(0);
|
|
string err_desc = root["description"].ToString();
|
|
LogError(StringFormat("Ok = %d, err code = %d, err desc = %s", ok, err_code, err_desc), FUNCION_ACTUAL);
|
|
return(ERR_JSON_NOT_OK);
|
|
}
|
|
|
|
//---
|
|
CCustomMessage msg;
|
|
|
|
//---
|
|
//Print("num: " , root["result"].Size());
|
|
CJsonIteratorArray it = root["result"].BeginArr();
|
|
while(it.IsValid())
|
|
{
|
|
TSN::CJsonNode item = it.Val();
|
|
|
|
//---
|
|
m_update_id = item["update_id"].ToInt(0) + 1;
|
|
if(m_first_remove)
|
|
{
|
|
it.Next();
|
|
continue;
|
|
}
|
|
|
|
//--- Chat
|
|
const long chat_id = item["message"]["chat"]["id"].ToInt(0);
|
|
if(m_chats_id_permitidos.SearchLinear(chat_id) < 0)
|
|
{
|
|
// Print("INvalid chat");
|
|
// Print(item.ComplexToString());
|
|
it.Next();
|
|
continue;
|
|
}
|
|
// Print("it");
|
|
|
|
//---
|
|
msg.chat_id = chat_id;
|
|
msg.chat_first_name = item["message"]["chat"]["first_name"].ToString();
|
|
msg.chat_last_name = item["message"]["chat"]["last_name"].ToString();
|
|
msg.chat_username = item["message"]["chat"]["username"].ToString();
|
|
msg.chat_type = item["message"]["chat"]["type"].ToString();
|
|
|
|
//--- Update id
|
|
msg.update_id = m_update_id - 1;
|
|
|
|
//--- Message
|
|
msg.message_id = item["message"]["message_id"].ToInt(0);
|
|
msg.message_date = (datetime)item["message"]["date"].ToInt(0);
|
|
msg.message_text = item["message"]["text"].ToString();
|
|
|
|
//--- From
|
|
msg.from_id = item["message"]["from"]["id"].ToInt(0);
|
|
msg.from_first_name = item["message"]["from"]["first_name"].ToString();
|
|
msg.from_last_name = item["message"]["from"]["last_name"].ToString();
|
|
msg.from_username = item["message"]["from"]["username"].ToString();
|
|
|
|
//--- find the chat
|
|
int index = -1;
|
|
for(int j = 0; j < m_chats.Total(); j++)
|
|
{
|
|
CCustomChat *chat = m_chats.GetNodeAtIndex(j);
|
|
if(chat.m_id == msg.chat_id)
|
|
{
|
|
index = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Print("sasas");
|
|
//--- add new one to the chat list
|
|
if(index == -1)
|
|
{
|
|
m_chats.Add(new CCustomChat());
|
|
CCustomChat *chat = m_chats.GetLastNode();
|
|
chat.m_id = msg.chat_id;
|
|
chat.m_time = TimeLocal();
|
|
chat.m_state = 0;
|
|
chat.m_new_one = msg;
|
|
chat.m_new_one.done = false;
|
|
}
|
|
else
|
|
{
|
|
CCustomChat *chat = m_chats.GetNodeAtIndex(index);
|
|
chat.m_time = TimeLocal();
|
|
chat.m_new_one = msg;
|
|
chat.m_new_one.done = false;
|
|
}
|
|
|
|
it.Next();
|
|
}
|
|
|
|
m_first_remove = false;
|
|
}
|
|
//---
|
|
return(res);
|
|
}
|
|
|
|
|
|
#endif
|
|
//+--------
|
|
//+------------------------------------------------------------------+
|