forked from nique_372/TelegramByLeo
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
| |
//+--------
| |
//+------------------------------------------------------------------+
|