
Erstellen eines integrierten MQL5-Telegram-Expertenberaters (Teil 6): Responsive Inline-Schaltflächen hinzufügen
Einführung
Dieser Artikel befasst sich damit, wie wir mit MetaQuotes Language 5 (MQL5) unseren Expert Advisor für Nutzer über Telegram reaktionsschneller und interaktiver machen. Im fünften Teil dieser Serie haben wir den Grundstein für unseren Bot gelegt, indem wir die Fähigkeit implementiert haben, auf Befehle und Nachrichten von Telegram zu reagieren und nutzerdefinierte Tasten zu erstellen. In diesem Segment erhöhen wir die Interaktivität unseres Bots durch die Integration von Inline-Schaltflächen, die verschiedene Aktionen auslösen und dynamisch auf Nutzereingaben reagieren.
Der Artikel ist so aufgebaut, dass er sich mit einigen Schlüsselkomponenten befasst. Zunächst werden wir Inline-Schaltflächen in Telegram-Bots vorstellen, einschließlich ihrer Funktion, ihrer Nützlichkeit und ihrer Vorteile gegenüber anderen Methoden zur Erstellung einer Bot-Oberfläche. Anschließend werden wir besprechen, wie diese Inline-Schaltflächen in MQL5 verwendet werden können, sodass sie Teil der Nutzeroberfläche unseres Expert Advisors werden können.
Von dort aus werden wir demonstrieren, wie wir die Callback-Anfragen behandeln, die von Telegram gesendet werden, wenn ein Nutzer eine Taste drückt. Dabei wird die Aktion des Nutzers verarbeitet und der nächste geeignete Schritt für den Bot in der Konversation mit dem Nutzer bestimmt. Schließlich werden wir die integrierten Funktionen des Bots testen, um sicherzustellen, dass alles einwandfrei funktioniert. Hier sind die Themen, die wir in diesem Artikel behandeln werden:
- Einführung in Inline-Buttons in Telegram-Bots
- Integration von Inline-Schaltflächen in MQL5
- Handhabung von Callback-Abfragen für Schaltflächenaktionen
- Testen der Implementierung der Inline-Button-Zustände
- Schlussfolgerung
Am Ende dieses Artikels werden Sie verstehen, wie Sie Inline-Buttons in Ihren MQL5-Expert Advisor für Telegram integrieren und verwalten können, um die Funktionalität Ihres Trading Bots zu verbessern und ihn für die Nutzer reaktionsfähiger und interaktiver zu machen. Fangen wir also an.
Einführung in Inline-Buttons in Telegram-Bots
Inline-Buttons sind interaktive Elemente, die direkt in Telegram-Bot-Nachrichten erscheinen und es Nutzern ermöglichen, mit einer einzigen Berührung Aktionen auszuführen. Diese Schaltflächen verwenden JavaScript Object Notation (JSON) für formatierte Inline-Tastaturbeschriftungen, um ihr Aussehen und Verhalten zu definieren, und bieten im Vergleich zu herkömmlichen Methoden eine besser integrierte und reaktionsschnellere Schnittstelle. Indem diese Schaltflächen direkt in Nachrichten eingebettet werden, können Bots den Nutzern ein optimiertes Erlebnis und unmittelbare Interaktion bieten, ohne dass zusätzliche Textbefehle oder Nachrichten erforderlich sind. Um einen Einblick zu geben, worüber wir genau sprechen, haben wir eine visuelle Illustration von Inline-Schaltflächen wie unten dargestellt:
Der Hauptvorteil von Inline-Buttons gegenüber herkömmlichen Antworttastaturen liegt darin, dass sie in der Nachricht selbst verbleiben, wodurch die Interaktion nahtloser und kontextbezogener wird. Inline-Schaltflächen, die unter Verwendung von JSON definiert werden, ermöglichen komplexe Nutzerinteraktionen und dynamische Reaktionen. Dieser Ansatz macht separate Menüs oder zusätzliche Meldungen überflüssig, wodurch die Unübersichtlichkeit verringert und das Engagement des Nutzers durch sofortige Rückmeldungen und Aktionen erhöht wird. Mit dieser Einsicht können wir nun, wie im nächsten Abschnitt beschrieben, mit der Implementierung in MQL5 für MetaTrader 5 beginnen.
Integration von Inline-Schaltflächen in MQL5
Die Einbindung von Inline-Buttons in unseren MQL5 Expert Advisor (EA) erfordert ein Framework, das die Interaktionen von Nachrichten und die verschiedenen Zustände der Buttons verarbeiten kann. Dies wird durch die Erstellung und Verwaltung mehrerer Klassen erreicht, die jeweils für einen anderen Teil der Verarbeitung von Anrufen und Nachrichten zuständig sind. Wir werden die Integration im Detail erklären, einschließlich der verwendeten Klassen und Funktionen, und, was noch wichtiger ist, warum sie verwendet werden und wie sie zum Funktionieren des Bots mit Inline-Schaltflächen beitragen. Als Erstes müssen wir eine Klasse entwerfen, die alle Einzelheiten einer von Telegram empfangenen Nachricht kapseln kann.
//+------------------------------------------------------------------+ //| Class_Message | //+------------------------------------------------------------------+ class Class_Message : public CObject { public: Class_Message(); // Constructor ~Class_Message(){}; // Destructor bool done; //--- Indicates if a message has been processed. long update_id; //--- Stores the update ID from Telegram. long message_id; //--- Stores the message ID. long from_id; //--- Stores the sender’s ID. string from_first_name; //--- Stores the sender’s first name. string from_last_name; //--- Stores the sender’s last name. string from_username; //--- Stores the sender’s username. long chat_id; //--- Stores the chat ID. string chat_first_name; //--- Stores the chat’s first name. string chat_last_name; //--- Stores the chat’s last name. string chat_username; //--- Stores the chat’s username. string chat_type; //--- Stores the chat type. datetime message_date; //--- Stores the date of the message. string message_text; //--- Stores the text of the message. };
Hier definieren wir die Klasse „Class_Message“, die als Container für alle relevanten Details über die von Telegram empfangenen Nachrichten dient. Diese Klasse ist für die Verwaltung und Verarbeitung von Nachrichtendaten in unserem MQL5 Expert Advisor (EA) unerlässlich.
In dieser Klasse sind mehrere öffentliche Attribute enthalten, die bestimmte Aspekte einer Nachricht erfassen. Das Attribut „done“ zeigt an, ob die Nachricht verarbeitet wurde. Die Attribute „update_id“ und „message_id“ speichern eindeutige Bezeichner für die Aktualisierung bzw. die Nachricht. Die Attribute „from_id“, „from_first_name“, „from_last_name“ und „from_username“ enthalten Informationen über den Absender der Nachricht. In ähnlicher Weise erfassen „chat_id“, „chat_first_name“, „chat_last_name“, „chat_username“ und „chat_type“ Einzelheiten über den Chat, in dem die Nachricht gesendet wurde. Das Attribut „message_date“ speichert das Datum und die Uhrzeit der Nachricht, „message_text“ den eigentlichen Inhalt der Nachricht. Nachdem wir die Klassenmitglieder definiert haben, können wir mit der Initialisierung unserer Klassenmitglieder fortfahren.
//+------------------------------------------------------------------+ //| Constructor to initialize class members | //+------------------------------------------------------------------+ Class_Message::Class_Message(void) { done = false; //--- Sets default value indicating message not yet processed. update_id = 0; //--- Initializes update ID to zero. message_id = 0; //--- Initializes message ID to zero. from_id = 0; //--- Initializes sender ID to zero. from_first_name = NULL; //--- Sets sender's first name to NULL (empty). from_last_name = NULL; //--- Sets sender's last name to NULL (empty). from_username = NULL; //--- Sets sender's username to NULL (empty). chat_id = 0; //--- Initializes chat ID to zero. chat_first_name = NULL; //--- Sets chat’s first name to NULL (empty). chat_last_name = NULL; //--- Sets chat’s last name to NULL (empty). chat_username = NULL; //--- Sets chat’s username to NULL (empty). chat_type = NULL; //--- Sets chat type to NULL (empty). message_date = 0; //--- Initializes message date to zero. message_text = NULL; //--- Sets message text to NULL (empty). }
Hier initialisieren wir den „Class_Message“-Konstruktor, um Standardwerte für alle Attribute der Klasse festzulegen. Das Attribut „done“ wird auf „false“ gesetzt, um anzuzeigen, dass die Nachricht noch nicht bearbeitet wurde. Wir initialisieren „update_id“, „message_id“, „from_id“ und „chat_id“ mit 0, wobei „from_first_name“, „from_last_name“, „from_username“, „chat_first_name“, „chat_last_name“, „chat_username“ und „chat_type“ auf NULL gesetzt werden, um anzuzeigen, dass diese Felder leer sind. Schließlich wird „message_date“ auf 0 gesetzt, und „message_text“ wird mit NULL initialisiert, um sicherzustellen, dass jede neue Instanz von „Class_Message“ mit Standardwerten beginnt, bevor sie mit tatsächlichen Daten gefüllt wird. Über dieselbe Logik definieren wir die Chat-Klasse, in der wir unsere Chat-Aktualisierungsdetails wie unten beschrieben speichern:
//+------------------------------------------------------------------+ //| Class_Chat | //+------------------------------------------------------------------+ class Class_Chat : public CObject { public: Class_Chat(){}; //--- Declares an empty constructor. ~Class_Chat(){}; //--- Declares an empty destructor. long member_id; //--- Stores the chat ID. int member_state; //--- Stores the state of the chat. datetime member_time; //--- Stores the time of chat activities. Class_Message member_last; //--- Instance of Class_Message to store the last message. Class_Message member_new_one; //--- Instance of Class_Message to store the new message. };
Nach der Definition der Klasse „Chats“ müssen wir eine weitere Klasse definieren, die für Rückrufanfragen zuständig ist. Sie ist für die Bearbeitung der spezifischen Daten im Zusammenhang mit Callback-Abfragen (callback query), die sich von normalen Nachrichten unterscheiden, unerlässlich. Callback-Abfragen liefern eindeutige Daten, wie z. B. die Callback-Daten und die Interaktion, die die Abfrage ausgelöst hat, die in Standardmeldungen nicht enthalten sind. Die Klasse wird es uns also ermöglichen, diese speziellen Daten effektiv zu erfassen und zu verwalten. Darüber hinaus wird es uns ermöglichen, Nutzerinteraktionen mit Inline-Schaltflächen auf eine besondere Weise zu behandeln. Durch diese Trennung wird sichergestellt, dass wir Tastendrucke genau verarbeiten und darauf reagieren können und sie von anderen Arten von Nachrichten und Interaktionen unterscheiden. Die Umsetzung wird wie folgt aussehen:
//+------------------------------------------------------------------+ //| Class_CallbackQuery | //+------------------------------------------------------------------+ class Class_CallbackQuery : public CObject { public: string id; //--- Stores the callback query ID. long from_id; //--- Stores the sender’s ID. string from_first_name; //--- Stores the sender’s first name. string from_last_name; //--- Stores the sender’s last name. string from_username; //--- Stores the sender’s username. long message_id; //--- Stores the message ID related to the callback. string message_text; //--- Stores the message text. string data; //--- Stores the callback data. long chat_id; //--- Stores the chat ID to send responses. };
Hier definieren wir eine Klasse namens „Class_CallbackQuery“, um die Daten zu verwalten, die mit Callback-Abfragen von Telegram verbunden sind. Diese Klasse ist entscheidend für die Handhabung von Interaktionen mit Inline-Schaltflächen. In der Klasse werden verschiedene Variablen deklariert, um spezifische Informationen für Callback-Abfragen zu speichern. Die Variable „id“ enthält die eindeutige Kennung für die Callback-Abfrage, die es uns ermöglicht, zwischen verschiedenen Abfragen zu unterscheiden. „from_id“ speichert die ID des Absenders, die uns hilft, den Nutzer zu identifizieren, der den Callback ausgelöst hat. Wir verwenden „from_first_name“, „from_last_name“ und „from_username“, um die Namensangaben des Absenders zu erfassen.
Die Variable „message_id“ erfasst die ID der mit dem Callback verbundenen Nachricht, während „message_text“ den Text dieser Nachricht enthält. „data“ enthält die mit der Inline-Schaltfläche gesendeten Callback-Daten, die für die Bestimmung der aufgrund der gedrückten Schaltfläche auszuführenden Aktion entscheidend sind. Schließlich wird in „chat_id“ die Chat-ID gespeichert, an die die Antworten gesendet werden sollen, um sicherzustellen, dass die Antwort den richtigen Chat-Kontext erreicht. Der Rest der Klassendefinition und Initialisierung des Experten bleibt gleich, außer dass wir jetzt eine zusätzliche nutzerdefinierte Funktion für die Verarbeitung von Callback-Abfragen einfügen müssen.
//+------------------------------------------------------------------+ //| Class_Bot_EA | //+------------------------------------------------------------------+ class Class_Bot_EA { private: string member_token; //--- Stores the bot’s token. string member_name; //--- Stores the bot’s name. long member_update_id; //--- Stores the last update ID processed by the bot. CArrayString member_users_filter; //--- Array to filter messages from specific users. bool member_first_remove; //--- Indicates if the first message should be removed. protected: CList member_chats; //--- List to store chat objects. public: Class_Bot_EA(); //--- Constructor. ~Class_Bot_EA(){}; //--- Destructor. int getChatUpdates(); //--- Function to get updates from Telegram. void ProcessMessages(); //--- Function to process incoming messages. void ProcessCallbackQuery(Class_CallbackQuery &cb_query); //--- Function to process callback queries. }; //+------------------------------------------------------------------+ //| Constructor for Class_Bot_EA | //+------------------------------------------------------------------+ Class_Bot_EA::Class_Bot_EA(void) { member_token = NULL; //--- Initialize bot token to NULL (empty). member_token = getTrimmedToken(InpToken); //--- Assign bot token by trimming input token. member_name = NULL; //--- Initialize bot name to NULL. member_update_id = 0; //--- Initialize last update ID to zero. member_first_remove = true; //--- Set first remove flag to true. member_chats.Clear(); //--- Clear the list of chat objects. member_users_filter.Clear(); //--- Clear the user filter array. }
Nachdem wir alle erforderlichen Klassen definiert haben, können wir fortfahren, um die Details der Chat-Aktualisierung zu erhalten.
//+------------------------------------------------------------------+ //| Function to get chat updates from Telegram | //+------------------------------------------------------------------+ int Class_Bot_EA::getChatUpdates(void) { //... return 0; //--- Return 0 to indicate successful processing of updates. }
Hier definieren wir die Funktion „getChatUpdates“ als eine Methode der Klasse „Class_Bot_EA“. Diese Funktion soll Updates von der Telegram-API abrufen: Updates, die entweder aus neuen Nachrichten oder Callback-Anfragen bestehen, die der Bot noch nicht bearbeitet hat. Die derzeitige Implementierung von „getChatUpdates“ gibt einen Integer-Wert von 0 zurück, was üblicherweise bedeutet, dass der Vorgang erfolgreich abgeschlossen wurde. Durch die Rückgabe von 0 signalisieren wir, dass wir die Aktualisierungen gezogen und verarbeitet haben, ohne dass es zu Problemen kam. Der nächste Schritt besteht darin, diese Funktion so auszufüllen, dass sie das tut, was sie tun soll: Aktualisierungen von der API abrufen.
if (member_token == NULL) { //--- If bot token is empty Print("ERR: TOKEN EMPTY"); //--- Print error message indicating empty token. return (-1); //--- Return -1 to indicate error. }
Wir ermitteln, ob die Variable „member_token“ leer ist. Wenn „member_token“ NULL ist, bedeutet dies, dass wir kein Bot-Token erhalten haben. Also lassen wir den Nutzer mit „ERR:TOKEN EMPTY“ wissen, dass es eine notwendige Information gibt, die nicht geliefert wurde, und wir geben -1 zurück, um eine Fehlerbedingung zu signalisieren, die die Funktion daran hindert, weiterzumachen. Wenn wir diesen Schritt bestanden haben, können wir die Anfrage zum Erhalt der Chat-Updates stellen.
string out; //--- String to hold response data. string url = TELEGRAM_BASE_URL + "/bot" + member_token + "/getUpdates"; //--- Construct URL for Telegram API. string params = "offset=" + IntegerToString(member_update_id); //--- Set parameters including the offset based on the last update ID. int res = postRequest(out, url, params, WEB_TIMEOUT); //--- Send a POST request to Telegram with a timeout.
Zunächst definieren wir eine Variable namens „out“ vom Typ string. Wir richten diese „out“-Variable ein, um die Antwortdaten zu speichern, die wir von der Telegram-API zurückerhalten werden. Anschließend erstellen wir die API-URL, die zum Abrufen der Aktualisierungen erforderlich ist. Dazu kombinieren wir eine „TELEGRAM_BASE_URL“ mit mehreren anderen Komponenten: „/bot“ und das Token für den Bot, das in „member_token“ enthalten ist, sowie „/getUpdates“, den Endpunkt, den wir ansteuern, um Updates von Telegram zu erhalten. Die Telegram-API ist Teil der anderen Hauptplattform, die unsere Anwendung nutzt, und mit der „getUpdates“-Methode ziehen wir neue Daten von dieser Plattform ein. Anschließend rufen wir die API auf und erlauben ihr, neue Daten aus unserer Anwendung an uns zurückzugeben. Anhand der Ergebnisse können wir dann weitere Änderungen vornehmen.
if (res == 0) { //--- If request succeeds (res = 0) CJSONValue obj_json(NULL, jv_UNDEF); //--- Create a JSON object to parse the response. bool done = obj_json.Deserialize(out); //--- Deserialize the response. if (!done) { //--- If deserialization fails Print("ERR: JSON PARSING"); //--- Print error message indicating JSON parsing error. return (-1); //--- Return -1 to indicate error. } bool ok = obj_json["ok"].ToBool(); //--- Check if the response has "ok" field set to true. if (!ok) { //--- If "ok" field is false Print("ERR: JSON NOT OK"); //--- Print error message indicating that JSON response is not okay. return (-1); //--- Return -1 to indicate error. } }
Zunächst wird festgestellt, ob die Anfrage erfolgreich war, indem der Wert der Variablen „res“ überprüft wird. Wenn „res“ gleich 0 ist, wissen wir, dass die Anfrage erfolgreich war, und wir können mit der Bearbeitung der Antwort fortfahren. Wir erstellen das „CJSONValue“-Objekt „obj_json“, das wir zum Parsen der Antwort verwenden. Das Objekt wird im Zustand „NULL“ und mit „jv_UNDEF“ initialisiert, was einen undefinierten Zustand oder ein zum Empfang von Daten vorbereitetes Objekt bezeichnet. Nach dem Parsen mit „out“ haben wir ein Objekt, das die geparsten Daten enthält, oder es ist ein Fehler beim Parsen aufgetreten.
Wenn die Deserialisierung fehlschlägt - was dadurch angezeigt wird, dass die Variable „done“ falsch ist - wird die Fehlermeldung „ERR“ ausgegeben: JSON PARSING“ und geben „-1“ zurück, um ein Problem zu signalisieren. Wenn wir die Daten erfolgreich deserialisieren, prüfen wir, ob die Antwort ein Feld mit der Bezeichnung „ok“ enthält. Wir wandeln dies mit der Methode „ToBool“ in einen boolschen Wert um und speichern das Ergebnis in der Variablen „ok“. Wenn „ok“ falsch ist - was bedeutet, dass die Anfrage auf der Seite des Servers nicht erfolgreich war, wird „ERR“:JSON NOT OK“ ausgegeben und „-1“ zurückgegeben. Auf diese Weise stellen wir sicher, dass wir sowohl die Deserialisierung der Antwort als auch ihren Inhalt ordnungsgemäß behandeln. Anschließend werden die einzelnen Antworten nach der folgenden Logik durchlaufen.
int total = ArraySize(obj_json["result"].m_elements); //--- Get the total number of update elements. for (int i = 0; i < total; i++) { //--- Iterate through each update element. CJSONValue obj_item = obj_json["result"].m_elements[i]; //--- Access individual update element. if (obj_item["message"].m_type != jv_UNDEF) { //--- Check if the update has a message. Class_Message obj_msg; //--- Create an instance of Class_Message to store the message details. obj_msg.update_id = obj_item["update_id"].ToInt(); //--- Extract and store update ID. obj_msg.message_id = obj_item["message"]["message_id"].ToInt(); //--- Extract and store message ID. obj_msg.message_date = (datetime)obj_item["message"]["date"].ToInt(); //--- Extract and store message date. obj_msg.message_text = obj_item["message"]["text"].ToStr(); //--- Extract and store message text. obj_msg.message_text = decodeStringCharacters(obj_msg.message_text); //--- Decode any special characters in the message text. } }
Um mit der Untersuchung der Gesamtzahl der Aktualisierungselemente in der Antwort zu beginnen, verwenden wir die Funktion ArraySize, um die Elemente innerhalb des Arrays „m_elements“ des Objekts „result“ in „obj_json“ zu zählen. Wir speichern die Anzahl in der Variablen „total“. Als Nächstes richten wir eine Schleife ein, die jedes Aktualisierungselement aus dem Array „m_elements“ wiederholt verarbeitet. Es sind „total“-Elemente zu verarbeiten; daher reicht die Schleifenvariable von 0 bis („total“ - 1). Bei jeder Iteration zeigt der aktuelle Wert „i“ der Schleifenvariable an, auf welches Element des Arrays „m_elements“ wir zugreifen. Wir weisen das Element i der Variablen „obj_item“ zu. Jetzt wird geprüft, ob die aktuelle Aktualisierung (das „obj_item“) eine gültige „Nachricht“ enthält.
Als Nächstes instanziieren wir ein Objekt der „Class_Message“ mit dem Namen „obj_msg“, das die Einzelheiten der betreffenden Nachricht enthält. Das erste Feld, das wir in „obj_msg“ ausfüllen, ist das Feld „update_id“. Dazu extrahieren wir die „update_id“ aus dem „obj_item“, wandeln sie in eine Ganzzahl um und tragen sie in „obj_msg.update_id“ ein. Das nächste Feld, auf das wir in „obj_msg“ zugreifen, ist das Feld „message_id“. Für diesen Wert extrahieren wir wiederum Informationen aus dem Feld „message“ von „obj_item“. Wir wandeln den Wert im Feld „message_id“ von „obj_item“ in eine Ganzzahl um und setzen ihn in „obj_msg.message_id“ ein. Danach füllen wir das Feld „datetime“ von „obj_msg“ mit dem „date“-Wert von „item“. Anschließend füllen wir das Feld „message_text“ in „obj_msg“ aus. Wir extrahieren den Wert „text“ aus „message“, wandeln ihn in eine Zeichenkette um und platzieren ihn in „obj_msg.message_text“. Schließlich verwenden wir die Funktion „decodeStringCharacters“, um sicherzustellen, dass alle Sonderzeichen im „message_text“ korrekt dargestellt werden. Ein ähnlicher Ansatz wird verwendet, um die anderen Antwortdetails zu erhalten.
obj_msg.from_id = obj_item["message"]["from"]["id"].ToInt(); //--- Extract and store the sender's ID. obj_msg.from_first_name = obj_item["message"]["from"]["first_name"].ToStr(); //--- Extract and store the sender's first name. obj_msg.from_first_name = decodeStringCharacters(obj_msg.from_first_name); //--- Decode any special characters in the sender's first name. obj_msg.from_last_name = obj_item["message"]["from"]["last_name"].ToStr(); //--- Extract and store the sender's last name. obj_msg.from_last_name = decodeStringCharacters(obj_msg.from_last_name); //--- Decode any special characters in the sender's last name. obj_msg.from_username = obj_item["message"]["from"]["username"].ToStr(); //--- Extract and store the sender's username. obj_msg.from_username = decodeStringCharacters(obj_msg.from_username); //--- Decode any special characters in the sender's username. obj_msg.chat_id = obj_item["message"]["chat"]["id"].ToInt(); //--- Extract and store the chat ID. obj_msg.chat_first_name = obj_item["message"]["chat"]["first_name"].ToStr(); //--- Extract and store the chat's first name. obj_msg.chat_first_name = decodeStringCharacters(obj_msg.chat_first_name); //--- Decode any special characters in the chat's first name. obj_msg.chat_last_name = obj_item["message"]["chat"]["last_name"].ToStr(); //--- Extract and store the chat's last name. obj_msg.chat_last_name = decodeStringCharacters(obj_msg.chat_last_name); //--- Decode any special characters in the chat's last name. obj_msg.chat_username = obj_item["message"]["chat"]["username"].ToStr(); //--- Extract and store the chat's username. obj_msg.chat_username = decodeStringCharacters(obj_msg.chat_username); //--- Decode any special characters in the chat's username. obj_msg.chat_type = obj_item["message"]["chat"]["type"].ToStr(); //--- Extract and store the chat type.
Nachdem wir die Chatdetails erhalten haben, verarbeiten wir die Nachricht anhand der zugehörigen Chat-ID.
//--- Process the message based on chat ID. member_update_id = obj_msg.update_id + 1; //--- Update the last processed update ID.
Nachdem wir die erforderlichen Nachrichtendetails extrahiert und gespeichert haben, aktualisieren wir die zuletzt verarbeitete Aktualisierungs-ID. Wir erreichen dies, indem wir den Wert von „obj_msg.update_id“ plus 1 der Variablen „member_update_id“ zuweisen. Dadurch wird sichergestellt, dass wir bei der nächsten Verarbeitung von Aktualisierungen diese übergehen und mit der nächsten fortfahren können. Schließlich müssen wir eine Filterprüfung auf die Nutzernachrichten anwenden.
//--- Check if we need to filter messages based on user or if no filter is applied. if (member_users_filter.Total() == 0 || (member_users_filter.Total() > 0 && member_users_filter.SearchLinear(obj_msg.from_username) >= 0)) { int index = -1; //--- Initialize index to -1 (indicating no chat found). for (int j = 0; j < member_chats.Total(); j++) { //--- Iterate through all chat objects. Class_Chat *chat = member_chats.GetNodeAtIndex(j); //--- Get chat object by index. if (chat.member_id == obj_msg.chat_id) { //--- If chat ID matches index = j; //--- Store the index. break; //--- Break the loop since we found the chat. } } if (index == -1) { //--- If no matching chat was found member_chats.Add(new Class_Chat); //--- Create a new chat object and add it to the list. Class_Chat *chat = member_chats.GetLastNode(); //--- Get the last (newly added) chat object. chat.member_id = obj_msg.chat_id; //--- Assign the chat ID. chat.member_time = TimeLocal(); //--- Record the current time for the chat. chat.member_state = 0; //--- Initialize the chat state to 0. chat.member_new_one.message_text = obj_msg.message_text; //--- Store the new message in the chat. chat.member_new_one.done = false; //--- Mark the new message as not processed. } else { //--- If matching chat was found Class_Chat *chat = member_chats.GetNodeAtIndex(index); //--- Get the chat object by index. chat.member_time = TimeLocal(); //--- Update the time for the chat. chat.member_new_one.message_text = obj_msg.message_text; //--- Store the new message. chat.member_new_one.done = false; //--- Mark the new message as not processed. } }
Um Nachrichten auf der Grundlage des Nutzers zu filtern oder alle Nachrichten ohne Filterung durchzulassen, prüfen wir zunächst, ob „member_users_filter“ Elemente enthält. Wenn der Filter leer ist („total == 0“), werden alle Nachrichten durchgelassen. Wenn der Filter Elemente enthält („total > 0“), wird geprüft, ob der Nutzername des Absenders („obj_msg.from_username“) im Filter vorhanden ist. Wir verwenden eine sequentielle Suchmethode, „SearchLinear“, bei der der Nutzername des Absenders mit dem Filter abgeglichen wird, um festzustellen, ob er vorhanden ist. Wenn der Nutzername gefunden wird (die Methode gibt einen Index von 0 oder mehr zurück), wird die Nachricht normal weiterverarbeitet. Nach diesem Filterungsschritt wird der Chat der Nachricht nachgeschlagen. Wir suchen den Nutzernamen des Absenders im Filter, sodass nur bestimmte Nutzernamen (die des Absenders oben im Filter) durchgelassen werden können.
Wenn die „chat.member_id“ mit der Chat-ID der Nachricht („obj_msg.chat_id“) übereinstimmt, zeichnen wir zunächst den aktuellen Index in der Variablen „index“ während einer Schleife auf und brechen dann aus dieser Schleife aus, da wir den richtigen Chat gefunden haben. Wenn wir keine Treffer für den Chat finden und „index“ weiterhin -1 bleibt, erstellen wir ein neues Chat-Objekt und fügen es mit der Methode „Add“ in die „member_chats“ ein. „GetLastNode“ hilft uns dann, das neu geprägte Chat-Objekt zu sammeln, das wir im Zeiger „chat“ aufbewahren. Wir geben die Chat-ID aus „obj_msg.chat_id“ an „chat.member_id“ weiter und verwenden die Funktion TimeLocal, um die aktuelle Zeit an „chat.member_time“ zu binden. Wir setzen den „member_state“ des Chats gleich zu Beginn auf 0 und speichern die neue Nachricht in „chat.member_new_one.message_text“.
Wir zeigen auch an, dass die Nachricht unbearbeitet ist, indem wir „chat.member_new_one.done“ auf false setzen. Wenn wir einen passenden Chat finden (d.h. „index“ ist nicht -1), rufen wir das entsprechende Chatobjekt mit „GetNodeAtIndex“ ab und aktualisieren seine „member_time“ mit der aktuellen Zeit. Anschließend wird die neue Nachricht in „chat.member_new_one.message_text“ gespeichert und erneut als unbearbeitet markiert, indem „chat.member_new_one.done“ auf false gesetzt wird. Dadurch wird sichergestellt, dass der Chat mit der neuesten Nachricht aktualisiert wird und dass das System weiß, dass die Nachricht noch nicht bearbeitet wurde. Als Nächstes müssen wir die Callback-Abfragen der Telegram-Chats behandeln.
//--- Handle callback queries from Telegram. if (obj_item["callback_query"].m_type != jv_UNDEF) { //--- Check if there is a callback query in the update. Class_CallbackQuery obj_cb_query; //--- Create an instance of Class_CallbackQuery. //... }
Zunächst wird geprüft, ob die aktuelle Aktualisierung („obj_item“) eine Callback-Abfrage enthält, indem überprüft wird, ob der Typ des Feldes „callback_query“ („m_type“) ungleich „jv_UNDEF“ ist. Dadurch wird sichergestellt, dass eine Callback-Abfrage innerhalb der Aktualisierung existiert. Wenn diese Bedingung erfüllt ist, wird eine Instanz des Objekts „Class_CallbackQuery“ mit dem Namen „obj_cb_query“ erstellt. Dieses Objekt wird zum Speichern und Verwalten der Details der Callback-Abfrage verwendet. Anschließend können wir das Objekt verwenden, um die Daten der Callback-Abfrage abzurufen und zu speichern.
obj_cb_query.id = obj_item["callback_query"]["id"].ToStr(); //--- Extract and store the callback query ID. obj_cb_query.from_id = obj_item["callback_query"]["from"]["id"].ToInt(); //--- Extract and store the sender's ID. obj_cb_query.from_first_name = obj_item["callback_query"]["from"]["first_name"].ToStr(); //--- Extract and store the sender's first name. obj_cb_query.from_first_name = decodeStringCharacters(obj_cb_query.from_first_name); //--- Decode any special characters in the sender's first name. obj_cb_query.from_last_name = obj_item["callback_query"]["from"]["last_name"].ToStr(); //--- Extract and store the sender's last name. obj_cb_query.from_last_name = decodeStringCharacters(obj_cb_query.from_last_name); //--- Decode any special characters in the sender's last name. obj_cb_query.from_username = obj_item["callback_query"]["from"]["username"].ToStr(); //--- Extract and store the sender's username. obj_cb_query.from_username = decodeStringCharacters(obj_cb_query.from_username); //--- Decode any special characters in the sender's username. obj_cb_query.message_id = obj_item["callback_query"]["message"]["message_id"].ToInt(); //--- Extract and store the message ID related to the callback. obj_cb_query.message_text = obj_item["callback_query"]["message"]["text"].ToStr(); //--- Extract and store the message text related to the callback. obj_cb_query.message_text = decodeStringCharacters(obj_cb_query.message_text); //--- Decode any special characters in the message text. obj_cb_query.data = obj_item["callback_query"]["data"].ToStr(); //--- Extract and store the callback data. obj_cb_query.data = decodeStringCharacters(obj_cb_query.data); //--- Decode any special characters in the callback data. obj_cb_query.chat_id = obj_item["callback_query"]["message"]["chat"]["id"].ToInt(); //--- Extract and store the chat ID.
Wir beginnen mit den Details der Callback-Abfrage selbst. Die ID der Callback-Abfrage wird dem Feld „callback_query“ entnommen. Wir verwenden die Methode „ToStr“, um sie in das String-Format umzuwandeln, und speichern sie in „obj_cb_query.id“. Die nächste Information, die wir extrahieren, ist die ID des Absenders, die wir dem Feld „from“ entnehmen. Auch hier verwenden wir die Methode „ToInt“ und speichern die umgewandelte Zahl in „obj_cb_query.from_id“. Danach nehmen wir den Vornamen des Absenders, der sich im Feld „from“ befindet, und wandeln ihn in das String-Format um. Der Vorname des Absenders wird in „obj_cb_query.from_first_name“ gespeichert. Als Letztes verwenden wir die Funktion „decodeStringCharacters“, um alle Sonderzeichen zu dekodieren, die im Vornamen vorkommen könnten.
Parallel dazu wird der Nachname des Absenders ermittelt, in eine Zeichenkette umgewandelt und in „obj_cb_query.from_last_name“ abgelegt. Wie zuvor rufen wir „decodeStringCharacters“ auf, um alle Sonderzeichen im Nachnamen zu decodieren. Das Verfahren, um den Nutzernamen des Absenders zu erhalten, ist dasselbe: Wir extrahieren den Nutzernamen, speichern ihn in „obj_cb_query.from_username“ und verwenden „decodeStringCharacters“, um alle Sonderzeichen zu bearbeiten, die die korrekte Funktion des Nutzernamens in der Zukunft behindern könnten.
Als Nächstes befassen wir uns mit der zugehörigen Nachricht der Callback-Abfrage. Wir nehmen die Nachrichten-ID aus dem Feld „message“, wandeln sie in eine Ganzzahl um und speichern sie in „obj_cb_query.message_id“. In der Zwischenzeit wird auch der Nachrichtentext extrahiert und in eine Zeichenkette umgewandelt, die in „obj_cb_query.message_text“ gespeichert wird. Alle Sonderzeichen im Text werden dekodiert. Anschließend wenden wir uns den Callback-Daten zu. Wir extrahieren sie, wandeln sie in eine Zeichenkette um und speichern sie in „obj_cb_query.data“. Wie alle anderen Daten sind auch die Callback-Daten mit Sonderzeichen kodiert.
Schließlich erhalten wir von der Callback-Abfrage die ID des Chats, an den die Nachricht gesendet wurde, wandeln diese in eine Ganzzahl um und tragen sie in „obj_cb_query.chat_id“ ein. So erhalten wir alle Informationen über die Callback-Abfrage, einschließlich des Nutzers, der im Chat war, der Nachricht und der Callback-Daten. Anschließend verarbeiten wir die Daten und aktualisieren die Iteration.
ProcessCallbackQuery(obj_cb_query); //--- Call function to process the callback query. member_update_id = obj_item["update_id"].ToInt() + 1; //--- Update the last processed update ID for callback queries.
Hier rufen wir die Funktion „ProcessCallbackQuery“ auf und übergeben das Objekt „obj_cb_query“ als Argument. Diese Funktion ist für die Bearbeitung der Callback-Anfrage und die Verarbeitung der zuvor gesammelten Details wie Nutzerinformationen, Chat-ID, Nachrichtentext und Callback-Daten zuständig. Durch den Aufruf dieser Funktion stellen wir sicher, dass die Callback-Abfrage entsprechend ihrem spezifischen Inhalt behandelt wird.
Nach der Verarbeitung der Callback-Abfrage aktualisieren wir die zuletzt verarbeitete Aktualisierungs-ID, indem wir die „update_id“ aus dem Feld „obj_item“ abrufen, in eine Ganzzahl umwandeln und dann 1 dazu addieren. Dieser Wert wird in der Datei „member_update_id“ gespeichert, die die zuletzt durchgeführte Aktualisierung enthält. Dieser Schritt stellt sicher, dass wir dieselbe Callback-Abfrage in zukünftigen Iterationen nicht erneut bearbeiten und den Aktualisierungsfortschritt effizient verfolgen können. Schließlich müssen wir nach der Verarbeitung der ersten Nachricht die Nachricht als bereits bearbeitet kennzeichnen, um eine erneute Verarbeitung zu vermeiden.
member_first_remove = false; //--- After processing the first message, mark that the first message has been handled.
Wir setzen die Variable „member_first_remove“ auf „false“, nachdem wir die erste Nachricht bearbeitet haben. Das bedeutet, dass wir uns um die erste Nachricht kümmern, und wenn etwas Besonderes für sie getan werden muss, haben wir es jetzt getan. Warum machen wir diesen Schritt? Sie soll kennzeichnen, dass die erste Nachricht bearbeitet wurde und dass sie nicht noch einmal bearbeitet werden wird. Auf diese Weise wird sichergestellt, dass zukünftige Logik, die davon abhängt, dass die erste Nachricht nicht verarbeitet wird, nicht ausgeführt wird, da sie nicht benötigt wird.
Der vollständige Quellcodeausschnitt, der für das Abrufen und Speichern von Chatnachrichten und Callback-Abfragen verantwortlich ist, ist unten dargestellt:
//+------------------------------------------------------------------+ //| Function to get chat updates from Telegram | //+------------------------------------------------------------------+ int Class_Bot_EA::getChatUpdates(void) { if (member_token == NULL) { //--- If bot token is empty Print("ERR: TOKEN EMPTY"); //--- Print error message indicating empty token. return (-1); //--- Return -1 to indicate error. } string out; //--- String to hold response data. string url = TELEGRAM_BASE_URL + "/bot" + member_token + "/getUpdates"; //--- Construct URL for Telegram API. string params = "offset=" + IntegerToString(member_update_id); //--- Set parameters including the offset based on the last update ID. int res = postRequest(out, url, params, WEB_TIMEOUT); //--- Send a POST request to Telegram with a timeout. if (res == 0) { //--- If request succeeds (res = 0) CJSONValue obj_json(NULL, jv_UNDEF); //--- Create a JSON object to parse the response. bool done = obj_json.Deserialize(out); //--- Deserialize the response. if (!done) { //--- If deserialization fails Print("ERR: JSON PARSING"); //--- Print error message indicating JSON parsing error. return (-1); //--- Return -1 to indicate error. } bool ok = obj_json["ok"].ToBool(); //--- Check if the response has "ok" field set to true. if (!ok) { //--- If "ok" field is false Print("ERR: JSON NOT OK"); //--- Print error message indicating that JSON response is not okay. return (-1); //--- Return -1 to indicate error. } int total = ArraySize(obj_json["result"].m_elements); //--- Get the total number of update elements. for (int i = 0; i < total; i++) { //--- Iterate through each update element. CJSONValue obj_item = obj_json["result"].m_elements[i]; //--- Access individual update element. if (obj_item["message"].m_type != jv_UNDEF) { //--- Check if the update has a message. Class_Message obj_msg; //--- Create an instance of Class_Message to store the message details. obj_msg.update_id = obj_item["update_id"].ToInt(); //--- Extract and store update ID. obj_msg.message_id = obj_item["message"]["message_id"].ToInt(); //--- Extract and store message ID. obj_msg.message_date = (datetime)obj_item["message"]["date"].ToInt(); //--- Extract and store message date. obj_msg.message_text = obj_item["message"]["text"].ToStr(); //--- Extract and store message text. obj_msg.message_text = decodeStringCharacters(obj_msg.message_text); //--- Decode any special characters in the message text. obj_msg.from_id = obj_item["message"]["from"]["id"].ToInt(); //--- Extract and store the sender's ID. obj_msg.from_first_name = obj_item["message"]["from"]["first_name"].ToStr(); //--- Extract and store the sender's first name. obj_msg.from_first_name = decodeStringCharacters(obj_msg.from_first_name); //--- Decode any special characters in the sender's first name. obj_msg.from_last_name = obj_item["message"]["from"]["last_name"].ToStr(); //--- Extract and store the sender's last name. obj_msg.from_last_name = decodeStringCharacters(obj_msg.from_last_name); //--- Decode any special characters in the sender's last name. obj_msg.from_username = obj_item["message"]["from"]["username"].ToStr(); //--- Extract and store the sender's username. obj_msg.from_username = decodeStringCharacters(obj_msg.from_username); //--- Decode any special characters in the sender's username. obj_msg.chat_id = obj_item["message"]["chat"]["id"].ToInt(); //--- Extract and store the chat ID. obj_msg.chat_first_name = obj_item["message"]["chat"]["first_name"].ToStr(); //--- Extract and store the chat's first name. obj_msg.chat_first_name = decodeStringCharacters(obj_msg.chat_first_name); //--- Decode any special characters in the chat's first name. obj_msg.chat_last_name = obj_item["message"]["chat"]["last_name"].ToStr(); //--- Extract and store the chat's last name. obj_msg.chat_last_name = decodeStringCharacters(obj_msg.chat_last_name); //--- Decode any special characters in the chat's last name. obj_msg.chat_username = obj_item["message"]["chat"]["username"].ToStr(); //--- Extract and store the chat's username. obj_msg.chat_username = decodeStringCharacters(obj_msg.chat_username); //--- Decode any special characters in the chat's username. obj_msg.chat_type = obj_item["message"]["chat"]["type"].ToStr(); //--- Extract and store the chat type. //--- Process the message based on chat ID. member_update_id = obj_msg.update_id + 1; //--- Update the last processed update ID. if (member_first_remove) { //--- If it's the first message after starting the bot continue; //--- Skip processing it. } //--- Check if we need to filter messages based on user or if no filter is applied. if (member_users_filter.Total() == 0 || (member_users_filter.Total() > 0 && member_users_filter.SearchLinear(obj_msg.from_username) >= 0)) { int index = -1; //--- Initialize index to -1 (indicating no chat found). for (int j = 0; j < member_chats.Total(); j++) { //--- Iterate through all chat objects. Class_Chat *chat = member_chats.GetNodeAtIndex(j); //--- Get chat object by index. if (chat.member_id == obj_msg.chat_id) { //--- If chat ID matches index = j; //--- Store the index. break; //--- Break the loop since we found the chat. } } if (index == -1) { //--- If no matching chat was found member_chats.Add(new Class_Chat); //--- Create a new chat object and add it to the list. Class_Chat *chat = member_chats.GetLastNode(); //--- Get the last (newly added) chat object. chat.member_id = obj_msg.chat_id; //--- Assign the chat ID. chat.member_time = TimeLocal(); //--- Record the current time for the chat. chat.member_state = 0; //--- Initialize the chat state to 0. chat.member_new_one.message_text = obj_msg.message_text; //--- Store the new message in the chat. chat.member_new_one.done = false; //--- Mark the new message as not processed. } else { //--- If matching chat was found Class_Chat *chat = member_chats.GetNodeAtIndex(index); //--- Get the chat object by index. chat.member_time = TimeLocal(); //--- Update the time for the chat. chat.member_new_one.message_text = obj_msg.message_text; //--- Store the new message. chat.member_new_one.done = false; //--- Mark the new message as not processed. } } } //--- Handle callback queries from Telegram. if (obj_item["callback_query"].m_type != jv_UNDEF) { //--- Check if there is a callback query in the update. Class_CallbackQuery obj_cb_query; //--- Create an instance of Class_CallbackQuery. obj_cb_query.id = obj_item["callback_query"]["id"].ToStr(); //--- Extract and store the callback query ID. obj_cb_query.from_id = obj_item["callback_query"]["from"]["id"].ToInt(); //--- Extract and store the sender's ID. obj_cb_query.from_first_name = obj_item["callback_query"]["from"]["first_name"].ToStr(); //--- Extract and store the sender's first name. obj_cb_query.from_first_name = decodeStringCharacters(obj_cb_query.from_first_name); //--- Decode any special characters in the sender's first name. obj_cb_query.from_last_name = obj_item["callback_query"]["from"]["last_name"].ToStr(); //--- Extract and store the sender's last name. obj_cb_query.from_last_name = decodeStringCharacters(obj_cb_query.from_last_name); //--- Decode any special characters in the sender's last name. obj_cb_query.from_username = obj_item["callback_query"]["from"]["username"].ToStr(); //--- Extract and store the sender's username. obj_cb_query.from_username = decodeStringCharacters(obj_cb_query.from_username); //--- Decode any special characters in the sender's username. obj_cb_query.message_id = obj_item["callback_query"]["message"]["message_id"].ToInt(); //--- Extract and store the message ID related to the callback. obj_cb_query.message_text = obj_item["callback_query"]["message"]["text"].ToStr(); //--- Extract and store the message text related to the callback. obj_cb_query.message_text = decodeStringCharacters(obj_cb_query.message_text); //--- Decode any special characters in the message text. obj_cb_query.data = obj_item["callback_query"]["data"].ToStr(); //--- Extract and store the callback data. obj_cb_query.data = decodeStringCharacters(obj_cb_query.data); //--- Decode any special characters in the callback data. obj_cb_query.chat_id = obj_item["callback_query"]["message"]["chat"]["id"].ToInt(); //--- Extract and store the chat ID. ProcessCallbackQuery(obj_cb_query); //--- Call function to process the callback query. member_update_id = obj_item["update_id"].ToInt() + 1; //--- Update the last processed update ID for callback queries. } } member_first_remove = false; //--- After processing the first message, mark that the first message has been handled. } return 0; //--- Return 0 to indicate successful processing of updates. }
Nachdem wir die Chat-Updates erhalten haben, müssen wir nun mit der Verarbeitung der Antworten fortfahren. Dies wird im folgenden Abschnitt behandelt.
Handhabung von Callback-Abfragen für Schaltflächenaktionen
In diesem Abschnitt behandeln wir eingehende Nachrichten und reagieren mit Inline-Schaltflächen auf der Grundlage bestimmter Befehle. Als Erstes müssen wir den Initialisierungsbefehl oder die vom Nutzer gesendete Nachricht verarbeiten, und von dort aus können wir die Inline-Schaltflächen instanziieren und dann die Callback-Abfragen abrufen. Wir können diesen Schritt nicht überspringen, weil wir nicht einfach eine Inline-Tastatur bereitstellen können, ohne vorher einen Befehl vom Nutzer zu erhalten. Das ist die Logik, die wir anwenden.
#define BTN_MENU "BTN_MENU" //--- Identifier for menu button //+------------------------------------------------------------------+ //| Process new messages | //+------------------------------------------------------------------+ void Class_Bot_EA::ProcessMessages(void){ //--- Loop through all chats for(int i=0; i<member_chats.Total(); i++){ Class_Chat *chat = member_chats.GetNodeAtIndex(i); //--- Get the current chat if(!chat.member_new_one.done){ //--- Check if the message has not been processed yet chat.member_new_one.done = true; //--- Mark the message as processed string text = chat.member_new_one.message_text; //--- Get the message text //--- Example of sending a message with inline buttons if (text == "Start" || text == "/start" || text == "Help" || text == "/help"){ string message = "Welcome! You can control me via inline buttons!"; //--- Welcome message //--- Define inline button to provide menu string buttons = "[[{\"text\": \"Provide Menu\", \"callback_data\": \""+BTN_MENU+"\"}]]"; sendMessageToTelegram(chat.member_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup } } } }
In diesem Fall richten wir eine Funktion namens „ProcessMessages“ ein, die sich um die in unserem System eingehenden Nutzernachrichten kümmert. Das erste, was diese Funktion tut, ist, die Menge aller Chats zu durchlaufen, die wir in „member_chats“ gespeichert haben. Für jeden dieser Chats erhalten wir das Chat-Objekt, das dem aktuellen Chat entspricht, indem wir „GetNodeAtIndex(i)“ aufrufen. Da wir nun den aktuellen Chat im Griff haben, prüfen wir, ob die Nachricht in „member_new_one“ bereits bearbeitet wurde. Ist dies nicht der Fall, kennzeichnen wir sie als bearbeitet.
Als Nächstes extrahieren wir den eigentlichen Inhalt der Nachricht mit „chat.member_new_one.message_text“. Wir werten diesen Inhalt aus, um festzustellen, ob der Nutzer Befehle wie „Start“, „/start“, „Help“ oder „/help“ gesendet hat. Wenn wir einen solchen Befehl erhalten, geben wir eine Nachricht zurück, die den Nutzer begrüßt und ihm sagt: „You can control me via inline buttons!“ (Sie können mich über Inline-Schaltflächen steuern!) Dann definieren wir eine Inline-Callback-Schaltfläche, die als Menüoption für den Nutzer dienen soll. Wir verwenden das Feld „callback_data“ der Schaltfläche, um anzugeben, dass sie mit „BTN_MENU“ verbunden ist. Wir formatieren die Schaltfläche als JSON-Objekt und speichern sie in der Variablen „buttons“.
Abschließend wird die Funktion „sendMessageToTelegram“ aufgerufen, um die Willkommensnachricht und unsere nutzerdefinierte Inline-Tastatur an den Nutzer zu senden. Diese Funktion nimmt drei Parameter entgegen: die „chat.member_id“, die „Nachricht“ und die Schaltflächenmarkierung, die von der Funktion „customInlineKeyboardMarkup“ erzeugt wird. Die Nachricht wird zusammen mit unseren Inline-Schaltflächen an den Nutzer gesendet. Sie können nun mit den Inline-Buttons so interagieren, wie ein typischer Nutzer mit einem Telegram-Bot interagiert. Da wir Neulinge auf dem Gebiet der Inline-Tastatur sind, wollen wir uns auf die Logik konzentrieren.
string buttons = "[[{\"text\": \"Provide Menu\", \"callback_data\": \""+BTN_MENU+"\"}]]";
Detaillierte Aufschlüsselung:
Äußere Klammern: Die gesamte Zeichenkette ist in doppelte Anführungszeichen (" ") eingeschlossen, was in vielen Programmiersprachen für die Definition von Stringliteralen typisch ist. Innerhalb dieser Zeichenkette sehen wir die Zeichen "[[ ... ]]". Diese Klammern werden verwendet, um die Struktur der Inline-Tastatur zu definieren:
- Der erste Satz von Klammern [ ... ] steht für eine Reihe von Zeilen in der Tastatur.
- Der zweite Satz von Klammern [ ... ] steht für eine Zeile innerhalb dieses Feldes. In diesem Fall gibt es nur eine Zeile.
Definition der Schaltfläche:
Innerhalb der zweiten Gruppe von Klammern befindet sich ein Objekt "Provide Menu", "callback_data": " + BTN_MENU + "}. Dieses Objekt definiert eine einzelne Schaltfläche:
- „text“: Diese Taste gibt die Bezeichnung der Schaltfläche an, die „Provide Menu“ (Menü bereitstellen) lautet. Dies ist der Text, der auf der Schaltfläche angezeigt wird, wenn der Nutzer sie sieht.
- „callback_data“: Dieser Schlüssel gibt die Daten an, die an den Bot zurückgesendet werden, wenn die Schaltfläche angeklickt wird. In diesem Fall ist der Wert „BTN_MENU“, eine Konstante, die wir an anderer Stelle im Code definiert haben. So kann der Bot erkennen, welche Schaltfläche angeklickt wurde, und entsprechend reagieren.
Kombinierte Elemente:
Die Konstante „BTN_MENU“ wird mittels String-Konkatenation in den JSON-String eingefügt. Dies ermöglicht die dynamische Einbindung der Callback-Daten der Schaltfläche. Wenn „BTN_MENU“ zum Beispiel „BTN_MENU“ ist, würde das resultierende JSON wie folgt aussehen: [{"text": "Provide Menu", "callback_data": "BTN_MENU"}].
Endgültiges Format:
Das endgültige Format der Zeichenkette für die Schaltflächen lautet, wenn sie im Code verwendet wird: „ [ [{"text": "Provide Menu", "callback_data": "BTN_MENU"}] ]“. Dieses Format gibt an, dass es eine Zeile auf der Tastatur gibt und dass diese Zeile eine Taste enthält.
Wenn die Telegram-API diese JSON-Struktur empfängt, interpretiert sie sie als eine Inline-Tastatur mit einer einzigen Schaltfläche. Wenn ein Nutzer auf diese Schaltfläche klickt, erhält der Bot in der Callback-Abfrage die Callback-Daten „BTN_MENU“, anhand derer er dann die passende Antwort bestimmen kann. In der Struktur haben wir eine nutzerdefinierte Funktion verwendet, um die Inline-Schaltfläche zu erstellen. Die Logik ist wie folgt:
//+------------------------------------------------------------------+ //| Create a custom inline keyboard markup for Telegram | //+------------------------------------------------------------------+ string customInlineKeyboardMarkup(const string buttons){ //--- Construct the JSON string for the inline keyboard markup string result = "{\"inline_keyboard\": " + UrlEncode(buttons) + "}"; //--- Encode buttons as JSON return(result); }
Die Funktion „customInlineKeyboardMarkup“ erstellt ein nutzerdefiniertes Inline-Keyboard-Markup für Telegram-Nachrichten. Dazu beginnen wir mit einem String-Parameter, „buttons“, der die JSON-Struktur enthält, die die Inline-Schaltflächen definiert. Unsere Aufgabe ist es, ein JSON-Objekt zu erstellen, das Telegram verwenden kann, um die Inline-Tastatur darzustellen. Wir beginnen mit der Bildung der JSON-Struktur mit dem Schlüssel „inline_keyboard“. Als Nächstes verwenden wir die Funktion „UrlEncode“, um alle Sonderzeichen zu behandeln, die in der Zeichenkette „buttons“ enthalten sein könnten. Dieser Kodierungsschritt ist wichtig, da es sonst zu Problemen mit Sonderzeichen in den Schaltflächendefinitionen kommen kann. Nach dem Anhängen der kodierten Zeichenfolge für die Schaltflächen wird das JSON-Objekt geschlossen. Die Ergebniszeichenfolge ist eine gültige JSON-Darstellung der Inline-Tastaturauszeichnung. Wir geben diese Zeichenkette zurück, damit er an die Telegram-API gesendet werden kann, die dann die Inline-Tastatur in der Nachricht interaktiv wiedergibt. Nach der Ausführung des Programms erhalten wir die folgende Ausgabe.
Wie wir sehen, war das ein Erfolg. Wir haben die Inline-Schaltfläche erstellt. Wir können jedoch noch nicht auf seine Klicks reagieren. Daher müssen wir die empfangene Callback-Abfrage erfassen und entsprechend auf den Klick reagieren. Um dies zu erreichen, müssen wir eine Funktion erstellen, die die Abfragedaten abruft.
//+------------------------------------------------------------------+ //| Function to process callback queries | //+------------------------------------------------------------------+ void Class_Bot_EA::ProcessCallbackQuery(Class_CallbackQuery &cb_query) { Print("Callback Query ID: ", cb_query.id); //--- Log the callback query ID Print("Chat Token: ", member_token); //--- Log the member token Print("From First Name: ", cb_query.from_first_name); //--- Log the sender's first name Print("From Last Name: ", cb_query.from_last_name); //--- Log the sender's last name Print("From Username: ", cb_query.from_username); //--- Log the sender's username Print("Message ID: ", cb_query.message_id); //--- Log the message ID Print("Message Text: ", cb_query.message_text); //--- Log the message text Print("Callback Data: ", cb_query.data); //--- Log the callback data }
Die Funktion „ProcessCallbackQuery“ verwaltet die Details einer Callback-Abfrage, die von Telegram kommt. Sie arbeitet mit einer Instanz von „Class_CallbackQuery“, die alle mit dem Rückruf verbundenen Informationen enthält. Zunächst wird die ID der Callback-Abfrage protokolliert, die eine eindeutige Kennung für die Abfrage darstellt und für die Verfolgung und Verwaltung der Abfrage unerlässlich ist. Als Nächstes protokolliert die Funktion den „member_token“. Die Rolle dieses Tokens ist es, anzugeben, welcher Bot oder welches Mitglied den Rückruf bearbeitet und somit sicherzustellen, dass der richtige Bot und nur ein Bot die Anfrage bearbeitet.
Wir erfassen dann den Vor- und Nachnamen des Absenders mit „cb_query.from_first_name“ bzw. „cb_query.from_last_name“. So können wir die Identität des Nutzers, der die Inline-Schaltfläche betätigt hat, feststellen und ihm eine persönliche Note geben, falls wir ihn in Zukunft einmal ansprechen müssen. Apropos Identität: Wir erfassen auch den Nutzernamen des Absenders mit „cb_query.from_username“. Dies gibt uns eine weitere Möglichkeit, den Nutzer in Zukunft direkt anzusprechen, falls dies erforderlich sein sollte. Nachdem wir die Identität des Absenders erfasst haben, protokollieren wir die ID der Nachricht, die mit dem Rückruf verbunden war, mit „cb_query.message_id“. Wenn wir diese ID kennen, wissen wir, um welche spezifische Nachricht es sich beim Tastendruck handelt.
Außerdem protokollieren wir den Nachrichtentext mit „cb_query.message_text“. Dies liefert Kontext über die Nachricht, wenn die Schaltfläche angeklickt wurde. Wir protokollieren auch die Callback-Daten mit „cb_query.data“. Diese Daten werden vom Klick auf die Schaltfläche zurückgesendet und dienen dazu, auf der Grundlage der Interaktion des Nutzers die zu ergreifenden Maßnahmen zu bestimmen. Durch die Protokollierung dieser Details erhalten wir einen umfassenden Überblick über die Callback-Abfrage. Dies ist praktisch für die Fehlersuche und ermöglicht ein besseres Verständnis der Nutzerinteraktionen mit dem Bot. Wenn wir das Programm ausführen, erhalten wir diese Ausgaben im Handelsterminal.
Da wir wissen, dass wir die Informationen erhalten, können wir die angeklickte Schaltfläche überprüfen und eine entsprechende Antwort erzeugen. In unserem Fall verwenden wir die Callback-Daten aus der Aktion der Menüschaltfläche. Zunächst werden wir die Schaltflächenkonstanten definieren. Zum besseren Verständnis haben wir ausführliche Kommentare hinzugefügt.
#define BTN_NAME "BTN_NAME" //--- Identifier for name button #define BTN_INFO "BTN_INFO" //--- Identifier for info button #define BTN_QUOTES "BTN_QUOTES" //--- Identifier for quotes button #define BTN_MORE "BTN_MORE" //--- Identifier for more options button #define BTN_SCREENSHOT "BTN_SCREENSHOT" //--- Identifier for screenshot button #define EMOJI_CANCEL "\x274C" //--- Cross mark emoji #define EMOJI_UP "\x2B06" //--- Upwards arrow emoji #define BTN_BUY "BTN_BUY" //--- Identifier for buy button #define BTN_CLOSE "BTN_CLOSE" //--- Identifier for close button #define BTN_NEXT "BTN_NEXT" //--- Identifier for next button #define EMOJI_PISTOL "\xF52B" //--- Pistol emoji #define BTN_CONTACT "BTN_CONTACT" //--- Identifier for contact button #define BTN_JOIN "BTN_JOIN" //--- Identifier for join button
Nachdem wir die Funktion definiert haben, können wir die Antworten abrufen.
//--- Respond based on the callback data string response_text; if (cb_query.data == BTN_MENU) { response_text = "You clicked "+BTN_MENU+"!"; //--- Prepare response text for BTN_MENU Print("RESPONSE = ", response_text); //--- Log the response //--- Send the response message to the correct group/channel chat ID sendMessageToTelegram(cb_query.chat_id, response_text, NULL); string message = "Information"; //--- Message to display options //--- Define inline buttons with callback data string buttons = "[[{\"text\": \"Get Expert's Name\", \"callback_data\": \""+BTN_NAME+"\"}]," "[{\"text\": \"Get Account Information\", \"callback_data\": \""+BTN_INFO+"\"}]," "[{\"text\": \"Get Current Market Quotes\", \"callback_data\": \""+BTN_QUOTES+"\"}]," "[{\"text\": \"More\", \"callback_data\": \""+BTN_MORE+"\"}, {\"text\": \"Screenshots\", \"callback_data\": \""+BTN_SCREENSHOT+"\"}, {\"text\": \""+EMOJI_CANCEL+"\", \"callback_data\": \""+EMOJI_CANCEL+"\"}]]"; sendMessageToTelegram(cb_query.chat_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup }
Hier verwalten wir die Antwort auf eine Callback-Abfrage auf der Grundlage ihrer Callback-Daten. Wir beginnen mit der Initialisierung einer String-Variablen, „response_text“, die die Nachricht enthält, die wir an den Nutzer zurücksenden wollen. Anschließend wird geprüft, ob die „callback_data“ aus der Callback-Abfrage („cb_query.data“) mit der Konstante „BTN_MENU“ übereinstimmen. Wenn dies der Fall ist, setzen wir „response_text“ auf "You clicked "+BTN_MENU+"!", was die Betätigung der Schaltfläche bestätigt und den Bezeichner für die angeklickte Schaltfläche enthält. Wir protokollieren diese Antwort mit der Funktion „Print“, um zu verfolgen, was gesendet wird.
Als Nächstes verwenden wir die Funktion „sendMessageToTelegram“, um die Nachricht „response_text“ an den durch „cb_query.chat_id“ identifizierten Chat zu senden. Da wir in diesem Stadium eine einfache Textnachricht ohne Inline-Tastatur senden, ist der dritte Parameter NULL, was anzeigt, dass keine zusätzliche Tastaturbeschriftung enthalten ist.
Nach dem Versand der ersten Nachricht bereiten wir eine neue Nachricht mit dem Text „Information“ vor, die dem Nutzer verschiedene Optionen bietet. Anschließend definieren wir die Inline-Schaltflächen mithilfe einer JSON-ähnlichen Struktur in der Zeichenfolge „buttons“. Diese Struktur umfasst Schaltflächen mit Bezeichnungen wie „Name des Experten abrufen“, „Kontoinformationen abrufen“, „Aktuelle Marktkurse abrufen“, „Mehr“, „Screenshots“ und „Abbrechen“. Jeder Taste werden spezifische „callback_data“-Werte zugewiesen, wie „BTN_NAME“, „BTN_INFO“, „BTN_QUOTES“, „BTN_MORE“, „BTN_SCREENSHOT“ und „EMOJI_CANCEL“, die dabei helfen, festzustellen, welche Taste gedrückt wurde.
Schließlich senden wir diese neue Nachricht zusammen mit der Inline-Tastatur mit der Funktion „sendMessageToTelegram“. Die Inline-Tastatur wird von der Funktion „customInlineKeyboardMarkup“ in JSON formatiert, um sicherzustellen, dass Telegram die Schaltflächen korrekt anzeigen kann. Dieser Ansatz ermöglicht es uns, die Nutzer interaktiv einzubinden, indem wir ihnen verschiedene Optionen direkt auf der Telegram-Oberfläche anbieten. Nach der Kompilierung erhalten wir die folgenden Ergebnisse.
Das war ein Erfolg. Nun müssen wir auf die entsprechenden Callback-Abfragedaten reagieren, die wir von den bereitgestellten Inline-Schaltflächen erhalten. Beginnen wir mit demjenigen, der für die Namensgebung des Programms verantwortlich ist.
else if (cb_query.data == BTN_NAME) { response_text = "You clicked "+BTN_NAME+"!"; //--- Prepare response text for BTN_NAME Print("RESPONSE = ", response_text); //--- Log the response string message = "The file name of the EA that I control is:\n"; //--- Message with EA file name message += "\xF50B"+__FILE__+" Enjoy.\n"; //--- Append the file name and a friendly message sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message }
Dieser Abschnitt verwaltet eine bestimmte Callback-Abfrage, bei der callback_data gleich „BTN_NAME“ ist. Zunächst wird ein Antworttext in der Variablen „response_text“ eingegeben. Wenn die „callback_data“ mit „BTN_NAME“ übereinstimmen, setzen wir „response_text“ auf „You clicked “ + BTN_NAME + „!“ Damit wird die Betätigung der Schaltfläche bestätigt und die Kennung der angeklickten Schaltfläche angegeben. Wir geben diese Antwort dann mit der Funktion „Print“ aus, um zu sehen, was an den Nutzer gesendet wird.
Anschließend erstellen wir eine neuartige Nachricht, die Einzelheiten über die EA-Datei (Expert Advisor) enthält, über die der Bot die Kontrolle hat. Dieses vom Bot generierte Schreiben beginnt mit den Worten „The file name of the EA that I control is:\n“ (Der Dateiname des EA, den ich kontrolliere, lautet:\n) und fügt dann den Namen der aktuellen Quelldatei, dargestellt durch __FILE__, an die Nachricht an, gekrönt von einem freundlichen „Viel Spaß“. Eine Besonderheit ist, dass das Schreiben mit dem Zeichen „\xF50B“ beginnt, das ein Symbol, eine typografische Verzierung oder einfach nur eine Möglichkeit darstellt, den Leser im Namen des Bot zu blenden.
Zum Schluss rufen wir die Funktion „sendMessageToTelegram“ auf, um die Nachricht an den Chat zu senden, der der „cb_query.chat_id“ entspricht. Für den dritten Parameter wird NULL übergeben, was bedeutet, dass keine Inline-Tastatur diese Nachricht begleiten wird. Wenn wir auf die Schaltfläche klicken, erhalten wir die folgende Antwort.
Das war ein Erfolg. Um nun die Reaktionsfähigkeit der anderen Schaltflächen, d. h. Kontoinformationen und Marktpreisnotierungen, zu ermitteln, wird ein ähnlicher Ansatz verwendet.
else if (cb_query.data == BTN_INFO) { response_text = "You clicked "+BTN_INFO+"!"; //--- Prepare response text for BTN_INFO Print("RESPONSE = ", response_text); //--- Log the response ushort MONEYBAG = 0xF4B0; //--- Define money bag emoji string MONEYBAGcode = ShortToString(MONEYBAG); //--- Convert emoji code to string string currency = AccountInfoString(ACCOUNT_CURRENCY); //--- Get the account currency //--- Construct the account information message string message = "\x2733\Account No: "+(string)AccountInfoInteger(ACCOUNT_LOGIN)+"\n"; message += "\x23F0\Account Server: "+AccountInfoString(ACCOUNT_SERVER)+"\n"; message += MONEYBAGcode+"Balance: "+(string)AccountInfoDouble(ACCOUNT_BALANCE)+" "+currency+"\n"; message += "\x2705\Profit: "+(string)AccountInfoDouble(ACCOUNT_PROFIT)+" "+currency+"\n"; sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message } else if (cb_query.data == BTN_QUOTES) { response_text = "You clicked "+BTN_QUOTES+"!"; //--- Prepare response text for BTN_QUOTES Print("RESPONSE = ", response_text); //--- Log the response double Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); //--- Get the current ask price double Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Get the current bid price //--- Construct the market quotes message string message = "\xF170 Ask: "+(string)Ask+"\n"; message += "\xF171 Bid: "+(string)Bid+"\n"; sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message }
Nach der Kompilierung erhalten wir die folgenden Ergebnisse:
Das war ein Erfolg. Wir fahren nun fort, auch die Inline-Schaltfläche „More“ zu behandeln. Bis zu diesem Punkt können Sie sehen, dass wir die Schnittstelle oder das Chatfeld nicht mit Nachrichten überladen. Sie ist sauber und wir verwenden die Inline-Schaltflächen effizient wieder.
else if (cb_query.data == BTN_MORE) { response_text = "You clicked "+BTN_MORE+"!"; //--- Prepare response text for BTN_MORE Print("RESPONSE = ", response_text); //--- Log the response string message = "Choose More Options Below:\n"; //--- Message to prompt for additional options message += "Trading Operations"; //--- Title for trading operations //--- Define inline buttons for additional options string buttons = "[[{\"text\": \""+EMOJI_UP+"\", \"callback_data\": \""+EMOJI_UP+"\"}]," "[{\"text\": \"Buy\", \"callback_data\": \""+BTN_BUY+"\"}, {\"text\": \"Close\", \"callback_data\": \""+BTN_CLOSE+"\"}, {\"text\": \"Next\", \"callback_data\": \""+BTN_NEXT+"\"}]]"; sendMessageToTelegram(cb_query.chat_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup }
Hier behandeln wir eine Callback-Abfrage, bei der die Callback-Daten „BTN_MORE“ sind. Wir beginnen mit der Vorbereitung einer Antwortnachricht, die in der Variablen „response_text“ gespeichert wird. Wenn die Callback-Daten mit „BTN_MORE“ übereinstimmen, setzen wir „response_text“ auf "You clicked "+BTN_MORE+"!", was den Tastendruck bestätigt und die Kennung für die angeklickte Schaltfläche enthält. Diese Antwort wird mit der Funktion „Print“ protokolliert, um zu verfolgen, was gesendet wird.
Als Nächstes erstellen wir eine neue Nachricht, in der der Nutzer aufgefordert wird, aus zusätzlichen Optionen zu wählen. Die „message“-Variable beginnt mit „Choose More Options Below:\n“ (Wählen Sie unten weitere Optionen), gefolgt von „Trading Operations“ (Handelsaktivitäten), das als Titel für die Reihe von Optionen im Zusammenhang mit dem Handel dient. Anschließend definieren wir die Inline-Schaltflächen für diese zusätzlichen Optionen mithilfe einer JSON-ähnlichen Struktur in der Zeichenkette „buttons“. Diese Struktur umfasst:
- Eine Schaltfläche mit einem Emoji „EMOJI_UP“ und den entsprechenden „callback_data“ als „EMOJI_UP“.
- Eine Reihe von Schaltflächen für verschiedene Handelsoperationen: "Buy", "Close" und "Next" (Kaufen, Schließen und Weiter), jeweils mit den Werten von „callback_data“ für „BTN_BUY“, „BTN_CLOSE“ und „BTN_NEXT“.
Schließlich verwenden wir die Funktion „sendMessageToTelegram“, um diese Nachricht zusammen mit der Inline-Tastatur an den durch „cb_query.chat_id“ identifizierten Chat zu senden. Das Inline-Keyboard-Markup wird mit der Funktion „customInlineKeyboardMarkup“ in JSON formatiert. Wenn wir auf diese Schaltfläche klicken, sollten wir eine weitere erweiterte Schaltfläche erhalten. Dies wird im Folgenden veranschaulicht:
Das lief wie erwartet. Wir arbeiten jetzt nur noch an den neuen Schaltflächen, die auftauchen. Erstens ist es die nach oben gerichtete Emoji-Taste. Wenn es angeklickt wird, wollen wir zum vorherigen Menü, also dem Hauptmenü, zurückkehren.
else if (cb_query.data == EMOJI_UP) { response_text = "You clicked "+EMOJI_UP+"!"; //--- Prepare response text for EMOJI_UP Print("RESPONSE = ", response_text); //--- Log the response string message = "Choose a menu item:\n"; //--- Message to prompt for menu selection message += "Information"; //--- Title for information options //--- Define inline buttons for menu options string buttons = "[[{\"text\": \"Get Expert's Name\", \"callback_data\": \""+BTN_NAME+"\"}]," "[{\"text\": \"Get Account Information\", \"callback_data\": \""+BTN_INFO+"\"}]," "[{\"text\": \"Get Current Market Quotes\", \"callback_data\": \""+BTN_QUOTES+"\"}]," "[{\"text\": \"More\", \"callback_data\": \""+BTN_MORE+"\"}, {\"text\": \"Screenshots\", \"callback_data\": \""+BTN_SCREENSHOT+"\"}, {\"text\": \""+EMOJI_CANCEL+"\", \"callback_data\": \""+EMOJI_CANCEL+"\"}]]"; sendMessageToTelegram(cb_query.chat_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup }
Hier wird einfach die Standardtastatur des Hauptmenüs verwendet und gesendet. Mit der gleichen Logik reagieren wir auf die anderen Schaltflächen für Eröffnen und Schließen von Positionen wie unten dargestellt.
else if (cb_query.data == BTN_BUY) { response_text = "You clicked "+BTN_BUY+"!"; //--- Prepare response text for BTN_BUY Print("RESPONSE = ", response_text); //--- Log the response CTrade obj_trade; //--- Create a trade object double Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); //--- Get the current ask price double Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Get the current bid price //--- Open a buy position obj_trade.Buy(0.01, NULL, 0, Bid - 300 * _Point, Bid + 300 * _Point); double entry = 0, sl = 0, tp = 0, vol = 0; ulong ticket = obj_trade.ResultOrder(); //--- Get the ticket number of the new order if (ticket > 0) { if (PositionSelectByTicket(ticket)) { //--- Select the position by ticket entry = PositionGetDouble(POSITION_PRICE_OPEN); //--- Get the entry price sl = PositionGetDouble(POSITION_SL); //--- Get the stop loss price tp = PositionGetDouble(POSITION_TP); //--- Get the take profit price vol = PositionGetDouble(POSITION_VOLUME); //--- Get the volume } } //--- Construct the message with position details string message = "\xF340\Opened BUY Position:\n"; message += "Ticket: "+(string)ticket+"\n"; message += "Open Price: "+(string)entry+"\n"; message += "Lots: "+(string)vol+"\n"; message += "SL: "+(string)sl+"\n"; message += "TP: "+(string)tp+"\n"; sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message } else if (cb_query.data == BTN_CLOSE) { response_text = "You clicked "+BTN_CLOSE+"!"; //--- Prepare response text for BTN_CLOSE Print("RESPONSE = ", response_text); //--- Log the response CTrade obj_trade; //--- Create a trade object int totalOpenBefore = PositionsTotal(); //--- Get the total number of open positions before closing obj_trade.PositionClose(_Symbol); //--- Close the position for the symbol int totalOpenAfter = PositionsTotal(); //--- Get the total number of open positions after closing //--- Construct the message with position closure details string message = "\xF62F\Closed Position:\n"; message += "Total Positions (Before): "+(string)totalOpenBefore+"\n"; message += "Total Positions (After): "+(string)totalOpenAfter+"\n"; sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message }
Wenn wir das Programm ausführen, erhalten wir folgende Ergebnisse.
Fantastisch. In ähnlicher Weise fügen wir die anderen Kontrollsegmente wie folgt hinzu.
else if (cb_query.data == BTN_NEXT) { response_text = "You clicked "+BTN_NEXT+"!"; //--- Prepare response text for BTN_NEXT Print("RESPONSE = ", response_text); //--- Log the response string message = "Choose Still More Options Below:\n"; //--- Message to prompt for further options message += "More Options"; //--- Title for more options //--- Define inline buttons for additional options string buttons = "[[{\"text\": \""+EMOJI_UP+"\", \"callback_data\": \""+EMOJI_UP+"\"}, {\"text\": \"Contact\", \"callback_data\": \""+BTN_CONTACT+"\"}, {\"text\": \"Join\", \"callback_data\": \""+BTN_JOIN+"\"},{\"text\": \""+EMOJI_PISTOL+"\", \"callback_data\": \""+EMOJI_PISTOL+"\"}]]"; sendMessageToTelegram(cb_query.chat_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup } else if (cb_query.data == BTN_CONTACT) { response_text = "You clicked "+BTN_CONTACT+"!"; //--- Prepare response text for BTN_CONTACT Print("RESPONSE = ", response_text); //--- Log the response string message = "Contact the developer via link below:\n"; //--- Message with contact link message += "https://t.me/Forex_Algo_Trader"; sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message } else if (cb_query.data == BTN_JOIN) { response_text = "You clicked "+BTN_JOIN+"!"; //--- Prepare response text for BTN_JOIN Print("RESPONSE = ", response_text); //--- Log the response string message = "You want to be part of our MQL5 Community?\n"; //--- Message inviting to join the community message += "Welcome! <a href=\"https://t.me/forexalgo_trading\">Click me</a> to join.\n"; message += "<s>Civil Engineering</s> Forex AlgoTrading\n"; //--- Strikethrough text message += "<pre>This is a sample of our MQL5 code</pre>\n"; //--- Preformatted text message += "<u><i>Remember to follow community guidelines!\xF64F\</i></u>\n"; //--- Italic and underline text message += "<b>Happy Trading!</b>\n"; //--- Bold text sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message } else if (cb_query.data == EMOJI_PISTOL) { response_text = "You clicked "+EMOJI_PISTOL+"!"; //--- Prepare response text for EMOJI_PISTOL Print("RESPONSE = ", response_text); //--- Log the response string message = "Choose More Options Below:\n"; //--- Message to prompt for more options message += "Trading Operations"; //--- Title for trading operations //--- Define inline buttons for additional trading options string buttons = "[[{\"text\": \""+EMOJI_UP+"\", \"callback_data\": \""+EMOJI_UP+"\"}]," "[{\"text\": \"Buy\", \"callback_data\": \""+BTN_BUY+"\"}, {\"text\": \"Close\", \"callback_data\": \""+BTN_CLOSE+"\"}, {\"text\": \"Next\", \"callback_data\": \""+BTN_NEXT+"\"}]]"; sendMessageToTelegram(cb_query.chat_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup }
Dies sorgt für die Schaltflächen im Feld „Next“ und deren Reaktionsfähigkeit. Dann müssen wir uns um die Screenshot-Schaltfläche kümmern, die sich im Menü der Inline-Hauptschaltfläche befindet.
else if (cb_query.data == BTN_SCREENSHOT) { response_text = "You clicked "+BTN_SCREENSHOT+"!"; //--- Prepare response text for BTN_SCREENSHOT Print("RESPONSE = ", response_text); //--- Log the response string message = "Okay. Command 'get Current Chart Screenshot' received.\n"; //--- Message acknowledging screenshot command message += "Screenshot sending process initiated \xF60E"; //--- Emoji indicating process initiation sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message string caption = "Screenshot of Symbol: "+_Symbol+ //--- Caption for screenshot " ("+EnumToString(ENUM_TIMEFRAMES(_Period))+ //--- Timeframe ") @ Time: "+TimeToString(TimeCurrent()); //--- Current time //--- Send the screenshot to Telegram sendScreenshotToTelegram(cb_query.chat_id, _Symbol, _Period, caption); }
Schließlich müssen wir uns um die Schaltfläche „Cancel“ (Abbrechen) kümmern, indem wir die aktuellen Inline-Schaltflächen entfernen, um wieder von vorne beginnen zu können.
else if (cb_query.data == EMOJI_CANCEL) { response_text = "You clicked "+EMOJI_CANCEL+"!"; //--- Prepare response text for EMOJI_CANCEL Print("RESPONSE = ", response_text); //--- Log the response string message = "Choose /start or /help to begin."; //--- Message for user guidance sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message //--- Reset the inline button state by removing the keyboard removeInlineButtons(member_token, cb_query.chat_id, cb_query.message_id); }
Hier wird ein ähnlicher Ansatz wie die Logik, die wir verwendet haben, umgesetzt, nur dass wir eine weitere Funktion zum Entfernen der Inline-Schaltflächen hinzugefügt haben.
//+------------------------------------------------------------------+ //| Remove inline buttons by editing message reply markup | //+------------------------------------------------------------------+ void removeInlineButtons(string memberToken, long chatID, long messageID){ //--- Reset the inline button state by removing the keyboard string url = TELEGRAM_BASE_URL + "/bot" + memberToken + "/editMessageReplyMarkup"; //--- API URL to edit message string params = "chat_id=" + IntegerToString(chatID) + //--- Chat ID parameter "&message_id=" + IntegerToString(messageID) + //--- Message ID parameter "&reply_markup=" + UrlEncode("{\"inline_keyboard\":[]}"); //--- Empty inline keyboard string response; int res = postRequest(response, url, params, WEB_TIMEOUT); //--- Send request to Telegram API }
Hier definieren wir die Funktion „removeInlineButtons“. Sein Zweck ist es, Inline-Schaltflächen aus einer zuvor gesendeten Nachricht zu entfernen, indem das Antwort-Markup der Nachricht geändert wird. Die Funktion hat drei Parameter: „memberToken“ (das Authentifizierungs-Token des Bots), „chatID“ (die ID des Chats, in dem die Nachricht gesendet wurde) und „messageID“ (die ID der Nachricht, die die Inline-Schaltflächen enthält). Zuerst konstruieren wir die API-Endpunkt-URL für die Methode „editMessageReplyMarkup“ von Telegram. Wir tun dies, indem wir die „TELEGRAM_BASE_URL“ mit „/bot“ und dem „memberToken“ kombinieren. Das ist die URL, die wir für die Kommunikation mit den Servern von Telegram verwenden werden.
Dann geben wir die Zeichenkette „params“ an, die die erforderlichen Parameter für den API-Aufruf enthält. Wir fügen den Parameter „chat_id“ hinzu. Um den Wert dafür zu erhalten, wandeln wir die Variable „chatID“ von einer Ganzzahl in eine Zeichenkette um. Dasselbe gilt für den Parameter „message_id“. Schließlich weisen wir die API an, die Inline-Schaltflächen zu entfernen, indem wir ein leeres „reply_markup“-Feld senden. Der Wert für dieses Feld ist ein leerer JSON-String, den wir durch „UrlEncoding“ des Wertes der Variablen „emptyInlineKeyboard“ erhalten.
Nachdem wir die Parameter festgelegt haben, deklarieren wir die Variable „response“, um zu speichern, was der Server zurückschickt, und rufen „postRequest“ auf, um die API-Anfrage an Telegram zu senden. Die Funktion „postRequest“ sendet die Anfrage mit der angegebenen URL und den Parametern zusammen mit einem Timeout („WEB_TIMEOUT“) für den Fall, dass etwas schief geht. Wenn die Anfrage erfolgreich ist, haben wir das gewünschte Ergebnis - eine Nachricht ohne Inline-Schaltflächen, deren Zustand effektiv zurückgesetzt wird. Wenn die Callback-Daten nicht erkannt werden, geben wir einen Ausdruck zurück, der besagt, dass die angeklickte Schaltfläche unbekannt ist, was bedeutet, dass die Schaltfläche nicht erkannt wird.
else { response_text = "Unknown button!"; //--- Prepare response text for unknown buttons Print("RESPONSE = ", response_text); //--- Log the response }
Wenn wir auf die Schaltfläche Abbrechen klicken, erhalten wir die folgende Ausgabe.
Das war ein Erfolg. Der vollständige Quellcode für die Verarbeitung von Callback-Abfragen lautet wie folgt.
#define BTN_MENU "BTN_MENU" //--- Identifier for menu button //+------------------------------------------------------------------+ //| Process new messages | //+------------------------------------------------------------------+ void Class_Bot_EA::ProcessMessages(void){ //--- Loop through all chats for(int i=0; i<member_chats.Total(); i++){ Class_Chat *chat = member_chats.GetNodeAtIndex(i); //--- Get the current chat if(!chat.member_new_one.done){ //--- Check if the message has not been processed yet chat.member_new_one.done = true; //--- Mark the message as processed string text = chat.member_new_one.message_text; //--- Get the message text //--- Example of sending a message with inline buttons if (text == "Start" || text == "/start" || text == "Help" || text == "/help"){ string message = "Welcome! You can control me via inline buttons!"; //--- Welcome message //--- Define inline button to provide menu string buttons = "[[{\"text\": \"Provide Menu\", \"callback_data\": \""+BTN_MENU+"\"}]]"; sendMessageToTelegram(chat.member_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup } } } } #define BTN_NAME "BTN_NAME" //--- Identifier for name button #define BTN_INFO "BTN_INFO" //--- Identifier for info button #define BTN_QUOTES "BTN_QUOTES" //--- Identifier for quotes button #define BTN_MORE "BTN_MORE" //--- Identifier for more options button #define BTN_SCREENSHOT "BTN_SCREENSHOT" //--- Identifier for screenshot button #define EMOJI_CANCEL "\x274C" //--- Cross mark emoji #define EMOJI_UP "\x2B06" //--- Upwards arrow emoji #define BTN_BUY "BTN_BUY" //--- Identifier for buy button #define BTN_CLOSE "BTN_CLOSE" //--- Identifier for close button #define BTN_NEXT "BTN_NEXT" //--- Identifier for next button #define EMOJI_PISTOL "\xF52B" //--- Pistol emoji #define BTN_CONTACT "BTN_CONTACT" //--- Identifier for contact button #define BTN_JOIN "BTN_JOIN" //--- Identifier for join button //+------------------------------------------------------------------+ //| Function to process callback queries | //+------------------------------------------------------------------+ void Class_Bot_EA::ProcessCallbackQuery(Class_CallbackQuery &cb_query) { Print("Callback Query ID: ", cb_query.id); //--- Log the callback query ID Print("Chat Token: ", member_token); //--- Log the member token Print("From First Name: ", cb_query.from_first_name); //--- Log the sender's first name Print("From Last Name: ", cb_query.from_last_name); //--- Log the sender's last name Print("From Username: ", cb_query.from_username); //--- Log the sender's username Print("Message ID: ", cb_query.message_id); //--- Log the message ID Print("Message Text: ", cb_query.message_text); //--- Log the message text Print("Callback Data: ", cb_query.data); //--- Log the callback data //--- Respond based on the callback data string response_text; if (cb_query.data == BTN_MENU) { response_text = "You clicked "+BTN_MENU+"!"; //--- Prepare response text for BTN_MENU Print("RESPONSE = ", response_text); //--- Log the response //--- Send the response message to the correct group/channel chat ID sendMessageToTelegram(cb_query.chat_id, response_text, NULL); string message = "Information"; //--- Message to display options //--- Define inline buttons with callback data string buttons = "[[{\"text\": \"Get Expert's Name\", \"callback_data\": \""+BTN_NAME+"\"}]," "[{\"text\": \"Get Account Information\", \"callback_data\": \""+BTN_INFO+"\"}]," "[{\"text\": \"Get Current Market Quotes\", \"callback_data\": \""+BTN_QUOTES+"\"}]," "[{\"text\": \"More\", \"callback_data\": \""+BTN_MORE+"\"}, {\"text\": \"Screenshots\", \"callback_data\": \""+BTN_SCREENSHOT+"\"}, {\"text\": \""+EMOJI_CANCEL+"\", \"callback_data\": \""+EMOJI_CANCEL+"\"}]]"; sendMessageToTelegram(cb_query.chat_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup } else if (cb_query.data == BTN_NAME) { response_text = "You clicked "+BTN_NAME+"!"; //--- Prepare response text for BTN_NAME Print("RESPONSE = ", response_text); //--- Log the response string message = "The file name of the EA that I control is:\n"; //--- Message with EA file name message += "\xF50B"+__FILE__+" Enjoy.\n"; //--- Append the file name and a friendly message sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message } else if (cb_query.data == BTN_INFO) { response_text = "You clicked "+BTN_INFO+"!"; //--- Prepare response text for BTN_INFO Print("RESPONSE = ", response_text); //--- Log the response ushort MONEYBAG = 0xF4B0; //--- Define money bag emoji string MONEYBAGcode = ShortToString(MONEYBAG); //--- Convert emoji code to string string currency = AccountInfoString(ACCOUNT_CURRENCY); //--- Get the account currency //--- Construct the account information message string message = "\x2733\Account No: "+(string)AccountInfoInteger(ACCOUNT_LOGIN)+"\n"; message += "\x23F0\Account Server: "+AccountInfoString(ACCOUNT_SERVER)+"\n"; message += MONEYBAGcode+"Balance: "+(string)AccountInfoDouble(ACCOUNT_BALANCE)+" "+currency+"\n"; message += "\x2705\Profit: "+(string)AccountInfoDouble(ACCOUNT_PROFIT)+" "+currency+"\n"; sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message } else if (cb_query.data == BTN_QUOTES) { response_text = "You clicked "+BTN_QUOTES+"!"; //--- Prepare response text for BTN_QUOTES Print("RESPONSE = ", response_text); //--- Log the response double Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); //--- Get the current ask price double Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Get the current bid price //--- Construct the market quotes message string message = "\xF170 Ask: "+(string)Ask+"\n"; message += "\xF171 Bid: "+(string)Bid+"\n"; sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message } else if (cb_query.data == BTN_MORE) { response_text = "You clicked "+BTN_MORE+"!"; //--- Prepare response text for BTN_MORE Print("RESPONSE = ", response_text); //--- Log the response string message = "Choose More Options Below:\n"; //--- Message to prompt for additional options message += "Trading Operations"; //--- Title for trading operations //--- Define inline buttons for additional options string buttons = "[[{\"text\": \""+EMOJI_UP+"\", \"callback_data\": \""+EMOJI_UP+"\"}]," "[{\"text\": \"Buy\", \"callback_data\": \""+BTN_BUY+"\"}, {\"text\": \"Close\", \"callback_data\": \""+BTN_CLOSE+"\"}, {\"text\": \"Next\", \"callback_data\": \""+BTN_NEXT+"\"}]]"; sendMessageToTelegram(cb_query.chat_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup } else if (cb_query.data == EMOJI_CANCEL) { response_text = "You clicked "+EMOJI_CANCEL+"!"; //--- Prepare response text for EMOJI_CANCEL Print("RESPONSE = ", response_text); //--- Log the response string message = "Choose /start or /help to begin."; //--- Message for user guidance sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message //--- Reset the inline button state by removing the keyboard removeInlineButtons(member_token, cb_query.chat_id, cb_query.message_id); } else if (cb_query.data == EMOJI_UP) { response_text = "You clicked "+EMOJI_UP+"!"; //--- Prepare response text for EMOJI_UP Print("RESPONSE = ", response_text); //--- Log the response string message = "Choose a menu item:\n"; //--- Message to prompt for menu selection message += "Information"; //--- Title for information options //--- Define inline buttons for menu options string buttons = "[[{\"text\": \"Get Expert's Name\", \"callback_data\": \""+BTN_NAME+"\"}]," "[{\"text\": \"Get Account Information\", \"callback_data\": \""+BTN_INFO+"\"}]," "[{\"text\": \"Get Current Market Quotes\", \"callback_data\": \""+BTN_QUOTES+"\"}]," "[{\"text\": \"More\", \"callback_data\": \""+BTN_MORE+"\"}, {\"text\": \"Screenshots\", \"callback_data\": \""+BTN_SCREENSHOT+"\"}, {\"text\": \""+EMOJI_CANCEL+"\", \"callback_data\": \""+EMOJI_CANCEL+"\"}]]"; sendMessageToTelegram(cb_query.chat_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup } else if (cb_query.data == BTN_BUY) { response_text = "You clicked "+BTN_BUY+"!"; //--- Prepare response text for BTN_BUY Print("RESPONSE = ", response_text); //--- Log the response CTrade obj_trade; //--- Create a trade object double Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); //--- Get the current ask price double Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Get the current bid price //--- Open a buy position obj_trade.Buy(0.01, NULL, 0, Bid - 300 * _Point, Bid + 300 * _Point); double entry = 0, sl = 0, tp = 0, vol = 0; ulong ticket = obj_trade.ResultOrder(); //--- Get the ticket number of the new order if (ticket > 0) { if (PositionSelectByTicket(ticket)) { //--- Select the position by ticket entry = PositionGetDouble(POSITION_PRICE_OPEN); //--- Get the entry price sl = PositionGetDouble(POSITION_SL); //--- Get the stop loss price tp = PositionGetDouble(POSITION_TP); //--- Get the take profit price vol = PositionGetDouble(POSITION_VOLUME); //--- Get the volume } } //--- Construct the message with position details string message = "\xF340\Opened BUY Position:\n"; message += "Ticket: "+(string)ticket+"\n"; message += "Open Price: "+(string)entry+"\n"; message += "Lots: "+(string)vol+"\n"; message += "SL: "+(string)sl+"\n"; message += "TP: "+(string)tp+"\n"; sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message } else if (cb_query.data == BTN_CLOSE) { response_text = "You clicked "+BTN_CLOSE+"!"; //--- Prepare response text for BTN_CLOSE Print("RESPONSE = ", response_text); //--- Log the response CTrade obj_trade; //--- Create a trade object int totalOpenBefore = PositionsTotal(); //--- Get the total number of open positions before closing obj_trade.PositionClose(_Symbol); //--- Close the position for the symbol int totalOpenAfter = PositionsTotal(); //--- Get the total number of open positions after closing //--- Construct the message with position closure details string message = "\xF62F\Closed Position:\n"; message += "Total Positions (Before): "+(string)totalOpenBefore+"\n"; message += "Total Positions (After): "+(string)totalOpenAfter+"\n"; sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message } else if (cb_query.data == BTN_NEXT) { response_text = "You clicked "+BTN_NEXT+"!"; //--- Prepare response text for BTN_NEXT Print("RESPONSE = ", response_text); //--- Log the response string message = "Choose Still More Options Below:\n"; //--- Message to prompt for further options message += "More Options"; //--- Title for more options //--- Define inline buttons for additional options string buttons = "[[{\"text\": \""+EMOJI_UP+"\", \"callback_data\": \""+EMOJI_UP+"\"}, {\"text\": \"Contact\", \"callback_data\": \""+BTN_CONTACT+"\"}, {\"text\": \"Join\", \"callback_data\": \""+BTN_JOIN+"\"},{\"text\": \""+EMOJI_PISTOL+"\", \"callback_data\": \""+EMOJI_PISTOL+"\"}]]"; sendMessageToTelegram(cb_query.chat_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup } else if (cb_query.data == BTN_CONTACT) { response_text = "You clicked "+BTN_CONTACT+"!"; //--- Prepare response text for BTN_CONTACT Print("RESPONSE = ", response_text); //--- Log the response string message = "Contact the developer via link below:\n"; //--- Message with contact link message += "https://t.me/Forex_Algo_Trader"; sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message } else if (cb_query.data == BTN_JOIN) { response_text = "You clicked "+BTN_JOIN+"!"; //--- Prepare response text for BTN_JOIN Print("RESPONSE = ", response_text); //--- Log the response string message = "You want to be part of our MQL5 Community?\n"; //--- Message inviting to join the community message += "Welcome! <a href=\"https://t.me/forexalgo_trading\">Click me</a> to join.\n"; message += "<s>Civil Engineering</s> Forex AlgoTrading\n"; //--- Strikethrough text message += "<pre>This is a sample of our MQL5 code</pre>\n"; //--- Preformatted text message += "<u><i>Remember to follow community guidelines!\xF64F\</i></u>\n"; //--- Italic and underline text message += "<b>Happy Trading!</b>\n"; //--- Bold text sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message } else if (cb_query.data == EMOJI_PISTOL) { response_text = "You clicked "+EMOJI_PISTOL+"!"; //--- Prepare response text for EMOJI_PISTOL Print("RESPONSE = ", response_text); //--- Log the response string message = "Choose More Options Below:\n"; //--- Message to prompt for more options message += "Trading Operations"; //--- Title for trading operations //--- Define inline buttons for additional trading options string buttons = "[[{\"text\": \""+EMOJI_UP+"\", \"callback_data\": \""+EMOJI_UP+"\"}]," "[{\"text\": \"Buy\", \"callback_data\": \""+BTN_BUY+"\"}, {\"text\": \"Close\", \"callback_data\": \""+BTN_CLOSE+"\"}, {\"text\": \"Next\", \"callback_data\": \""+BTN_NEXT+"\"}]]"; sendMessageToTelegram(cb_query.chat_id, message, customInlineKeyboardMarkup(buttons)); //--- Send the inline keyboard markup } else if (cb_query.data == BTN_SCREENSHOT) { response_text = "You clicked "+BTN_SCREENSHOT+"!"; //--- Prepare response text for BTN_SCREENSHOT Print("RESPONSE = ", response_text); //--- Log the response string message = "Okay. Command 'get Current Chart Screenshot' received.\n"; //--- Message acknowledging screenshot command message += "Screenshot sending process initiated \xF60E"; //--- Emoji indicating process initiation sendMessageToTelegram(cb_query.chat_id, message, NULL); //--- Send the message string caption = "Screenshot of Symbol: "+_Symbol+ //--- Caption for screenshot " ("+EnumToString(ENUM_TIMEFRAMES(_Period))+ //--- Timeframe ") @ Time: "+TimeToString(TimeCurrent()); //--- Current time //--- Send the screenshot to Telegram sendScreenshotToTelegram(cb_query.chat_id, _Symbol, _Period, caption); } else { response_text = "Unknown button!"; //--- Prepare response text for unknown buttons Print("RESPONSE = ", response_text); //--- Log the response } //--- Optionally, reset the inline button state by removing the keyboard // removeInlineButtons(member_token, cb_query.chat_id, cb_query.message_id); } //+------------------------------------------------------------------+ //| Create a custom inline keyboard markup for Telegram | //+------------------------------------------------------------------+ string customInlineKeyboardMarkup(const string buttons){ //--- Construct the JSON string for the inline keyboard markup string result = "{\"inline_keyboard\": " + UrlEncode(buttons) + "}"; //--- Encode buttons as JSON return(result); } //+------------------------------------------------------------------+ //| Remove inline buttons by editing message reply markup | //+------------------------------------------------------------------+ void removeInlineButtons(string memberToken, long chatID, long messageID){ //--- Reset the inline button state by removing the keyboard string url = TELEGRAM_BASE_URL + "/bot" + memberToken + "/editMessageReplyMarkup"; //--- API URL to edit message string params = "chat_id=" + IntegerToString(chatID) + //--- Chat ID parameter "&message_id=" + IntegerToString(messageID) + //--- Message ID parameter "&reply_markup=" + UrlEncode("{\"inline_keyboard\":[]}"); //--- Empty inline keyboard string response; int res = postRequest(response, url, params, WEB_TIMEOUT); //--- Send request to Telegram API }
Kurz gesagt, wir verwalten Callback-Anfragen, indem wir auf bestimmte Tastendrücke antworten und die Nutzer zu den entsprechenden Inline-Tastaturoptionen leiten. Dies verbessert die Interaktion, da kontextabhängige Meldungen und Optionen angezeigt werden. Als Nächstes werden wir die Integration testen, um sicherzustellen, dass diese Funktionen wie gewünscht funktionieren und die Interaktionen korrekt verarbeitet werden.
Testen der Implementierung der Inline-Button-Zustände
In diesem Abschnitt werden wir überprüfen, wie gut die Inline-Schaltflächen mit dem Bot für Telegram und MQL5 Expert Advisor zusammenarbeiten. Dabei werden Nutzeraktionen, wie das Drücken von Tasten, simuliert und sichergestellt, dass der Bot die Callbacks korrekt verarbeitet. Wir beurteilen die korrekte Anzeige, Entfernung oder Aktualisierung von Inline-Schaltflächen auf der Grundlage der Nutzerinteraktion. Um mehr Klarheit zu schaffen, haben wir ein Video erstellt, das die Funktionsweise der Integration demonstriert und das schrittweise Verhalten des Bots bei der Reaktion auf das Drücken von Inline-Schaltflächen zeigt. Dadurch wird sichergestellt, dass die Einrichtung in Echtzeit wie erwartet funktioniert. Nachfolgend finden Sie eine Abbildung.
Wir haben die Schaltflächeninteraktionen und Callback-Abfragen getestet, um sicherzustellen, dass der Bot korrekt mit den Nutzereingaben arbeitet und dass die Inline-Schaltflächenzustände bei Bedarf aktualisiert oder zurückgesetzt werden. Dies bietet einen nicht-linearen Interaktionsstil, der das Engagement erhöht und eine effizientere Erfahrung bei der Steuerung des Bots über Telegram ermöglicht.
Schlussfolgerung
Abschließend haben wir den Übergang von Callback-Abfragen und Inline-Buttons in den Telegram-Bot eingebaut und getestet. Jetzt kann der Bot auf Nutzereingaben mit maßgeschneiderten Nachrichten reagieren und über Inline-Tastaturen interaktive Optionen anbieten. Die Nutzerfreundlichkeit wurde durch die Hinzufügung von einfach zu bedienenden Echtzeit-Schaltflächen für Aktionen wie den Zugriff auf Menüs, das Abrufen von Experteninformationen oder das Ausführen von Befehlen im Zusammenhang mit dem Handel verbessert.
Wir haben das System getestet und können bestätigen, dass es wie vorgesehen funktioniert, jede Callback korrekt verarbeitet und den Nutzern das entsprechende Feedback gibt, das sie benötigen. Dabei behält der Bot seine Dialogqualität bei, die dazu beiträgt, das Interesse des Nutzers und die Nutzerfreundlichkeit aufrechtzuerhalten. Wir können sehen, dass die Inline-Schaltflächen effizienter sind, da sie das Chat-Feld nicht überladen, genau wie beabsichtigt. Wir hoffen, dass Sie den Artikel ausführlich und leicht verständlich fanden.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/15823





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.