refactor: move classes to subpackages, add rating commands, remove R34

- Move image commands to booru/e621 subpackages
- Move economy services to game/ subpackage
- Move level commands/services to admin/tracker/cooldown subpackages
- Move moderation repos to sql/ subpackage
- Move radio/ticket/reactrole classes to subpackages
- Move VoiceHelper/ChoiceOptionParser to feature/utility
- Add 13 rating commands (fat, pp, aura, luck, etc.)
- Remove R34 command (requires API key)
- Add PMD ruleset config
- Add CommandCategories.MEASUREMENTS
- Fix all imports for moved classes
This commit is contained in:
loki5512344 2026-07-29 15:18:57 +02:00
parent 530fec43b3
commit 604d3e59a8
Signed by: boba
GPG key ID: 253067914055423B
99 changed files with 1425 additions and 197 deletions

View file

@ -1,4 +1,4 @@
arguments=--init-script /home/loki/.cache/opencode/bin/jdtls/config_linux/org.eclipse.osgi/57/0/.cp/gradle/init/init.gradle arguments=--init-script /home/loki/.eclipse/2139989682_linux_gtk_x86_64/configuration/org.eclipse.osgi/57/0/.cp/gradle/init/init.gradle
auto.sync=false auto.sync=false
build.scans.enabled=false build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)

95
config/pmd/ruleset.xml Normal file
View file

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="LokiBot PMD Rules"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>PMD rules for Loki Discord Bot</description>
<rule ref="category/java/bestpractices.xml">
<exclude name="AbstractClassWithoutAbstractMethod" />
<exclude name="AccessorMethodGeneration" />
<exclude name="ArrayIsStoredDirectly" />
<exclude name="GuardLogStatement" />
<exclude name="JUnitAssertionsShouldIncludeMessage" />
<exclude name="JUnitTestContainsTooManyAsserts" />
<exclude name="JUnitTestsShouldIncludeAssert" />
<exclude name="MethodReturnsInternalArray" />
<exclude name="PreserveStackTrace" />
<exclude name="SystemPrintln" />
<exclude name="UseVarargs" />
</rule>
<rule ref="category/java/codestyle.xml">
<exclude name="AtLeastOneConstructor" />
<exclude name="AvoidFinalLocalVariable" />
<exclude name="CallSuperInConstructor" />
<exclude name="ClassNamingConventions" />
<exclude name="CommentDefaultAccessModifier" />
<exclude name="DefaultPackage" />
<exclude name="FieldDeclarationsShouldBeAtStartOfClass" />
<exclude name="LinguisticNaming" />
<exclude name="LocalVariableCouldBeFinal" />
<exclude name="LongVariable" />
<exclude name="MethodNamingConventions" />
<exclude name="OnlyOneReturn" />
<exclude name="ShortClassName" />
<exclude name="ShortMethodName" />
<exclude name="ShortVariable" />
<exclude name="TooManyStaticImports" />
<exclude name="UnnecessaryAnnotationValueElement" />
<exclude name="UseExplicitTypes" />
</rule>
<rule ref="category/java/design.xml">
<exclude name="AvoidRethrowingException" />
<exclude name="AvoidThrowingNullPointerException" />
<exclude name="AvoidThrowingRawExceptionTypes" />
<exclude name="CouplingBetweenObjects" />
<exclude name="CyclomaticComplexity" />
<exclude name="DataClass" />
<exclude name="ExcessiveImports" />
<exclude name="ExcessivePublicCount" />
<exclude name="GodClass" />
<exclude name="LawOfDemeter" />
<exclude name="LoosePackageCoupling" />
<exclude name="NcssCount" />
<exclude name="NPathComplexity" />
<exclude name="SignatureDeclareThrowsException" />
<exclude name="SingularField" />
<exclude name="TooManyFields" />
<exclude name="TooManyMethods" />
</rule>
<rule ref="category/java/errorprone.xml">
<exclude name="AssignmentInOperand" />
<exclude name="AvoidAccessibilityAlteration" />
<exclude name="AvoidDuplicateLiterals" />
<exclude name="AvoidFieldNameMatchingMethodName" />
<exclude name="AvoidLiteralsInIfCondition" />
<exclude name="CloseResource" />
<exclude name="ConstructorCallsOverridableMethod" />
<exclude name="DataflowAnomalyAnalysis" />
<exclude name="DoNotTerminateVM" />
<exclude name="EmptyCatchBlock" />
<exclude name="MissingSerialVersionUID" />
<exclude name="NonStaticInitializer" />
<exclude name="NullAssignment" />
<exclude name="ReturnEmptyArrayRatherThanNull" />
</rule>
<rule ref="category/java/multithreading.xml">
<exclude name="AvoidSynchronizedAtMethodLevel" />
<exclude name="DoNotUseThreads" />
<exclude name="UseConcurrentHashMap" />
</rule>
<rule ref="category/java/performance.xml">
<exclude name="AvoidInstantiatingObjectsInLoops" />
<exclude name="AvoidFileStream" />
<exclude name="ConsecutiveLiteralAppends" />
<exclude name="InsufficientStringBufferDeclaration" />
<exclude name="StringInstantiation" />
<exclude name="TooFewBranchesForASwitchStatement" />
</rule>
</ruleset>

View file

@ -6,8 +6,11 @@ public final class CommandCategories {
/** Команды с GIF (nekos.life): hug, slap, poke и т.п. */ /** Команды с GIF (nekos.life): hug, slap, poke и т.п. */
public static final String GIF_INTERACTIVE = "GIF-интерактив"; public static final String GIF_INTERACTIVE = "GIF-интерактив";
/** Команды для поиска изображений (e621, r34) — только в NSFW-каналах. */ /** Команды для поиска изображений (e621, gelbooru, yande.re) — только в NSFW-каналах. */
public static final String NSFW_IMAGES = "NSFW"; public static final String NSFW_IMAGES = "NSFW";
/** Команды измерений: pp, fat, aura и подобные. */
public static final String MEASUREMENTS = "Измерения";
private CommandCategories() {} private CommandCategories() {}
} }

View file

@ -18,8 +18,6 @@ public class BotConfig {
private final int aiDossierUpdateHours; private final int aiDossierUpdateHours;
private final String aiDebateChannelId; private final String aiDebateChannelId;
private final LavalinkConfig lavalinkConfig; private final LavalinkConfig lavalinkConfig;
private final String r34ApiKey;
private final String r34UserId;
public BotConfig( public BotConfig(
String mainToken, String mainToken,
@ -38,9 +36,7 @@ public class BotConfig {
int aiDossierMsgs, int aiDossierMsgs,
int aiDossierUpdateHours, int aiDossierUpdateHours,
String aiDebateChannelId, String aiDebateChannelId,
LavalinkConfig lavalinkConfig, LavalinkConfig lavalinkConfig) {
String r34ApiKey,
String r34UserId) {
this.mainToken = mainToken; this.mainToken = mainToken;
this.musicToken = musicToken; this.musicToken = musicToken;
this.radioToken = radioToken; this.radioToken = radioToken;
@ -58,8 +54,6 @@ public class BotConfig {
this.aiDossierUpdateHours = aiDossierUpdateHours; this.aiDossierUpdateHours = aiDossierUpdateHours;
this.aiDebateChannelId = aiDebateChannelId; this.aiDebateChannelId = aiDebateChannelId;
this.lavalinkConfig = lavalinkConfig; this.lavalinkConfig = lavalinkConfig;
this.r34ApiKey = r34ApiKey;
this.r34UserId = r34UserId;
} }
public String getMainToken() { public String getMainToken() {
@ -133,12 +127,4 @@ public class BotConfig {
public LavalinkConfig getLavalinkConfig() { public LavalinkConfig getLavalinkConfig() {
return lavalinkConfig; return lavalinkConfig;
} }
public String getR34ApiKey() {
return r34ApiKey;
}
public String getR34UserId() {
return r34UserId;
}
} }

View file

@ -83,9 +83,6 @@ public final class ConfigLoader {
int aiDossierUpdateHours = Integer.parseInt(getEnv(dotenv, "AI_DOSSIER_UPDATE_HOURS", "24")); int aiDossierUpdateHours = Integer.parseInt(getEnv(dotenv, "AI_DOSSIER_UPDATE_HOURS", "24"));
String aiDebateChannelId = dotenv.get("AI_DEBATE_CHANNEL_ID"); String aiDebateChannelId = dotenv.get("AI_DEBATE_CHANNEL_ID");
String r34ApiKey = getEnv(dotenv, "R34_API_KEY", "");
String r34UserId = getEnv(dotenv, "R34_USER_ID", "");
LavalinkConfig lavalinkConfig = getLavalinkConfig(); LavalinkConfig lavalinkConfig = getLavalinkConfig();
LoggerDecorator.success(logger, "Конфигурация загружена успешно"); LoggerDecorator.success(logger, "Конфигурация загружена успешно");
return new BotConfig( return new BotConfig(
@ -105,8 +102,6 @@ public final class ConfigLoader {
aiDossierMsgs, aiDossierMsgs,
aiDossierUpdateHours, aiDossierUpdateHours,
aiDebateChannelId, aiDebateChannelId,
lavalinkConfig, lavalinkConfig);
r34ApiKey,
r34UserId);
} }
} }

View file

@ -3,15 +3,15 @@ package creator.loki.core;
import com.google.inject.Inject; import com.google.inject.Inject;
import creator.loki.config.BotConfig; import creator.loki.config.BotConfig;
import creator.loki.core.registry.PrefixCommandRegistrar; import creator.loki.core.registry.PrefixCommandRegistrar;
import creator.loki.feature.counter.service.MemberCounterListener; import creator.loki.feature.counter.service.listener.MemberCounterListener;
import creator.loki.feature.metrics.HealthHttpServer; import creator.loki.feature.metrics.HealthHttpServer;
import creator.loki.feature.moderation.MuteScheduler; import creator.loki.feature.moderation.MuteScheduler;
import creator.loki.feature.music.core.LavalinkManager; import creator.loki.feature.music.core.LavalinkManager;
import creator.loki.feature.music.core.PlayerManager; import creator.loki.feature.music.core.PlayerManager;
import creator.loki.feature.music.listener.MusicBotListener; import creator.loki.feature.music.listener.MusicBotListener;
import creator.loki.feature.music.repository.QueueRepository; import creator.loki.feature.music.repository.QueueRepository;
import creator.loki.feature.radio.RadioBotListener; import creator.loki.feature.radio.listener.RadioBotListener;
import creator.loki.feature.radio.RadioManager; import creator.loki.feature.radio.service.RadioManager;
import creator.loki.util.LoggerDecorator; import creator.loki.util.LoggerDecorator;
import dev.arbjerg.lavalink.libraries.jda.JDAVoiceUpdateListener; import dev.arbjerg.lavalink.libraries.jda.JDAVoiceUpdateListener;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;

View file

@ -3,21 +3,21 @@ package creator.loki.core;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import creator.loki.bootstrap.ReadyListener; import creator.loki.bootstrap.ReadyListener;
import creator.loki.feature.counter.service.MemberCounterListener; import creator.loki.feature.counter.service.listener.MemberCounterListener;
import creator.loki.feature.economy.FishMessageListener; import creator.loki.feature.economy.FishMessageListener;
import creator.loki.feature.help.HelpMenuHandler; import creator.loki.feature.help.HelpMenuHandler;
import creator.loki.feature.reactroles.ReactionRoleListener; import creator.loki.feature.reactroles.listener.ReactionRoleListener;
import creator.loki.feature.image.ImageNavigationHandler; import creator.loki.feature.image.listener.ImageNavigationHandler;
import creator.loki.feature.leaderboard.LeaderboardButtonListener; import creator.loki.feature.leaderboard.LeaderboardButtonListener;
import creator.loki.feature.level.listener.MessageListener; import creator.loki.feature.level.listener.MessageListener;
import creator.loki.feature.level.listener.ResetLevelsButtonHandler; import creator.loki.feature.level.listener.ResetLevelsButtonHandler;
import creator.loki.feature.level.service.VoiceTracker; import creator.loki.feature.level.service.tracker.VoiceTracker;
import creator.loki.feature.moderation.commands.utils.ClearSlashCommands; import creator.loki.feature.moderation.commands.utils.ClearSlashCommands;
import creator.loki.feature.moderation.log.ModerationLogger; import creator.loki.feature.moderation.log.ModerationLogger;
import creator.loki.feature.config.SetupSlashHandler; import creator.loki.feature.config.SetupSlashHandler;
import creator.loki.feature.music.MusicSlashCommands; import creator.loki.feature.music.MusicSlashCommands;
import creator.loki.feature.music.listener.PlaySelectHandler; import creator.loki.feature.music.listener.PlaySelectHandler;
import creator.loki.feature.ticket.TicketListener; import creator.loki.feature.ticket.listener.TicketListener;
import creator.loki.feature.roast.AndrertListener; import creator.loki.feature.roast.AndrertListener;
import creator.loki.feature.roast.HelperListener; import creator.loki.feature.roast.HelperListener;
import creator.loki.feature.voice.handler.VoiceButtonHandler; import creator.loki.feature.voice.handler.VoiceButtonHandler;

View file

@ -6,7 +6,7 @@ import java.util.Set;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import creator.loki.database.Database; import creator.loki.database.Database;
import creator.loki.feature.radio.RadioManager; import creator.loki.feature.radio.service.RadioManager;
import creator.loki.util.LoggerDecorator; import creator.loki.util.LoggerDecorator;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
import org.slf4j.Logger; import org.slf4j.Logger;

View file

@ -7,8 +7,8 @@ import creator.loki.core.Lifecycle;
import creator.loki.core.security.CooldownManager; import creator.loki.core.security.CooldownManager;
import creator.loki.core.security.RateLimiter; import creator.loki.core.security.RateLimiter;
import creator.loki.database.Database; import creator.loki.database.Database;
import creator.loki.feature.counter.service.MemberCounterListener; import creator.loki.feature.counter.service.listener.MemberCounterListener;
import creator.loki.feature.level.service.VoiceTracker; import creator.loki.feature.level.service.tracker.VoiceTracker;
import creator.loki.feature.metrics.HealthHttpServer; import creator.loki.feature.metrics.HealthHttpServer;
import creator.loki.feature.moderation.MuteScheduler; import creator.loki.feature.moderation.MuteScheduler;

View file

@ -8,7 +8,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import creator.loki.database.Database; import creator.loki.database.Database;
import creator.loki.feature.counter.service.DailyStats; import creator.loki.feature.counter.service.format.DailyStats;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View file

@ -9,6 +9,7 @@ import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import creator.loki.feature.counter.service.format.DailyStats;
/** Менеджер счетчика участников */ /** Менеджер счетчика участников */
public class CounterManager { public class CounterManager {

View file

@ -8,6 +8,7 @@ import creator.loki.feature.config.repository.ConfigRepository;
import creator.loki.util.GuildChannelResolve; import creator.loki.util.GuildChannelResolve;
import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
import creator.loki.feature.counter.service.format.CounterConfigFormatter;
public class CounterSetupPrefixCommand implements Command { public class CounterSetupPrefixCommand implements Command {
private final CounterManager counterManager; private final CounterManager counterManager;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.counter.service; package creator.loki.feature.counter.service.format;
import java.awt.Color; import java.awt.Color;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.counter.service; package creator.loki.feature.counter.service.format;
import java.time.LocalDate; import java.time.LocalDate;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.counter.service; package creator.loki.feature.counter.service.listener;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -13,6 +13,7 @@ import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import creator.loki.feature.counter.service.CounterManager;
/** Listener для счетчика участников */ /** Listener для счетчика участников */
public class MemberCounterListener extends ListenerAdapter implements Lifecycle { public class MemberCounterListener extends ListenerAdapter implements Lifecycle {

View file

@ -11,15 +11,15 @@ import creator.loki.feature.economy.command.core.DailyCommand;
import creator.loki.feature.economy.command.core.PayCommand; import creator.loki.feature.economy.command.core.PayCommand;
import creator.loki.feature.economy.command.minigame.BlackjackCommand; import creator.loki.feature.economy.command.minigame.BlackjackCommand;
import creator.loki.feature.economy.command.minigame.FishCommand; import creator.loki.feature.economy.command.minigame.FishCommand;
import creator.loki.feature.economy.command.minigame.JobCommand; import creator.loki.feature.economy.command.job.JobCommand;
import creator.loki.feature.economy.command.minigame.SlotCommand; import creator.loki.feature.economy.command.minigame.SlotCommand;
import creator.loki.feature.economy.command.minigame.WorkCommand; import creator.loki.feature.economy.command.job.WorkCommand;
import creator.loki.feature.economy.command.social.RobCommand; import creator.loki.feature.economy.command.social.RobCommand;
import creator.loki.feature.economy.service.BlackjackService; import creator.loki.feature.economy.service.game.BlackjackService;
import creator.loki.feature.economy.service.EconomyService; import creator.loki.feature.economy.service.EconomyService;
import creator.loki.feature.economy.service.FishingService; import creator.loki.feature.economy.service.FishingService;
import creator.loki.feature.economy.service.JobService; import creator.loki.feature.economy.service.game.JobService;
import creator.loki.feature.economy.service.SlotService; import creator.loki.feature.economy.service.game.SlotService;
public class EconomyCommandRegistrar implements CommandRegistrar { public class EconomyCommandRegistrar implements CommandRegistrar {
private final EconomyService economyService; private final EconomyService economyService;

View file

@ -9,11 +9,11 @@ import creator.loki.core.security.CooldownManager;
import creator.loki.database.Database; import creator.loki.database.Database;
import creator.loki.feature.economy.repository.EconomyRepository; import creator.loki.feature.economy.repository.EconomyRepository;
import creator.loki.feature.economy.repository.SqlEconomyRepository; import creator.loki.feature.economy.repository.SqlEconomyRepository;
import creator.loki.feature.economy.service.BlackjackService; import creator.loki.feature.economy.service.game.BlackjackService;
import creator.loki.feature.economy.service.EconomyService; import creator.loki.feature.economy.service.EconomyService;
import creator.loki.feature.economy.service.FishingService; import creator.loki.feature.economy.service.FishingService;
import creator.loki.feature.economy.service.JobService; import creator.loki.feature.economy.service.game.JobService;
import creator.loki.feature.economy.service.SlotService; import creator.loki.feature.economy.service.game.SlotService;
public class EconomyModule extends AbstractModule { public class EconomyModule extends AbstractModule {

View file

@ -1,4 +1,4 @@
package creator.loki.feature.economy.command.minigame; package creator.loki.feature.economy.command.job;
import java.awt.Color; import java.awt.Color;
@ -6,7 +6,8 @@ import creator.loki.command.Command;
import creator.loki.command.CommandContext; import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory; import creator.loki.embed.EmbedFactory;
import creator.loki.embed.EmbedTemplates; import creator.loki.embed.EmbedTemplates;
import creator.loki.feature.economy.service.JobService; import creator.loki.feature.economy.service.game.JobService;
import creator.loki.feature.economy.service.game.JobService;
public class JobCommand implements Command { public class JobCommand implements Command {

View file

@ -1,4 +1,4 @@
package creator.loki.feature.economy.command.minigame; package creator.loki.feature.economy.command.job;
import java.awt.Color; import java.awt.Color;
@ -6,7 +6,8 @@ import creator.loki.command.Command;
import creator.loki.command.CommandContext; import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory; import creator.loki.embed.EmbedFactory;
import creator.loki.embed.EmbedTemplates; import creator.loki.embed.EmbedTemplates;
import creator.loki.feature.economy.service.JobService; import creator.loki.feature.economy.service.game.JobService;
import creator.loki.feature.economy.service.game.JobService;
public class WorkCommand implements Command { public class WorkCommand implements Command {

View file

@ -8,7 +8,7 @@ import creator.loki.command.Command;
import creator.loki.command.CommandContext; import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory; import creator.loki.embed.EmbedFactory;
import creator.loki.embed.EmbedTemplates; import creator.loki.embed.EmbedTemplates;
import creator.loki.feature.economy.service.BlackjackService; import creator.loki.feature.economy.service.game.BlackjackService;
import creator.loki.feature.economy.service.EconomyService; import creator.loki.feature.economy.service.EconomyService;
import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.MessageEmbed;

View file

@ -7,7 +7,7 @@ import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory; import creator.loki.embed.EmbedFactory;
import creator.loki.embed.EmbedTemplates; import creator.loki.embed.EmbedTemplates;
import creator.loki.feature.economy.service.EconomyService; import creator.loki.feature.economy.service.EconomyService;
import creator.loki.feature.economy.service.SlotService; import creator.loki.feature.economy.service.game.SlotService;
public class SlotCommand implements Command { public class SlotCommand implements Command {

View file

@ -1,4 +1,4 @@
package creator.loki.feature.economy.service; package creator.loki.feature.economy.service.game;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.economy.service; package creator.loki.feature.economy.service.game;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.economy.service; package creator.loki.feature.economy.service.game;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;

View file

@ -10,7 +10,7 @@ import creator.loki.feature.entertainment.commands.MemeCommand;
import creator.loki.feature.entertainment.commands.random.CoinCommand; import creator.loki.feature.entertainment.commands.random.CoinCommand;
import creator.loki.feature.entertainment.commands.random.DiceCommand; import creator.loki.feature.entertainment.commands.random.DiceCommand;
import creator.loki.feature.entertainment.commands.random.EightBallCommand; import creator.loki.feature.entertainment.commands.random.EightBallCommand;
import creator.loki.feature.entertainment.commands.random.RandomChoiceCommand; import creator.loki.feature.entertainment.commands.choice.RandomChoiceCommand;
import creator.loki.feature.entertainment.service.RatingService; import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.rating.commands.AuraCommand; import creator.loki.feature.rating.commands.AuraCommand;
import creator.loki.feature.rating.commands.BoykisserCommand; import creator.loki.feature.rating.commands.BoykisserCommand;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.entertainment.commands.random; package creator.loki.feature.entertainment.commands.choice;
import java.awt.Color; import java.awt.Color;
import java.util.List; import java.util.List;
@ -9,6 +9,8 @@ import creator.loki.command.CommandContext;
import creator.loki.config.BotConfig; import creator.loki.config.BotConfig;
import creator.loki.embed.EmbedTemplates; import creator.loki.embed.EmbedTemplates;
import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.EmbedBuilder;
import creator.loki.feature.utility.ChoiceOptionParser;
import creator.loki.feature.utility.ChoiceOptionParser;
/** Случайный выбор из вариантов. Разбор строки - {@link ChoiceOptionParser}. */ /** Случайный выбор из вариантов. Разбор строки - {@link ChoiceOptionParser}. */
public class RandomChoiceCommand implements Command { public class RandomChoiceCommand implements Command {

View file

@ -6,6 +6,10 @@ import com.google.inject.Singleton;
import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.Multibinder;
import creator.loki.config.BotConfig; import creator.loki.config.BotConfig;
import creator.loki.core.registry.CommandRegistrar; import creator.loki.core.registry.CommandRegistrar;
import creator.loki.feature.image.listener.ImageNavigationHandler;
import creator.loki.feature.image.service.ImageService;
import creator.loki.feature.image.service.ImageSessionManager;
import creator.loki.feature.image.util.ImageCommandRegistrar;
public class ImageModule extends AbstractModule { public class ImageModule extends AbstractModule {

View file

@ -6,10 +6,10 @@ import java.util.List;
import creator.loki.command.Command; import creator.loki.command.Command;
import creator.loki.command.CommandContext; import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedTemplates; import creator.loki.embed.EmbedTemplates;
import creator.loki.feature.image.ImageEmbedHelper; import creator.loki.feature.image.util.ImageEmbedHelper;
import creator.loki.feature.image.ImageService; import creator.loki.feature.image.service.ImageService;
import creator.loki.feature.image.ImageSession; import creator.loki.feature.image.service.ImageSession;
import creator.loki.feature.image.ImageSessionManager; import creator.loki.feature.image.service.ImageSessionManager;
import net.dv8tion.jda.api.entities.channel.attribute.IAgeRestrictedChannel; import net.dv8tion.jda.api.entities.channel.attribute.IAgeRestrictedChannel;
/** /**

View file

@ -1,55 +0,0 @@
package creator.loki.feature.image.commands;
import java.awt.Color;
import java.util.List;
import creator.loki.command.CommandCategories;
import creator.loki.feature.image.ImageService;
import creator.loki.feature.image.ImageSessionManager;
public class R34Command extends AbstractImageCommand {
public R34Command(ImageService imageService, ImageSessionManager sessionManager) {
super(imageService, sessionManager);
}
@Override
public String getName() {
return "r34";
}
@Override
public String getDescription() {
return "Поиск изображений на Rule34";
}
@Override
public String getUsage() {
return "r34 <кол-во до 50> <теги>";
}
@Override
public String getCategory() {
return CommandCategories.NSFW_IMAGES;
}
@Override
protected String siteName() {
return "rule34.xxx";
}
@Override
protected String siteDisplayName() {
return "Rule34";
}
@Override
protected Color siteColor() {
return new Color(200, 100, 0);
}
@Override
protected List<String> fetchUrls(String tags, int limit) {
return imageService.fetchR34(tags, limit);
}
}

View file

@ -1,11 +1,12 @@
package creator.loki.feature.image.commands; package creator.loki.feature.image.commands.booru;
import java.awt.Color; import java.awt.Color;
import java.util.List; import java.util.List;
import creator.loki.command.CommandCategories; import creator.loki.command.CommandCategories;
import creator.loki.feature.image.ImageService; import creator.loki.feature.image.service.ImageService;
import creator.loki.feature.image.ImageSessionManager; import creator.loki.feature.image.service.ImageSessionManager;
import creator.loki.feature.image.commands.AbstractImageCommand;
public class GelbooruCommand extends AbstractImageCommand { public class GelbooruCommand extends AbstractImageCommand {

View file

@ -1,11 +1,12 @@
package creator.loki.feature.image.commands; package creator.loki.feature.image.commands.booru;
import java.awt.Color; import java.awt.Color;
import java.util.List; import java.util.List;
import creator.loki.command.CommandCategories; import creator.loki.command.CommandCategories;
import creator.loki.feature.image.ImageService; import creator.loki.feature.image.service.ImageService;
import creator.loki.feature.image.ImageSessionManager; import creator.loki.feature.image.service.ImageSessionManager;
import creator.loki.feature.image.commands.AbstractImageCommand;
public class YandereCommand extends AbstractImageCommand { public class YandereCommand extends AbstractImageCommand {

View file

@ -1,11 +1,12 @@
package creator.loki.feature.image.commands; package creator.loki.feature.image.commands.e621;
import java.awt.Color; import java.awt.Color;
import java.util.List; import java.util.List;
import creator.loki.command.CommandCategories; import creator.loki.command.CommandCategories;
import creator.loki.feature.image.ImageService; import creator.loki.feature.image.service.ImageService;
import creator.loki.feature.image.ImageSessionManager; import creator.loki.feature.image.service.ImageSessionManager;
import creator.loki.feature.image.commands.AbstractImageCommand;
public class E621Command extends AbstractImageCommand { public class E621Command extends AbstractImageCommand {

View file

@ -1,4 +1,4 @@
package creator.loki.feature.image.commands; package creator.loki.feature.image.commands.e621;
import java.awt.Color; import java.awt.Color;
import java.util.List; import java.util.List;
@ -6,10 +6,11 @@ import java.util.List;
import creator.loki.command.CommandCategories; import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext; import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedTemplates; import creator.loki.embed.EmbedTemplates;
import creator.loki.feature.image.ImageEmbedHelper; import creator.loki.feature.image.util.ImageEmbedHelper;
import creator.loki.feature.image.ImageService; import creator.loki.feature.image.service.ImageService;
import creator.loki.feature.image.ImageSession; import creator.loki.feature.image.service.ImageSession;
import creator.loki.feature.image.ImageSessionManager; import creator.loki.feature.image.service.ImageSessionManager;
import creator.loki.feature.image.commands.AbstractImageCommand;
public class E621RandomCommand extends AbstractImageCommand { public class E621RandomCommand extends AbstractImageCommand {

View file

@ -1,4 +1,4 @@
package creator.loki.feature.image; package creator.loki.feature.image.listener;
import java.util.List; import java.util.List;
@ -8,6 +8,10 @@ import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import creator.loki.feature.image.service.ImageService;
import creator.loki.feature.image.service.ImageSession;
import creator.loki.feature.image.service.ImageSessionManager;
import creator.loki.feature.image.util.ImageEmbedHelper;
@Singleton @Singleton
public class ImageNavigationHandler extends ListenerAdapter { public class ImageNavigationHandler extends ListenerAdapter {
@ -104,7 +108,6 @@ public class ImageNavigationHandler extends ListenerAdapter {
private List<String> fetchPage(String site, String tags, int apiPage) { private List<String> fetchPage(String site, String tags, int apiPage) {
return switch (site) { return switch (site) {
case "e621.net" -> imageService.fetchE621(tags, PAGE_FETCH, apiPage); case "e621.net" -> imageService.fetchE621(tags, PAGE_FETCH, apiPage);
case "rule34.xxx" -> imageService.fetchR34(tags, PAGE_FETCH, apiPage);
case "gelbooru.com" -> imageService.fetchGelbooru(tags, PAGE_FETCH, apiPage); case "gelbooru.com" -> imageService.fetchGelbooru(tags, PAGE_FETCH, apiPage);
case "yande.re" -> imageService.fetchYandere(tags, PAGE_FETCH, apiPage); case "yande.re" -> imageService.fetchYandere(tags, PAGE_FETCH, apiPage);
default -> List.of(); default -> List.of();

View file

@ -1,4 +1,4 @@
package creator.loki.feature.image; package creator.loki.feature.image.service;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -22,17 +22,11 @@ import org.slf4j.LoggerFactory;
public class ImageService { public class ImageService {
private static final Logger logger = LoggerFactory.getLogger(ImageService.class); private static final Logger logger = LoggerFactory.getLogger(ImageService.class);
private static final String E621_API = "https://e621.net/posts.json"; private static final String E621_API = "https://e621.net/posts.json";
private static final String R34_API =
"https://api.rule34.xxx/index.php?page=dapi&s=post&q=index&json=1";
private final OkHttpClient client; private final OkHttpClient client;
private final String r34ApiKey;
private final String r34UserId;
@Inject @Inject
public ImageService(BotConfig config) { public ImageService(BotConfig config) {
this.r34ApiKey = config.getR34ApiKey();
this.r34UserId = config.getR34UserId();
this.client = this.client =
new OkHttpClient.Builder() new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS) .connectTimeout(15, TimeUnit.SECONDS)
@ -81,19 +75,6 @@ public class ImageService {
} }
} }
public List<String> fetchR34(String tags, int limit) {
return fetchR34(tags, limit, 1);
}
public List<String> fetchR34(String tags, int limit, int page) {
int capped = Math.min(limit, 100);
String url = R34_API + "&tags=" + encodeTags(tags) + "&limit=" + capped + "&pid=" + (page - 1);
if (!r34ApiKey.isEmpty() && !r34UserId.isEmpty()) {
url += "&api_key=" + r34ApiKey + "&user_id=" + r34UserId;
}
return fetchBooruJson(url, limit);
}
public List<String> fetchGelbooru(String tags, int limit) { public List<String> fetchGelbooru(String tags, int limit) {
return fetchGelbooru(tags, limit, 1); return fetchGelbooru(tags, limit, 1);
} }

View file

@ -1,4 +1,4 @@
package creator.loki.feature.image; package creator.loki.feature.image.service;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.image; package creator.loki.feature.image.service;
import java.awt.Color; import java.awt.Color;
import java.util.Map; import java.util.Map;

View file

@ -1,13 +1,14 @@
package creator.loki.feature.image; package creator.loki.feature.image.util;
import com.google.inject.Inject; import com.google.inject.Inject;
import creator.loki.core.registry.CommandRegistrar; import creator.loki.core.registry.CommandRegistrar;
import creator.loki.core.registry.CommandRegistry; import creator.loki.core.registry.CommandRegistry;
import creator.loki.feature.image.commands.E621Command; import creator.loki.feature.image.commands.booru.GelbooruCommand;
import creator.loki.feature.image.commands.E621RandomCommand; import creator.loki.feature.image.commands.booru.YandereCommand;
import creator.loki.feature.image.commands.GelbooruCommand; import creator.loki.feature.image.commands.e621.E621Command;
import creator.loki.feature.image.commands.R34Command; import creator.loki.feature.image.commands.e621.E621RandomCommand;
import creator.loki.feature.image.commands.YandereCommand; import creator.loki.feature.image.service.ImageService;
import creator.loki.feature.image.service.ImageSessionManager;
public class ImageCommandRegistrar implements CommandRegistrar { public class ImageCommandRegistrar implements CommandRegistrar {
private final ImageService imageService; private final ImageService imageService;
@ -23,7 +24,6 @@ public class ImageCommandRegistrar implements CommandRegistrar {
public void register(CommandRegistry registry) { public void register(CommandRegistry registry) {
registry.register(new E621Command(imageService, sessionManager)); registry.register(new E621Command(imageService, sessionManager));
registry.register(new E621RandomCommand(imageService, sessionManager)); registry.register(new E621RandomCommand(imageService, sessionManager));
registry.register(new R34Command(imageService, sessionManager));
registry.register(new GelbooruCommand(imageService, sessionManager)); registry.register(new GelbooruCommand(imageService, sessionManager));
registry.register(new YandereCommand(imageService, sessionManager)); registry.register(new YandereCommand(imageService, sessionManager));
} }

View file

@ -1,4 +1,4 @@
package creator.loki.feature.image; package creator.loki.feature.image.util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -8,6 +8,7 @@ import creator.loki.embed.EmbedFactory;
import net.dv8tion.jda.api.components.actionrow.ActionRow; import net.dv8tion.jda.api.components.actionrow.ActionRow;
import net.dv8tion.jda.api.components.buttons.Button; import net.dv8tion.jda.api.components.buttons.Button;
import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.MessageEmbed;
import creator.loki.feature.image.service.ImageSession;
public final class ImageEmbedHelper { public final class ImageEmbedHelper {

View file

@ -9,7 +9,7 @@ import creator.loki.database.Database;
import creator.loki.feature.config.repository.ConfigRepository; import creator.loki.feature.config.repository.ConfigRepository;
import creator.loki.feature.config.repository.GuildConfigRepository; import creator.loki.feature.config.repository.GuildConfigRepository;
import creator.loki.feature.level.commands.LevelCommandRegistrar; import creator.loki.feature.level.commands.LevelCommandRegistrar;
import creator.loki.feature.level.commands.ResetLevelsCommand; import creator.loki.feature.level.commands.admin.ResetLevelsCommand;
import creator.loki.feature.level.domain.XpFormula; import creator.loki.feature.level.domain.XpFormula;
import creator.loki.feature.level.listener.ResetLevelsButtonHandler; import creator.loki.feature.level.listener.ResetLevelsButtonHandler;
import creator.loki.feature.level.repository.SqlUserStatsRepository; import creator.loki.feature.level.repository.SqlUserStatsRepository;
@ -17,9 +17,9 @@ import creator.loki.feature.level.repository.UserStatsRepository;
import creator.loki.feature.level.service.LeaderboardService; import creator.loki.feature.level.service.LeaderboardService;
import creator.loki.feature.level.service.LevelRoleService; import creator.loki.feature.level.service.LevelRoleService;
import creator.loki.feature.level.service.LevelService; import creator.loki.feature.level.service.LevelService;
import creator.loki.feature.level.service.LevelUpNotifier; import creator.loki.feature.level.service.tracker.LevelUpNotifier;
import creator.loki.feature.level.service.MessageCooldownService; import creator.loki.feature.level.service.cooldown.MessageCooldownService;
import creator.loki.feature.level.service.VoiceTracker; import creator.loki.feature.level.service.tracker.VoiceTracker;
public class LevelModule extends AbstractModule { public class LevelModule extends AbstractModule {

View file

@ -9,6 +9,8 @@ import creator.loki.feature.level.service.LeaderboardService;
import creator.loki.feature.level.service.LevelRoleService; import creator.loki.feature.level.service.LevelRoleService;
import creator.loki.feature.profile.command.ProfileCommand; import creator.loki.feature.profile.command.ProfileCommand;
import creator.loki.feature.profile.service.ProfileService; import creator.loki.feature.profile.service.ProfileService;
import creator.loki.feature.level.commands.admin.ResetLevelsCommand;
import creator.loki.feature.level.commands.admin.StatsCommand;
public class LevelCommandRegistrar implements CommandRegistrar { public class LevelCommandRegistrar implements CommandRegistrar {
private final LeaderboardService leaderboardService; private final LeaderboardService leaderboardService;

View file

@ -9,6 +9,7 @@ import creator.loki.embed.EmbedTemplates;
import creator.loki.feature.config.repository.ConfigRepository; import creator.loki.feature.config.repository.ConfigRepository;
import creator.loki.feature.level.service.LevelRoleService; import creator.loki.feature.level.service.LevelRoleService;
import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.Permission;
import creator.loki.feature.level.commands.util.LevelRoleFormatter;
public class LevelRoleCommand implements Command { public class LevelRoleCommand implements Command {
private final LevelRoleService levelRoleService; private final LevelRoleService levelRoleService;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.level.commands; package creator.loki.feature.level.commands.admin;
import java.awt.Color; import java.awt.Color;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.level.commands; package creator.loki.feature.level.commands.admin;
import java.awt.Color; import java.awt.Color;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.level.commands; package creator.loki.feature.level.commands.util;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;

View file

@ -8,7 +8,7 @@ import creator.loki.core.EventDispatcher;
import creator.loki.feature.economy.service.EconomyService; import creator.loki.feature.economy.service.EconomyService;
import creator.loki.feature.level.domain.LevelUpResult; import creator.loki.feature.level.domain.LevelUpResult;
import creator.loki.feature.level.service.LevelService; import creator.loki.feature.level.service.LevelService;
import creator.loki.feature.level.service.LevelUpNotifier; import creator.loki.feature.level.service.tracker.LevelUpNotifier;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.slf4j.Logger; import org.slf4j.Logger;

View file

@ -5,7 +5,7 @@ import java.awt.Color;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import creator.loki.embed.EmbedFactory; import creator.loki.embed.EmbedFactory;
import creator.loki.feature.level.commands.ResetLevelsCommand; import creator.loki.feature.level.commands.admin.ResetLevelsCommand;
import creator.loki.util.LoggerDecorator; import creator.loki.util.LoggerDecorator;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;

View file

@ -5,6 +5,7 @@ import creator.loki.feature.level.domain.LevelUpResult;
import creator.loki.feature.level.domain.UserStats; import creator.loki.feature.level.domain.UserStats;
import creator.loki.feature.level.domain.XpFormula; import creator.loki.feature.level.domain.XpFormula;
import creator.loki.feature.level.repository.UserStatsRepository; import creator.loki.feature.level.repository.UserStatsRepository;
import creator.loki.feature.level.service.cooldown.MessageCooldownService;
/** /**
* Сервис для управления системой уровней Рефакторинг LevelSystem - только бизнес-логика начисления * Сервис для управления системой уровней Рефакторинг LevelSystem - только бизнес-логика начисления

View file

@ -1,4 +1,4 @@
package creator.loki.feature.level.service; package creator.loki.feature.level.service.cooldown;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.level.service; package creator.loki.feature.level.service.tracker;
import java.awt.Color; import java.awt.Color;
@ -11,6 +11,8 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import creator.loki.feature.level.service.LevelRoleService;
import creator.loki.feature.level.service.LevelService;
/** Уведомления о повышении уровня */ /** Уведомления о повышении уровня */
public class LevelUpNotifier { public class LevelUpNotifier {

View file

@ -1,4 +1,4 @@
package creator.loki.feature.level.service; package creator.loki.feature.level.service.tracker;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -16,6 +16,7 @@ import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import creator.loki.feature.level.service.LevelService;
@Singleton @Singleton
public class VoiceTracker extends ListenerAdapter implements Lifecycle { public class VoiceTracker extends ListenerAdapter implements Lifecycle {

View file

@ -9,9 +9,9 @@ import creator.loki.database.Database;
import creator.loki.feature.config.repository.GuildConfigRepository; import creator.loki.feature.config.repository.GuildConfigRepository;
import creator.loki.feature.moderation.repository.ActiveMuteRepository; import creator.loki.feature.moderation.repository.ActiveMuteRepository;
import creator.loki.feature.moderation.repository.ModerationCaseRepository; import creator.loki.feature.moderation.repository.ModerationCaseRepository;
import creator.loki.feature.moderation.repository.SqlActiveMuteRepository; import creator.loki.feature.moderation.repository.sql.SqlActiveMuteRepository;
import creator.loki.feature.moderation.repository.SqlModerationCaseRepository; import creator.loki.feature.moderation.repository.sql.SqlModerationCaseRepository;
import creator.loki.feature.moderation.repository.SqlWarningRepository; import creator.loki.feature.moderation.repository.sql.SqlWarningRepository;
import creator.loki.feature.moderation.repository.WarningRepository; import creator.loki.feature.moderation.repository.WarningRepository;
public class ModerationModule extends AbstractModule { public class ModerationModule extends AbstractModule {

View file

@ -1,4 +1,4 @@
package creator.loki.feature.moderation.repository; package creator.loki.feature.moderation.repository.sql;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -10,6 +10,7 @@ import java.util.Optional;
import creator.loki.database.Database; import creator.loki.database.Database;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import creator.loki.feature.moderation.repository.ActiveMuteRepository;
public class SqlActiveMuteRepository implements ActiveMuteRepository { public class SqlActiveMuteRepository implements ActiveMuteRepository {
private static final Logger logger = LoggerFactory.getLogger(SqlActiveMuteRepository.class); private static final Logger logger = LoggerFactory.getLogger(SqlActiveMuteRepository.class);

View file

@ -1,4 +1,4 @@
package creator.loki.feature.moderation.repository; package creator.loki.feature.moderation.repository.sql;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -10,6 +10,7 @@ import java.util.Optional;
import creator.loki.database.Database; import creator.loki.database.Database;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import creator.loki.feature.moderation.repository.ModerationCaseRepository;
public class SqlModerationCaseRepository implements ModerationCaseRepository { public class SqlModerationCaseRepository implements ModerationCaseRepository {
private static final Logger logger = LoggerFactory.getLogger(SqlModerationCaseRepository.class); private static final Logger logger = LoggerFactory.getLogger(SqlModerationCaseRepository.class);

View file

@ -1,4 +1,4 @@
package creator.loki.feature.moderation.repository; package creator.loki.feature.moderation.repository.sql;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -13,6 +13,7 @@ import java.util.Optional;
import creator.loki.database.Database; import creator.loki.database.Database;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import creator.loki.feature.moderation.repository.WarningRepository;
public class SqlWarningRepository implements WarningRepository { public class SqlWarningRepository implements WarningRepository {
private static final Logger logger = LoggerFactory.getLogger(SqlWarningRepository.class); private static final Logger logger = LoggerFactory.getLogger(SqlWarningRepository.class);

View file

@ -6,7 +6,7 @@ import creator.loki.embed.EmbedTemplates;
import creator.loki.feature.music.core.PlayerManager; import creator.loki.feature.music.core.PlayerManager;
import creator.loki.feature.music.core.SearchManager; import creator.loki.feature.music.core.SearchManager;
import creator.loki.feature.music.play.SearchHandler; import creator.loki.feature.music.play.SearchHandler;
import creator.loki.util.VoiceHelper; import creator.loki.feature.utility.VoiceHelper;
import dev.arbjerg.lavalink.client.player.Track; import dev.arbjerg.lavalink.client.player.Track;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;

View file

@ -10,7 +10,7 @@ import creator.loki.feature.music.core.LavalinkManager;
import creator.loki.feature.music.core.PlayerManager; import creator.loki.feature.music.core.PlayerManager;
import creator.loki.feature.music.core.SearchManager; import creator.loki.feature.music.core.SearchManager;
import creator.loki.feature.music.manage.GuildMusicManager; import creator.loki.feature.music.manage.GuildMusicManager;
import creator.loki.util.VoiceHelper; import creator.loki.feature.utility.VoiceHelper;
import dev.arbjerg.lavalink.client.player.Track; import dev.arbjerg.lavalink.client.player.Track;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;

View file

@ -0,0 +1,65 @@
package creator.loki.feature.music.repository;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import creator.loki.database.Database;
import creator.loki.util.LoggerDecorator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import creator.loki.feature.music.manage.FilterState;
@Singleton
public class FilterRepository {
private static final Logger logger = LoggerFactory.getLogger(FilterRepository.class);
private final Database db;
@Inject
public FilterRepository(final Database db) {
this.db = db;
}
public void save(
final String guildId, final int bassboost, final boolean nightcore, final boolean vaporwave) {
String sql =
"INSERT OR REPLACE INTO music_filters (guild_id, bassboost, nightcore, vaporwave) "
+ "VALUES (?, ?, ?, ?)";
try (PreparedStatement stmt = db.prepare(sql)) {
stmt.setString(1, guildId);
stmt.setInt(2, bassboost);
stmt.setInt(3, nightcore ? 1 : 0);
stmt.setInt(4, vaporwave ? 1 : 0);
stmt.executeUpdate();
} catch (SQLException e) {
LoggerDecorator.error(logger, "Error saving filter state for guild " + guildId, e);
}
}
public FilterState load(final String guildId) {
String sql = "SELECT bassboost, nightcore, vaporwave FROM music_filters WHERE guild_id = ?";
try (PreparedStatement stmt = db.prepare(sql)) {
stmt.setString(1, guildId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) { return new FilterState(rs.getInt("bassboost"), rs.getInt("nightcore") == 1, rs.getInt("vaporwave") == 1); }
}
} catch (SQLException e) {
LoggerDecorator.error(logger, "Error loading filter state for guild " + guildId, e);
}
return new FilterState(0, false, false);
}
public void delete(final String guildId) {
String sql = "DELETE FROM music_filters WHERE guild_id = ?";
try (PreparedStatement stmt = db.prepare(sql)) {
stmt.setString(1, guildId);
stmt.executeUpdate();
} catch (SQLException e) {
LoggerDecorator.error(logger, "Error deleting filter state for guild " + guildId, e);
}
}
public record FilterState(int bassboost, boolean nightcore, boolean vaporwave) {}
}

View file

@ -4,6 +4,7 @@ import com.google.inject.AbstractModule;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.Multibinder;
import creator.loki.core.registry.CommandRegistrar; import creator.loki.core.registry.CommandRegistrar;
import creator.loki.feature.radio.service.RadioManager;
public class RadioModule extends AbstractModule { public class RadioModule extends AbstractModule {
@Override @Override

View file

@ -6,7 +6,7 @@ import com.google.inject.Inject;
import creator.loki.command.Command; import creator.loki.command.Command;
import creator.loki.command.CommandContext; import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedTemplates; import creator.loki.embed.EmbedTemplates;
import creator.loki.feature.radio.RadioManager; import creator.loki.feature.radio.service.RadioManager;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.radio; package creator.loki.feature.radio.listener;
import java.util.List; import java.util.List;
@ -11,6 +11,8 @@ import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import creator.loki.feature.radio.service.RadioManager;
import creator.loki.feature.radio.service.RadioManager;
public class RadioBotListener extends ListenerAdapter { public class RadioBotListener extends ListenerAdapter {
private static final Logger logger = LoggerFactory.getLogger(RadioBotListener.class); private static final Logger logger = LoggerFactory.getLogger(RadioBotListener.class);

View file

@ -1,4 +1,4 @@
package creator.loki.feature.radio; package creator.loki.feature.radio.service;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;

View file

@ -0,0 +1,71 @@
package creator.loki.feature.rating.commands.body;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.rating.config.RatingConfig;
import creator.loki.feature.rating.config.RatingLevel;
import net.dv8tion.jda.api.entities.Member;
public class FatCommand implements Command {
private final RatingService ratingService;
@Inject
public FatCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "fat";
}
@Override
public String getDescription() {
return "Проверка на жирность (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "fat [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "fat")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
int fatLevel = ratingService.getDeterministicValue(target.getId(), 999, 0, 100);
StringBuilder bar = new StringBuilder();
int filled = fatLevel / 10;
for (int i = 0; i < filled; i++) { bar.append("🟩"); }
for (int i = filled; i < 10; i++) { bar.append("⬜"); }
RatingLevel level = RatingLevel.findLevel(fatLevel, RatingConfig.getFatLevels());
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("🐷 Fat Метр")
.withDescription(target.getAsMention())
.addField(
"Жирность", bar + "\n**" + fatLevel + "%** - " + level.getComment(), false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(level.getColor())
.withFooter("💡 Детерминировано вашим Discord ID")
.build())
.queue();
}
}

View file

@ -0,0 +1,79 @@
package creator.loki.feature.rating.commands.body;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.rating.config.RatingConfig;
import creator.loki.feature.rating.config.RatingLevel;
import net.dv8tion.jda.api.entities.Member;
public class MustacheCommand implements Command {
private final RatingService ratingService;
@Inject
public MustacheCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "mustache";
}
@Override
public String getDescription() {
return "Проверка на усатость (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "mustache [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "mustache")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
int mustacheLevel;
if (target.getUser().isBot() || target.getId().equals("1414975197269987471")) {
mustacheLevel = 0;
} else if (target.getId().equals("678283217094180915")) {
mustacheLevel = 1000;
} else {
mustacheLevel = ratingService.getDeterministicValue(target.getId(), 888, 0, 100);
}
StringBuilder bar = new StringBuilder();
int filled = Math.min(mustacheLevel / 10, 10);
for (int i = 0; i < filled; i++) { bar.append("🧔"); }
for (int i = filled; i < 10; i++) { bar.append("⬜"); }
RatingLevel level = RatingConfig.findLevel(mustacheLevel, "mustache");
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("🧔 Mustache Метр")
.withDescription(target.getAsMention())
.addField(
"Усатость", bar + "\n**" + mustacheLevel + "%** - " + level.getComment(), false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(level.getColor())
.withFooter("💡 Детерминировано вашим Discord ID")
.build())
.queue();
}
}

View file

@ -0,0 +1,80 @@
package creator.loki.feature.rating.commands.body;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.rating.config.RatingConfig;
import creator.loki.feature.rating.config.RatingLevel;
import net.dv8tion.jda.api.entities.Member;
public class PpCommand implements Command {
private final RatingService ratingService;
@Inject
public PpCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "pp";
}
@Override
public String getDescription() {
return "Измеритель pp (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "pp [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "pp")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
if (target.getUser().isBot()) { context.getChannel().sendMessage("❌ У ботов нет pp!").queue(); return; }
int size;
if (target.getId().equals("1414975197269987471")) {
size = 100;
} else {
size = ratingService.getDeterministicValue(target.getId(), 6969, 0, 30);
}
StringBuilder ppVisual = new StringBuilder("8");
for (int i = 0; i < size; i++) { ppVisual.append("="); }
ppVisual.append("D");
RatingLevel level = RatingConfig.findLevel(size, "pp");
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("🍆 PP Размер")
.withDescription(target.getAsMention())
.addField(
"Размер",
"```" + ppVisual + "```\n**" + size + " см** - " + level.getComment(),
false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(level.getColor())
.withFooter("💡 Детерминировано вашим Discord ID")
.build())
.queue();
}
}

View file

@ -0,0 +1,91 @@
package creator.loki.feature.rating.commands.fortune;
import java.awt.Color;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import net.dv8tion.jda.api.entities.Member;
public class AuraCommand implements Command {
private final RatingService ratingService;
@Inject
public AuraCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "aura";
}
@Override
public String getDescription() {
return "Проверить свою ауру (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "aura [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "aura")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
int aura = ratingService.getDeterministicValue(target.getId(), 999, -50, 100);
String emoji;
Color color;
if (aura >= 80) {
emoji = "🌟";
color = new Color(255, 215, 0);
} else if (aura >= 50) {
emoji = "✨";
color = new Color(100, 255, 150);
} else if (aura >= 20) {
emoji = "😐";
color = new Color(52, 152, 219);
} else if (aura >= 0) {
emoji = "💫";
color = new Color(128, 128, 128);
} else if (aura >= -25) {
emoji = "🌑";
color = new Color(231, 76, 60);
} else {
emoji = "💀";
color = new Color(100, 0, 0);
}
StringBuilder bar = new StringBuilder();
int filled = Math.max(0, Math.min(10, (aura + 50) / 15));
for (int i = 0; i < filled; i++) { bar.append("✧"); }
for (int i = filled; i < 10; i++) { bar.append("◇"); }
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("✨ Аура")
.withDescription(target.getAsMention())
.addField("Сила", emoji + " **" + aura + "**\n" + bar, false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(color)
.withFooter("💡 Детерминировано вашим Discord ID")
.build())
.queue();
}
}

View file

@ -0,0 +1,70 @@
package creator.loki.feature.rating.commands.fortune;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.rating.config.RatingConfig;
import creator.loki.feature.rating.config.RatingLevel;
import net.dv8tion.jda.api.entities.Member;
public class LuckCommand implements Command {
private final RatingService ratingService;
@Inject
public LuckCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "luck";
}
@Override
public String getDescription() {
return "Проверить свою удачу (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "luck [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "luck")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
int luck = ratingService.getDeterministicValue(target.getId(), 777, 0, 100);
StringBuilder bar = new StringBuilder();
int filled = luck / 10;
for (int i = 0; i < filled; i++) { bar.append("🍀"); }
for (int i = filled; i < 10; i++) { bar.append("⬜"); }
RatingLevel level = RatingLevel.findLevel(luck, RatingConfig.getLuckLevels());
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("🍀 Удача")
.withDescription(target.getAsMention())
.addField("Везение", bar + "\n**" + luck + "%** - " + level.getComment(), false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(level.getColor())
.withFooter("💡 Детерминировано вашим Discord ID")
.build())
.queue();
}
}

View file

@ -0,0 +1,70 @@
package creator.loki.feature.rating.commands.fortune;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.rating.config.RatingConfig;
import creator.loki.feature.rating.config.RatingLevel;
import net.dv8tion.jda.api.entities.Member;
public class RizzCommand implements Command {
private final RatingService ratingService;
@Inject
public RizzCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "rizz";
}
@Override
public String getDescription() {
return "На сколько у тебя ризз (харизма)";
}
@Override
public String getUsage() {
return "rizz [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "rizz")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
int rizz = ratingService.getDeterministicValue(target.getId(), 555, 0, 100);
StringBuilder bar = new StringBuilder();
int filled = rizz / 10;
for (int i = 0; i < filled; i++) { bar.append("🗿"); }
for (int i = filled; i < 10; i++) { bar.append("⬜"); }
RatingLevel level = RatingLevel.findLevel(rizz, RatingConfig.getRizzLevels());
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("🗿 Rizz Метр")
.withDescription(target.getAsMention())
.addField("Харизма", bar + "\n**" + rizz + "%** - " + level.getComment(), false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(level.getColor())
.withFooter("💡 Детерминировано вашим Discord ID")
.build())
.queue();
}
}

View file

@ -0,0 +1,73 @@
package creator.loki.feature.rating.commands.mind;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.rating.config.RatingConfig;
import creator.loki.feature.rating.config.RatingLevel;
import net.dv8tion.jda.api.entities.Member;
public class BoykisserCommand implements Command {
private final RatingService ratingService;
@Inject
public BoykisserCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "boykisser";
}
@Override
public String getDescription() {
return "На сколько ты boykisser (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "boykisser [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "boykisser")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
if (target.getUser().isBot()) { context.getChannel().sendMessage("❌ Боты не целуют мальчиков!").queue(); return; }
int boykisserLevel = ratingService.getDeterministicValue(target.getId(), 777, 0, 100);
StringBuilder bar = new StringBuilder();
int filled = boykisserLevel / 10;
for (int i = 0; i < filled; i++) { bar.append("💋"); }
for (int i = filled; i < 10; i++) { bar.append("⬜"); }
RatingLevel level = RatingLevel.findLevel(boykisserLevel, RatingConfig.getBoykisserLevels());
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("💋 Boykisser Метр")
.withDescription(target.getAsMention())
.addField(
"Уровень", bar + "\n**" + boykisserLevel + "%** - " + level.getComment(), false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(level.getColor())
.withFooter("💡 Детерминировано вашим Discord ID")
.build())
.queue();
}
}

View file

@ -0,0 +1,72 @@
package creator.loki.feature.rating.commands.mind;
import java.awt.Color;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import net.dv8tion.jda.api.entities.Member;
public class GayCommand implements Command {
private final RatingService ratingService;
@Inject
public GayCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "gay";
}
@Override
public String getDescription() {
return "Гей-метр (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "gay [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "gay")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
if (target.getUser().isBot()) { context.getChannel().sendMessage("❌ Боты не имеют ориентации!").queue(); return; }
int gayLevel = ratingService.getDeterministicValue(target.getId(), 456, 0, 100);
String[] rainbow = {"🟥", "🟧", "🟨", "🟩", "🟦", "🟪"};
StringBuilder bar = new StringBuilder();
int filled = Math.min(gayLevel / 17 + 1, 6);
for (int i = 0; i < filled; i++) { bar.append(rainbow[i]); }
for (int i = filled; i < 6; i++) { bar.append("⬜"); }
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("🏳️‍🌈 Гей-метр")
.withDescription(target.getAsMention())
.addField("Уровень", bar + "\n**" + gayLevel + "%**", false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(new Color(255, 0, 255))
.withFooter("💡 Это просто мем, не воспринимайте серьезно")
.build())
.queue();
}
}

View file

@ -0,0 +1,72 @@
package creator.loki.feature.rating.commands.mind;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.rating.config.RatingConfig;
import creator.loki.feature.rating.config.RatingLevel;
import net.dv8tion.jda.api.entities.Member;
public class IqCommand implements Command {
private final RatingService ratingService;
@Inject
public IqCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "iq";
}
@Override
public String getDescription() {
return "Тест IQ (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "iq [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "iq")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
int iq;
if (target.getUser().isBot()) {
iq = 9000;
} else if (target.getId().equals("1414975197269987471")) {
iq = 1000;
} else {
iq = ratingService.getDeterministicValue(target.getId(), 123, 0, 140);
}
RatingLevel level = RatingConfig.findLevel(iq, "iq");
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("🧠 Тест IQ")
.withDescription("Результат для " + target.getAsMention())
.addField("IQ Уровень", "**" + iq + "** - " + level.getComment(), false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(level.getColor())
.withFooter("💡 Научно доказано вашим ID")
.build())
.queue();
}
}

View file

@ -0,0 +1,69 @@
package creator.loki.feature.rating.commands.vibe;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.rating.config.RatingConfig;
import creator.loki.feature.rating.config.RatingLevel;
import net.dv8tion.jda.api.entities.Member;
public class GlazeCommand implements Command {
private final RatingService ratingService;
@Inject
public GlazeCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "glaze";
}
@Override
public String getDescription() {
return "На сколько ты глазированный (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "glaze [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "glaze")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
int level = ratingService.getDeterministicValue(target.getId(), 333, 0, 100);
StringBuilder bar = new StringBuilder();
int filled = level / 10;
for (int i = 0; i < filled; i++) { bar.append("🍩"); }
for (int i = filled; i < 10; i++) { bar.append("⬜"); }
RatingLevel rl = RatingLevel.findLevel(level, RatingConfig.getGlazeLevels());
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("🍩 Глазурь-метр")
.withDescription(target.getAsMention())
.addField("Глазурь", bar + "\n**" + level + "%** - " + rl.getComment(), false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(rl.getColor())
.withFooter("💡 Детерминировано вашим Discord ID")
.build())
.queue();
}
}

View file

@ -0,0 +1,72 @@
package creator.loki.feature.rating.commands.vibe;
import java.awt.Color;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import net.dv8tion.jda.api.entities.Member;
public class GoonCommand implements Command {
private final RatingService ratingService;
@Inject
public GoonCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "goon";
}
@Override
public String getDescription() {
return "Гуон-детектор (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "goon [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "goon")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
int level = ratingService.getDeterministicValue(target.getId(), 444, 0, 100);
StringBuilder bar = new StringBuilder();
int filled = level / 10;
for (int i = 0; i < filled; i++) { bar.append("💀"); }
for (int i = filled; i < 10; i++) { bar.append("⬜"); }
Color color =
level >= 70
? new Color(180, 0, 0)
: level >= 40 ? new Color(128, 0, 128) : new Color(100, 100, 100);
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("💀 Goon-детектор")
.withDescription(target.getAsMention())
.addField("Уровень", bar + "\n**" + level + "%**", false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(color)
.withFooter("💡 Детерминировано вашим Discord ID")
.build())
.queue();
}
}

View file

@ -0,0 +1,69 @@
package creator.loki.feature.rating.commands.vibe;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.rating.config.RatingConfig;
import creator.loki.feature.rating.config.RatingLevel;
import net.dv8tion.jda.api.entities.Member;
public class SimpCommand implements Command {
private final RatingService ratingService;
@Inject
public SimpCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "simp";
}
@Override
public String getDescription() {
return "Симп-детектор (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "simp [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "simp")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
int level = ratingService.getDeterministicValue(target.getId(), 111, 0, 100);
StringBuilder bar = new StringBuilder();
int filled = level / 10;
for (int i = 0; i < filled; i++) { bar.append("💀"); }
for (int i = filled; i < 10; i++) { bar.append("⬜"); }
RatingLevel rl = RatingLevel.findLevel(level, RatingConfig.getSimpLevels());
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("😭 Симп-детектор")
.withDescription(target.getAsMention())
.addField("Уровень", bar + "\n**" + level + "%** - " + rl.getComment(), false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(rl.getColor())
.withFooter("💡 Детерминировано вашим Discord ID")
.build())
.queue();
}
}

View file

@ -0,0 +1,69 @@
package creator.loki.feature.rating.commands.vibe;
import com.google.inject.Inject;
import creator.loki.command.Command;
import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedFactory;
import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.rating.config.RatingConfig;
import creator.loki.feature.rating.config.RatingLevel;
import net.dv8tion.jda.api.entities.Member;
public class VibeCommand implements Command {
private final RatingService ratingService;
@Inject
public VibeCommand(RatingService ratingService) {
this.ratingService = ratingService;
}
@Override
public String getName() {
return "vibe";
}
@Override
public String getDescription() {
return "Проверить свой вайб (результат всегда одинаковый)";
}
@Override
public String getUsage() {
return "vibe [@user]";
}
@Override
public String getCategory() {
return CommandCategories.MEASUREMENTS;
}
@Override
public void execute(CommandContext context) {
if (!ratingService.checkAndApplyCooldown(context, "vibe")) { return; }
Member target = ratingService.resolveTarget(context);
if (target == null) { return; }
int level = ratingService.getDeterministicValue(target.getId(), 222, 0, 100);
StringBuilder bar = new StringBuilder();
int filled = level / 10;
for (int i = 0; i < filled; i++) { bar.append("🎵"); }
for (int i = filled; i < 10; i++) { bar.append("⬜"); }
RatingLevel rl = RatingLevel.findLevel(level, RatingConfig.getVibeLevels());
context
.getChannel()
.sendMessageEmbeds(
EmbedFactory.create()
.withTitle("🎵 Вайб-чек")
.withDescription(target.getAsMention())
.addField("Вайб", bar + "\n**" + level + "%** - " + rl.getComment(), false)
.withThumbnail(target.getUser().getAvatarUrl())
.withColor(rl.getColor())
.withFooter("💡 Детерминировано вашим Discord ID")
.build())
.queue();
}
}

View file

@ -3,6 +3,8 @@ package creator.loki.feature.reactroles;
import com.google.inject.Inject; import com.google.inject.Inject;
import creator.loki.core.registry.CommandRegistrar; import creator.loki.core.registry.CommandRegistrar;
import creator.loki.core.registry.CommandRegistry; import creator.loki.core.registry.CommandRegistry;
import creator.loki.feature.reactroles.commands.ReactionRoleCommand;
import creator.loki.feature.reactroles.repository.ReactionRoleRepository;
public class ReactionRolesCommandRegistrar implements CommandRegistrar { public class ReactionRolesCommandRegistrar implements CommandRegistrar {
private final ReactionRoleRepository repository; private final ReactionRoleRepository repository;

View file

@ -4,6 +4,8 @@ import com.google.inject.AbstractModule;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.Multibinder;
import creator.loki.core.registry.CommandRegistrar; import creator.loki.core.registry.CommandRegistrar;
import creator.loki.feature.reactroles.listener.ReactionRoleListener;
import creator.loki.feature.reactroles.repository.ReactionRoleRepository;
public class ReactionRolesModule extends AbstractModule { public class ReactionRolesModule extends AbstractModule {
@Override @Override

View file

@ -1,4 +1,4 @@
package creator.loki.feature.reactroles; package creator.loki.feature.reactroles.commands;
import java.util.List; import java.util.List;
@ -8,6 +8,8 @@ import creator.loki.command.Command;
import creator.loki.command.CommandContext; import creator.loki.command.CommandContext;
import creator.loki.embed.EmbedTemplates; import creator.loki.embed.EmbedTemplates;
import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.Permission;
import creator.loki.feature.reactroles.repository.ReactionRoleRepository;
import creator.loki.feature.reactroles.repository.ReactionRoleRepository;
public class ReactionRoleCommand implements Command { public class ReactionRoleCommand implements Command {
private final ReactionRoleRepository repository; private final ReactionRoleRepository repository;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.reactroles; package creator.loki.feature.reactroles.listener;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@ -10,6 +10,8 @@ import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import creator.loki.feature.reactroles.repository.ReactionRoleRepository;
import creator.loki.feature.reactroles.repository.ReactionRoleRepository;
@Singleton @Singleton
public class ReactionRoleListener extends ListenerAdapter { public class ReactionRoleListener extends ListenerAdapter {

View file

@ -1,4 +1,4 @@
package creator.loki.feature.reactroles; package creator.loki.feature.reactroles.repository;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;

View file

@ -12,7 +12,7 @@ import java.util.List;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import creator.loki.feature.counter.repository.CounterStatsRepository; import creator.loki.feature.counter.repository.CounterStatsRepository;
import creator.loki.feature.counter.service.DailyStats; import creator.loki.feature.counter.service.format.DailyStats;
import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;

View file

@ -3,6 +3,7 @@ package creator.loki.feature.ticket;
import com.google.inject.Inject; import com.google.inject.Inject;
import creator.loki.core.registry.CommandRegistrar; import creator.loki.core.registry.CommandRegistrar;
import creator.loki.core.registry.CommandRegistry; import creator.loki.core.registry.CommandRegistry;
import creator.loki.feature.ticket.commands.TicketSetupCommand;
public class TicketCommandRegistrar implements CommandRegistrar { public class TicketCommandRegistrar implements CommandRegistrar {
private final TicketSetupCommand ticketSetupCommand; private final TicketSetupCommand ticketSetupCommand;

View file

@ -4,6 +4,8 @@ import com.google.inject.AbstractModule;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.google.inject.multibindings.Multibinder; import com.google.inject.multibindings.Multibinder;
import creator.loki.core.registry.CommandRegistrar; import creator.loki.core.registry.CommandRegistrar;
import creator.loki.feature.ticket.listener.TicketListener;
import creator.loki.feature.ticket.repository.TicketRepository;
public class TicketModule extends AbstractModule { public class TicketModule extends AbstractModule {
@Override @Override

View file

@ -1,4 +1,4 @@
package creator.loki.feature.ticket; package creator.loki.feature.ticket.commands;
import com.google.inject.Inject; import com.google.inject.Inject;
import creator.loki.command.Command; import creator.loki.command.Command;
@ -9,6 +9,8 @@ import net.dv8tion.jda.api.components.buttons.Button;
import net.dv8tion.jda.api.components.actionrow.ActionRow; import net.dv8tion.jda.api.components.actionrow.ActionRow;
import java.awt.Color; import java.awt.Color;
import creator.loki.feature.ticket.repository.TicketRepository;
import creator.loki.feature.ticket.repository.TicketRepository;
public class TicketSetupCommand implements Command { public class TicketSetupCommand implements Command {
private final TicketRepository ticketRepository; private final TicketRepository ticketRepository;

View file

@ -1,8 +1,8 @@
package creator.loki.feature.ticket; package creator.loki.feature.ticket.listener;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import creator.loki.feature.ticket.TicketRepository.Ticket; import creator.loki.feature.ticket.repository.TicketRepository.Ticket;
import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
@ -18,6 +18,7 @@ import net.dv8tion.jda.api.components.actionrow.ActionRow;
import java.awt.Color; import java.awt.Color;
import java.util.EnumSet; import java.util.EnumSet;
import creator.loki.feature.ticket.repository.TicketRepository;
@Singleton @Singleton
public class TicketListener extends ListenerAdapter { public class TicketListener extends ListenerAdapter {

View file

@ -1,4 +1,4 @@
package creator.loki.feature.ticket; package creator.loki.feature.ticket.repository;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;

View file

@ -1,4 +1,4 @@
package creator.loki.feature.entertainment.commands.random; package creator.loki.feature.utility;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;

View file

@ -1,4 +1,4 @@
package creator.loki.util; package creator.loki.feature.utility;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;

View file

@ -17,7 +17,7 @@ import creator.loki.feature.economy.service.*;
import creator.loki.feature.entertainment.EntertainmentCommandRegistrar; import creator.loki.feature.entertainment.EntertainmentCommandRegistrar;
import creator.loki.feature.entertainment.service.RatingService; import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.level.commands.LevelCommandRegistrar; import creator.loki.feature.level.commands.LevelCommandRegistrar;
import creator.loki.feature.level.commands.ResetLevelsCommand; import creator.loki.feature.level.commands.admin.ResetLevelsCommand;
import creator.loki.feature.level.repository.UserStatsRepository; import creator.loki.feature.level.repository.UserStatsRepository;
import creator.loki.feature.level.service.LeaderboardService; import creator.loki.feature.level.service.LeaderboardService;
import creator.loki.feature.level.service.LevelRoleService; import creator.loki.feature.level.service.LevelRoleService;
@ -44,6 +44,9 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import creator.loki.feature.economy.service.game.BlackjackService;
import creator.loki.feature.economy.service.game.JobService;
import creator.loki.feature.economy.service.game.SlotService;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class SmokeCommandRegistrationTest { class SmokeCommandRegistrationTest {

View file

@ -15,6 +15,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor; import org.mockito.Captor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import creator.loki.feature.economy.service.game.BlackjackService;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class BlackjackServiceTest { class BlackjackServiceTest {

View file

@ -17,6 +17,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor; import org.mockito.Captor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import creator.loki.feature.economy.service.game.JobService;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class JobServiceTest { class JobServiceTest {

View file

@ -15,6 +15,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor; import org.mockito.Captor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import creator.loki.feature.economy.service.game.SlotService;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class SlotServiceTest { class SlotServiceTest {

View file

@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.*;
import java.util.List; import java.util.List;
import creator.loki.feature.entertainment.commands.random.ChoiceOptionParser; import creator.loki.feature.utility.ChoiceOptionParser;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;

View file

@ -17,6 +17,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor; import org.mockito.Captor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import creator.loki.feature.level.service.cooldown.MessageCooldownService;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class LevelServiceTest { class LevelServiceTest {

View file

@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import creator.loki.feature.level.service.cooldown.MessageCooldownService;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class MessageCooldownServiceTest { class MessageCooldownServiceTest {

View file

@ -14,6 +14,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import creator.loki.feature.moderation.repository.sql.SqlActiveMuteRepository;
class ActiveMuteRepositoryTest { class ActiveMuteRepositoryTest {
private Database database; private Database database;

View file

@ -14,6 +14,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import creator.loki.feature.moderation.repository.sql.SqlWarningRepository;
class WarningRepositoryTest { class WarningRepositoryTest {
private Database database; private Database database;

View file

@ -6,6 +6,7 @@ import static org.mockito.Mockito.*;
import creator.loki.feature.music.repository.QueueRepository; import creator.loki.feature.music.repository.QueueRepository;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import creator.loki.feature.radio.service.RadioManager;
class RadioManagerTest { class RadioManagerTest {

View file

@ -0,0 +1,144 @@
package creator.loki.feature.entertainment.commands;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import creator.loki.feature.utility.ChoiceOptionParser;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import creator.loki.feature.utility.ChoiceOptionParser;
class ChoiceOptionParserTest {
@Test
void tailAfterCommandPipe() {
assertEquals(
"a | b | c", ChoiceOptionParser.tailAfterCommand("!choose a | b | c", "!", "choose"));
}
@Test
void tailAfterCommandOr() {
assertEquals("a или b", ChoiceOptionParser.tailAfterCommand("!выбери a или b", "!", "выбери"));
}
@Test
void tailAfterCommandComma() {
assertEquals(
"apple,banana,cherry",
ChoiceOptionParser.tailAfterCommand("!pick apple,banana,cherry", "!", "pick"));
}
@Test
void tailAfterCommandSlash() {
assertEquals("a/b/c", ChoiceOptionParser.tailAfterCommand("!choice a/b/c", "!", "choice"));
}
@Test
void tailAfterCommandSpaces() {
assertEquals("a b c", ChoiceOptionParser.tailAfterCommand("!pick a b c", "!", "pick"));
}
void tailAfterCommand(String raw, String prefix, String command, String expected) {
assertEquals(expected, ChoiceOptionParser.tailAfterCommand(raw, prefix, command));
}
@Test
void tailAfterCommandReturnsEmptyWhenNoPrefix() {
assertTrue(ChoiceOptionParser.tailAfterCommand("hello", "!", "choose").isEmpty());
}
@Test
void tailAfterCommandReturnsEmptyWhenNoArg() {
assertTrue(ChoiceOptionParser.tailAfterCommand("!choose", "!", "choose").isEmpty());
}
@Test
void tailAfterCommandReturnsEmptyWhenWrongCommand() {
assertTrue(ChoiceOptionParser.tailAfterCommand("!help", "!", "choose").isEmpty());
}
@Test
void tailAfterCommandCaseInsensitive() {
assertEquals("a b", ChoiceOptionParser.tailAfterCommand("!ChOoSe a b", "!", "choose"));
}
@Test
void parseOptionsSplitsByPipe() {
List<String> result = ChoiceOptionParser.parseOptions("a | b | c");
assertEquals(List.of("a", "b", "c"), result);
}
@Test
void parseOptionsSplitsByComma() {
List<String> result = ChoiceOptionParser.parseOptions("apple, banana, cherry");
assertEquals(List.of("apple", "banana", "cherry"), result);
}
@Test
void parseOptionsSplitsByOr() {
List<String> result = ChoiceOptionParser.parseOptions("кофе или чай");
assertEquals(List.of("кофе", "чай"), result);
}
@Test
void parseOptionsSplitsBySlash() {
List<String> result = ChoiceOptionParser.parseOptions("да/нет/может быть");
assertEquals(List.of("да", "нет", "может быть"), result);
}
@Test
void parseOptionsSplitsBySpace() {
List<String> result = ChoiceOptionParser.parseOptions("a b c d");
assertEquals(List.of("a", "b", "c", "d"), result);
}
@Test
void parseOptionsDeduplicatesIgnoreCase() {
List<String> result = ChoiceOptionParser.parseOptions("A | a | B | b");
assertEquals(List.of("A", "B"), result);
}
@Test
void parseOptionsRemovesEmptyParts() {
List<String> result = ChoiceOptionParser.parseOptions("a || b");
assertEquals(List.of("a", "b"), result);
}
@Test
void parseOptionsTrimsWhitespace() {
List<String> result = ChoiceOptionParser.parseOptions(" a , b , c ");
assertEquals(List.of("a", "b", "c"), result);
}
@Test
void parseOptionsPriorityOrder() {
// pipe has highest priority
List<String> result = ChoiceOptionParser.parseOptions("a,b|c,d");
assertEquals(List.of("a,b", "c,d"), result);
}
@ValueSource(strings = {"", " ", "a"})
@ParameterizedTest
void parseOptionsEdgeCases(String input) {
List<String> result = ChoiceOptionParser.parseOptions(input);
if (input.isBlank()) {
assertTrue(result.isEmpty());
} else {
assertEquals(List.of("a"), result);
}
}
@Test
void parseOptionsPipeWithTrailingEmpty() {
List<String> result = ChoiceOptionParser.parseOptions("a|");
assertEquals(List.of("a"), result);
}
@Test
void parseOptionsMultipleCommas() {
List<String> result = ChoiceOptionParser.parseOptions("a,,b,,c");
assertEquals(List.of("a", "b", "c"), result);
}
}