//+------------------------------------------------------------------+ //| CommandsApi.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_COMMANDSAPI_MQH #define TELEGRAMBYLEO_SRC_COMMANDSAPI_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "Events.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTelegramCommandBot : public CTelegramBot { protected: CHashMapFast(ulong, int) m_commands; // por ahora si quizas luego lista de priridad con un sorted list //--- bool ResolveCommand(const uchar& in[], int i, int len); bool IsMyUsername(const uchar& in[], int i, int e); //--- Cada comando virtual void OnCommand(int command_idx, long chat_id, CJsonNode& json) {} public: CTelegramCommandBot(void) {} ~CTelegramCommandBot(void) {} //--- void SetCommand(const string& command, int idx); bool RemoveCommand(const string& command); //--- void OnMessage(CJsonNode& node) override final; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTelegramCommandBot::IsMyUsername(const uchar& in[], int i, int e) { //--- // 1. e apnta a la posicion "end + 1" //--- const int my_len = StringLen(m_me.username); // len del user name que tenemos const int l = (e - i); // len de lo que encontramos if(l != my_len) // no es igual... return false; //--- for(int x = 0; x < l; x++) if(in[i++] != m_me.username[x]) return false; //--- return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTelegramCommandBot::ResolveCommand(const uchar &in[], int i, int len) { // len = min len += i; i++; // salta \ //--- // 1. una sola pasada para veriicar y hash (no dos) ulong h = FNV_OFFSET_BASIS; uchar c = in[i]; while(true) { if(c == ' ' || c == '@') break; //-- h ^= c; h *= FNV_PRIME; //-- if(++i >= len) break; //--- c = in[i]; } //--- if(i < len && c == '@') { //--- i++; // salta '@' int bot_start = i; while(i < len && in[i] != ' ') i++; //--- if(!IsMyUsername(in, bot_start, i)) return false; } //--- // vol en vez de int (ternario menos par ael TryGet==true? 1 : value) return m_commands.ContainsKey(h); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTelegramCommandBot::SetCommand(const string &command, int idx) { //--- hash ulong h = FNV_OFFSET_BASIS; const int len = StringLen(command); FNV1a_64_AsM_Str(command, len, h, FNV_PRIME) //--- if(m_commands.ReserveSlot(h)) // no existe { const uint p = m_commands.m_last_pos_tbl; m_commands.m_table[p].key = h; m_commands.m_table[p].value = idx; } else { // Ya existe... modificamos el valor m_commands.m_table[m_commands.m_last_pos_tbl].value = idx; } } //+------------------------------------------------------------------+ bool CTelegramCommandBot::RemoveCommand(const string &command) { //--- hash ulong h = FNV_OFFSET_BASIS; const int len = StringLen(command); FNV1a_64_AsM_Str(command, len, h, FNV_PRIME) //--- return m_commands.Remove(h); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTelegramCommandBot::OnMessage(CJsonNode &node) { //--- CJsonNode text = node["text"]; const int start = text.StrStart(); const int len = text.StrLenRaw(); if(!text.IsValid() || len < 1 || text.m_ctx.m_raw[start] != '/') return; //--- //parseamos el texto inplace (sobre buffer ucahr) sin copia y conusltmaos el indice del grupo (commmando) via hashmap if(!ResolveCommand(text.m_ctx.m_raw, start, len)) { return; } //--- const uint p = m_commands.m_last_pos_tbl; OnCommand(m_commands.m_table[p].value, node["chat"]["id"].ToInt(0), node); } } //+------------------------------------------------------------------+ #endif // TELEGRAMBYLEO_SRC_COMMANDSAPI_MQH