TelegramByLeo/Src/Old/Telegram.mqh

599 lines
17 KiB
MQL5

2026-09-17 10:25:08 -05:00
//+------------------------------------------------------------------+
//| 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>
2026-09-22 17:06:21 -05:00
2026-09-17 10:25:08 -05:00
//+------------------------------------------------------------------+
//| 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);
2026-09-23 15:26:06 -05:00
if(!ok)
2026-09-17 10:25:08 -05:00
{
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
//+--------
//+------------------------------------------------------------------+