Conversation
* Added the base module folders and module.json * Added all folders necessary and the configuration files for each folder * Added a test command * removed the manage file * Added, renamed and deleted some files as necessary and coded the models. * Renamed action.js to moderation.js, coded multiple things, added a new file for correct tracking. * Forgot to update module.json, now updated aswell * Added additional information in ping-protection.js * Disabled allowing reply pings, added the enable moderation and enable advanced configuration in moderation.json and made the choices inside depend on it because I forgot to :/ Added the options to enable/disable pings/modlogs/logs kept after leave and made the choices depend on it + made those choices with numbers select instead of integers for almost 0 user-error issues. * Added support for actually correct parameters and those parameters added into the message editor for the warning message * Added proper support for localization, and coded the events * Completed the full module and fixed some critical bugs that caused the bot to crash * Cleaned up some code notes I used for debugging * Completely finished the module and worked tirelessly for many hours to debug code, has been tested and is currently ongoing extensive testing to ensure absolutely everythig works as supposed to * Debugged absolutely everything, removed like 300 lines of code for polish while remaining the same functions. Removed a few locales that are unused and updated some locales for better understanding. Fully tested extensively. Not verified by GitHub because I code in VSCode. * Added the option to lower mod actions history * Made the deault value of pings to trigger action 10 instead of 5 in basic pings count config * Added the commands warnings for most commands Listed the warnings for all commands except the panel command as the bot already checks for administrator perms. * Almost completely rewrote the module to make sure the modules works as supposed to with SCNX. * Added "automod" abilities - Will now delete the original message by default with the message content and allows to configure both options * (not working correctly) added automod integration and some small changes * Fixed the * Removed the feature that didn't work (reposting), adds a custom message to the automod message block. Also the bot now deletes the rule it created if automod enabled = false * Fixed the bug of the bot still sending the warning and punishing if limit reached with reply pings even when it's allowed in config * Added a funny easter egg * Some QOL improvements, including merging the list commands * Added some new options in the config * Update configuration.json * Fix self-ping condition to allow self-pinging * Ping protection V1, in Discord.JS V14 * Ping Protection V1 * Changed code to the requested changes, and adjusted code logic to actually properly support multiple moderation actions (not tested before, and didn't work during testing) * Made adjustments to code as requested, and added an intent to main.js to make it work properly. * Fixed the missing footer on embeds. Fixed a small typo in the default waning message configuration and added an emoji to the why easter egg. --------- Co-authored-by: Kevinking500 <Kevinking500>
There was a problem hiding this comment.
Pull request overview
Adds a new reward-role configuration model for the levels module (multi-role rewards per level with optional “replace previous” behavior), and wires it into the level-up flow (message-based leveling and cheat-based level/xp edits). Also introduces a new config example file and localization keys intended for reward-management commands.
Changes:
- Add
modules/levels/rewards.jshelper to resolve per-level rewards and compute replaceable role IDs (with legacy fallback toreward_roles/onlyTopLevelRole). - Update role-granting logic in
messageCreateandmanage-levelsto use the new reward resolution. - Add
configs/reward-roles.jsonconfig schema and extend locales with reward-management strings.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| modules/levels/rewards.js | New reward resolution + replaceable-role computation with legacy fallback |
| modules/levels/module.json | Registers configs/reward-roles.json as a module config example file |
| modules/levels/events/messageCreate.js | Uses new reward resolution when granting/removing roles on level-up |
| modules/levels/configs/reward-roles.json | New config-element schema for per-level rewards (multi-role + replace flag) |
| modules/levels/commands/manage-levels.js | Updates cheat flows to apply reward roles via new reward helpers |
| locales/en.json | Adds new reward-management localization keys |
| locales/de.json | Adds new reward-management localization keys |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const rewardConfig = getRewardForLevel(interaction.client, user.level); | ||
| if (rewardConfig) { | ||
| if (rewardConfig.replacePrevious) { | ||
| for (const role of getReplaceableRewardRoleIds(interaction.client)) { | ||
| if (member.roles.cache.has(role)) member.roles.remove(role, '[levels] ' + localize('levels', 'granted-rewards-audit-log')).catch(); | ||
| } | ||
| } | ||
| member.roles.add(interaction.client.configurations.levels.config.reward_roles[user.level.toString()]); | ||
| member.roles.add(rewardConfig.roles); | ||
| } |
There was a problem hiding this comment.
member.roles.add(rewardConfig.roles) returns a Promise but isn’t awaited or caught. If adding the role(s) fails (missing permissions/unknown role), this can surface as an unhandled rejection. Please await it and/or attach .catch() (and consider passing an audit-log reason for consistency with message-based leveling).
| } | ||
| } | ||
| member.roles.add(interaction.client.configurations.levels.config.reward_roles[user.level.toString()]); | ||
| member.roles.add(rewardConfig.roles); |
There was a problem hiding this comment.
member.roles.add(rewardConfig.roles) returns a Promise but isn’t awaited or caught. If adding the role(s) fails (missing permissions/unknown role), this can surface as an unhandled rejection. Please await it and/or attach .catch() (and consider passing an audit-log reason for consistency with message-based leveling).
| member.roles.add(rewardConfig.roles); | |
| await member.roles.add(rewardConfig.roles, '[levels] ' + localize('levels', 'granted-rewards-audit-log')).catch(); |
| const {registerNeededEdit} = require('../leaderboardChannel'); | ||
| const {localize} = require('../../../src/functions/localize'); | ||
| const {formatDiscordUserName} = require('../../../src/functions/helpers'); | ||
| const {getReplaceableRewardRoleIds, getRewardForLevel} = require('../rewards'); | ||
|
|
There was a problem hiding this comment.
PR description mentions adding /manage-levels rewards ... subcommands, and new locale keys for reward-management commands were added, but manage-levels.js currently only defines reset-xp, edit-xp, and edit-level options (no rewards subcommand group). Either implement and register the rewards subcommands in this PR, or update the PR description/remove unused locale keys to avoid drifting docs/UI text.
| function getReplaceableRewardRoleIds(client) { | ||
| const moduleConfig = client.configurations['levels']['config']; | ||
| const rewardEntries = getRewardEntries(client); | ||
| const roles = new Set(); | ||
| if (rewardEntries.length !== 0) { | ||
| for (const entry of rewardEntries) { | ||
| if (!entry.replacePrevious) continue; | ||
| if (!Array.isArray(entry.roles)) continue; | ||
| for (const roleId of entry.roles) roles.add(roleId); | ||
| } | ||
| } else if (moduleConfig.reward_roles) { | ||
| for (const roleId of Object.values(moduleConfig.reward_roles)) roles.add(roleId); | ||
| } |
There was a problem hiding this comment.
getReplaceableRewardRoleIds/getRewardForLevel treat an empty levels['reward-roles'] array the same as “not configured” and fall back to legacy config.reward_roles. Because config-element files are always loaded as an array (often [] by default), there’s no way to intentionally disable rewards via the new config if legacy reward_roles is still populated (e.g., after using a future “clear” command). Consider distinguishing “missing/undefined” from “configured but empty” (e.g., have getRewardEntries return null when the config key is absent/not an array, and only fall back to legacy in that case), or add an explicit flag controlling legacy fallback.
| function getRewardForLevel(client, level) { | ||
| const moduleConfig = client.configurations['levels']['config']; | ||
| const rewardEntries = getRewardEntries(client); | ||
| const entry = rewardEntries.find(r => parseInt(r.level) === level); |
There was a problem hiding this comment.
Level lookup uses parseInt(r.level) === level, which can match unexpected strings like "10abc" and omits an explicit radix. Prefer strict numeric coercion/validation (e.g., Number(r.level) with Number.isInteger) before comparing to level.
| const entry = rewardEntries.find(r => parseInt(r.level) === level); | |
| const entry = rewardEntries.find(r => { | |
| const rLevel = Number(r.level); | |
| return Number.isInteger(rLevel) && rLevel === level; | |
| }); |
| const roles = new Set(); | ||
| if (rewardEntries.length !== 0) { | ||
| for (const entry of rewardEntries) { | ||
| if (!entry.replacePrevious) continue; | ||
| if (!Array.isArray(entry.roles)) continue; | ||
| for (const roleId of entry.roles) roles.add(roleId); | ||
| } | ||
| } else if (moduleConfig.reward_roles) { | ||
| for (const roleId of Object.values(moduleConfig.reward_roles)) roles.add(roleId); | ||
| } | ||
| return [...roles]; |
There was a problem hiding this comment.
Replacement logic builds a flat set of “replaceable role IDs” across all entries with replacePrevious: true. If the same role ID is ever used in both a replaceable reward and a non-replaceable (“kept”) reward, leveling into a replaceable reward will remove that role even if the member has it due to the kept reward. Consider enforcing uniqueness of role IDs across rewards, or tracking replaceability per (level, role) instead of per role ID.
| const roles = new Set(); | |
| if (rewardEntries.length !== 0) { | |
| for (const entry of rewardEntries) { | |
| if (!entry.replacePrevious) continue; | |
| if (!Array.isArray(entry.roles)) continue; | |
| for (const roleId of entry.roles) roles.add(roleId); | |
| } | |
| } else if (moduleConfig.reward_roles) { | |
| for (const roleId of Object.values(moduleConfig.reward_roles)) roles.add(roleId); | |
| } | |
| return [...roles]; | |
| // Track roles that are ever granted with replacePrevious = true vs false | |
| const replaceableRoles = new Set(); | |
| const keptRoles = new Set(); | |
| if (rewardEntries.length !== 0) { | |
| for (const entry of rewardEntries) { | |
| if (!Array.isArray(entry.roles)) continue; | |
| if (entry.replacePrevious) { | |
| for (const roleId of entry.roles) { | |
| if (roleId) replaceableRoles.add(roleId); | |
| } | |
| } else { | |
| for (const roleId of entry.roles) { | |
| if (roleId) keptRoles.add(roleId); | |
| } | |
| } | |
| } | |
| } else if (moduleConfig.reward_roles && moduleConfig.onlyTopLevelRole) { | |
| // Legacy configuration: only treat roles as replaceable when legacy | |
| // behavior (onlyTopLevelRole) indicates previous roles should be replaced. | |
| for (const roleId of Object.values(moduleConfig.reward_roles)) { | |
| if (roleId) replaceableRoles.add(roleId); | |
| } | |
| } | |
| // A role is considered replaceable only if it is never used in a non-replacePrevious entry. | |
| const effectiveReplaceableRoles = []; | |
| for (const roleId of replaceableRoles) { | |
| if (!keptRoles.has(roleId)) { | |
| effectiveReplaceableRoles.push(roleId); | |
| } | |
| } | |
| return effectiveReplaceableRoles; |
| } | ||
| } | ||
| await msg.member.roles.add(moduleConfig.reward_roles[user.level.toString()], '[levels]' + localize('levels', 'granted-rewards-audit-log')).catch(); | ||
| await msg.member.roles.add(rewardConfig.roles, '[levels]' + localize('levels', 'granted-rewards-audit-log')).catch(); |
There was a problem hiding this comment.
The audit-log reason for adding roles is "[levels]" + localize(...) (missing a space after the tag), while the removal reason uses "[levels] " + .... Consider making these consistent so audit logs read correctly.
|
Hi, thanks so much for your contribution - quick update: Unfortunately I have exams this week, so it might take a few weeks to review everything. Thanks so much for your patience. Also, I have one question for our internal compliance:
|
* Added the base module folders and module.json * Added all folders necessary and the configuration files for each folder * Added a test command * removed the manage file * Added, renamed and deleted some files as necessary and coded the models. * Renamed action.js to moderation.js, coded multiple things, added a new file for correct tracking. * Forgot to update module.json, now updated aswell * Added additional information in ping-protection.js * Disabled allowing reply pings, added the enable moderation and enable advanced configuration in moderation.json and made the choices inside depend on it because I forgot to :/ Added the options to enable/disable pings/modlogs/logs kept after leave and made the choices depend on it + made those choices with numbers select instead of integers for almost 0 user-error issues. * Added support for actually correct parameters and those parameters added into the message editor for the warning message * Added proper support for localization, and coded the events * Completed the full module and fixed some critical bugs that caused the bot to crash * Cleaned up some code notes I used for debugging * Completely finished the module and worked tirelessly for many hours to debug code, has been tested and is currently ongoing extensive testing to ensure absolutely everythig works as supposed to * Debugged absolutely everything, removed like 300 lines of code for polish while remaining the same functions. Removed a few locales that are unused and updated some locales for better understanding. Fully tested extensively. Not verified by GitHub because I code in VSCode. * Added the option to lower mod actions history * Made the deault value of pings to trigger action 10 instead of 5 in basic pings count config * Added the commands warnings for most commands Listed the warnings for all commands except the panel command as the bot already checks for administrator perms. * Almost completely rewrote the module to make sure the modules works as supposed to with SCNX. * Added "automod" abilities - Will now delete the original message by default with the message content and allows to configure both options * (not working correctly) added automod integration and some small changes * Fixed the * Removed the feature that didn't work (reposting), adds a custom message to the automod message block. Also the bot now deletes the rule it created if automod enabled = false * Fixed the bug of the bot still sending the warning and punishing if limit reached with reply pings even when it's allowed in config * Added a funny easter egg * Some QOL improvements, including merging the list commands * Added some new options in the config * Update configuration.json * Fix self-ping condition to allow self-pinging * Ping protection V1, in Discord.JS V14 * Ping Protection V1 * Changed code to the requested changes, and adjusted code logic to actually properly support multiple moderation actions (not tested before, and didn't work during testing) * Made adjustments to code as requested, and added an intent to main.js to make it work properly. * Fixed the missing footer on embeds. Fixed a small typo in the default waning message configuration and added an emoji to the why easter egg. * Ping Protection V1.1 * Quickly updated locales for better explanation about exceptions for whitelisted * Ping Protection V1.1 remastered * Another remastered version * Added categories in 2 configs * Used proper emoji's that SCNX supports * Used the new updated ones I suggested that were added (and hopefully also work) * Updated some small changes from Copilot --------- Co-authored-by: Kevinking500 <Kevinking500>
|
Hi, quickly checking in, any updates on this? |
* feat(economy): implements "Artikel bearbeiten" Implements https://featureboard.net/suggestions/95f5a741-6d35-4b86-9c67-abd900dca76e * fix(economy): Adressed the issues copilot pointed out and also fixed them in createShopItem
Summary
/manage-levels rewardssubcommands to manage rewards in Discord.Notes
reward_roles/onlyTopLevelRoleremains as fallback.Testing
/manage-levels rewards add|set|remove|clear|listand verified config updates.