184 lines
5.4 KiB
MQL5
184 lines
5.4 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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
|