//+------------------------------------------------------------------+ //| 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 #include #include "Common.mqh" #include "..\\JsonParserByLeo\\Src\\JsonNode.mqh" #include //+------------------------------------------------------------------+ //| 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); } /* "underlined\n" "bold\n" "italic\n" "strikethrough\n" "URL\n" "inline fixed-width code\n" "
pre-formatted monospace
\n" "
pre-formatted fixed-width code block written in the MQL5 programming language
\n" "
Block quotation started\nBlock quotation continued\nThe last line of the block quotation
" "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 //+-------- //+------------------------------------------------------------------+