Telegram Bot auf Kotlin: Command / Sudo Null IT News
Vorheriger Teil: Telegram Bot auf Kotlin: Einführung
Dies ist ein Zwischenteil des Tutorials zum Erstellen von Telegramm-Bots basierend auf Bibliotheken Plagubot und tgbotapi. Insbesondere werden wir in diesem Teil über einen ziemlich einfachen Vergleich mit dem Geplanten sprechen Plugin für die Befehlsregistrierung beim Start und deren Installation/Reinigung weiter zur Laufzeit.
Es ist erwähnenswert, dass dieser Artikel einen vereinfachten Code enthält. Mit seiner Hilfe wird es möglich sein, ein Analogon des Plugins zu erstellen, das sich am Ende herausgestellt hat, und dennoch wird es in dem Artikel etwas oberflächlicher sein. Wenn möglich, habe ich versucht, Spoiler hinzuzufügen, wo der Code am Ende anders war, also sollte es keine Probleme geben.
Also die Aufgabe
Wie oben erwähnt, ist die Aufgabe dieses Plugins sehr einfach – die Bot-Befehle beim Start zu registrieren und dabei den Befehlssatz ändern zu können. Natürlich möchte ich für jedes Team die volle Bandbreite an Parametern spezifizieren können, nämlich:
Basislösung
Da wir irgendwo anfangen müssen, um die Befehle zu übernehmen, die der Bot anfangs setzt, wäre die einfachste Option, DI zu verwenden, um alle Befehle von anderen Plugins und Teilen der Anwendung zu erhalten. Gleichzeitig reicht es in diesen ganz anderen Plugins und Teilen der Anwendung aus, einen Befehl mit einer Bindung an den gewünschten Typ und einer zufälligen Kennung zu registrieren (damit DI nicht auf einen Typkonflikt schwört):
// So holen wir Befehle innerhalb des Plugins mit den Befehlen koin.getAll
Darüber hinaus muss die Möglichkeit bereitgestellt werden, aktuelle Bot-Befehle hinzuzufügen / zu entfernen. Zu diesem Zweck ist es möglich, eine einfache Set / Unset-Schnittstelle zu verwenden, wie zum Beispiel:
Schnittstelle CommandsKeeper { Spaß aussetzen addCommand (Befehl: CommandType) Spaß aussetzen removeCommand (Befehl: CommandType) }
Wenn man sich das Finale anschaut CommandsKeeper, werden Sie viel internes sehen. Dies ist die Fähigkeit der Sprache, die Sichtbarkeit von Elementen innerhalb eines bestimmten Moduls einzuschränken. Kurz bspw. onScopeChanged wird nur für Plugin-Teile verfügbar sein
Nicht zuletzt das Plugin selbst. Tatsächlich muss er zu Beginn alle in DI registrierten Befehle sammeln, sie selbst in CommandsKeeper einfügen und irgendwie auf Änderungen in Befehlen hören und sie aktualisieren.
Puzzle Stücke
Denn im Telegramm-Bot-API Es gibt keine Entität, die sowohl einen Befehl mit einer Beschreibung und seinem Geltungsbereich als auch einen Sprachcode enthalten würde, wir müssen selbst eine solche Entität erstellen. Aus offensichtlichen Gründen wird dies eine ziemlich einfache Datumsklasse mit drei Feldern sein:
Datenklasse BotCommandFullInfo( val command: BotCommand, val scope: BotCommandScope = BotCommandScope.Default, val languageCode: String? = null ) { val key: Pair
musste ich hinzufügen Wertklasse CommandsKeeperKey für Schlüssel, die nur alle notwendigen Informationen für den Befehl enthält: BotCommandScope und Sprachcode. Infolgedessen laufen im Plugin und seiner API alle Aufrufe auf Aufrufe mit CommandsKeeperKey hinaus
Für CommandsKeeper können Sie die einfachste Implementierung basierend auf der Karte vornehmen, in der die Schlüssel der Befehlssatzkontext sind – Befehlsbereich und Sprachcode. Ein Beispiel für eine grundlegende Implementierung basierend auf der oben vorgestellten Schnittstelle:
class CommandsKeeper( // Abrufen der beim Start festzulegenden Befehle im Konstruktor val preset: List
Tatsächlich haben wir im Wesentlichen eine ziemlich einfache Klasse: Wir können einen Befehl in anderen Plugins hinzufügen (addCommand) oder entfernen (removeCommand), und innerhalb des Team-Plugin-Projekts können wir eine Reihe von Befehlen für den Kontext abrufen und den Befehl abonnieren Änderungen. All dies wird mit der Synchronisierung zum Zeitpunkt der Installation / Löschung eines Teams gewürzt
Stimmt, es ist nicht gerade idiomatisch 🙁
Und es wäre idiomatisch, eine versiegelte Schnittstelle für den Aufgabentyp und ein paar Datenklassen zum Hinzufügen / Löschen von Befehlen zu erstellen und alles mit einem verzögerten Anhang an einen Kanal zu senden, von dem wir auf das Ergebnis warten. Wie Sie anhand der Nachteile dieses Ansatzes sehen können, ist er sehr umständlich. Von den Pluspunkten – wir werden keine Synchronisationen mehr haben und möglicherweise wird solcher Code für Coroutinen leichter zu verdauen sein und folglich für ihre Arbeit im Allgemeinen
Das dickste Stück
Wie Sie sich vorstellen können, wird das fetteste Stück auf diesem Kuchen das Plugin selbst sein. Im Wesentlichen ist es jedoch sehr einfach: einen CommandsKeeper erstellen und bei DI registrieren, die Bot-Befehle zum Zeitpunkt der Installation festlegen und sie verfolgen, während der Bot läuft. Hier ist das Skelett unseres Plugins:
@Serializable object CommandsPlugin : Plugin { override fun Module.setupDI(database: Database, params: JsonObject) {} override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {} }
Das heißt, im Plugin benötigt das Plugin auf der Basisebene nichts anderes als das Überschreiben der Plugin-Methoden. Und jetzt fügen wir die Erstellung und Registrierung von CommandsKeeper zu setupDI hinzu:
override fun Module.setupDI(database: Database, params: JsonObject) { // Eine einzelne CommandsKeeper-Instanz registrieren single { // Eine CommandsKeeperImpl-Instanz erstellen CommandsKeeper( // Alle registrierten BotCommandFullInfo-Instanzen abrufen und Duplikate vermeiden getAll
Wir werden nichts anderes in DI registrieren. Da wir die Aktualisierung von Befehlen an zwei Stellen angegeben haben, nämlich beim Initialisieren des Bots und beim Ändern des Befehlssatzes, wäre es angebracht, das Installieren von Befehlen und das Entfernen von Befehlen bei deren Abwesenheit in eine separate Funktion zu trennen:
private suspend fun BehaviourContext.setScopeCommands(scope: BotCommandScope, languageCode: String?, commands: List
Es gibt mehrere Nuancen in der endgültigen Implementierung:
Es ist wünschenswert, solchen Code in runCatching / trycatch einzurahmen
Die Liste der Eingabebefehle ist nullable, da in der CommandsKeeper-Implementierung von Github die get-Methode null zurückgibt, wenn kein Befehlssatz vorhanden ist
Arbeiten Sie im Bot:
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) { // CommandsKeeper in DI oben registrieren lassen val commandsKeeper = koin.get
Alles in allem war es das 🙂
Ergebnisse
Als Ergebnis haben wir ein ziemlich einfaches Plugin erstellt, mit dem Sie Bot-Befehle zentral verwalten können. Der vollständige Code, Verbindungsanweisungen und andere nützliche Informationen sind enthalten Github-Repositories. Viel Spaß beim Benutzen!