//+------------------------------------------------------------------+ //| Inline.mqh | //| Copyright 2026, Niquel Mendoza. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Niquel Mendoza." #property link "https://www.mql5.com" #property strict #ifndef TELEGRAMBYLEO_SRC_BOTTONS_INLINE_MQH #define TELEGRAMBYLEO_SRC_BOTTONS_INLINE_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "Def.mqh" #include "KeyboardBase.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #define TLGBYLEO_TXT_OFF_TEXT_OPEN 0 #define TLGBYLEO_TXT_LEN_TEXT_OPEN 9 #define TLGBYLEO_TXT_OFF_STYLE_KEY 9 #define TLGBYLEO_TXT_LEN_STYLE_KEY 10 #define TLGBYLEO_TXT_OFF_URL_KEY 19 #define TLGBYLEO_TXT_LEN_URL_KEY 8 #define TLGBYLEO_TXT_OFF_CALLBACK_DATA_KEY 27 #define TLGBYLEO_TXT_LEN_CALLBACK_DATA_KEY 18 #define TLGBYLEO_TXT_OFF_COPY_TEXT_OPEN 45 #define TLGBYLEO_TXT_LEN_COPY_TEXT_OPEN 22 #define TLGBYLEO_TXT_OFF_SWITCH_INLINE_QUERY_KEY 67 #define TLGBYLEO_TXT_LEN_SWITCH_INLINE_QUERY_KEY 24 #define TLGBYLEO_TXT_OFF_SWITCH_INLINE_QUERY_CURR_CHAT_KEY 91 #define TLGBYLEO_TXT_LEN_SWITCH_INLINE_QUERY_CURR_CHAT_KEY 37 #define TLGBYLEO_TXT_OFF_DISABLED_FIELD 128 #define TLGBYLEO_TXT_LEN_DISABLED_FIELD 14 #define TLGBYLEO_TXT_OFF_SWITCH_CHOSEN_CHAT_OPEN 142 #define TLGBYLEO_TXT_LEN_SWITCH_CHOSEN_CHAT_OPEN 36 #define TLGBYLEO_TXT_OFF_QUERY_KEY 178 #define TLGBYLEO_TXT_LEN_QUERY_KEY 9 #define TLGBYLEO_TXT_OFF_TRUE_LIT 187 #define TLGBYLEO_TXT_LEN_TRUE_LIT 4 #define TLGBYLEO_TXT_OFF_FALSE_LIT 191 #define TLGBYLEO_TXT_LEN_FALSE_LIT 5 #define TLGBYLEO_TXT_OFF_INLINE_KEYBOARD_OPEN 196 #define TLGBYLEO_TXT_LEN_INLINE_KEYBOARD_OPEN 20 //--- const uchar g_tlginline_keyboard_texts[] = { '{', '"', 't', 'e', 'x', 't', '"', ':', '"', // [0..8] TEXT_OPEN: {"text":" ',', '"', 's', 't', 'y', 'l', 'e', '"', ':', '"', // [9..18] STYLE_KEY: ,"style":" ',', '"', 'u', 'r', 'l', '"', ':', '"', // [19..26] URL_KEY: ,"url":" ',', '"', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', '_', 'd', 'a', 't', 'a', '"', ':', '"', // [27..44] CALLBACK_DATA_KEY: ,"callback_data":" ',', '"', 'c', 'o', 'p', 'y', '_', 't', 'e', 'x', 't', '"', ':', '{', '"', 't', 'e', 'x', 't', '"', ':', '"', // [45..66] COPY_TEXT_OPEN: ,"copy_text":{"text":" ',', '"', 's', 'w', 'i', 't', 'c', 'h', '_', 'i', 'n', 'l', 'i', 'n', 'e', '_', 'q', 'u', 'e', 'r', 'y', '"', ':', '"', // [67..90] SWITCH_INLINE_QUERY_KEY: ,"switch_inline_query":" ',', '"', 's', 'w', 'i', 't', 'c', 'h', '_', 'i', 'n', 'l', 'i', 'n', 'e', '_', 'q', 'u', 'e', 'r', 'y', '_', 'c', 'u', 'r', 'r', 'e', 'n', 't', '_', 'c', 'h', 'a', 't', '"', ':', '"', // [91..127] SWITCH_INLINE_QUERY_CURR_CHAT_KEY ',', '"', 'd', 'i', 's', 'a', 'b', 'l', 'e', 'd', '"', ':', '{', '}', // [128..141] DISABLED_FIELD: ,"disabled":{} ',', '"', 's', 'w', 'i', 't', 'c', 'h', '_', 'i', 'n', 'l', 'i', 'n', 'e', '_', 'q', 'u', 'e', 'r', 'y', '_', 'c', 'h', 'o', 's', 'e', 'n', '_', 'c', 'h', 'a', 't', '"', ':', '{', // [142..177] SWITCH_CHOSEN_CHAT_OPEN '"', 'q', 'u', 'e', 'r', 'y', '"', ':', '"', // [178..186] QUERY_KEY: "query":" 't', 'r', 'u', 'e', // [187..190] TRUE_LIT 'f', 'a', 'l', 's', 'e', // [191..195] FALSE_LIT '{', '"', 'i', 'n', 'l', 'i', 'n', 'e', '_', 'k', 'e', 'y', 'b', 'o', 'a', 'r', 'd', '"', ':', '[' // [196..215] INLINE_KEYBOARD_OPEN: {"inline_keyboard":[ }; //+------------------------------------------------------------------+ #define TLGBYLEO_BTN_OPEN_TEXT(text_v, text_len_v) \ m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_TEXT_OPEN, TLGBYLEO_TXT_LEN_TEXT_OPEN); \ CJsonEscape::Encode(text_v, text_len_v, m_buf, m_pos); \ m_buf[m_pos++] = '"'; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTlgInlineKeyboard : public CTlgKeyboardBase { public: //--- //ulong m_meta[]; //int m_meta_s; //--- ENUM_TLGBYLEO_INLINEKEYBOARD_STYLE m_style; private: // int m_meta_r; public: CTlgInlineKeyboard(uint initial_cap = 256); ~CTlgInlineKeyboard(void) {} //--- __forceinline void Clear() { m_pos = 0; m_in_row = false; m_in_boton = false; } void Init(); void Finish(); //--- __forceinline CTlgInlineKeyboard* AddRow() { AddRowImpl(); return &this; } __forceinline CTlgInlineKeyboard* EndRow() { EndRowImpl(); return &this; } //--- botones //- normal CTlgInlineKeyboard* AddBtn(const string& text); //-- ext CTlgInlineKeyboard* AddBtnUrl(const string& text, const string& url); CTlgInlineKeyboard* AddBtnCallbackData(const string& text, const string& callback_data); CTlgInlineKeyboard* AddBtnCallbackData(const string& text, const CStringFixed& callback_data); CTlgInlineKeyboard* AddBtnCopyText(const string& text, const string& copy_text); // Inline query CTlgInlineKeyboard* AddBtnSwithInlineQuery(const string& text, const string& switch_inline_query); CTlgInlineKeyboard* AddBtnSwithInlineQueryCurrChat(const string& text, const string& switch_inline_query_current_chat); CTlgInlineKeyboard* AddBtnSwithInlineQueryChosenOther(const string& text, const string& query, uchar flags); // Disabled CTlgInlineKeyboard* AddBtnDisabled(const string& text, const CStringFixed& callback_data_empty); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CTlgInlineKeyboard::CTlgInlineKeyboard(uint initial_cap = 256) : CTlgKeyboardBase(initial_cap) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTlgInlineKeyboard::Init(void) { //--- m_pos = 0; m_in_row = false; m_in_boton = false; //--- {"inline_keyboard":[ TLGBYLEO_KEYBORAD_RESERVE(TLGBYLEO_TXT_LEN_INLINE_KEYBOARD_OPEN) m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_INLINE_KEYBOARD_OPEN, TLGBYLEO_TXT_LEN_INLINE_KEYBOARD_OPEN); } //+------------------------------------------------------------------+ void CTlgInlineKeyboard::Finish(void) { TLGBYLEO_KEYBORAD_RESERVE(2) m_buf[m_pos++] = ']'; m_buf[m_pos++] = '}'; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CTlgInlineKeyboard* CTlgInlineKeyboard::AddBtn(const string &text) { //--- 22 + const int len_style = (m_style != WRONG_VALUE) ? g_tlgbyleo_inlinekeyboar_style[m_style].len : 0; const int str_len = StringLen(text); const int req = (str_len * 6) + (len_style) + (2 + 4 + 2 + 2 + 1 + 1 + 5 + 3 + 2 + 1) + 1; TLGBYLEO_KEYBORAD_RESERVE(req) //--- coma si ya hay un boton antes en esta fila if(m_in_boton) m_buf[m_pos++] = ','; //--- TLGBYLEO_BTN_OPEN_TEXT(text, str_len) //--- style: unico caso que cambia entre metodos, se queda manual tal cual if(m_style != WRONG_VALUE) { m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_STYLE_KEY, TLGBYLEO_TXT_LEN_STYLE_KEY); m_pos += ArrayCopy(m_buf, g_tlgbyleo_inlinekeyboar_style[m_style].raw, m_pos, 0, len_style); m_buf[m_pos++] = '"'; } //--- m_buf[m_pos++] = '}'; m_in_boton = true; //--- return &this; } //+------------------------------------------------------------------+ CTlgInlineKeyboard* CTlgInlineKeyboard::AddBtnUrl(const string &text, const string &url) { //--- const int str_len = StringLen(text); const int url_len = StringLen(url); const int req = (str_len * 6) + (url_len * 6) + 32; TLGBYLEO_KEYBORAD_RESERVE(req) //--- coma si ya hay un boton antes en esta fila if(m_in_boton) m_buf[m_pos++] = ','; TLGBYLEO_BTN_OPEN_TEXT(text, str_len) //--- ,"url":"" m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_URL_KEY, TLGBYLEO_TXT_LEN_URL_KEY); CJsonEscape::Encode(url, url_len, m_buf, m_pos); m_buf[m_pos++] = '"'; //--- m_buf[m_pos++] = '}'; m_in_boton = true; //--- return &this; } //+------------------------------------------------------------------+ CTlgInlineKeyboard* CTlgInlineKeyboard::AddBtnCallbackData(const string &text, const string &callback_data) { //--- const int str_len = StringLen(text); const int cb_len = StringLen(callback_data); const int req = (str_len * 6) + (cb_len * 6) + 40; TLGBYLEO_KEYBORAD_RESERVE(req) //--- coma si ya hay un boton antes en esta fila if(m_in_boton) m_buf[m_pos++] = ','; //--- TLGBYLEO_BTN_OPEN_TEXT(text, str_len) //--- ,"callback_data":"" m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_CALLBACK_DATA_KEY, TLGBYLEO_TXT_LEN_CALLBACK_DATA_KEY); CJsonEscape::Encode(callback_data, cb_len, m_buf, m_pos); m_buf[m_pos++] = '"'; //--- m_buf[m_pos++] = '}'; m_in_boton = true; //--- return &this; } //+------------------------------------------------------------------+ CTlgInlineKeyboard* CTlgInlineKeyboard::AddBtnCallbackData(const string &text, const CStringFixed &callback_data) { //--- const int str_len = StringLen(text); const int req = (str_len * 6) + (callback_data.len * 6) + 40; TLGBYLEO_KEYBORAD_RESERVE(req) //--- coma si ya hay un boton antes en esta fila if(m_in_boton) m_buf[m_pos++] = ','; //--- TLGBYLEO_BTN_OPEN_TEXT(text, str_len) //--- ,"callback_data":"" m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_CALLBACK_DATA_KEY, TLGBYLEO_TXT_LEN_CALLBACK_DATA_KEY); CJsonEscape::Encode(callback_data.raw, callback_data.len, m_buf, m_pos); m_buf[m_pos++] = '"'; //--- m_buf[m_pos++] = '}'; m_in_boton = true; //--- return &this; } //+------------------------------------------------------------------+ CTlgInlineKeyboard* CTlgInlineKeyboard::AddBtnCopyText(const string &text, const string ©_text) { //--- const int str_len = StringLen(text); const int copy_len = StringLen(copy_text); const int req = (str_len * 6) + (copy_len * 6) + 48; TLGBYLEO_KEYBORAD_RESERVE(req) //--- coma si ya hay un boton antes en esta fila if(m_in_boton) m_buf[m_pos++] = ','; //--- TLGBYLEO_BTN_OPEN_TEXT(text, str_len) //--- ,"copy_text":{"text":""} m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_COPY_TEXT_OPEN, TLGBYLEO_TXT_LEN_COPY_TEXT_OPEN); CJsonEscape::Encode(copy_text, copy_len, m_buf, m_pos); m_buf[m_pos++] = '"'; m_buf[m_pos++] = '}'; //--- m_buf[m_pos++] = '}'; m_in_boton = true; //--- return &this; } //+------------------------------------------------------------------+ CTlgInlineKeyboard* CTlgInlineKeyboard::AddBtnSwithInlineQuery(const string &text, const string &switch_inline_query) { //--- const int str_len = StringLen(text); const int q_len = StringLen(switch_inline_query); const int req = (str_len * 6) + (q_len * 6) + 48; TLGBYLEO_KEYBORAD_RESERVE(req) //--- coma si ya hay un boton antes en esta fila if(m_in_boton) m_buf[m_pos++] = ','; //--- TLGBYLEO_BTN_OPEN_TEXT(text, str_len) //--- ,"switch_inline_query":"" m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_SWITCH_INLINE_QUERY_KEY, TLGBYLEO_TXT_LEN_SWITCH_INLINE_QUERY_KEY); CJsonEscape::Encode(switch_inline_query, q_len, m_buf, m_pos); m_buf[m_pos++] = '"'; //--- m_buf[m_pos++] = '}'; m_in_boton = true; //--- return &this; } //+------------------------------------------------------------------+ CTlgInlineKeyboard* CTlgInlineKeyboard::AddBtnSwithInlineQueryCurrChat(const string &text, const string &switch_inline_query_current_chat) { //--- const int str_len = StringLen(text); const int q_len = StringLen(switch_inline_query_current_chat); const int req = (str_len * 6) + (q_len * 6) + 64; TLGBYLEO_KEYBORAD_RESERVE(req) //--- coma si ya hay un boton antes en esta fila if(m_in_boton) m_buf[m_pos++] = ','; //--- TLGBYLEO_BTN_OPEN_TEXT(text, str_len) //--- ,"switch_inline_query_current_chat":"" m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_SWITCH_INLINE_QUERY_CURR_CHAT_KEY, TLGBYLEO_TXT_LEN_SWITCH_INLINE_QUERY_CURR_CHAT_KEY); CJsonEscape::Encode(switch_inline_query_current_chat, q_len, m_buf, m_pos); m_buf[m_pos++] = '"'; //--- m_buf[m_pos++] = '}'; m_in_boton = true; //--- return &this; } //+------------------------------------------------------------------+ CTlgInlineKeyboard* CTlgInlineKeyboard::AddBtnSwithInlineQueryChosenOther(const string &text, const string &query, uchar flags) { //--- const int str_len = StringLen(text); const int q_len = StringLen(query); const int req = (str_len * 6) + (q_len * 6) + 256; // generoso: objeto anidado + hasta 4 bools TLGBYLEO_KEYBORAD_RESERVE(req) //--- coma si ya hay un boton antes en esta fila if(m_in_boton) m_buf[m_pos++] = ','; //--- TLGBYLEO_BTN_OPEN_TEXT(text, str_len) //--- ,"switch_inline_query_chosen_chat":{ m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_SWITCH_CHOSEN_CHAT_OPEN, TLGBYLEO_TXT_LEN_SWITCH_CHOSEN_CHAT_OPEN); //--- query (opcional, va primero si viene) bool need_comma_inner = false; if(q_len > 0) { m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_QUERY_KEY, TLGBYLEO_TXT_LEN_QUERY_KEY); CJsonEscape::Encode(query, q_len, m_buf, m_pos); m_buf[m_pos++] = '"'; need_comma_inner = true; } //--- flags: 2 bits por campo, bajo=valor, alto=activo. Recorremos con CTZ (tabla de 8 bits). while(flags) { const uchar low = flags & (~flags + 1); // aisla el bit menos significativo prendido const uchar bitpos = g_tsntables_table_ctz_i8[low]; const uchar idx = bitpos >> 1; // indice en la tabla bool value; //--- detecamos true o false if((bitpos & 1) == 0) // es par. osea el ctz vale (0, 2, 4, 6).. por loq que el "bajo" esta prendido.. { // ahora revisamos el que le sigue if(((1u << (bitpos + 1)) & flags) == 0) // no esta activado.. osea tiene un valor (True) pero no esta activado (malformacion) { flags &= (flags - 1); // quitamos este flag continue; } //-- // Si tiene el que le sigue activado entonces quitamos dos de una.. value = true; flags &= (flags - 1); flags &= (flags - 1); } else { value = false; flags &= (flags - 1); } //--- coma entre campos if(need_comma_inner) m_buf[m_pos++] = ','; need_comma_inner = true; //--- key (con comillas y ":") indexada directo por el enum, sin switch m_pos += ArrayCopy(m_buf, g_tlgbyleo_switchchat_field_keys[idx].raw, m_pos, 0, g_tlgbyleo_switchchat_field_keys[idx].len); //--- valor if(value) m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_TRUE_LIT, TLGBYLEO_TXT_LEN_TRUE_LIT); else m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_FALSE_LIT, TLGBYLEO_TXT_LEN_FALSE_LIT); } //--- m_buf[m_pos++] = '}'; // cierra switch_inline_query_chosen_chat //--- m_buf[m_pos++] = '}'; // cierra el boton m_in_boton = true; //--- return &this; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ // 1. empty debe de seer ASCCI puro CTlgInlineKeyboard* CTlgInlineKeyboard::AddBtnDisabled(const string &text, const CStringFixed &callback_data_empty) { //--- const int str_len = StringLen(text); const int req = (str_len * 6) + (callback_data_empty.len) + 48; TLGBYLEO_KEYBORAD_RESERVE(req) //--- coma si ya hay un boton antes en esta fila if(m_in_boton) m_buf[m_pos++] = ','; //--- TLGBYLEO_BTN_OPEN_TEXT(text, str_len) //--- ,"callback_data":"" m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_CALLBACK_DATA_KEY, TLGBYLEO_TXT_LEN_CALLBACK_DATA_KEY); m_pos += ArrayCopy(m_buf, callback_data_empty.raw, m_pos, 0, callback_data_empty.len); m_buf[m_pos++] = '"'; //--- ,"disabled":{} m_pos += ArrayCopy(m_buf, g_tlginline_keyboard_texts, m_pos, TLGBYLEO_TXT_OFF_DISABLED_FIELD, TLGBYLEO_TXT_LEN_DISABLED_FIELD); //--- m_buf[m_pos++] = '}'; m_in_boton = true; //--- return &this; } } #endif // TELEGRAMBYLEO_SRC_BOTTONS_INLINE_MQH //+------------------------------------------------------------------+