Conversation
feat: Add Discord and Telegram ratebot plugins to send level rating n…
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughДобавлена асинхронная система событий для отслеживания действий пользователей. ActionController теперь эмитирует события через новый SDKEvents после вставки в базу данных. Введены типы AvailableActions и LikeItemType для публичного API, создан контекст на основе unctx, разработан EventFabric для управления эмиттерами и добавлены плагины Discord и Telegram для уведомлений о рейтинговании уровней. Changes
Sequence Diagram(s)sequenceDiagram
participant Controller as ActionController
participant SDK as useSDK()
participant Events as SDKEvents
participant Fabric as EventFabric
participant DiscordPlugin as Discord Plugin
participant TelegramPlugin as Telegram Plugin
participant Discord as Discord API
participant Telegram as Telegram API
Controller->>Controller: registerAction()
Controller->>Controller: Insert action to DB
Controller->>SDK: Get SDK instance
SDK->>Events: emitAction(level_rate, uid, targetId, data, context)
Events->>Fabric: useFabric('actions').emit(...)
par Discord Webhook
Fabric->>DiscordPlugin: onAction listener triggered
DiscordPlugin->>DiscordPlugin: Validate & prepare payload
DiscordPlugin->>Discord: POST webhook
Discord-->>DiscordPlugin: Success
and Telegram Message
Fabric->>TelegramPlugin: onAction listener triggered
TelegramPlugin->>TelegramPlugin: Validate & prepare message
TelegramPlugin->>Telegram: Send message via Bot API
Telegram-->>TelegramPlugin: Success
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (5)
sdk/events/types.ts (1)
4-8: Рассмотрите использование существующего типаMaybePromise.Возвращаемый тип
void | Promise<void>можно заменить наMaybePromise<void>, который уже определен вserver/utils/types.ts. Это улучшит согласованность типов в кодовой базе.Примените этот diff:
+import {MaybePromise} from "~~/server/utils/types"; + export type ActionListener = ( uid: number, targetId: number, data: ActionData -) => void | Promise<void> +) => MaybePromise<void>server/plugins/plugin-telegram-ratebot.ts (1)
29-74: Избегайте использованияas anyдля HTTP-методов.На строке 68 используется
method: "POST" as any, что ослабляет типобезопасность. Проверьте типы$fetch— возможно, можно использовать корректный тип без приведения, или использовать более конкретное приведение типа.Рассмотрите замену:
- method: "POST" as any, + method: "POST",Если возникает ошибка типизации, используйте более явное приведение или проверьте версию и типы библиотеки
$fetch.sdk/events/context.ts (1)
1-9: Неиспользуемый импорт.На строке 3 импортируется
H3EventContextизh3, но этот тип нигде не используется в файле. Рассмотрите его удаление для чистоты кода.import {createContext} from "unctx"; import {AsyncLocalStorage} from "node:async_hooks"; -import {H3EventContext} from "h3";server/plugins/plugin-discord-ratebot.ts (2)
57-58: Слабая валидация URL.Проверка
startsWith("http")пропустит невалидные URL вроде"httpxyz". Рекомендую использовать конструкторURLдля более надёжной валидации:- if (!moduleConfig.webhookUrl.startsWith("http")) - throw new Error("Webhook URL must be absolute") + try { + new URL(moduleConfig.webhookUrl) + } catch { + throw new Error("Webhook URL must be a valid absolute URL") + }
75-82: Излишнее приведение типаas any.
$fetchиз Nitro принимаетmethod: "POST"без необходимости приведения типа:try { await $fetch(moduleConfig.webhookUrl, { - method: "POST" as any, + method: "POST", body: webhookBody }) } catch (error) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
controller/ActionController.ts(3 hunks)sdk/events/SDKEvents.ts(1 hunks)sdk/events/context.ts(1 hunks)sdk/events/types.ts(1 hunks)server/plugins/plugin-discord-ratebot.ts(1 hunks)server/plugins/plugin-telegram-ratebot.ts(1 hunks)server/utils/types.ts(1 hunks)server/utils/useFabric.ts(1 hunks)server/utils/useSDK.ts(1 hunks)server/utils/useServerConfig.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
sdk/events/SDKEvents.ts (5)
controller/ActionController.ts (1)
AvailableActions(168-171)sdk/events/types.ts (2)
ActionListener(4-8)ActionInvoker(10-10)sdk/events/context.ts (2)
Context(13-16)ctx(6-9)server/utils/types.ts (1)
ArgumentTypes(8-8)drizzle/actions.ts (1)
ActionData(55-57)
sdk/events/types.ts (3)
drizzle/actions.ts (1)
ActionData(55-57)sdk/events/context.ts (1)
Context(13-16)server/utils/types.ts (1)
ArgumentTypes(8-8)
server/plugins/plugin-telegram-ratebot.ts (5)
server/utils/useSDK.ts (1)
useSDK(11-11)drizzle/actions.ts (1)
ActionData(55-57)server/utils/useLogger.ts (1)
useLogger(11-11)controller/LevelController.ts (1)
LevelController(9-187)controller/Level.ts (1)
LevelWithUser(9-11)
sdk/events/context.ts (2)
server/utils/useSDK.ts (1)
useEventContext(15-15)server/utils/useServerConfig.ts (1)
ServerConfig(17-56)
server/utils/useSDK.ts (2)
sdk/music/SDKMusic.ts (1)
SDKMusic(5-97)sdk/events/SDKEvents.ts (1)
SDKEvents(6-31)
server/plugins/plugin-discord-ratebot.ts (4)
server/utils/useSDK.ts (1)
useSDK(11-11)drizzle/actions.ts (1)
ActionData(55-57)server/utils/useLogger.ts (1)
useLogger(11-11)controller/Level.ts (1)
LevelWithUser(9-11)
controller/ActionController.ts (2)
server/utils/useSDK.ts (1)
useSDK(11-11)drizzle/actions.ts (1)
ActionData(55-57)
🔇 Additional comments (15)
server/utils/types.ts (1)
8-8: Хорошее добавление утилитного типа!Тип
ArgumentTypes<F>корректно извлекает типы аргументов функции и будет полезен для типизацииActionInvokerв системе событий.server/utils/useServerConfig.ts (1)
17-56: Правильное решение экспортировать тип!Экспорт
ServerConfigнеобходим для использования в публичном API контекста событий (Contextвsdk/events/context.ts). Структура конфигурации остается неизменной.server/utils/useSDK.ts (1)
3-15: Хорошая интеграция событийной системы в SDK!Добавление
SDKEventsследует существующему паттерну (аналогичноcommandsиmusic). ЭкспортuseEventContextобеспечивает удобный доступ к контексту событий из публичного API.sdk/events/SDKEvents.ts (1)
22-30: Логика эмиссии событий корректна.Метод
emitActionправильно передает все параметры в фабрику событий. После добавления импортаuseFabric, указанного в предыдущем комментарии, метод будет работать корректно.server/plugins/plugin-telegram-ratebot.ts (5)
19-27: Правильная регистрация плагина!Использование
defineNitroPluginи подписка на событиеlevel_rateреализованы корректно. Обработка ошибок с логированием предотвращает падение приложения при сбоях отправки уведомлений.
76-100: Отличная реализация форматирования сообщений!Функция
buildTelegramMessageхорошо структурирована с использованием опциональных цепочек, значений по умолчанию и фильтрации. Логика построения сообщения понятна и поддерживаема.
102-123: Логика определения сложности реализована корректно.Функция
describeDifficultyохватывает все диапазоны звезд от 0 до бесконечности. Комментарий правильно указывает, что fallback теоретически недостижим при корректных данных.
125-133: Проверьте маппинг сложности демонов.В объекте
mapотсутствует ключ3, хотя присутствуют ключи0,1,2, и4. Если значение3должно возвращать"Insane Demon"(через дефолтное значение), это поведение следует явно задокументировать. В противном случае, это может быть ошибкой.Пожалуйста, подтвердите:
- Является ли отсутствие ключа
3намеренным?- Если да, рассмотрите добавление комментария для ясности:
const resolveDemonLabel = (value: number) => { const map: Record<number, string> = { // 3 intentionally omitted - defaults to "Insane Demon" } return map[value] || "Insane Demon" }
135-154: Вспомогательные функции реализованы хорошо.Функции
resolveEpicиformatCoinsкорректно обрабатывают соответствующие значения с понятной логикой и правильными значениями по умолчанию.server/utils/useFabric.ts (2)
14-21: РеализацияuseFabricкорректна.Функция правильно управляет именованными экземплярами фабрики событий. Приведение типов
as unknown as EventEmitter<T>необходимо для обеспечения типобезопасности с обобщенными типами событий.
27-35: Отличная реализация временной фабрики!Функция
useTemporalFabricпредоставляет чистый API для создания изолированных эмиттеров событий с механизмом очистки. ИспользованиеremoveAllListeners()перед удалением предотвращает утечки памяти.controller/ActionController.ts (1)
168-173: LGTM!Типы
AvailableActionsиLikeItemTypeкорректно определены и соответствуют логике switch-case в методах класса.server/plugins/plugin-discord-ratebot.ts (3)
29-37: LGTM!Регистрация плагина корректна: используется правильный паттерн с
defineNitroPlugin, ошибки перехватываются и логируются.
85-134: LGTM!Функция
createWebhookBodyхорошо структурирована: корректно формирует embed для Discord, условно исключает поле "Epic Tier" когда оно не применимо, и правильно обрабатывает опциональные поля конфигурации.
136-156: LGTM!Вспомогательные функции
resolveDifficulty,resolveEpicиformatCoinsкорректно реализованы и покрывают все необходимые случаи.Also applies to: 169-188
| const resolveDemon = (value: number) => { | ||
| const map: Record<number, string> = { | ||
| 3: "Easy Demon", | ||
| 4: "Medium Demon", | ||
| 0: "Hard Demon", | ||
| 5: "Insane Demon", | ||
| 6: "Extreme Demon", | ||
| } | ||
| return map[value] || "Insane Demon" | ||
| } |
There was a problem hiding this comment.
Несогласованное значение по умолчанию для демонов.
Согласно LevelController.countDemonStats, значение по умолчанию (когда demonDifficulty не соответствует 3, 4, 5 или 6) — это Hard Demon, а не "Insane Demon":
const resolveDemon = (value: number) => {
const map: Record<number, string> = {
}
- return map[value] || "Insane Demon"
+ return map[value] || "Hard Demon"
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const resolveDemon = (value: number) => { | |
| const map: Record<number, string> = { | |
| 3: "Easy Demon", | |
| 4: "Medium Demon", | |
| 0: "Hard Demon", | |
| 5: "Insane Demon", | |
| 6: "Extreme Demon", | |
| } | |
| return map[value] || "Insane Demon" | |
| } | |
| const resolveDemon = (value: number) => { | |
| const map: Record<number, string> = { | |
| 3: "Easy Demon", | |
| 4: "Medium Demon", | |
| 0: "Hard Demon", | |
| 5: "Insane Demon", | |
| 6: "Extreme Demon", | |
| } | |
| return map[value] || "Hard Demon" | |
| } |
🤖 Prompt for AI Agents
In server/plugins/plugin-discord-ratebot.ts around lines 158 to 167, the
resolveDemon function returns "Insane Demon" as the fallback for unknown values,
but LevelController.countDemonStats uses "Hard Demon" as the default; change the
fallback to "Hard Demon" so the function returns map[value] || "Hard Demon" (no
other logic changes needed).
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
Summary by CodeRabbit
Примечания к выпуску
✏️ Tip: You can customize this high-level summary in your review settings.