Skip to content

feat: Add Discord and Telegram ratebot plugins to send level rating n…#6

Merged
TurboRigby merged 2 commits intofeat/eventhooksfrom
feat/ratebot
Dec 10, 2025
Merged

feat: Add Discord and Telegram ratebot plugins to send level rating n…#6
TurboRigby merged 2 commits intofeat/eventhooksfrom
feat/ratebot

Conversation

@TurboRigby
Copy link
Member

@TurboRigby TurboRigby commented Dec 10, 2025

…otifications.

Summary by CodeRabbit

  • Новые возможности
    • Добавлена интеграция с Discord для отправки уведомлений о рейтинговании уровней через webhook
    • Добавлена интеграция с Telegram для отправки уведомлений о рейтинговании уровней в бота

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 10, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
nitrocore Ready Ready Preview Comment Dec 10, 2025 11:53am

@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Обзор

Добавлены два новых модуля Discord и Telegram rate bot для сервера. Оба плагина прослушивают события level_rate, валидируют действия, получают данные уровня через LevelController и отправляют уведомления на внешние API (Discord webhook и Telegram Bot API) с информацией о рейтинге уровня.

Изменения

Когорта / Файл(ы) Описание изменений
Discord Rate Bot
server/plugins/plugin-discord-ratebot.ts
Новый модуль интеграции Discord webhook. Регистрирует Nitro плагин для обработки действий level_rate, валидирует URL webhook, получает данные уровня, строит embed с информацией о рейтинге (ID, создатель, сложность, признак, статус Epic, монеты) и отправляет POST запрос к webhook с поддержкой кастомизации (роль, имя, аватар). Включает утилиты для разрешения сложности, демон-сложности, epic-уровней и форматирования монет.
Telegram Rate Bot
server/plugins/plugin-telegram-ratebot.ts
Новый модуль интеграции Telegram Bot API. Регистрирует Nitro плагин для обработки действий level_rate, валидирует токен бота и ID чата, получает данные уровня, строит многострочное сообщение с деталями рейтинга и отправляет сообщение через Telegram API с опциональными параметрами (поток, отключение уведомлений). Содержит вспомогательные функции для построения сообщения и разрешения описаний сложности.

Диаграмма последовательности

sequenceDiagram
    participant Client
    participant ActionController
    participant DiscordPlugin as Discord Plugin
    participant LevelController
    participant DiscordAPI as Discord Webhook

    Client->>ActionController: level_rate action
    ActionController->>DiscordPlugin: trigger level_rate event
    
    DiscordPlugin->>DiscordPlugin: validate action type<br/>(must start with "Rate:")
    
    DiscordPlugin->>DiscordPlugin: check config<br/>(discord_ratebot enabled)
    
    DiscordPlugin->>LevelController: fetch level data
    LevelController-->>DiscordPlugin: level data
    
    DiscordPlugin->>DiscordPlugin: build webhook payload<br/>(difficulty, epic, coins, etc.)
    
    DiscordPlugin->>DiscordAPI: POST webhook URL<br/>(embed message)
    DiscordAPI-->>DiscordPlugin: response
    
    DiscordPlugin-->>Client: log success/error
Loading
sequenceDiagram
    participant Client
    participant ActionController
    participant TelegramPlugin as Telegram Plugin
    participant LevelController
    participant TelegramAPI as Telegram Bot API

    Client->>ActionController: level_rate action
    ActionController->>TelegramPlugin: trigger level_rate event
    
    TelegramPlugin->>TelegramPlugin: validate action type<br/>(must start with "Rate:")
    
    TelegramPlugin->>TelegramPlugin: check config<br/>(telegram_ratebot enabled,<br/>botToken, chatId present)
    
    TelegramPlugin->>LevelController: fetch level data
    LevelController-->>TelegramPlugin: level data
    
    TelegramPlugin->>TelegramPlugin: build Telegram message<br/>(moderator, level details,<br/>difficulty, stats)
    
    TelegramPlugin->>TelegramAPI: send message<br/>(botToken, chatId, text)
    TelegramAPI-->>TelegramPlugin: response
    
    TelegramPlugin-->>Client: log success/error
Loading

Оценка трудоёмкости проверки кода

🎯 3 (Умеренная) | ⏱️ ~25 минут

  • Две новые независимые модули плагинов с похожей структурой, но разными внешними API интеграциями (Discord webhook vs Telegram Bot API)
  • Требуется внимательная проверка логики валидации действий, обработки ошибок и формирования данных для отправки
  • Убедитесь в корректности разрешения сложности и маппирования enum значений (сложность демона, Epic уровни)
  • Проверьте обработку ошибок при получении данных уровня и отправке запросов к внешним API
  • Валидация конфигурации (URL webhook, токен бота, ID чата) должна предотвращать попытки отправки при отсутствии настроек

Связанные pull requests

  • NitroCore#4: Эта PR напрямую совпадает с текущей — добавляет те же файлы plugin-discord-ratebot.ts и plugin-telegram-ratebot.ts с идентичной логикой интеграции webhook и Telegram API.
  • NitroCore#3: Модифицирует те же модули ratebot плагинов, добавляя локализацию пользовательских строк на русский язык в plugin-discord-ratebot.ts и plugin-telegram-ratebot.ts.
  • NitroCore#2: Добавляет идентичные модули rate-bot плагинов с той же логикой обработки события action:level_rate и отправки данных в webhook и Telegram.

Рекомендуемые рецензенты

  • m41denx

Стихотворение

🐰 Крольчонок прыгает с утра,
Когда приходит рейтинг - вот беда!
Discord слышит, Telegram кричит,
Уровни на оценку в мир летят,
Webhook танцует, боты чирикают,
И сложности в красивых числах светят! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Заголовок описывает основное изменение — добавление плагинов Discord и Telegram для отправки уведомлений о рейтинге уровней, что соответствует основному содержанию PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@TurboRigby
Copy link
Member Author

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
server/plugins/plugin-telegram-ratebot.ts (2)

66-73: Рекомендуется добавить таймаут для запроса к Telegram API.

Отсутствие таймаута может привести к зависанию запроса при проблемах с сетью или недоступности Telegram API.

     try {
         await $fetch(`${telegramBase}/bot${moduleConfig.botToken}/sendMessage`, {
             method: "POST" as any,
-            body
+            body,
+            timeout: 10000
         })
     } catch (error) {

146-152: Логика formatCoins отличается от Discord-плагина.

В Telegram плагине используется формат ${verified}/${userCoins} подтверждены, а в Discord — ${verified}/${userCoins} verified. При verified >= userCoins также различается вывод. Рекомендуется унифицировать логику.

server/plugins/plugin-discord-ratebot.ts (2)

75-82: Рекомендуется добавить таймаут для запроса к Discord API.

Аналогично Telegram-плагину, отсутствие таймаута может привести к зависанию.

     try {
         await $fetch(moduleConfig.webhookUrl, {
             method: "POST" as any,
-            body: webhookBody
+            body: webhookBody,
+            timeout: 10000
         })
     } catch (error) {

136-156: Недостижимый код в resolveDifficulty.

Строка 155 (return { label: \${stars}★`, color: difficultyPalette.normal }`) недостижима — все значения stars уже обработаны в предыдущих условиях.

     if (stars >= 10) {
         const demonLabel = resolveDemon(demonDifficulty)
         return { label: demonLabel, color: difficultyPalette.demon }
     }
-    return { label: `${stars}★`, color: difficultyPalette.normal }
+    // Все значения stars обработаны выше
+    return { label: "Unknown", color: difficultyPalette.normal }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3268a33 and 9e60a08.

📒 Files selected for processing (2)
  • server/plugins/plugin-discord-ratebot.ts (1 hunks)
  • server/plugins/plugin-telegram-ratebot.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
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)
server/plugins/plugin-discord-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)
🔇 Additional comments (2)
server/plugins/plugin-telegram-ratebot.ts (1)

19-27: Обработка ошибок выглядит хорошо.

Плагин корректно оборачивает вызов в try-catch и логирует предупреждение при ошибке. Структура соответствует аналогичному Discord-плагину.

server/plugins/plugin-discord-ratebot.ts (1)

85-134: Функция createWebhookBody реализована корректно.

Хорошо структурированный embed с полями, условное исключение Epic Tier, поддержка опциональных настроек (mention, username, avatar).

Comment on lines +57 to +58
if (!moduleConfig.webhookUrl.startsWith("http"))
throw new Error("Webhook URL must be absolute")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Слабая валидация URL вебхука.

Проверка startsWith("http") пропустит как http://, так и https://. Discord вебхуки должны использовать HTTPS. Также стоит проверить, что URL ведёт на домен Discord.

-    if (!moduleConfig.webhookUrl.startsWith("http"))
-        throw new Error("Webhook URL must be absolute")
+    if (!moduleConfig.webhookUrl.startsWith("https://discord.com/api/webhooks/") &&
+        !moduleConfig.webhookUrl.startsWith("https://discordapp.com/api/webhooks/"))
+        throw new Error("Invalid Discord webhook URL")
🤖 Prompt for AI Agents
In server/plugins/plugin-discord-ratebot.ts around lines 57-58, the current
check only uses startsWith("http") which allows non-HTTPS and non-Discord URLs;
replace with robust validation: parse the URL using the URL constructor, ensure
protocol === "https:", validate hostname is a Discord webhook host (e.g.,
endsWith("discord.com") or endsWith("discordapp.com")), and optionally verify
the pathname matches the webhook pattern (contains "/api/webhooks/"); if any
check fails, throw a clear Error("Invalid Discord webhook URL: must be an
https:// Discord webhook URL").

Comment on lines 158 to 171
const resolveDemon = (value: number) => {
switch (value) {
case 0:
return "Easy Demon"
case 1:
return "Medium Demon"
case 2:
return "Hard Demon"
case 4:
return "Extreme Demon"
default:
return "Insane Demon"
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Маппинг демонов отличается от Telegram-плагина.

Discord использует case 0 → Easy Demon, а Telegram использует 0 → Hard Demon. Это критическое расхождение, которое нужно исправить в одном из плагинов (см. комментарий в Telegram-плагине).

🤖 Prompt for AI Agents
In server/plugins/plugin-discord-ratebot.ts around lines 158–171, the demon
difficulty mapping currently uses case 0 → "Easy Demon" which differs from the
Telegram plugin (where 0 → "Hard Demon"); update the switch so the Discord
plugin uses the same numeric→label mapping as the Telegram plugin (i.e., change
the case labels/returns to match Telegram’s mapping), run/adjust any tests or
consumers that depend on these labels, and add a brief inline comment noting
that this mapping must stay consistent across Discord and Telegram plugins.

Comment on lines 102 to 120
const describeDifficulty = (stars: number, demonDifficulty: number): DifficultyDescriptor => {
if (!stars)
return { name: "Unrated", stars: 0 }
if (stars === 1)
return { name: "Auto", stars }
if (stars === 2)
return { name: "Easy", stars }
if (stars === 3)
return { name: "Normal", stars }
if (stars === 4 || stars === 5)
return { name: "Hard", stars }
if (stars === 6 || stars === 7)
return { name: "Harder", stars }
if (stars === 8 || stars === 9)
return { name: "Insane", stars }
if (stars >= 10)
return { name: resolveDemonLabel(demonDifficulty), stars }
return { name: `${stars}★`, stars }
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n server/plugins/plugin-telegram-ratebot.ts | sed -n '102,135p'

Repository: RigbyHost/NitroCore

Length of output: 1346


🏁 Script executed:

fd -e ts -name "*discord*" -path "*/plugins/*"

Repository: RigbyHost/NitroCore

Length of output: 293


🏁 Script executed:

fd -e ts -name "*.ts" -path "*/plugins/*" | head -20

Repository: RigbyHost/NitroCore

Length of output: 293


🏁 Script executed:

fd -type f -name "*discord*" server/plugins/

Repository: RigbyHost/NitroCore

Length of output: 233


🏁 Script executed:

ls -la server/plugins/

Repository: RigbyHost/NitroCore

Length of output: 686


🏁 Script executed:

cat -n server/plugins/plugin-discord-ratebot.ts

Repository: RigbyHost/NitroCore

Length of output: 7525


🏁 Script executed:

rg "demonDifficulty" server/plugins/ -C 2

Repository: RigbyHost/NitroCore

Length of output: 2615


Remove unreachable code and align demon difficulty mapping with Discord plugin.

Line 119 is unreachable—all star values from 0 to 10+ are handled by the preceding conditions. Additionally, resolveDemonLabel uses inconsistent demon difficulty mappings compared to the Discord plugin's resolveDemon:

  • Telegram: 0 → Hard Demon, 3 → Easy Demon, 4 → Medium Demon, 6 → Extreme Demon
  • Discord: 0 → Easy Demon, 1 → Medium Demon, 2 → Hard Demon, 4 → Extreme Demon

This causes the same difficulty level to display differently across channels.

Update the function to remove the unreachable fallback and align the mapping:

const describeDifficulty = (stars: number, demonDifficulty: number): DifficultyDescriptor => {
    if (!stars)
        return { name: "Unrated", stars: 0 }
    if (stars === 1)
        return { name: "Auto", stars }
    if (stars === 2)
        return { name: "Easy", stars }
    if (stars === 3)
        return { name: "Normal", stars }
    if (stars === 4 || stars === 5)
        return { name: "Hard", stars }
    if (stars === 6 || stars === 7)
        return { name: "Harder", stars }
    if (stars === 8 || stars === 9)
        return { name: "Insane", stars }
-    if (stars >= 10)
-        return { name: resolveDemonLabel(demonDifficulty), stars }
-    return { name: `${stars}★`, stars }
+    return { name: resolveDemonLabel(demonDifficulty), stars }
 }

 const resolveDemonLabel = (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"
+    switch (value) {
+        case 0:
+            return "Easy Demon"
+        case 1:
+            return "Medium Demon"
+        case 2:
+            return "Hard Demon"
+        case 4:
+            return "Extreme Demon"
+        default:
+            return "Insane Demon"
+    }
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In server/plugins/plugin-telegram-ratebot.ts around lines 102 to 120, remove the
unreachable final fallback and update the demon-label mapping to match the
Discord plugin: ensure stars 0→Unrated, 1→Auto, 2→Easy, 3→Normal, 4-5→Hard,
6-7→Harder, 8-9→Insane, and for stars >=10 call resolveDemonLabel but change
resolveDemonLabel to map demonDifficulty 0→Easy Demon, 1→Medium Demon, 2→Hard
Demon, 4→Extreme Demon (matching Discord); delete the unreachable return and
ensure no star values are left unhandled.

@TurboRigby TurboRigby merged commit c6b873e into feat/eventhooks Dec 10, 2025
5 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 10, 2025
@m41denx m41denx deleted the feat/ratebot branch December 10, 2025 12:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant