feat: configurable level roles via !levelrole admin command

- LevelRoleCommand: set/remove/clear role mappings per level
- LevelRoleService: reads from ConfigRepository, falls back to defaults
- LevelCommandRegistrar: wires new command
This commit is contained in:
loki5512344 2026-06-20 00:33:28 +02:00
parent 283b30f979
commit 0787e98921
6 changed files with 237 additions and 10 deletions

View file

@ -8,6 +8,8 @@ import org.slf4j.LoggerFactory;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
* Репозиторий для работы с конфигурацией
@ -54,6 +56,19 @@ public class ConfigRepository implements StringConfigStore {
}
}
public Map<String, String> getAll() {
Map<String, String> result = new HashMap<>();
try (PreparedStatement stmt = database.prepare("SELECT key, value FROM config")) {
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
result.put(rs.getString("key"), rs.getString("value"));
}
} catch (SQLException e) {
logger.error("Ошибка получения всех конфигураций", e);
}
return result;
}
/**
* Удалить значение конфигурации
*/

View file

@ -3,7 +3,9 @@ package creator.loki.feature.level;
import com.google.inject.Inject;
import creator.loki.core.registry.CommandRegistrar;
import creator.loki.core.registry.CommandRegistry;
import creator.loki.feature.config.repository.ConfigRepository;
import creator.loki.feature.leaderboard.LeaderboardCommand;
import creator.loki.feature.level.commands.LevelRoleCommand;
import creator.loki.feature.level.service.LeaderboardService;
import creator.loki.feature.level.service.LevelService;
import creator.loki.feature.profile.ProfileCommand;
@ -13,16 +15,22 @@ public class LevelCommandRegistrar implements CommandRegistrar {
private final LeaderboardService leaderboardService;
private final ResetLevelsCommand resetLevelsCommand;
private final ProfileService profileService;
private final LevelRoleService levelRoleService;
private final ConfigRepository configRepository;
@Inject
public LevelCommandRegistrar(
LeaderboardService leaderboardService,
ResetLevelsCommand resetLevelsCommand,
ProfileService profileService
ProfileService profileService,
LevelRoleService levelRoleService,
ConfigRepository configRepository
) {
this.leaderboardService = leaderboardService;
this.resetLevelsCommand = resetLevelsCommand;
this.profileService = profileService;
this.levelRoleService = levelRoleService;
this.configRepository = configRepository;
}
@Override
@ -31,5 +39,6 @@ public class LevelCommandRegistrar implements CommandRegistrar {
registry.register(new StatsCommand());
registry.register(resetLevelsCommand);
registry.register(new ProfileCommand(profileService));
registry.register(new LevelRoleCommand(levelRoleService, configRepository));
}
}

View file

@ -58,8 +58,8 @@ public class LevelModule extends AbstractModule {
@Provides
@Singleton
LevelRoleService provideLevelRoleService() {
return new LevelRoleService();
LevelRoleService provideLevelRoleService(ConfigRepository configRepository) {
return new LevelRoleService(configRepository);
}
@Provides

View file

@ -1,5 +1,6 @@
package creator.loki.feature.level;
import creator.loki.feature.config.repository.ConfigRepository;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import org.slf4j.Logger;
@ -10,7 +11,7 @@ import java.util.Map;
public class LevelRoleService {
private static final Logger logger = LoggerFactory.getLogger(LevelRoleService.class);
private static final Map<Integer, String> REWARD_ROLES = Map.of(
private static final Map<Integer, String> DEFAULT_ROLES = Map.of(
5, "Активный",
10, "Постоянный",
20, "Ветеран",
@ -18,8 +19,28 @@ public class LevelRoleService {
50, "Мастер"
);
private final ConfigRepository configRepository;
public LevelRoleService(ConfigRepository configRepository) {
this.configRepository = configRepository;
}
public String getRoleForLevel(int level) {
String configRole = configRepository.get("level_role_" + level);
if (configRole != null) return configRole;
return DEFAULT_ROLES.get(level);
}
public void setRoleForLevel(int level, String roleName) {
configRepository.set("level_role_" + level, roleName);
}
public void removeRoleForLevel(int level) {
configRepository.delete("level_role_" + level);
}
public String assignRewardRole(Member member, int newLevel) {
String roleName = REWARD_ROLES.get(newLevel);
String roleName = getRoleForLevel(newLevel);
if (roleName == null) return null;
List<Role> roles = member.getGuild().getRolesByName(roleName, true);

View file

@ -0,0 +1,182 @@
package creator.loki.feature.level.commands;
import creator.loki.command.Command;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedTemplates;
import creator.loki.feature.config.repository.ConfigRepository;
import creator.loki.feature.level.LevelRoleService;
import net.dv8tion.jda.api.Permission;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class LevelRoleCommand implements Command {
private final LevelRoleService levelRoleService;
private final ConfigRepository configRepository;
public LevelRoleCommand(LevelRoleService levelRoleService, ConfigRepository configRepository) {
this.levelRoleService = levelRoleService;
this.configRepository = configRepository;
}
@Override
public String getName() {
return "levelrole";
}
@Override
public String getDescription() {
return "Управление ролями за уровни";
}
@Override
public String getUsage() {
return "levelrole [set <level> <role-name>|remove <level>|clear]";
}
@Override
public String getCategory() {
return "Настройка";
}
@Override
public void execute(CommandContext context) {
if (context.getAuthor() == null || !context.getAuthor().hasPermission(Permission.ADMINISTRATOR)) {
context.getChannel().sendMessageEmbeds(
EmbedTemplates.error("Недостаточно прав", "Эта команда доступна только администраторам.")
).queue();
return;
}
List<String> args = context.getArgs();
if (args.isEmpty()) {
showMappings(context);
return;
}
switch (args.get(0).toLowerCase()) {
case "set" -> handleSet(context, args);
case "remove" -> handleRemove(context, args);
case "clear" -> handleClear(context);
default -> showMappings(context);
}
}
private void showMappings(CommandContext context) {
Map<String, String> allConfig = configRepository.getAll();
Map<Integer, String> customRoles = allConfig.entrySet().stream()
.filter(e -> e.getKey().startsWith("level_role_"))
.collect(Collectors.toMap(
e -> Integer.parseInt(e.getKey().substring("level_role_".length())),
Map.Entry::getValue
));
StringBuilder sb = new StringBuilder();
sb.append("**Текущие настройки ролей за уровни:**\n\n");
for (int level : new int[]{5, 10, 20, 30, 50}) {
String custom = customRoles.get(level);
String effective = levelRoleService.getRoleForLevel(level);
sb.append("**Уровень ").append(level).append(":** ");
if (custom != null) {
sb.append("`").append(effective).append("`");
} else {
sb.append(effective).append(" *(по умолчанию)*");
}
sb.append("\n");
}
for (Map.Entry<Integer, String> entry : customRoles.entrySet()) {
int level = entry.getKey();
if (level != 5 && level != 10 && level != 20 && level != 30 && level != 50) {
sb.append("**Уровень ").append(level).append(":** `").append(entry.getValue()).append("`\n");
}
}
context.getChannel().sendMessageEmbeds(
EmbedTemplates.info("Управление ролями", sb.toString())
).queue();
}
private void handleSet(CommandContext context, List<String> args) {
if (args.size() < 3) {
context.getChannel().sendMessageEmbeds(
EmbedTemplates.error("Ошибка", "Использование: `!levelrole set <уровень> <название роли>`")
).queue();
return;
}
int level;
try {
level = Integer.parseInt(args.get(1));
} catch (NumberFormatException e) {
context.getChannel().sendMessageEmbeds(
EmbedTemplates.error("Ошибка", "Уровень должен быть числом.")
).queue();
return;
}
if (level < 1) {
context.getChannel().sendMessageEmbeds(
EmbedTemplates.error("Ошибка", "Уровень должен быть положительным числом.")
).queue();
return;
}
String roleName = String.join(" ", args.subList(2, args.size()));
if (roleName.isEmpty()) {
context.getChannel().sendMessageEmbeds(
EmbedTemplates.error("Ошибка", "Название роли не может быть пустым.")
).queue();
return;
}
levelRoleService.setRoleForLevel(level, roleName);
context.getChannel().sendMessageEmbeds(
EmbedTemplates.success("Роль назначена",
"Роль `" + roleName + "` будет выдаваться за уровень **" + level + "**.")
).queue();
}
private void handleRemove(CommandContext context, List<String> args) {
if (args.size() < 2) {
context.getChannel().sendMessageEmbeds(
EmbedTemplates.error("Ошибка", "Использование: `!levelrole remove <уровень>`")
).queue();
return;
}
int level;
try {
level = Integer.parseInt(args.get(1));
} catch (NumberFormatException e) {
context.getChannel().sendMessageEmbeds(
EmbedTemplates.error("Ошибка", "Уровень должен быть числом.")
).queue();
return;
}
levelRoleService.removeRoleForLevel(level);
context.getChannel().sendMessageEmbeds(
EmbedTemplates.success("Роль сброшена",
"Для уровня **" + level + "** будет использоваться роль по умолчанию.")
).queue();
}
private void handleClear(CommandContext context) {
Map<String, String> allConfig = configRepository.getAll();
int removed = 0;
for (String key : allConfig.keySet()) {
if (key.startsWith("level_role_")) {
configRepository.delete(key);
removed++;
}
}
context.getChannel().sendMessageEmbeds(
EmbedTemplates.success("Настройки сброшены",
"Удалено пользовательских ролей: **" + removed + "**. Будут использоваться роли по умолчанию.")
).queue();
}
}