Skip to content

Configuration

Declare ConfigSchema and your module gets a settings UI in .config, typed validation, and persistence — without writing any of it.

Declaring settings

go
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:

go
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.

go
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

TypeValidatorNotes
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:

go
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",
	}
}

Released under the GNU AGPL v3 License.