feat: add ResetLevelsCommand, user voice presets with extended settings, fix channel name format
This commit is contained in:
parent
9f6e19e5c6
commit
63f8763ff9
5 changed files with 349 additions and 3 deletions
|
|
@ -0,0 +1,104 @@
|
|||
package creator.loki.feature.level;
|
||||
|
||||
import creator.loki.embed.EmbedFactory;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* Обработчик кнопок подтверждения сброса уровней
|
||||
*/
|
||||
public class ResetLevelsButtonHandler extends ListenerAdapter {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ResetLevelsButtonHandler.class);
|
||||
private final ResetLevelsCommand resetCommand;
|
||||
|
||||
public ResetLevelsButtonHandler(ResetLevelsCommand resetCommand) {
|
||||
this.resetCommand = resetCommand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onButtonInteraction(ButtonInteractionEvent event) {
|
||||
String buttonId = event.getComponentId();
|
||||
|
||||
// Проверяем что это наша кнопка
|
||||
if (!buttonId.startsWith("reset_levels_")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Извлекаем ID пользователя из кнопки
|
||||
String[] parts = buttonId.split(":");
|
||||
if (parts.length != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
String authorId = parts[1];
|
||||
|
||||
// Проверяем что кнопку нажал тот же пользователь
|
||||
if (!event.getUser().getId().equals(authorId)) {
|
||||
event.reply("❌ Только инициатор команды может подтвердить действие!")
|
||||
.setEphemeral(true)
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
|
||||
if (buttonId.startsWith("reset_levels_confirm:")) {
|
||||
// Подтверждение
|
||||
event.deferReply().queue();
|
||||
|
||||
try {
|
||||
String guildId = event.getGuild().getId();
|
||||
String userId = event.getUser().getId();
|
||||
|
||||
// Выполняем сброс
|
||||
resetCommand.executeReset(guildId, userId);
|
||||
|
||||
// Удаляем исходное сообщение с кнопками
|
||||
event.getMessage().delete().queue();
|
||||
|
||||
// Отправляем подтверждение
|
||||
event.getHook().sendMessageEmbeds(
|
||||
EmbedFactory.create()
|
||||
.setTitle("✅ Сброс уровней выполнен")
|
||||
.setDescription(
|
||||
"Уровни и XP всех пользователей успешно сброшены!\n\n" +
|
||||
"**Сохранено:**\n" +
|
||||
"• Время в голосовых каналах\n" +
|
||||
"• Количество сообщений\n" +
|
||||
"• Репутация\n\n" +
|
||||
"**Сброшено:**\n" +
|
||||
"• Уровень → 0\n" +
|
||||
"• XP → 0\n\n" +
|
||||
"Теперь используется новая система уровней!"
|
||||
)
|
||||
.setColor(Color.GREEN)
|
||||
.setFooter("Инициатор: " + event.getUser().getName(), event.getUser().getAvatarUrl())
|
||||
.build()
|
||||
).queue();
|
||||
|
||||
logger.info("Сброс уровней подтверждён пользователем: {}", event.getUser().getName());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Ошибка при выполнении сброса", e);
|
||||
event.getHook().sendMessageEmbeds(
|
||||
EmbedFactory.create()
|
||||
.setTitle("❌ Ошибка")
|
||||
.setDescription("Произошла ошибка при сбросе уровней: " + e.getMessage())
|
||||
.setColor(Color.RED)
|
||||
.build()
|
||||
).queue();
|
||||
}
|
||||
|
||||
} else if (buttonId.startsWith("reset_levels_cancel:")) {
|
||||
// Отмена
|
||||
event.getMessage().delete().queue();
|
||||
event.reply("❌ Сброс уровней отменён.")
|
||||
.setEphemeral(true)
|
||||
.queue();
|
||||
|
||||
logger.info("Сброс уровней отменён пользователем: {}", event.getUser().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
152
src/main/java/creator/loki/feature/level/ResetLevelsCommand.java
Normal file
152
src/main/java/creator/loki/feature/level/ResetLevelsCommand.java
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
package creator.loki.feature.level;
|
||||
|
||||
import creator.loki.command.Command;
|
||||
import creator.loki.command.CommandContext;
|
||||
import creator.loki.database.ConfigRepository;
|
||||
import creator.loki.embed.EmbedFactory;
|
||||
import creator.loki.repository.UserStatsRepository;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* Одноразовая команда для сброса уровней и XP всех пользователей
|
||||
* Сохраняет время в войсе и количество сообщений
|
||||
*/
|
||||
public class ResetLevelsCommand implements Command {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ResetLevelsCommand.class);
|
||||
private static final String RESET_KEY = "levels_reset_executed";
|
||||
|
||||
private final UserStatsRepository repository;
|
||||
private final ConfigRepository configRepo;
|
||||
|
||||
public ResetLevelsCommand(UserStatsRepository repository, ConfigRepository configRepo) {
|
||||
this.repository = repository;
|
||||
this.configRepo = configRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "resetlevels";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Сбросить уровни и XP всех пользователей (одноразовая команда, только для админов)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "resetlevels";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCategory() {
|
||||
return "Настройка";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandContext context) {
|
||||
// Проверка прав администратора
|
||||
if (context.getAuthor() == null || !context.getAuthor().hasPermission(Permission.ADMINISTRATOR)) {
|
||||
context.getChannel().sendMessageEmbeds(
|
||||
EmbedFactory.create()
|
||||
.setTitle("❌ Недостаточно прав")
|
||||
.setDescription("Эта команда доступна только администраторам.")
|
||||
.setColor(Color.RED)
|
||||
.build()
|
||||
).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверка, была ли команда уже выполнена
|
||||
String guildId = context.getGuild().getId();
|
||||
String resetKey = guildId + ":" + RESET_KEY;
|
||||
String resetValue = configRepo.get(resetKey);
|
||||
boolean alreadyReset = "true".equals(resetValue);
|
||||
|
||||
if (alreadyReset) {
|
||||
context.getChannel().sendMessageEmbeds(
|
||||
EmbedFactory.create()
|
||||
.setTitle("⚠️ Команда уже выполнена")
|
||||
.setDescription("Сброс уровней уже был выполнен ранее. Эта команда одноразовая и не может быть использована повторно.")
|
||||
.setColor(Color.ORANGE)
|
||||
.addField("💡 Информация", "Если нужно сбросить уровни снова, обратитесь к разработчику бота.", false)
|
||||
.build()
|
||||
).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
// Показываем предупреждение с кнопками подтверждения
|
||||
context.getChannel().sendMessageEmbeds(
|
||||
EmbedFactory.create()
|
||||
.setTitle("⚠️ ВНИМАНИЕ: Сброс уровней")
|
||||
.setDescription(
|
||||
"Вы собираетесь сбросить **уровни и XP** всех пользователей на сервере.\n\n" +
|
||||
"**Что будет сброшено:**\n" +
|
||||
"• Уровень → 0\n" +
|
||||
"• XP → 0\n\n" +
|
||||
"**Что НЕ будет затронуто:**\n" +
|
||||
"• Время в голосовых каналах\n" +
|
||||
"• Количество сообщений\n" +
|
||||
"• Репутация\n\n" +
|
||||
"**⚠️ Это действие НЕОБРАТИМО и может быть выполнено только ОДИН РАЗ!**"
|
||||
)
|
||||
.setColor(Color.RED)
|
||||
.addField("📊 Новая система уровней",
|
||||
"После сброса будет использоваться новая формула:\n" +
|
||||
"• XP для уровня N = 200 × N\n" +
|
||||
"• Пример: для 15 уровня нужно 22,800 XP",
|
||||
false)
|
||||
.setFooter("Подтвердите действие нажатием кнопки ниже")
|
||||
.build()
|
||||
).setActionRow(
|
||||
Button.success("reset_levels_confirm:" + context.getAuthor().getId(), "✅ Подтвердить сброс"),
|
||||
Button.danger("reset_levels_cancel:" + context.getAuthor().getId(), "❌ Отмена")
|
||||
).queue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет сброс уровней (вызывается из ButtonHandler)
|
||||
*/
|
||||
public void executeReset(String guildId, String userId) {
|
||||
try {
|
||||
logger.info("========================================");
|
||||
logger.info("Начало сброса уровней для гильдии: {}", guildId);
|
||||
logger.info("Инициатор: {}", userId);
|
||||
logger.info("========================================");
|
||||
|
||||
// Получаем всех пользователей (без guildId - метод возвращает всех)
|
||||
var allStats = repository.getAllStats();
|
||||
int resetCount = 0;
|
||||
|
||||
// Фильтруем по guildId и сбрасываем только XP и уровень
|
||||
for (var entry : allStats.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
// Ключ формата "guildId:userId"
|
||||
if (key.startsWith(guildId + ":")) {
|
||||
var stats = entry.getValue();
|
||||
stats.setTotalXP(0);
|
||||
stats.setLevel(0);
|
||||
// Сохраняем обратно
|
||||
repository.save(stats);
|
||||
resetCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Помечаем что команда выполнена
|
||||
String resetKey = guildId + ":" + RESET_KEY;
|
||||
configRepo.set(resetKey, "true");
|
||||
|
||||
logger.info("Сброс завершён. Обработано пользователей: {}", resetCount);
|
||||
logger.info("========================================");
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Ошибка при сбросе уровней", e);
|
||||
throw new RuntimeException("Ошибка при сбросе уровней: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,9 +42,12 @@ public class VoiceChannelListener extends ListenerAdapter {
|
|||
VoiceChannel personalChannel = voiceManager.createPersonalChannel(member, guild);
|
||||
|
||||
if (personalChannel != null) {
|
||||
// Применяем сохранённый пресет пользователя
|
||||
voiceManager.applyUserPreset(personalChannel, member.getId());
|
||||
|
||||
// Перемещаем пользователя в созданный канал
|
||||
guild.moveVoiceMember(member, personalChannel).queue(
|
||||
success -> logger.info("Пользователь {} перемещен в личный канал", member.getEffectiveName()),
|
||||
success -> logger.info("Пользователь {} перемещен в личный канал с применённым пресетом", member.getEffectiveName()),
|
||||
error -> logger.error("Ошибка при перемещении пользователя", error)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public class VoiceChannelManager {
|
|||
*/
|
||||
public VoiceChannel createPersonalChannel(Member member, Guild guild) {
|
||||
try {
|
||||
String channelName = member.getEffectiveName() + "'s Channel";
|
||||
String channelName = "Канал: " + member.getEffectiveName();
|
||||
|
||||
VoiceChannel channel = guild.createVoiceChannel(channelName)
|
||||
.setParent(guild.getCategoryById(categoryId))
|
||||
|
|
@ -171,4 +171,62 @@ public class VoiceChannelManager {
|
|||
public String getTriggerChannelId() { return triggerChannelId; }
|
||||
public String getCategoryId() { return categoryId; }
|
||||
public String getTextChannelId() { return textChannelId; }
|
||||
|
||||
/**
|
||||
* Сохранить пресет пользователя
|
||||
*/
|
||||
public void saveUserPreset(String userId, VoicePreset preset) {
|
||||
Map<String, VoicePreset> presets = getUserPresets();
|
||||
presets.put(userId, preset);
|
||||
dataStore.setData("voice_user_presets", presets);
|
||||
logger.info("Сохранён пресет для пользователя {}", userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить пресет пользователя
|
||||
*/
|
||||
public VoicePreset getUserPreset(String userId) {
|
||||
Map<String, VoicePreset> presets = getUserPresets();
|
||||
return presets.getOrDefault(userId, new VoicePreset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Применить пресет пользователя к каналу
|
||||
*/
|
||||
public void applyUserPreset(VoiceChannel channel, String userId) {
|
||||
VoicePreset preset = getUserPreset(userId);
|
||||
|
||||
try {
|
||||
// Применяем настройки
|
||||
if (preset.getName() != null && !preset.getName().isEmpty()) {
|
||||
channel.getManager().setName(preset.getName()).queue();
|
||||
}
|
||||
|
||||
if (preset.getLimit() >= 0) {
|
||||
channel.getManager().setUserLimit(preset.getLimit()).queue();
|
||||
}
|
||||
|
||||
if (preset.getBitrate() > 0) {
|
||||
channel.getManager().setBitrate(preset.getBitrate()).queue();
|
||||
}
|
||||
|
||||
// Применяем регион
|
||||
if (preset.getRegion() != null && !preset.getRegion().equals("auto")) {
|
||||
// JDA 5 не поддерживает регионы напрямую, но можно сохранить в настройках
|
||||
logger.info("Регион {} будет применён для канала {}", preset.getRegion(), channel.getId());
|
||||
}
|
||||
|
||||
logger.info("Применён пресет пользователя {} к каналу {}", userId, channel.getId());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Ошибка при применении пресета", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, VoicePreset> getUserPresets() {
|
||||
Type type = new TypeToken<Map<String, VoicePreset>>(){}.getType();
|
||||
Map<String, VoicePreset> presets = dataStore.getData("voice_user_presets", type);
|
||||
return presets != null ? presets : new ConcurrentHashMap<>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,55 @@
|
|||
package creator.loki.feature.voice;
|
||||
|
||||
import net.dv8tion.jda.api.Region;
|
||||
|
||||
/**
|
||||
* Пресет настроек голосового канала
|
||||
* Пресет настроек голосового канала с расширенными опциями
|
||||
*/
|
||||
public class VoicePreset {
|
||||
private String name;
|
||||
private int limit;
|
||||
private int bitrate;
|
||||
private String region; // Регион канала (auto, russia, europe, us-west, etc.)
|
||||
private boolean locked; // Закрыт ли канал
|
||||
private String allowedRoleId; // ID роли с доступом (если канал закрыт)
|
||||
private String deniedRoleId; // ID роли без доступа
|
||||
|
||||
public VoicePreset() {
|
||||
// Конструктор по умолчанию для Gson
|
||||
this.name = "Мой канал";
|
||||
this.limit = 0;
|
||||
this.bitrate = 64000;
|
||||
this.region = "auto";
|
||||
this.locked = false;
|
||||
this.allowedRoleId = null;
|
||||
this.deniedRoleId = null;
|
||||
}
|
||||
|
||||
public VoicePreset(String name, int limit, int bitrate) {
|
||||
this.name = name;
|
||||
this.limit = limit;
|
||||
this.bitrate = bitrate;
|
||||
this.region = "auto";
|
||||
this.locked = false;
|
||||
this.allowedRoleId = null;
|
||||
this.deniedRoleId = null;
|
||||
}
|
||||
|
||||
// Getters
|
||||
public String getName() { return name; }
|
||||
public int getLimit() { return limit; }
|
||||
public int getBitrate() { return bitrate; }
|
||||
public String getRegion() { return region; }
|
||||
public boolean isLocked() { return locked; }
|
||||
public String getAllowedRoleId() { return allowedRoleId; }
|
||||
public String getDeniedRoleId() { return deniedRoleId; }
|
||||
|
||||
// Setters
|
||||
public void setName(String name) { this.name = name; }
|
||||
public void setLimit(int limit) { this.limit = limit; }
|
||||
public void setBitrate(int bitrate) { this.bitrate = bitrate; }
|
||||
public void setRegion(String region) { this.region = region; }
|
||||
public void setLocked(boolean locked) { this.locked = locked; }
|
||||
public void setAllowedRoleId(String allowedRoleId) { this.allowedRoleId = allowedRoleId; }
|
||||
public void setDeniedRoleId(String deniedRoleId) { this.deniedRoleId = deniedRoleId; }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue