Aufbau eines Moduls
Jedes Modul erfüllt goroku.Module. Das Einbetten von goroku.Base liefert einen Standardwert für alles außer Name und Commands — nur diese beiden Methoden musst du schreiben.
type Module interface {
Name() string
Strings() map[string]string
Init(client *CustomTelegramClient, db *Database) error
ClientReady() error
OnUnload() error
OnDlmod() error
Commands() map[string]CommandHandler
Watchers() []WatcherHandler
}Pflicht
Name
Die Identität des Moduls in .help, .unloadmod und der Konfigurationsoberfläche. Es ist zugleich der Go-Typname und der Schlüssel, unter dem deine Daten liegen.
func (m *Weather) Name() string { return "Weather" }Commands
Ordnet einem Befehlswort einen Handler zu. Nutzer tippen das Wort nach dem Präfix, aus "weather" wird also .weather.
func (m *Weather) Commands() map[string]goroku.CommandHandler {
return map[string]goroku.CommandHandler{
"weather": m.WeatherCmd,
"forecast": m.ForecastCmd,
}
}Ein CommandHandler ist func(msg *goroku.Message) error. Ein zurückgegebener Fehler wird dem Nutzer gemeldet; der Dispatcher fängt zudem Panics ab, ein Absturz in deinem Handler legt den Bot also nicht lahm.
Optional, mit Standardwerten aus Base
Strings
Text für Nutzer und Quelle für m.T. Zwei Schlüsselpräfixe sind besonders:
| Schlüssel | Bedeutung |
|---|---|
name | Anzeigename in .help |
_cls_doc | Was das Modul tut |
_cmd_doc_<Befehl> | Was dieser Befehl tut |
Alles andere gehört dir und wird mit m.T abgerufen.
func (m *Weather) Strings() map[string]string {
return map[string]string{
"name": "Weather",
"_cls_doc": "Aktuelles Wetter für eine Stadt",
"_cmd_doc_weather": "[Stadt] — Wetter anzeigen",
"no_city": "❌ <b>Welche Stadt?</b>",
}
}Init
Läuft einmal bei der Registrierung, bevor irgendein Befehl auslösen kann. Base hat m.Client, m.DB und m.Translator zu diesem Zeitpunkt schon gefüllt — nutze Init also nur für deine eigene Einrichtung.
func (m *Weather) Init(client *goroku.CustomTelegramClient, db *goroku.Database) error {
if err := m.Base.Init(client, db); err != nil {
return err
}
m.cache = make(map[string]string)
return nil
}Rufe m.Base.Init zuerst auf
Der Loader füllt die Felder vor dem Init-Aufruf, im Normalbetrieb ist dieser Aufruf also überflüssig. Lass ihn trotzdem stehen: so funktioniert das Modul auch, wenn es in einem Test direkt erzeugt wird, ohne den Loader.
ClientReady
Läuft, sobald die Telegram-Verbindung steht. Für alles, was beim Start die API braucht; Init kann vor dem Verbindungsaufbau laufen.
func (m *Weather) ClientReady() error {
me := m.Client.Me()
_ = me
return nil
}OnUnload
Läuft, wenn das Modul entfernt wird. Gib hier Goroutinen, Timer und Dateihandles frei.
func (m *Weather) OnUnload() error {
close(m.stop)
return nil
}WARNING
Ein Panic in OnUnload wird abgefangen und geloggt, das Modul wird aber trotzdem entfernt. Verlass dich für korrektheitskritische Aufräumarbeiten nicht auf OnUnload.
OnDlmod
Läuft direkt nach der Installation über .dlmod. Praktisch für eine einmalige Begrüßung oder Ersteinrichtung.
Watchers
Handler, die jede Nachricht sehen, nicht nur Befehle. Siehe Watcher.
Jenseits des Interface
Mehrere Fähigkeiten sind über eigene Interfaces optional — deklariere die Methode, und Goroku greift sie auf:
| Interface | Methode | Zweck |
|---|---|---|
ModuleWithMeta | CommandMetas() | Aliase und Einschränkungen |
ModuleWithConfigSchema | ConfigSchema() | Typisierte Einstellungen |
ModuleWithConfigReady | ConfigReady(map[string]any) | Auf Änderungen reagieren |
ModuleWithTranslationAliases | TranslationAliases() | Zusätzliche Namen für Übersetzungsdateien |
Vollständige Liste unter Optionale Interfaces.