
Vaporware — это программное или аппаратное обеспечение, которое рекламировалось, но еще не доступно для использования. Очевидно, что выпуск пароварки — это не то, чем можно гордиться. И все же мне удалось это сделать случайно. Вы можете учиться на моих ошибках, чтобы не совершать ту же ошибку. Итак, как это произошло и как я это исправил.
1 марта 2022 года я приступил к созданию Disgo, обертки Discord API, разработанной, чтобы быть гибкой, производительной, безопасной и потокобезопасной. Disgo стремится предоставить все функции Discord API, а также дополнительное ограничение скорости, структурированное ведение журнала, управление сегментами и кэширование… Однако в то время была одна проблема: мне еще предстояло закоммитить ни одной строки кода. Вместо этого я создал файл README, в котором изложил свое видение Disgo. Что я придумал?
Проектная документация
Запросы
Чтобы использовать большинство API-оболочек, вы должны усвоить три вещи.
- Язык программирования
- API потребителя (библиотека)
- API продюсера (Discord)
Это связано с тем, что большинство программистов начинают создавать библиотеки с помощью функций: например, функция отправки сообщения — ChannelMessage(…), а функция ответа на взаимодействие — InteractionRespond(…). Каждая функция имеет свои собственные параметры, что требует от вас прочтения документации библиотеки, чтобы понять, как выполнять каждую задачу.
Это огромная трата времени и ненужная сложность.
Disgo использует единую функцию для отправки HTTP-запросов(без утверждения типа или обобщений).
Send(*Client)
Вот и все. Легко запомнить и еще проще использовать. Эта функция всегда поддерживает один параметр: клиент, которым отправляется запрос. Однако вам по-прежнему нужен способ указать, какие данные на самом деле отправляются. Если вы не используете для этого функции, то какая альтернатива? Структуры.
// Create a Create Global Application Command request.
request := disgo.CreateGlobalApplicationCommand{
Name: "main",
Description: "A basic command.",
...
}
// Send the request.
command, err := request.Send(bot)
Каждая структура содержит все данные, которые понадобятся пользователю для отправки действительного запроса. Сюда входят параметры строки запроса URL, поля JSON и файлы. Таким образом, разработчику нужно будет понять только две вещи, чтобы использовать Disgo.
- Как создать структуру в Go.
- Как использовать Discord API.
Больше не нужно тратить время на изучение деталей реализации потребителем API. Как я это сделал? До выпуска Disgo v0.10.0 я этого не делал. Вот в чем проблема: вся библиотека была просто проектом, над которым работали. Это привело к единственному инциденту намного позже.
События
Другие библиотеки Go используют утверждение типа или дженерики для преобразования данных событий JSON из Discord в фактический объект Go, на который разработчики могут ссылаться во время выполнения. Дисго нет. В результате это намного быстрее. Конечно, до выпуска Disgo v0.10.0 это было верно только теоретически. Реализация этой функции обсуждается далее в этой статье.
Disgo также поддерживает автоматический расчет намерений; функция, которой нет ни в одной другой библиотеке.
Кэширование
Документация по решению использовать дополнительный кеш была опубликована позже: #39. Тем не менее, это решение было принято во время разработки Disgo. Подводя итог, можно сказать, что использование кеша, встроенного в библиотеку HTTP, увеличивает нагрузку, в лучшем случае сложно, а в худшем — неправильно.
Обновления
Прошло 6 лет с момента выпуска API Discord, но тот же самый API продолжает претерпевать многочисленные изменения, которые несколько изменчивы. Чтобы быть точным, Discord НЕфактически предоставляет документацию по своему API. Вместо этого сообщество используется для определения текущего состояния API и его документирования. Кроме того, фичи исторически рассматриваются исключительно с учетом интересов компании, что приводит к спорам, когда фича плохо реализована.
Discord неоднократно запрашивали Machine Readable API, но безрезультатно. Так что изменения в API Discord вносятся часто, но без эффективной стратегии для применения этих изменений к потребителям API. По этим причинам Disgo нужно было поддерживать высокую скорость разработки (во время и после его первоначального создания).
Статический анализ кода используется для поддержки форматированного кода, свободного от известных проблем с безопасностью. Также был запланирован обширный интеграционный тест, который охватывает большую часть API Discord. Кроме того, в Go есть детектор гонки данных, который позволяет нам обнаруживать и устранять любые потенциальные проблемы с гонкой данных или тупиковые ситуации. Таким образом, Disgo обеспечивает надежную защиту от гонки данных, не жертвуя при этом возможностью одновременного выполнения кода.
Самой важной из всех задач было завершение функций. Несколько библиотек сделали это, но ни одна из них не использовала Go.
Реализация дизайна
Язык Go использует теги JSON для демаршалирования и маршалинга полей в своих структурах. Однако это означает, что важно различать пустой и нулевой. В то время основная оболочка Go API — DiscordGo — добавляла эти теги по мере разработки, в результате чего некоторые функции Discord API были недоступны без форка. Чтобы решить эту проблему, мы создали библиотеку типов Go API для Discord API под названием Dasgo. Эта библиотека построена по спецификации и НЕспецифична для реализации Disgo; это означает, что другие оболочки Go API могут использовать его для своих типов.
Когда Dasgo используется многими людьми, нагрузка на сопровождающих Go Discord API Wrappers резко снижается. Это связано с тем, что новое изменение в структурах данных Discord API заслуживает изменения только базовых типов Go, которые предоставляет Dasgo. Сам Go обеспечивает хеширование версий, что позволяет библиотекам указывать конкретную версию или ветку для импорта. Таким образом, можно поддерживать несколько версий конечных точек и каждую версию API, которую может использовать бот Discord.
Запросы
В проектной документации указано, как API Disgo будет работать для HTTP-запросов, но как я на самом деле это реализовал? Напоминаю, что я не планировал использовать утверждение типа или дженерики: в Go и то, и другое имеет незначительные последствия для производительности. В результате я не мог использовать структуры в качестве параметра в функции Send(*Client). Вместо этого будут использоваться функции приемника.
func (r *CreateGlobalApplicationCommand) Send(bot *Client) (*ApplicationCommand, error) {
...
}
Однако есть одна проблема. Функция получателя, подобная этой, должна быть создана для каждого запроса. Тем не менее API Discord поддерживает 176 конечных точек (не считая вариантов, составляющих маршрут). Что мы собирались делать? Войдите в Copygen, генератор кода на основе типов, который я создал для создания функций копирования типа в тип. С помощью Copygen я смог использовать типы, которые мы определили в Dasgo, для создания функции Send(*Client) для каждого запроса.
Лучшая часть? Обновления так же просты, как запуск одной команды.
При создании Disgo я также обнаружил, что текущая документация по ограничению скорости вводит в заблуждение. Подробнее об этом в другой статье.
События
Шлюз Discord — это соединение TCP WebSocket, которое отправляет пакеты JSON или двоичных данных в полезной нагрузке. Это просто использовать в JavaScript, поскольку язык нетипизирован и интерпретируется. Напротив, Go статически типизирован и скомпилирован. В результате другие оболочки Go API реализуют обработку событий по типу подтверждения входящих полезных данных. Единственный способ избежать утверждения типа – разобрать полезные данные непосредственно в объект(структуру). Однако это непростая задача, так как демаршаллинг должен выполняться одновременно(из соединения WebSocket), чтобы не блокировать обработку других событий.
Не говоря уже о том, что нам нужно было предоставить разработчику способ обработки событий.
В Disgo обработчики событий добавляются или удаляются с помощью функций Handle и Remove. И все же это — опять же — одна функция для представления сотен событий. Как мы сделали это без использования утверждения типа? Необходимо было пойти на компромисс, поскольку вы не можете определить событие, которое обрабатывается, без предварительной спецификации. Чтобы быть точным, функция Handle не могла принимать единственное событие в качестве параметра, так как это потребовало бы использования отражения, чтобы определить, какой объект события следует демаршалировать.
// Add an event handler to the bot.
bot.Handle(disgo.FlagGatewayEventNameInteractionCreate, func(i *disgo.InteractionCreate) {
log.Printf("main called by %s", i.User.Username)
})
Вместо этого пользователь передает строку (указанную Dasgo), которая сообщает нам, какое событие будет удалено. Недостатком является то, что может возникнуть ошибка пользователя (разработчика), если в функцию будет передана несовпадающая строка и событие. В результате разработчикам рекомендуется обрабатывать ошибки этих функций, поскольку ошибка может произойти, хотя и маловероятно. С этого момента создание горутины (для разработчика) для демаршалирования полезной нагрузки события и горутины для каждого обработчика становится простым.
Copygen использовался для создания всего необходимого кода для обработки событий.
Намерения шлюза раздражают; особенно в текущих оболочках API. Каждый раз, когда вы хотите обработать новое событие, вы должны убедиться, что оно не требует намерения. Когда это произойдет, вы должны добавить это намерение к своему боту в любой момент времени, указанный API-оболочкой. Это много работы. Вместо этого Disgo — в погоне за обработкой событий без отражения — может автоматически вычислять намерения, необходимые разработчику. Это позволяет разработчику больше сосредоточиться на функциональности своего бота и меньше на технических деталях.
Релиз Disgo
Было несколько сложностей с завершением Disgo, которые привели к его выпуску в незавершенном состоянии. Чтобы быть точным, Disgo v0.10.0 поддерживала использование каждого запроса и события, предоставленного Discord API. Однако такие функции, как сегментирование, управление кешем и всеобъемлющее интеграционное тестирование, не были реализованы. Напротив, видение Disgo было направлено на основную ветвь. Такой, чтобы любой мог прочитать его и неосознанно предположить, что его обещания уже реализованы. Не задумываясь над его интерпретацией, я сделал v0.10.0 БЕТА-релиз.
Что мы получили после всей этой тяжелой работы?
Электронная почта
«Уважаемый SwitchUpCB,
Ваша библиотека disgo — это произведение искусства… ЕСЛИ ЭТО БУДЕТ РАБОТАТЬ!!! Черт побери! Я потратил как минимум три минуты на импорт этого ДЕРЬМА! Еще две настройки примеров… Только чтобы понять, что это на самом деле не завершено??? ЧТО С ВАМИ НЕ ТАКОЕ! Проклятый придурок. Может быть, не публикуйте вещи, пока они не будут завершены. хм? Хм?! И что теперь? Что мне нужно сделать, чтобы использовать этот гребаный инструмент… Ты что, гей? Знаешь что. Дай мне пососать твой член. Дай мне пососать твой член. Пусть bumbleboy7 сосет твой член! ПОЗВОЛЬТЕ BUMBLEBOY7 ПОСОСАТЬ ВАШ ЧЛЕН Н****. ПОЗВОЛЯТЬ. БАМБЛБОЙ7. сосать. ТВОЙ. Д*К!
Спасибо,
шмель7”
Да… Навеяно Филнобепом… и полностью фальшиво.
Очевидно, ничего страшного на самом деле не произошло. Был один пользователь, который обнаружил серьезную ошибку, из-за которой люди не могли использовать команды приложения. Я предложил этому пользователю компенсировать его время, но он не рассердился. На самом деле уже было заявление об ограничении ответственности v0.10.0. Возможно, другим библиотекам следует создать проектную документацию, прежде чем связываться с обещаниями обратной совместимости. В результате получится библиотека, не уступающая Disgo.
Используйте Disgo
Создайте бота Discord в Go: Disgo v0.10.1 был выпущен 17 ноября 2022 г..