fix(rating): ratingoverview uses deterministic values matching rating commands

- RatingService.getRatingValue(type, userId) centralizes per-type salts/special cases
  (pp 6969/0-30, iq 123/0-140, mustache 888/0-100 + id overrides, gay 456, fat 999, boykisser 777)
- Pp/Iq/Mustache/Fat/Gay/Boykisser commands now delegate to it
- RatingOverviewCommand no longer reads random generateratings DB rows; iterates
  members (no bots/super-admin) and computes min/max like the real commands do
- birb: some-random-api.com is dead -> api.alexflipnote.dev/birb
This commit is contained in:
loki5512344 2026-08-19 14:57:18 +02:00
parent 405590f5da
commit 566cadc86f
Signed by: boba
GPG key ID: 253067914055423B
11 changed files with 79 additions and 43 deletions

View file

@ -105,7 +105,7 @@ public class AnimalApi {
"https://shibe.online/api/shibes?count=1&urls=true&httpsUrls=true", "https://shibe.online/api/shibes?count=1&urls=true&httpsUrls=true",
firstArrayString()), firstArrayString()),
new Endpoint("https://dog.ceo/api/breed/shiba/images/random", objectKey("message")))), new Endpoint("https://dog.ceo/api/breed/shiba/images/random", objectKey("message")))),
BIRB("🐦", List.of(new Endpoint("https://some-random-api.com/img/birb", objectKey("link")))), BIRB("🐦", List.of(new Endpoint("https://api.alexflipnote.dev/birb", objectKey("file")))),
NEKO("😺", List.of(new Endpoint(NEKOS_BASE + "neko", objectKey("url")))), NEKO("😺", List.of(new Endpoint(NEKOS_BASE + "neko", objectKey("url")))),
WAIFU( WAIFU(
"🌸", "🌸",

View file

@ -27,6 +27,29 @@ public class RatingService {
return new Random(seed).nextInt(max - min + 1) + min; return new Random(seed).nextInt(max - min + 1) + min;
} }
/** Детерминированное значение рейтинга, совпадающее с командами (!pp, !iq, !mustache и т.д.). */
public int getRatingValue(final String ratingType, final String userId) {
return switch (ratingType) {
case "gay" -> getDeterministicValue(userId, 456, 0, 100);
case "iq" ->
userId.equals("1414975197269987471") ? 1000 : getDeterministicValue(userId, 123, 0, 140);
case "pp" ->
userId.equals("1414975197269987471") ? 100 : getDeterministicValue(userId, 6969, 0, 30);
case "mustache" -> {
if (userId.equals("1414975197269987471")) {
yield 0;
}
if (userId.equals("678283217094180915")) {
yield 1000;
}
yield getDeterministicValue(userId, 888, 0, 100);
}
case "fat" -> getDeterministicValue(userId, 999, 0, 100);
case "boykisser" -> getDeterministicValue(userId, 777, 0, 100);
default -> throw new IllegalArgumentException("Неизвестный тип рейтинга: " + ratingType);
};
}
public Member resolveTarget(final CommandContext context) { public Member resolveTarget(final CommandContext context) {
List<Member> mentioned = context.getMessage().getMentions().getMembers(); List<Member> mentioned = context.getMessage().getMentions().getMembers();
return mentioned.isEmpty() ? context.getAuthor() : mentioned.get(0); return mentioned.isEmpty() ? context.getAuthor() : mentioned.get(0);

View file

@ -1,15 +1,15 @@
package creator.loki.feature.rating.commands.board; package creator.loki.feature.rating.commands.board;
import java.util.HashSet; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Map;
import creator.loki.command.Command; import creator.loki.command.Command;
import creator.loki.command.CommandCategories; import creator.loki.command.CommandCategories;
import creator.loki.command.CommandContext; import creator.loki.command.CommandContext;
import creator.loki.config.BotConfig; import creator.loki.config.BotConfig;
import creator.loki.embed.EmbedFactory; import creator.loki.embed.EmbedFactory;
import creator.loki.feature.rating.repository.RatingsRepository; import creator.loki.feature.entertainment.service.RatingService;
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;
@ -17,13 +17,13 @@ import net.dv8tion.jda.api.entities.Member;
public class RatingOverviewCommand implements Command { public class RatingOverviewCommand implements Command {
private static final List<String> TYPES = private static final List<String> TYPES =
List.of("ship", "gay", "iq", "pp", "mustache", "fat", "boykisser"); List.of("gay", "iq", "pp", "mustache", "fat", "boykisser");
private final RatingsRepository ratingsRepository; private final RatingService ratingService;
private final BotConfig config; private final BotConfig config;
public RatingOverviewCommand(RatingsRepository ratingsRepository, BotConfig config) { public RatingOverviewCommand(RatingService ratingService, BotConfig config) {
this.ratingsRepository = ratingsRepository; this.ratingService = ratingService;
this.config = config; this.config = config;
} }
@ -62,20 +62,24 @@ public class RatingOverviewCommand implements Command {
return; return;
} }
Set<String> excluded = new HashSet<>(); Map<String, RatingEntry> maxByType = new HashMap<>();
excluded.add(config.getSuperAdminId()); Map<String, RatingEntry> minByType = new HashMap<>();
for (Member member : guild.getMembers()) { for (Member member : guild.getMembers()) {
if (member.getUser().isBot()) { if (member.getUser().isBot() || config.getSuperAdminId().equals(member.getUser().getId())) {
excluded.add(member.getUser().getId()); continue;
}
String userId = member.getUser().getId();
for (String type : TYPES) {
int value = ratingService.getRatingValue(type, userId);
updateExtreme(maxByType, type, userId, value, true);
updateExtreme(minByType, type, userId, value, false);
} }
} }
StringBuilder description = new StringBuilder(); StringBuilder description = new StringBuilder();
for (String type : TYPES) { for (String type : TYPES) {
RatingsRepository.RatingEntry max = RatingEntry max = maxByType.get(type);
ratingsRepository.getExtremeRating(guild.getId(), type, excluded, true); RatingEntry min = minByType.get(type);
RatingsRepository.RatingEntry min =
ratingsRepository.getExtremeRating(guild.getId(), type, excluded, false);
if (max == null || min == null) { if (max == null || min == null) {
continue; continue;
} }
@ -83,21 +87,18 @@ public class RatingOverviewCommand implements Command {
.append("**") .append("**")
.append(type) .append(type)
.append("**\n🏆 <@") .append("**\n🏆 <@")
.append(max.userId()) .append(max.userId)
.append("> — **") .append("> — **")
.append(max.value()) .append(max.value)
.append("**\n📉 <@") .append("**\n📉 <@")
.append(min.userId()) .append(min.userId)
.append("> — **") .append("> — **")
.append(min.value()) .append(min.value)
.append("**\n\n"); .append("**\n\n");
} }
if (description.isEmpty()) { if (description.isEmpty()) {
context context.getChannel().sendMessage("❌ На сервере нет участников для подсчёта.").queue();
.getChannel()
.sendMessage("❌ Пока никто не имеет рейтингов. Сначала `!generateratings`.")
.queue();
return; return;
} }
@ -111,4 +112,20 @@ public class RatingOverviewCommand implements Command {
.build()) .build())
.queue(); .queue();
} }
private void updateExtreme(
final Map<String, RatingEntry> byType,
final String type,
final String userId,
final int value,
final boolean highest) {
RatingEntry current = byType.get(type);
if (current == null
|| (highest && value > current.value)
|| (!highest && value < current.value)) {
byType.put(type, new RatingEntry(userId, value));
}
}
private record RatingEntry(String userId, int value) {}
} }

View file

@ -50,7 +50,7 @@ public class FatCommand implements Command {
return; return;
} }
int fatLevel = ratingService.getDeterministicValue(target.getId(), 999, 0, 100); int fatLevel = ratingService.getRatingValue("fat", target.getId());
StringBuilder bar = new StringBuilder(); StringBuilder bar = new StringBuilder();
int filled = fatLevel / 10; int filled = fatLevel / 10;
for (int i = 0; i < filled; i++) { for (int i = 0; i < filled; i++) {

View file

@ -51,12 +51,10 @@ public class MustacheCommand implements Command {
} }
int mustacheLevel; int mustacheLevel;
if (target.getUser().isBot() || target.getId().equals("1414975197269987471")) { if (target.getUser().isBot()) {
mustacheLevel = 0; mustacheLevel = 0;
} else if (target.getId().equals("678283217094180915")) {
mustacheLevel = 1000;
} else { } else {
mustacheLevel = ratingService.getDeterministicValue(target.getId(), 888, 0, 100); mustacheLevel = ratingService.getRatingValue("mustache", target.getId());
} }
StringBuilder bar = new StringBuilder(); StringBuilder bar = new StringBuilder();

View file

@ -55,12 +55,7 @@ public class PpCommand implements Command {
return; return;
} }
int size; int size = ratingService.getRatingValue("pp", target.getId());
if (target.getId().equals("1414975197269987471")) {
size = 100;
} else {
size = ratingService.getDeterministicValue(target.getId(), 6969, 0, 30);
}
StringBuilder ppVisual = new StringBuilder("8"); StringBuilder ppVisual = new StringBuilder("8");
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {

View file

@ -55,7 +55,7 @@ public class BoykisserCommand implements Command {
return; return;
} }
int boykisserLevel = ratingService.getDeterministicValue(target.getId(), 777, 0, 100); int boykisserLevel = ratingService.getRatingValue("boykisser", target.getId());
StringBuilder bar = new StringBuilder(); StringBuilder bar = new StringBuilder();
int filled = boykisserLevel / 10; int filled = boykisserLevel / 10;
for (int i = 0; i < filled; i++) { for (int i = 0; i < filled; i++) {

View file

@ -55,7 +55,7 @@ public class GayCommand implements Command {
return; return;
} }
int gayLevel = ratingService.getDeterministicValue(target.getId(), 456, 0, 100); int gayLevel = ratingService.getRatingValue("gay", target.getId());
String[] rainbow = {"🟥", "🟧", "🟨", "🟩", "🟦", "🟪"}; String[] rainbow = {"🟥", "🟧", "🟨", "🟩", "🟦", "🟪"};
StringBuilder bar = new StringBuilder(); StringBuilder bar = new StringBuilder();
int filled = Math.min(gayLevel / 17 + 1, 6); int filled = Math.min(gayLevel / 17 + 1, 6);

View file

@ -53,10 +53,8 @@ public class IqCommand implements Command {
int iq; int iq;
if (target.getUser().isBot()) { if (target.getUser().isBot()) {
iq = 9000; iq = 9000;
} else if (target.getId().equals("1414975197269987471")) {
iq = 1000;
} else { } else {
iq = ratingService.getDeterministicValue(target.getId(), 123, 0, 140); iq = ratingService.getRatingValue("iq", target.getId());
} }
RatingLevel level = RatingConfig.findLevel(iq, "iq"); RatingLevel level = RatingConfig.findLevel(iq, "iq");

View file

@ -10,6 +10,7 @@ import creator.loki.feature.config.repository.ConfigRepository;
import creator.loki.feature.counter.repository.CounterStatsRepository; import creator.loki.feature.counter.repository.CounterStatsRepository;
import creator.loki.feature.counter.service.CounterManager; import creator.loki.feature.counter.service.CounterManager;
import creator.loki.feature.counter.service.CounterSetupPrefixCommand; import creator.loki.feature.counter.service.CounterSetupPrefixCommand;
import creator.loki.feature.entertainment.service.RatingService;
import creator.loki.feature.help.commands.HelpCommand; import creator.loki.feature.help.commands.HelpCommand;
import creator.loki.feature.info.commands.AvatarCommand; import creator.loki.feature.info.commands.AvatarCommand;
import creator.loki.feature.info.commands.InfoCommand; import creator.loki.feature.info.commands.InfoCommand;
@ -40,6 +41,7 @@ public class UtilityCommandRegistrar implements CommandRegistrar {
private final ReputationRepository reputationRepository; private final ReputationRepository reputationRepository;
private final CooldownManager cooldownManager; private final CooldownManager cooldownManager;
private final ServerStatsService serverStatsService; private final ServerStatsService serverStatsService;
private final RatingService ratingService;
@Inject @Inject
public UtilityCommandRegistrar( public UtilityCommandRegistrar(
@ -52,7 +54,8 @@ public class UtilityCommandRegistrar implements CommandRegistrar {
RatingsRepository ratingsRepository, RatingsRepository ratingsRepository,
ReputationRepository reputationRepository, ReputationRepository reputationRepository,
CooldownManager cooldownManager, CooldownManager cooldownManager,
final CounterStatsRepository counterStatsRepository) { final CounterStatsRepository counterStatsRepository,
RatingService ratingService) {
this.commandRegistry = commandRegistry; this.commandRegistry = commandRegistry;
this.config = config; this.config = config;
this.metrics = metrics; this.metrics = metrics;
@ -63,6 +66,7 @@ public class UtilityCommandRegistrar implements CommandRegistrar {
this.reputationRepository = reputationRepository; this.reputationRepository = reputationRepository;
this.cooldownManager = cooldownManager; this.cooldownManager = cooldownManager;
this.serverStatsService = new ServerStatsService(counterStatsRepository); this.serverStatsService = new ServerStatsService(counterStatsRepository);
this.ratingService = ratingService;
} }
@Override @Override
@ -82,7 +86,7 @@ public class UtilityCommandRegistrar implements CommandRegistrar {
registry.register(new GenerateRatingsCommand(ratingsRepository, config)); registry.register(new GenerateRatingsCommand(ratingsRepository, config));
registry.register(new RatingBoardCommand(RatingBoardCommand.Flavor.TOP, ratingsRepository)); registry.register(new RatingBoardCommand(RatingBoardCommand.Flavor.TOP, ratingsRepository));
registry.register(new RatingBoardCommand(RatingBoardCommand.Flavor.BOTTOM, ratingsRepository)); registry.register(new RatingBoardCommand(RatingBoardCommand.Flavor.BOTTOM, ratingsRepository));
registry.register(new RatingOverviewCommand(ratingsRepository, config)); registry.register(new RatingOverviewCommand(ratingService, config));
registry.register(new RepCommand(reputationRepository, cooldownManager)); registry.register(new RepCommand(reputationRepository, cooldownManager));
registry.register(new MinusRepCommand(reputationRepository, cooldownManager)); registry.register(new MinusRepCommand(reputationRepository, cooldownManager));

View file

@ -130,7 +130,8 @@ class SmokeCommandRegistrationTest {
mock(RatingsRepository.class), mock(RatingsRepository.class),
mock(ReputationRepository.class), mock(ReputationRepository.class),
mock(CooldownManager.class), mock(CooldownManager.class),
mock(CounterStatsRepository.class)); mock(CounterStatsRepository.class),
mock(RatingService.class));
utilityRegistrar.register(registry); utilityRegistrar.register(registry);
var musicRegistrar = var musicRegistrar =