Команди
Реєстрація
func (m *Weather) Commands() map[string]goroku.CommandHandler {
return map[string]goroku.CommandHandler{
"weather": m.WeatherCmd,
}
}Ключ мапи — слово команди. З префіксом за замовчуванням . це реєструє .weather. Регістр у ключах не важливий.
Імена команд і аліасів унікальні серед усіх завантажених модулів. Якщо weather уже зайнятий іншим модулем, реєстрація впаде з помилкою колізії, де названо конфліктне ім'я, а не мовчки перезапише чужу команду.
Аліаси
Додайте CommandMetas — це необов'язковий інтерфейс, оголосити метод достатньо:
func (m *Weather) CommandMetas() map[string]goroku.CommandMeta {
return map[string]goroku.CommandMeta{
"weather": {
Aliases: []string{"w", "wx"},
},
}
}Тепер працюють .weather, .w і .wx.
Обмеження місця виклику
Поля CommandMeta — це фільтри. Диспетчер перевіряє їх до запуску обробника, тому невідповідна команда просто не спрацьовує.
func (m *Weather) CommandMetas() map[string]goroku.CommandMeta {
return map[string]goroku.CommandMeta{
"weather": {
Aliases: []string{"w"},
OnlyOwner: true, // тільки ви
OnlyGroups: true, // тільки в групах
NoForwarded: true, // ігнорувати переслані
},
}
}Найуживаніші:
| Поле | Дія |
|---|---|
OnlyOwner | Тільки власник акаунта |
OnlyPM / NoPM | Тільки / ніколи в приватних чатах |
OnlyGroups, OnlyChannels, OnlyChats | Обмеження за типом чату |
OnlyReply / NoReply | Вимагати / забороняти відповідь на повідомлення |
OnlyMedia, OnlyPhotos, OnlyVideos, OnlyDocs | Вимагати вкладення |
NoMedia, NoStickers, NoAudio, NoDoc | Відхиляти вкладення |
Ratelimit | Застосовувати ліміти частоти за користувачем і чатом |
Filter | func(*Message) bool для всього іншого |
Повний перелік — у CommandMeta.
Збіг за вмістом
"weather": {
StartsWith: "місто ",
Regex: `^\d{5}$`,
},Regex компілюється один раз під час реєстрації. Некоректний шаблон покладе реєстрацію, а не мовчки ніколи не збігатиметься.
Помилки
Поверніть помилку — і користувач її побачить:
func (m *Weather) WeatherCmd(msg *goroku.Message) error {
city := msg.Args()
if city == "" {
return msg.Answer(m.T("no_city", "❌ <b>Яке місто?</b>"))
}
data, err := m.fetch(city)
if err != nil {
return fmt.Errorf("отримання погоди для %q: %w", city, err)
}
return msg.Answer(data)
}Два різні результати:
msg.Answer(...)зі зрозумілим текстом — очікувана ситуація на кшталт відсутніх аргументів. Повертайтеnil.return err— щось справді зламалося. Goroku залогує це й покаже користувачу❌ Помилка виконання команди: <ваш текст>.
Загортайте помилки контекстом (fmt.Errorf("...: %w", err)) — саме цей текст ви читатимете в лозі о третій ночі.
Ліміти частоти
За замовчуванням команди не обмежені. Вмикається на команду:
"weather": { Ratelimit: true },Диспетчер застосує налаштовані вікна за користувачем і чатом та повідомить користувача про перевищення.
Паралельність
Обробники виконуються паралельно, кожен у своїй горутині з обмеженого пулу. Два наслідки:
- Спільний стан на структурі модуля захищайте м'ютексом. Модуль — один екземпляр на всі чати.
- Довга робота припустима й не блокує інші команди, але поважайте скасування через
msg.Context(), інакше завершення бота затягнеться.
type Weather struct {
goroku.Base
mu sync.Mutex
cache map[string]string
}
func (m *Weather) WeatherCmd(msg *goroku.Message) error {
m.mu.Lock()
cached, ok := m.cache[msg.Args()]
m.mu.Unlock()
if ok {
return msg.Answer(cached)
}
return m.fetchAndAnswer(msg)
}