Skip to content

reward system for level system#175

Open
gg123bb wants to merge 12 commits intoScootKit:mainfrom
gg123bb:beta-discordjsv14
Open

reward system for level system#175
gg123bb wants to merge 12 commits intoScootKit:mainfrom
gg123bb:beta-discordjsv14

Conversation

@gg123bb
Copy link

@gg123bb gg123bb commented Feb 10, 2026

Summary

  • Add per-level reward roles with multiple roles per level.
  • Allow per-reward replacement behavior (replaceable vs. kept rewards).
  • Add /manage-levels rewards subcommands to manage rewards in Discord.
  • Update localization strings for the new reward commands.

Notes

  • Rewards now support multiple roles and per-level replace behavior.
  • Existing reward_roles/onlyTopLevelRole remains as fallback.

Testing

  • Used /manage-levels rewards add|set|remove|clear|list and verified config updates.

SCDerox and others added 7 commits January 9, 2026 14:33
* 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>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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.js helper to resolve per-level rewards and compute replaceable role IDs (with legacy fallback to reward_roles/onlyTopLevelRole).
  • Update role-granting logic in messageCreate and manage-levels to use the new reward resolution.
  • Add configs/reward-roles.json config 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.

Comment on lines 30 to 38
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);
}
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

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).

Copilot uses AI. Check for mistakes.
}
}
member.roles.add(interaction.client.configurations.levels.config.reward_roles[user.level.toString()]);
member.roles.add(rewardConfig.roles);
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

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).

Suggested change
member.roles.add(rewardConfig.roles);
await member.roles.add(rewardConfig.roles, '[levels] ' + localize('levels', 'granted-rewards-audit-log')).catch();

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 5
const {registerNeededEdit} = require('../leaderboardChannel');
const {localize} = require('../../../src/functions/localize');
const {formatDiscordUserName} = require('../../../src/functions/helpers');
const {getReplaceableRewardRoleIds, getRewardForLevel} = require('../rewards');

Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +18
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);
}
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
function getRewardForLevel(client, level) {
const moduleConfig = client.configurations['levels']['config'];
const rewardEntries = getRewardEntries(client);
const entry = rewardEntries.find(r => parseInt(r.level) === level);
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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;
});

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +19
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];
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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;

Copilot uses AI. Check for mistakes.
}
}
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();
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
@SCDerox
Copy link
Member

SCDerox commented Feb 14, 2026

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:

  • Please disclose whether Generative Large Language Models ("Generative AI", e.g. Coplilot) were used and to what extent to write this code. In addition, please confirm that you have reviewed and tested any generated code thoroughly. For details, please review our AI Policy.

* 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>
@SCDerox
Copy link
Member

SCDerox commented Feb 27, 2026

Hi, quickly checking in, any updates on this?

SCDerox and others added 3 commits February 27, 2026 13:33
* 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
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.

5 participants