TelegramBot. La fonctionnalité de base. Mouches séparément, cÎtelettes séparément. (2e partie)

Nous continuons Ă  dĂ©velopper les fonctionnalitĂ©s de base du bot dans les tĂ©lĂ©grammes. Dans les parties prĂ©cĂ©dentes, nous avons discutĂ© du fait que le travail du bot sur la rĂ©ception des messages, le traitement et l'envoi devrait ĂȘtre divisĂ©. Essayons d'utiliser les outils de base de Java Core pour rendre notre bot multithread et asynchrone. Nous allons proposer une tĂąche qui prend beaucoup de temps Ă  traiter. Voyons comment fonctionnent les commandes du tĂ©lĂ©gramme et comment elles doivent ĂȘtre traitĂ©es.

Ceci est une continuation de la premiÚre partie de l'article sur la programmation des bots pour les télégrammes en Java
Instructions TelegramBot pour créer des fonctionnalités de base pour le bot. (Partie 1)
Pour qui c'est encore intĂ©ressant, vous ĂȘtes les bienvenus


Je dois dire tout de suite que dans cette partie, beaucoup de tout a Ă©tĂ© ajoutĂ© en mĂȘme temps et nous analyserons tranquillement toutes les fonctionnalitĂ©s qui ont permis au bot de pouvoir multithread et pourquoi elles sont nĂ©cessaires.

Comme d'habitude du principal:
Vous pouvez trouver tout le code prĂȘt Ă  l'emploi pour cet article dans la branche Part2-Handlers du rĂ©fĂ©rentiel git.
Le code fonctionne pleinement, il suffit d'incliner, de modifier les données pour l'autorisation du bot (nom et token) et d'exécuter la méthode principale dans la classe App.class.

Veuillez noter que cette classe envoie une notification à l'administrateur du bot lorsque le bot démarre que le bot a démarré. L'ID administrateur du bot est également spécifié dans la classe App.class et si vous ne le changez pas, votre bot essaiera de m'envoyer des messages :)

Et plus loin sur les points, nous analyserons les changements qui sont apparus aprĂšs la sortie de la premiĂšre partie.

Traitement des commandes


Pour commencer, examinons ce concept de ce qu'est une équipe en général dans un systÚme de communication avec un bot de télégramme. Selon les paramÚtres du bot, il peut voir tous les messages de n'importe quel format ou uniquement des commandes spécialement conçues. Quelle est la différence et
oĂč vous pouvez rencontrer ces options de message.

  1. Texte brut, messages réguliers.
    Sous cette forme, le bot reçoit des messages lorsqu'ils lui Ă©crivent en PM. Et pourtant, si dans les paramĂštres du bot le mode de confidentialitĂ© dans les groupes est dĂ©sactivĂ©, le bot commence Ă  voir tous les messages complĂštement. Si ce paramĂštre est activĂ©, lorsqu'il est ajoutĂ© au groupe, le bot ne voit que les commandes qui lui sont adressĂ©es. À quoi ils ressemblent - voir le deuxiĂšme paragraphe
  2. Des équipes spécialement conçues
    Ces commandes commencent toujours par une barre oblique: /
    AprĂšs cela vient l'Ă©quipe elle-mĂȘme. Le texte de la commande doit ĂȘtre sans espaces. Un exemple:
    / start
    Avec cette commande, tout utilisateur commence toujours la communication avec votre bot. Par consĂ©quent, selon les rĂšgles de bonne forme, la rĂ©ponse Ă  cette commande doit ĂȘtre prescrite.

    Toutes les commandes avec lesquelles votre bot sait travailler, il est conseillé d'ajouter à la liste des compétences dans les paramÚtres de votre bot. Tout cela se fait dans un télégramme avec @BotFather.

    En appelant la commande / myBots, sélectionnez votre bot puis le bouton "Edit Bot"
    Vous obtiendrez une fenĂȘtre oĂč tous les paramĂštres du bot seront affichĂ©s, puis vous pourrez configurer toute son interface et indiquer avec quelles commandes votre bot peut fonctionner.



    Ils sont définis dans ce format:



    Et aprÚs cela, lorsque vous commencez à entrer une commande dans votre bot, il affichera de l'aide avec une liste des commandes répertoriées:



    Et il y a encore une nuance. Un groupe peut contenir plusieurs bots, et s'ils ont des commandes communes (et des commandes communes seront obligatoires, le mĂȘme dĂ©marrage et l'aide sont implĂ©mentĂ©s dans la plupart des bots), alors une partie sera ajoutĂ©e Ă  la commande elle-mĂȘme, indiquant Ă  quel bot cette commande appartient. Et la commande ressemblera complĂštement Ă  ceci:
    / start @ test_habr_bot

Et maintenant, connaissant toutes ces nuances, créons avec vous une telle option de traitement qui devrait comprendre les commandes commençant par une barre oblique et sachant comment distinguer si la commande est adressée spécifiquement à votre bot ou à une autre.

Créez un package qui contiendra les classes responsables du traitement des commandes.
package com.example.telegrambot.command

Dans la classe Command, nous listons toutes les commandes que notre bot devrait ĂȘtre capable de comprendre.

public enum Command { NONE, NOTFORME, NOTIFY, START, HELP, ID } 

Comme vous l'avez vu plus tĂŽt, j'ai indiquĂ© Ă  @BotFather que le bot je devrais ĂȘtre capable de comprendre 4 Ă©quipes. Ce sera un dĂ©marrage et une aide standard. Nous en ajoutons un utile - id. Et un de plus, notifiez, dont je parlerai un peu plus tard. Et deux Ă©quipes NONE et NOTFORME, qui nous diront que le message texte n'est pas du tout une commande, ou que cette commande n'est pas pour notre bot.

Ajouter une autre classe d'assistance ParsedCommand

 import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class ParsedCommand { Command command = Command.NONE; String text=""; } 

Son objectif principal est de stocker le rĂ©sultat de l'analyse du texte dans les objets de cette classe. Il ne contiendra que l'Ă©quipe elle-mĂȘme et tout le texte qui suit l'Ă©quipe.

Et nous écrirons une classe séparée qui analysera les équipes pour nous. Classe d' analyseur

 import javafx.util.Pair; import org.apache.log4j.Logger; public class Parser { private static final Logger log = Logger.getLogger(Parser.class); private final String PREFIX_FOR_COMMAND = "/"; private final String DELIMITER_COMMAND_BOTNAME = "@"; private String botName; public Parser(String botName) { this.botName = botName; } public ParsedCommand getParsedCommand(String text) { String trimText = ""; if (text != null) trimText = text.trim(); ParsedCommand result = new ParsedCommand(Command.NONE, trimText); if ("".equals(trimText)) return result; Pair<String, String> commandAndText = getDelimitedCommandFromText(trimText); if (isCommand(commandAndText.getKey())) { if (isCommandForMe(commandAndText.getKey())) { String commandForParse = cutCommandFromFullText(commandAndText.getKey()); Command commandFromText = getCommandFromText(commandForParse); result.setText(commandAndText.getValue()); result.setCommand(commandFromText); } else { result.setCommand(Command.NOTFORME); result.setText(commandAndText.getValue()); } } return result; } private String cutCommandFromFullText(String text) { return text.contains(DELIMITER_COMMAND_BOTNAME) ? text.substring(1, text.indexOf(DELIMITER_COMMAND_BOTNAME)) : text.substring(1); } private Command getCommandFromText(String text) { String upperCaseText = text.toUpperCase().trim(); Command command = Command.NONE; try { command = Command.valueOf(upperCaseText); } catch (IllegalArgumentException e) { log.debug("Can't parse command: " + text); } return command; } private Pair<String, String> getDelimitedCommandFromText(String trimText) { Pair<String, String> commandText; if (trimText.contains(" ")) { int indexOfSpace = trimText.indexOf(" "); commandText = new Pair<>(trimText.substring(0, indexOfSpace), trimText.substring(indexOfSpace + 1)); } else commandText = new Pair<>(trimText, ""); return commandText; } private boolean isCommandForMe(String command) { if (command.contains(DELIMITER_COMMAND_BOTNAME)) { String botNameForEqual = command.substring(command.indexOf(DELIMITER_COMMAND_BOTNAME) + 1); return botName.equals(botNameForEqual); } return true; } private boolean isCommand(String text) { return text.startsWith(PREFIX_FOR_COMMAND); } } 

Bref. Lors de l'initialisation de l'analyseur, nous devons passer le nom de notre bot dans le constructeur afin que l'analyseur puisse distinguer ses commandes des Ă©trangers.

Eh bien, nous appelons simplement la méthode publique

 public ParsedCommand getParsedCommand(String text) 

À qui nous passons le texte du message dans les arguments, et il doit nous renvoyer une commande et le texte du message venant aprùs la commande.

Vous pouvez voir comment l'analyseur fonctionne dans la classe de test .

Mouches séparément, escalopes séparément


Maintenant, nous devons apprendre à notre robot à recevoir séparément des messages, à traiter et à envoyer des réponses. AprÚs une série d'essais et d'erreurs, j'en suis venu à cette logique de l'application.
La classe principale Bot fonctionnera dans le thread principal de l'application et ne sera occupée que par le fait que tous les messages reçus seront placés dans une file d'attente spéciale et sera également un conteneur pour les messages que nous prévoyons d'envoyer à l'utilisateur en réponse.

Les changements dans cette classe sont trÚs mineurs. Nous avons ajouté deux files d'attente:

 public final Queue<Object> sendQueue = new ConcurrentLinkedQueue<>(); public final Queue<Object> receiveQueue = new ConcurrentLinkedQueue<>(); 

et légÚrement réécrit le code de fonction public void onUpdateReceived (Update update)

 @Override public void onUpdateReceived(Update update) { log.debug("Receive new Update. updateID: " + update.getUpdateId()); receiveQueue.add(update); } 

Pourquoi Encore une fois, j'ai essayé différentes options. Et le principal problÚme du multithreading est de travailler avec des données partagées. Et surtout, j'ai aimé la façon dont l'implémentation des files d'attente multithread ConcurrentLinkedQueue <> () gÚre cela.
Et comme vous pouvez le voir, dans les deux files d'attente, nous stockons les types de données d'objet. Ceci est un autre signet pour l'avenir. Ainsi, nous ne sommes pas attachés aux types de messages reçus. Dans la file d'attente entrante, nous pouvons ajouter non seulement des objets du type Mise à jour, mais aussi certains autres objets dont nous avons besoin.

La mĂȘme chose avec la file d'attente d'envoi. Comme nous pouvons envoyer diffĂ©rents types de messages et qu'ils n'ont pas de parent commun - nous utilisons Ă©galement un type de donnĂ©es commun - Object.
Si vous exécutez le bot sous cette forme, cela fonctionnera, mais ne fera rien. Il enregistrera tous les messages reçus dans le journal et les mettra dans la file d'attente.
Par conséquent, nous avons besoin d'une sorte de thread qui s'occupe de prendre les messages reçus de la file d'attente et d'effectuer certaines actions sur eux et de mettre les résultats de son travail dans la file d'attente sendQueue .

Créons un package séparé: service et nous n'y aurons que 2 classes:

MessageReciever - gestionnaire de messages reçus
MessageSender est le gestionnaire de file d'attente de messages Ă  envoyer Ă  l'utilisateur.

Nous considérerons leur travail un peu plus bas, mais pour l'instant, nous décrirons leur utilisation dans notre classe de départ App

Une fois notre bot connecté, nous démarrons nos gestionnaires dans des threads séparés:

 MessageReciever messageReciever = new MessageReciever(test_habr_bot); MessageSender messageSender = new MessageSender(test_habr_bot); test_habr_bot.botConnect(); Thread receiver = new Thread(messageReciever); receiver.setDaemon(true); receiver.setName("MsgReciever"); receiver.setPriority(PRIORITY_FOR_RECEIVER); receiver.start(); Thread sender = new Thread(messageSender); sender.setDaemon(true); sender.setName("MsgSender"); sender.setPriority(PRIORITY_FOR_SENDER); sender.start(); 

Pour les deux threads, nous spĂ©cifions le mode dĂ©mon. Cela est nĂ©cessaire pour que les threads fonctionnent tant que le thread principal est en cours d'exĂ©cution et pour se terminer dĂšs qu'il arrĂȘte son travail.

Nous ne voudrions pas d'abord traiter le gestionnaire de messages entrants - examinons le fonctionnement de la classe MessageSender .

Voyons ce qu'il peut faire et ce qu'il fait:

  • Naturellement, il s'agit d'un hĂ©ritage de l'interface pour le multithreading:
    implémente runnable
    et implémentation de la fonction run

     @Override public void run() 

    Ici, nous commençons la boucle infinie, qui est uniquement occupée par le fait qu'elle vérifie la file d'attente d'envoi et appelle la commande d'envoi

     private void send(Object object) 

    si quelque chose apparaĂźt dans la file d'attente.
  • Dans le constructeur de classe, nous passons l'objet de la classe Bot, car nous en prendrons des objets pour envoyer des messages et avec lui nous les enverrons.
  • La mĂ©thode d'envoi dĂ©termine le type de message Ă  envoyer et lui applique la commande appropriĂ©e.

Eh bien, regardons maintenant le travail de la classe MessageReciever

Il doit, comme MessageSender, ĂȘtre multithread, dans le constructeur recevoir un objet de classe Bot, dans lequel il va prendre les messages reçus dans une boucle infinie, les traiter et le mettre dans la file d'attente pour envoyer les rĂ©sultats de son travail.

Ici, nous utilisons l'analyseur de commandes créé précédemment. Et puis nous ajoutons la possibilité d'utiliser différents types de gestionnaires pour nos équipes et certains d'entre eux nous rendrons multi-thread.

Le cycle de travail est trĂšs simple:

 @Override public void run() { log.info("[STARTED] MsgReciever. Bot class: " + bot); while (true) { for (Object object = bot.receiveQueue.poll(); object != null; object = bot.receiveQueue.poll()) { log.debug("New object for analyze in queue " + object.toString()); analyze(object); } try { Thread.sleep(WAIT_FOR_NEW_MESSAGE_DELAY); } catch (InterruptedException e) { log.error("Catch interrupt. Exit", e); return; } } } 

VĂ©rifiez la file d'attente. S'il y a quelque chose, lancez l'analyseur:

 private void analyze(Object object) 

S'il n'y a rien, nous attendons.

L'analyseur vérifie le type d'objet. S'il sait comment travailler avec lui, il démarre le prochain analyseur. Si vous ne pouvez pas - jure :)

Pourquoi Encore une fois, il s'agit d'un signet pour l'avenir et, je l'espĂšre, je le divulguerai dans les prochaines parties de cette sĂ©rie d'articles. Une telle implĂ©mentation nous permettra ensuite de former nos propres tĂąches pour le bot, de faire des listes de diffusion, des tĂąches quotidiennes. Pour cela, le rĂ©cepteur doit ĂȘtre capable de traiter non seulement des objets de type Update, mais aussi quelque chose Ă  nous. Mais plus Ă  ce sujet plus tard :)

Considérez l'analyseur pour le type de mise à jour plus en détail:

 private void analyzeForUpdateType(Update update) { Long chatId = update.getMessage().getChatId(); String inputText = update.getMessage().getText(); ParsedCommand parsedCommand = parser.getParsedCommand(inputText); AbstractHandler handlerForCommand = getHandlerForCommand(parsedCommand.getCommand()); String operationResult = handlerForCommand.operate(chatId.toString(), parsedCommand, update); if (!"".equals(operationResult)) { SendMessage message = new SendMessage(); message.setChatId(chatId); message.setText(operationResult); bot.sendQueue.add(message); } } 

Il dĂ©finit l'ID de chat. Obtient le texte du message. À l'aide de l'analyseur, il dĂ©termine si le message est une commande et dĂ©termine quel gestionnaire cette commande doit ĂȘtre traitĂ©e. Il commence Ă  traiter la commande et si le traitement de la commande renvoie du texte non vide - il forme un message Ă  envoyer Ă  l'utilisateur et le place dans la file d'attente.

Et puis vous devriez avoir une question: "Quel type de gestionnaire?". Il n'avait pas été question de lui auparavant et il n'était pas mentionné dans le code. D'accord. Nous allons maintenant analyser cette fonctionnalité.

Pour ce faire, créez un package séparé, qui stockera tous nos gestionnaires. Appelez-le gestionnaire
Créons une classe abstraite AbstractHandler

 import com.example.telegrambot.bot.Bot; import com.example.telegrambot.command.ParsedCommand; import org.telegram.telegrambots.api.objects.Update; public abstract class AbstractHandler { Bot bot; AbstractHandler(Bot bot) { this.bot = bot; } public abstract String operate(String chatId, ParsedCommand parsedCommand, Update update); } 

Il aura un constructeur de base dans lequel nous passerons avec quel objet Bot il devra interagir. Et une fonction abstraite Ă  opĂ©rer est dĂ©clarĂ©e, dont la mise en Ɠuvre devra s'inscrire dans les hĂ©ritiers de notre classe.

Et immĂ©diatement, nous implĂ©menterons le gestionnaire le plus simple qui ne fera rien et nous l’utiliserons lorsque nous ne pourrons pas comprendre le type de commande qui nous a Ă©tĂ© donnĂ© et qu’aucune rĂ©ponse n’est requise du bot.

DefaultHandler.java

 import com.example.telegrambot.bot.Bot; import com.example.telegrambot.command.ParsedCommand; import org.apache.log4j.Logger; import org.telegram.telegrambots.api.objects.Update; public class DefaultHandler extends AbstractHandler { private static final Logger log = Logger.getLogger(DefaultHandler.class); public DefaultHandler(Bot bot) { super(bot); } @Override public String operate(String chatId, ParsedCommand parsedCommand, Update update) { return ""; } } 

Comment nous l'appliquerons et oĂč nous obtiendrons les rĂ©sultats de son travail - nous analyserons un peu plus tard.

Le suivant est SystemHandler
Il traitera des commandes de base, telles que start, help, et nous lui demanderons également d'exécuter la commande id

La base de celui-ci ressemble Ă  ceci:

 import com.example.telegrambot.bot.Bot; import com.example.telegrambot.command.Command; import com.example.telegrambot.command.ParsedCommand; import org.apache.log4j.Logger; import org.telegram.telegrambots.api.methods.send.SendMessage; import org.telegram.telegrambots.api.objects.Update; public class SystemHandler extends AbstractHandler { private static final Logger log = Logger.getLogger(SystemHandler.class); private final String END_LINE = "\n"; public SystemHandler(Bot bot) { super(bot); } @Override public String operate(String chatId, ParsedCommand parsedCommand, Update update) { Command command = parsedCommand.getCommand(); switch (command) { case START: bot.sendQueue.add(getMessageStart(chatId)); break; case HELP: bot.sendQueue.add(getMessageHelp(chatId)); break; case ID: return "Your telegramID: " + update.getMessage().getFrom().getId(); } return ""; } 

Vous pouvez voir comment la réponse à la commande start and help est formée dans le code :)
Nous formons des messages texte et les mettons dans une file d'attente pour l'envoi. Sur ce, le travail du gestionnaire s'arrĂȘte. Qui et comment enverra ces messages - il s'en fiche du tout.
Et rappelez-vous, j'ai mentionné un peu plus haut qu'à la suite du gestionnaire, il renvoie des données de texte. Et si cette ligne n'est pas vide, il faut envoyer ce texte à l'utilisateur. C'est exactement la fonctionnalité que nous avons utilisée lors de l'élaboration de la commande ID:

 case ID: return "Your telegramID: " + update.getMessage().getFrom().getId(); 

Le gestionnaire retournera le texte avec l'ID utilisateur à la personne qui l'a appelé et déjà là un message sera généré pour l'envoi, qui ira ensuite dans la file d'attente.

Et au dĂ©but de l'article, j'ai mentionnĂ© que nous mettons en Ɠuvre cette option pour traiter un message d'un utilisateur qui a besoin de temps pour travailler. Et pour qu'il n'interfĂšre pas avec nos gestionnaires, nous allons l'affecter dans un flux sĂ©parĂ© et le laisser vaquer Ă  ses occupations sans distraire le reste.
En tant que fil "lourd", j'ai trouvé la commande notify. Le principe de son travail est le suivant.

En envoyant au bot une commande du formulaire:
/ notifier 300

Le bot devrait vous informer que l'Ă©quipe a compris et aprĂšs 300 secondes, il vous enverra une notification indiquant que 300 secondes se sont Ă©coulĂ©es. Cette Ă©quipe peut mĂȘme avoir une utilitĂ© pratique :)

Par exemple, vous mettez le feu aux boulettes et vous devez les retirer aprÚs 5 minutes. Le bot le fera parfaitement et vous enverra une notification dans le chat que le temps est écoulé.

Ou prenez une tùche plus sérieuse. Vous allez à une réunion importante et vous savez que lorsque vous communiquez avec quelqu'un, vous devrez interrompre la conversation. Pour cela, ils demandent généralement à des amis d'appeler ou d'écrire un message, ce qui sera un motif pour ne pas distraire de la conversation pendant longtemps et prendre des mesures. Mais pourquoi déranger des amis quand on a un bot? AprÚs lui avoir demandé une tùche à l'avance et indiqué l'heure, vous recevrez la notification nécessaire par télégrammes. Mais ce sont toutes les paroles. La tùche et cette commande ont été inventées uniquement pour vous montrer comment allouer dans un flux séparé quelque chose dont le travail peut prendre trÚs longtemps.

Donc, NotifyHandler :

 import com.example.telegrambot.ability.Notify; import com.example.telegrambot.bot.Bot; import com.example.telegrambot.command.ParsedCommand; import org.apache.log4j.Logger; import org.telegram.telegrambots.api.objects.Update; public class NotifyHandler extends AbstractHandler { private static final Logger log = Logger.getLogger(NotifyHandler.class); private final int MILLISEC_IN_SEC = 1000; private String WRONG_INPUT_MESSAGE = "Wrong input. Time must be specified as an integer greater than 0"; public NotifyHandler(Bot bot) { super(bot); } @Override public String operate(String chatId, ParsedCommand parsedCommand, Update update) { String text = parsedCommand.getText(); if ("".equals(text)) return "You must specify the delay time. Like this:\n" + "/notify 30"; long timeInSec; try { timeInSec = Long.parseLong(text.trim()); } catch (NumberFormatException e) { return WRONG_INPUT_MESSAGE; } if (timeInSec > 0) { Thread thread = new Thread(new Notify(bot, chatId, timeInSec * MILLISEC_IN_SEC)); thread.start(); } else return WRONG_INPUT_MESSAGE; return ""; } } 

Nous vĂ©rifions si le dĂ©lai nous a Ă©tĂ© indiquĂ© dans le texte. Sinon, nous le jurons. Si c'est le cas, nous commençons un nouveau fil, oĂč nous passons l'introduction sur nos instructions. Cette tĂąche sera gĂ©rĂ©e par une classe Notify distincte.
La fonctionnalitĂ© est extrĂȘmement simple. Il dort le nombre de secondes indiquĂ©. Mais au cours de son sommeil, votre bot est capable de recevoir d'autres messages, de communiquer avec vous et de lancer des notifications supplĂ©mentaires supplĂ©mentaires. Et tout cela fonctionne sĂ©parĂ©ment les uns des autres.

Et afin de compléter logiquement tout ce groupe avec des gestionnaires d'appel, revenons à notre classe MessageReciever et voyons comment nous comprenons de quel gestionnaire nous avons besoin et comment nous les exécutons .
Le gestionnaire nécessaire nous est retourné par la commande
 private AbstractHandler getHandlerForCommand(Command command) { if (command == null) { log.warn("Null command accepted. This is not good scenario."); return new DefaultHandler(bot); } switch (command) { case START: case HELP: case ID: SystemHandler systemHandler = new SystemHandler(bot); log.info("Handler for command[" + command.toString() + "] is: " + systemHandler); return systemHandler; case NOTIFY: NotifyHandler notifyHandler = new NotifyHandler(bot); log.info("Handler for command[" + command.toString() + "] is: " + notifyHandler); return notifyHandler; default: log.info("Handler for command[" + command.toString() + "] not Set. Return DefaultHandler"); return new DefaultHandler(bot); } } 

Maintenant, si vous souhaitez ajouter quelques commandes supplémentaires, vous devrez effectuer les opérations suivantes:

  1. Dans la classe Command , ajoutez la syntaxe de commande.
  2. Dans le récepteur, dans la fonction getHandlerForCommand, spécifiez qui sera responsable du traitement de cette commande.
  3. Et en fait, Ă©crivez ce gestionnaire.

Je dirai Ă  l'avance que le processus d'ajout de nouvelles Ă©quipes peut ĂȘtre simplifiĂ©. Les gestionnaires responsables peuvent ĂȘtre enregistrĂ©s immĂ©diatement dans la classe avec une liste de commandes. Mais je crains que le code ne soit pas facile Ă  comprendre. Le texte est trĂšs long. Mais je ne peux pas le battre en morceaux. Trois fonctions de base du bot sont dĂ©crites ici, qui ne fonctionnent qu'ensemble et il n'est pas juste d'en parler individuellement.

De quoi parlerons-nous dans les parties suivantes?

Nous devons comprendre comment former différents types de messages. Comment travailler avec le clavier et les boutons. Comment modifier vos anciens messages. Comment travailler avec les rappels. Comment confier des tùches au bot pour effectuer certaines actions. Comment créer un message interactif avec un bot et bien plus encore. Toutes les autres parties dépendent de vous et de votre activité.
Dans les commentaires, je me réjouis de vos commentaires et directions, que nous considérerons comme une priorité.

N'hésitez pas à poser des questions. Si quelque chose n'est pas indiqué dans l'article ou à un moment donné, ce n'est pas clair - écrivez-moi à ce sujet. Je corrigerai, éditerai ou clarifierai certainement les questions controversées.

Programmez avec plaisir et que la force et le beau code vous accompagnent :)

py.s.

Le bot Ă©crit dans cette partie de l'article fonctionne. Vous pouvez le tourmenter ici: @test_habr_bot
Vous pouvez Ă©galement tourmenter mon agenda: @EventCheckPlanner_Bot
Et le fan du film Cheeky : @FilmFanAmateurBot .

Source: https://habr.com/ru/post/fr481354/


All Articles