Configuration
Declare ConfigSchema and your module gets a settings UI in .config, typed validation, and persistence — without writing any of it.
Declaring settings
var _ goroku.ModuleWithConfigSchema = (*Weather)(nil)
func (m *Weather) ConfigSchema() []goroku.ConfigField {
return []goroku.ConfigField{
{
Key: "units",
Type: "choice",
Default: "metric",
Validator: &goroku.ChoiceValidator{PossibleValues: []string{"metric", "imperial"}},
},
{
Key: "api_key",
Type: "string",
Default: "",
Validator: &goroku.StringValidator{MaxLen: 128},
Secret: true,
},
{
Key: "cache_minutes",
Type: "int",
Default: 10,
Validator: &goroku.IntegerValidator{},
},
}
}The var _ = line is worth keeping: it makes the compiler confirm you match the interface instead of the method being silently ignored.
Reading settings
Values live in the database under your module name:
units := m.DB.GetString(m.Name(), "units", "metric")
minutes := m.DB.GetInt(m.Name(), "cache_minutes", 10)Reacting to changes
ConfigReady is called at startup and again whenever a user changes a setting. Return an error to reject the change — the user sees it and the old value stays.
func (m *Weather) ConfigReady(config map[string]any) error {
units, ok := config["units"].(string)
if !ok {
return fmt.Errorf("units must be a string, got %T", config["units"])
}
m.mu.Lock()
m.units = units
m.mu.Unlock()
return nil
}Validate loudly
If a value is unusable, return an error naming what is wrong. Accepting it and quietly doing nothing leaves the user with a setting that looks applied but is not.
Field types and validators
Type | Validator | Notes |
|---|---|---|
bool | &BooleanValidator{} | |
int | &IntegerValidator{} | Bounds via Minimum/HasMin, Maximum/HasMax |
float | &FloatValidator{} | |
string | &StringValidator{MaxLen: n} | Also MinLen |
choice | &ChoiceValidator{PossibleValues: ...} | Fixed set |
series | &SeriesValidator{} | List of values |
url | &URLValidator{} | |
link | &LinkValidator{} | Telegram links |
hidden | &HiddenValidator{} | Not shown in the UI |
Others available: RegExpValidator, TelegramIDValidator, EmojiValidator, EntityLikeValidator, UnionValidator, NoneTypeValidator.
Secrets
Secret: true marks a value as redacted in logs and replaced with a placeholder in backups. Restoring a backup keeps the live value rather than overwriting it with the marker.
Use it for API keys, tokens and passwords. It costs one field and removes a whole class of accident.
Documenting settings
Prefix a Strings key with _cfg_ and it becomes the description in the settings UI:
func (m *Weather) Strings() map[string]string {
return map[string]string{
"name": "Weather",
"_cfg_units": "Temperature units: metric or imperial",
"_cfg_api_key": "API key for the weather provider",
"_cfg_cache_minutes": "How long to cache a city's forecast",
}
}