В прошлый раз я защищал свою основу доступа SQLite. Посмотрим, продержится ли он в производстве. Жаль, что я никогда не получу такой большой груз, который должен что-то сломать. Ну ладно, подождем и посмотрим. Может быть, мне повезет, и два печальных параллельных запроса в конечном итоге вызовут панику у моего двоичного кода Go.

Сегодня я сосредоточусь на функциях. Я хочу сделать своего бота умным. Не совсем то, что нравится ИИ, просто звучит немного лучше, чем cmd.exe, когда вы вводите неправильную команду. Я не собираюсь делать ничего большого, больше похоже на то, чтобы окунуть палец ноги в воду, чтобы посмотреть, как там.

Во-первых, я хочу найти последнее событие с таким же именем, поскольку я сохраняю их в базе данных, и отправляю дату события. Представьте себе такое взаимодействие:

me: eat
bot: Previous 'eat' happened 6 hours ago

Чтобы найти событие с таким же именем, я должен попросить SQLite предоставить мне строку, в которой такой же пользователь, имя события и то же самое, и мне нужна последняя строка после того, как она отсортирована по дате. Конвертируя английский язык в SQL, я получаю:

SELECT date FROM events
    WHERE user = ? AND name = ?
    ORDER BY date
    DESC LIMIT 1

Довольно просто, а? Теперь, преобразовав это в Go и crawshaw.io/sqlite, я получу следующее:

// Default response
response := fmt.Sprintf("Fist time for '%s'", name)

// Get the last event with the same name and format the response
err := sqlitex.Exec(connection,
    "SELECT date FROM events "+
        "WHERE user = ? AND name = ? "+
        "ORDER BY date "+
        "DESC LIMIT 1",
    func(s *sqlite.Stmt) error {
        response = formatResponse(s.GetInt64("date"), name)
        return nil
    },
    message.From.ID,
    name)

// Send the message back to the user
go func() {
    bot.Send(tgbotapi.NewMessage(message.Chat.ID, response))
}()

Где formatResponse очень рудиментарен:

func formatResponse(date int64, name string) string {
    last := time.Unix(date, 0)
    return fmt.Sprintf("Previous '%s' happened on '%v'", name, last)
}

Теперь взаимодействие выглядит так:

Не совсем умный бот на планете, но мы куда-то идем.

Теперь я хотел бы, чтобы это звучало немного менее глупо и немного более человечно, что не всегда сочетается друг с другом. В этом случае они есть. Я не хочу, чтобы бот говорил что-то вроде happened on '2019-04-04 14:13:57 +0200 CEST', а говорил что-то вроде 8 minutes since или 1 year since. Вы не поверите, но для этого есть пакет. Добро пожаловать в hako / durafmt. С его помощью очень легко превратить разговор в нечто подобное:

Это намного читабельнее. Чтобы заставить его работать, мне просто нужно было немного изменить функцию formatResponse (теперь она также должна принимать текущую дату сообщения):

func formatResponse(name string, date int64, prevDate int64) string {
    prev := time.Unix(prevDate, 0)
    now := time.Unix(date, 0)
    duration := durafmt.ParseShort(now.Sub(prev))
    return fmt.Sprintf("%s since last '%s'", duration, name)
}

Очень просто. С небольшим количеством строк и менее чем за час работы у меня есть бот, который говорит по-человечески и может сказать мне, когда что-то произошло в последний раз. Отправим его! 🚢

Если вам интересно, код доступен на GitHub. Эта версия помечена тегом day-4.

Первоначально опубликовано на detunized.net 4 апреля 2019 г.