Skip to content

Команды

Регистрация

go
func (m *Weather) Commands() map[string]goroku.CommandHandler {
	return map[string]goroku.CommandHandler{
		"weather": m.WeatherCmd,
	}
}

Ключ карты — слово команды. С префиксом по умолчанию . это регистрирует .weather. Регистр в ключах не важен.

Имена команд и алиасов уникальны среди всех загруженных модулей. Если weather уже занят другим модулем, регистрация упадёт с ошибкой коллизии, где названо конфликтующее имя, а не молча перезапишет чужую команду.

Алиасы

Добавьте CommandMetas — это опциональный интерфейс, объявить метод достаточно:

go
func (m *Weather) CommandMetas() map[string]goroku.CommandMeta {
	return map[string]goroku.CommandMeta{
		"weather": {
			Aliases: []string{"w", "wx"},
		},
	}
}

Теперь работают .weather, .w и .wx.

Ограничение места вызова

Поля CommandMeta — это фильтры. Диспетчер проверяет их до запуска обработчика, поэтому неподходящая команда просто не срабатывает.

go
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Применять лимиты частоты по пользователю и чату
Filterfunc(*Message) bool для всего остального

Полный список — в CommandMeta.

Совпадение по содержимому

go
"weather": {
	StartsWith: "город ",
	Regex:      `^\d{5}$`,
},

Regex компилируется один раз при регистрации. Некорректный шаблон уронит регистрацию, а не будет молча никогда не совпадать.

Ошибки

Верните ошибку — и пользователь её увидит:

go
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)) — именно этот текст вы будете читать в логе в три часа ночи.

Лимиты частоты

По умолчанию команды не ограничены. Включается на команду:

go
"weather": { Ratelimit: true },

Диспетчер применит настроенные окна по пользователю и чату и сообщит пользователю о превышении.

Параллельность

Обработчики выполняются параллельно, каждый в своей горутине из ограниченного пула. Два следствия:

  • Общее состояние на структуре модуля защищайте мьютексом. Модуль — один экземпляр на все чаты.
  • Долгая работа допустима и не блокирует другие команды, но уважайте отмену через msg.Context(), иначе завершение бота затянется.
go
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)
}

Released under the GNU AGPL v3 License.