Модуль від початку до кінця
Зберемо модуль Notes: зберегти нотатку, показати список, видалити одну. Тут задіяно сховище, аргументи, аліаси, фільтри й переклади — майже все, що потрібно справжньому модулю.
1. Згенерувати
.newmod Notes2. Форма зберігання
Нотатки прив'язані до чату, тому одна мапа за ID чату зі списком нотаток:
go
package modules
import (
"fmt"
"strconv"
"strings"
"goroku/goroku"
"goroku/goroku/utils"
)
type Notes struct {
goroku.Base
}
func (m *Notes) Name() string { return "Notes" }
// notesFor повертає нотатки, збережені в одному чаті.
func (m *Notes) notesFor(chatID int64) []string {
all := m.DB.GetStringMapStringSlice(m.Name(), "notes", nil)
if all == nil {
return nil
}
return all[strconv.FormatInt(chatID, 10)]
}
// saveNotes замінює нотатки одного чату.
func (m *Notes) saveNotes(chatID int64, notes []string) error {
all := m.DB.GetStringMapStringSlice(m.Name(), "notes", nil)
if all == nil {
all = map[string][]string{}
}
all[strconv.FormatInt(chatID, 10)] = notes
if err := m.DB.SetStringMapStringSlice(m.Name(), "notes", all); err != nil {
return fmt.Errorf("збереження нотаток: %w", err)
}
return nil
}Геттер повертає копію, тому читаємо, змінюємо й записуємо назад.
3. Рядки
go
func (m *Notes) Strings() map[string]string {
return map[string]string{
"name": "Notes",
"_cls_doc": "Нотатки за чатами",
"_cmd_doc_note": "[текст] — зберегти нотатку або відповідайте на повідомлення",
"_cmd_doc_notes": "Показати збережені нотатки",
"_cmd_doc_delnote": "[номер] — видалити нотатку",
"saved": "📝 <b>Збережено під #{n}</b>",
"empty": "📭 <b>Нотаток поки немає</b>",
"no_text": "❌ <b>Надішліть текст або відповідайте на повідомлення</b>",
"bad_index": "❌ <b>Вкажіть номер нотатки, див. .notes</b>",
"deleted": "🗑 <b>Видалено #{n}</b>",
}
}4. Команди
go
func (m *Notes) Commands() map[string]goroku.CommandHandler {
return map[string]goroku.CommandHandler{
"note": m.NoteCmd,
"notes": m.NotesCmd,
"delnote": m.DelNoteCmd,
}
}
func (m *Notes) CommandMetas() map[string]goroku.CommandMeta {
return map[string]goroku.CommandMeta{
"note": {Aliases: []string{"n"}, OnlyOwner: true},
"notes": {OnlyOwner: true},
"delnote": {Aliases: []string{"dn"}, OnlyOwner: true},
}
}Лише власнику: команди працюють із вашим власним сховищем.
5. Збереження
go
func (m *Notes) NoteCmd(msg *goroku.Message) error {
text := msg.ArgsOrReply()
if text == "" {
return msg.Answer(m.T("no_text", "❌ <b>Надішліть текст або відповідайте на повідомлення</b>"))
}
notes := append(m.notesFor(msg.ChatID), text)
if err := m.saveNotes(msg.ChatID, notes); err != nil {
return err
}
tpl := m.T("saved", "📝 <b>Збережено під #{n}</b>")
return msg.Answer(strings.Replace(tpl, "{n}", strconv.Itoa(len(notes)), 1))
}ArgsOrReply безкоштовно дає і .note купити молоко, і .note у відповідь на повідомлення.
6. Список
go
func (m *Notes) NotesCmd(msg *goroku.Message) error {
notes := m.notesFor(msg.ChatID)
if len(notes) == 0 {
return msg.Answer(m.T("empty", "📭 <b>Нотаток поки немає</b>"))
}
var b strings.Builder
b.WriteString("📝 <b>Нотатки</b>\n\n")
for i, note := range notes {
fmt.Fprintf(&b, "%d. %s\n", i+1, utils.EscapeHTML(note))
}
return msg.Answer(b.String())
}EscapeHTML тут важливий: нотатка з < інакше зламала б повідомлення.
7. Видалення
go
func (m *Notes) DelNoteCmd(msg *goroku.Message) error {
notes := m.notesFor(msg.ChatID)
if len(notes) == 0 {
return msg.Answer(m.T("empty", "📭 <b>Нотаток поки немає</b>"))
}
n, err := strconv.Atoi(msg.Arg(0))
if err != nil || n < 1 || n > len(notes) {
return msg.Answer(m.T("bad_index", "❌ <b>Вкажіть номер нотатки, див. .notes</b>"))
}
notes = append(notes[:n-1], notes[n:]...)
if err := m.saveNotes(msg.ChatID, notes); err != nil {
return err
}
tpl := m.T("deleted", "🗑 <b>Видалено #{n}</b>")
return msg.Answer(strings.Replace(tpl, "{n}", strconv.Itoa(n), 1))
}msg.Arg(0) не виходить за межі — .delnote без аргументу дає "", що Atoi відхилить, і користувач отримає зрозуміле повідомлення.
8. Встановити
Надішліть файл, відповідайте на нього, .loadmod. Потім:
.note купити молоко
.note (у відповідь на щось)
.notes
.delnote 1Що тут було
| Можливість | Де |
|---|---|
| Сховище з читанням-зміною-записом | notesFor / saveNotes |
| Аргументи або текст відповіді | ArgsOrReply |
| Аргументи без виходу за межі | Arg(0) |
| Аліаси та «лише власник» | CommandMetas |
| Перекладний текст зі значенням за замовчуванням | m.T |
| Екранування користувацьких даних | utils.EscapeHTML |
| Загортання помилок | fmt.Errorf("...: %w", err) |
Далі
- Додайте налаштування
max_notes— Налаштування - Реагуйте на розмову — Вотчери
- Поділіться — Встановлення модуля