commit 94b35942862a04d01c8e7176ccbda017b9f3d5da
parent a56b6f2d64000c6c60c53fa0ff77ebcaf6a1e9ff
Author: Wim Dupont <wim@wimdupont.com>
Date: Sat, 1 Nov 2025 07:02:48 +0100
pvm autoadd
Diffstat:
9 files changed, 239 insertions(+), 3 deletions(-)
diff --git a/pom.xml b/pom.xml
@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
- <version>3.5.0</version>
+ <version>3.5.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wimdupont</groupId>
@@ -16,7 +16,7 @@
<properties>
<java.version>21</java.version>
- <dv8ation.jda.version>5.5.1</dv8ation.jda.version>
+ <dv8ation.jda.version>5.6.1</dv8ation.jda.version>
</properties>
<dependencies>
diff --git a/src/main/java/com/wimdupont/sxcybot/client/WiseOldManClient.java b/src/main/java/com/wimdupont/sxcybot/client/WiseOldManClient.java
@@ -0,0 +1,37 @@
+package com.wimdupont.sxcybot.client;
+
+import com.wimdupont.sxcybot.exceptions.EntityNotFoundException;
+import com.wimdupont.sxcybot.model.wiseoldman.GroupDto;
+import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import java.time.Duration;
+
+@Component
+public class WiseOldManClient {
+
+ private static final String URL = "https://api.wiseoldman.net/v2/groups/%s";
+ private static final String GROUP = "6516"; //"Best guild of Gielinor" - Wise Old Man
+ private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
+
+ public GroupDto getGroup(MessageChannel channel) throws EntityNotFoundException {
+ RestTemplate restTemplate = new RestTemplateBuilder()
+ .errorHandler(new ClientErrorHandler(channel))
+ .readTimeout(Duration.ofSeconds(30))
+ .build();
+ try {
+ GroupDto result = restTemplate.getForObject(String.format(URL, GROUP), GroupDto.class);
+ if (result == null) {
+ throw new Exception("Wise Old Man Groupo is null");
+ }
+ return result;
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage(), e);
+ throw new EntityNotFoundException(String.format("No WoM group available for %s.", GROUP));
+ }
+ }
+}
diff --git a/src/main/java/com/wimdupont/sxcybot/enums/Command.java b/src/main/java/com/wimdupont/sxcybot/enums/Command.java
@@ -31,7 +31,8 @@ public enum Command {
ROLE("Add/remove a role to/from a member."),
EDITROLE("Add/Delete record of roles."),
EDITPVM("Add/Edit/Delete record of the PvM Role competition"),
- EDITBOSS("Update multiplier for a OSRS Boss of the PvM Role competition");
+ EDITBOSS("Update multiplier for a OSRS Boss of the PvM Role competition"),
+ UPDATEPVM("Attempts to add Pvm Role Competitors using Wise Old Man data");
public final String description;
diff --git a/src/main/java/com/wimdupont/sxcybot/listeners/AdminCommandListener.java b/src/main/java/com/wimdupont/sxcybot/listeners/AdminCommandListener.java
@@ -9,6 +9,7 @@ import com.wimdupont.sxcybot.listeners.admin.EditPvmListener;
import com.wimdupont.sxcybot.listeners.admin.EditRoleListener;
import com.wimdupont.sxcybot.listeners.admin.EditRuleListener;
import com.wimdupont.sxcybot.listeners.admin.RoleAssignListener;
+import com.wimdupont.sxcybot.listeners.admin.pvmrole.AutoUpdatePvmListener;
import com.wimdupont.sxcybot.services.AccessService;
import jakarta.annotation.Nonnull;
import net.dv8tion.jda.api.entities.Role;
@@ -34,6 +35,7 @@ public class AdminCommandListener extends ListenerAdapter {
private final BanlistListener banlistListener;
private final EditRoleListener editRoleListener;
private final EditPvmListener editPvmListener;
+ private final AutoUpdatePvmListener autoUpdatePvmListener;
private final BossUpdateMultiplierListener bossUpdateMultiplierListener;
private final AccessService accessService;
@@ -43,6 +45,7 @@ public class AdminCommandListener extends ListenerAdapter {
BanlistListener banlistListener,
EditRoleListener editRoleListener,
EditPvmListener editPvmListener,
+ AutoUpdatePvmListener autoUpdatePvmListener,
BossUpdateMultiplierListener bossUpdateMultiplierListener,
AccessService accessService) {
this.editRuleListener = editRuleListener;
@@ -51,6 +54,7 @@ public class AdminCommandListener extends ListenerAdapter {
this.banlistListener = banlistListener;
this.editRoleListener = editRoleListener;
this.editPvmListener = editPvmListener;
+ this.autoUpdatePvmListener = autoUpdatePvmListener;
this.bossUpdateMultiplierListener = bossUpdateMultiplierListener;
this.accessService = accessService;
}
@@ -88,6 +92,10 @@ public class AdminCommandListener extends ListenerAdapter {
accessService.isAllowed(roleStream, event, GENERAL);
bossUpdateMultiplierListener.process(event);
}
+ case UPDATEPVM -> {
+ accessService.isAllowed(roleStream, event, ADMIN_ROLE);
+ autoUpdatePvmListener.process(event);
+ }
}
} catch (InsufficientPrivilegesException e) {
event.getChannel().sendMessage(e.getMessage()).queue();
diff --git a/src/main/java/com/wimdupont/sxcybot/listeners/admin/pvmrole/AutoUpdatePvmListener.java b/src/main/java/com/wimdupont/sxcybot/listeners/admin/pvmrole/AutoUpdatePvmListener.java
@@ -0,0 +1,30 @@
+package com.wimdupont.sxcybot.listeners.admin.pvmrole;
+
+import com.wimdupont.sxcybot.exceptions.EntityNotFoundException;
+import com.wimdupont.sxcybot.listeners.Listener;
+import com.wimdupont.sxcybot.services.guild.pvmrole.PvmRoleUserAutoAddService;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AutoUpdatePvmListener implements Listener {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ private final PvmRoleUserAutoAddService pvmRoleUserAutoAddService;
+
+ public AutoUpdatePvmListener(PvmRoleUserAutoAddService pvmRoleUserAutoAddService) {
+ this.pvmRoleUserAutoAddService = pvmRoleUserAutoAddService;
+ }
+
+ @Override
+ public void process(MessageReceivedEvent event) {
+ try {
+ pvmRoleUserAutoAddService.autoAdd(event.getChannel().asTextChannel());
+ } catch (EntityNotFoundException e) {
+ logger.error(e.getMessage(), e);
+ event.getMessage().getChannel().sendMessage(String.format("Error during Auto adding: %s.", e.getMessage())).queue();
+ }
+ }
+}
diff --git a/src/main/java/com/wimdupont/sxcybot/model/wiseoldman/GroupDto.java b/src/main/java/com/wimdupont/sxcybot/model/wiseoldman/GroupDto.java
@@ -0,0 +1,13 @@
+package com.wimdupont.sxcybot.model.wiseoldman;
+
+import java.util.List;
+
+public record GroupDto(
+ int id,
+ String name,
+ String description,
+ int homeworld,
+ List<MembershipDto> memberships
+) {
+
+}
diff --git a/src/main/java/com/wimdupont/sxcybot/model/wiseoldman/MembershipDto.java b/src/main/java/com/wimdupont/sxcybot/model/wiseoldman/MembershipDto.java
@@ -0,0 +1,13 @@
+package com.wimdupont.sxcybot.model.wiseoldman;
+
+import java.time.LocalDateTime;
+
+public record MembershipDto(
+ int playerId,
+ int groupId,
+ String role,
+ LocalDateTime createdAt,
+ LocalDateTime updatedAt,
+ PlayerDto player
+) {
+}
diff --git a/src/main/java/com/wimdupont/sxcybot/model/wiseoldman/PlayerDto.java b/src/main/java/com/wimdupont/sxcybot/model/wiseoldman/PlayerDto.java
@@ -0,0 +1,17 @@
+package com.wimdupont.sxcybot.model.wiseoldman;
+
+import java.time.LocalDateTime;
+
+public record PlayerDto(
+ int id,
+ String username,
+ String displayName,
+ String type,
+ String build,
+ String status,
+ LocalDateTime registeredAt,
+ LocalDateTime updatedAt,
+ LocalDateTime lastChangedAt,
+ LocalDateTime lastImportedAt
+) {
+}
diff --git a/src/main/java/com/wimdupont/sxcybot/services/guild/pvmrole/PvmRoleUserAutoAddService.java b/src/main/java/com/wimdupont/sxcybot/services/guild/pvmrole/PvmRoleUserAutoAddService.java
@@ -0,0 +1,117 @@
+package com.wimdupont.sxcybot.services.guild.pvmrole;
+
+import com.wimdupont.sxcybot.client.WiseOldManClient;
+import com.wimdupont.sxcybot.exceptions.EntityNotFoundException;
+import com.wimdupont.sxcybot.model.wiseoldman.MembershipDto;
+import com.wimdupont.sxcybot.repository.guild.pvmrole.dao.PvmRoleUser;
+import com.wimdupont.sxcybot.services.guild.ChannelDetailService;
+import com.wimdupont.sxcybot.util.JdaUtil;
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.entities.MessageEmbed.Field;
+import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map.Entry;
+
+@Service
+public class PvmRoleUserAutoAddService {
+
+ private final WiseOldManClient wiseOldManClient;
+ private final ChannelDetailService channelDetailService;
+ private final PvmRoleUserService pvmRoleUserService;
+ private final PvmRoleSnapshotComparatorService pvmRoleSnapshotComparatorService;
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ public PvmRoleUserAutoAddService(WiseOldManClient wiseOldManClient,
+ ChannelDetailService channelDetailService,
+ PvmRoleUserService pvmRoleUserService,
+ PvmRoleSnapshotComparatorService pvmRoleSnapshotComparatorService) {
+ this.wiseOldManClient = wiseOldManClient;
+ this.channelDetailService = channelDetailService;
+ this.pvmRoleUserService = pvmRoleUserService;
+ this.pvmRoleSnapshotComparatorService = pvmRoleSnapshotComparatorService;
+ }
+
+ public void autoAdd(TextChannel channel) throws EntityNotFoundException {
+ var group = wiseOldManClient.getGroup(channel);
+ var obscureList = new HashMap<String, List<String>>();
+ var unknownList = new ArrayList<String>();
+ channelDetailService.getJda().getGuilds().forEach(guild -> {
+ for (MembershipDto membership : group.memberships()) {
+ try {
+ pvmRoleUserService.findByRsn(membership.player().username());
+ } catch (EntityNotFoundException e) {
+ var discordUsers = guild.getMembersByNickname(membership.player().username(), true);
+ if (discordUsers.size() > 1) {
+ obscureList.put(membership.player().username(),
+ discordUsers.stream()
+ .map(f -> f.getUser().getEffectiveName())
+ .toList());
+ logger.warn("Multiple users found for rsn {}", membership.player().username());
+ }
+ if (!discordUsers.isEmpty()) {
+ var pvmRoleUser = pvmRoleUserService.save(PvmRoleUser.Builder.newBuilder()
+ .discordId(discordUsers.getFirst().getId())
+ .rsn(membership.player().username())
+ .build());
+ try {
+ pvmRoleSnapshotComparatorService.takeSnapshot(channel, pvmRoleUser, true, true);
+ } catch (EntityNotFoundException ex) {
+ logger.error("No snapshot taken for {} : {}", pvmRoleUser, ex.getMessage());
+ channel.sendMessage(String.format("Error whilst taking snapshot for %s.", pvmRoleUser.getRsn())).queue();
+ }
+ } else {
+ unknownList.add(membership.player().username());
+ logger.warn("No users found for rsn {}", membership.player().username());
+ }
+ }
+ }
+ if (!obscureList.isEmpty()) {
+ postObscures(channel, obscureList);
+ }
+ if (!unknownList.isEmpty()) {
+ postUnknowns(channel, unknownList);
+ }
+ });
+ }
+
+ private void postObscures(TextChannel channel, HashMap<String, List<String>> obscureList) {
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setColor(Color.red);
+ embedBuilder.setTitle("Obscure RSN's");
+ int i = 1;
+ for (Entry<String, List<String>> obscureRsn : obscureList.entrySet()) {
+ embedBuilder.addField(new Field(obscureRsn.getKey(), String.join(", ", obscureRsn.getValue()), true));
+ if (JdaUtil.requiresBuild(embedBuilder, obscureList.size(), i)) {
+ channel.sendMessageEmbeds(embedBuilder.build()).queue();
+ embedBuilder.clearFields();
+ }
+ i++;
+ }
+ if (!embedBuilder.getFields().isEmpty())
+ channel.sendMessageEmbeds(embedBuilder.build()).queue();
+ }
+
+ private void postUnknowns(TextChannel channel, List<String> unknownList) {
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setColor(Color.red);
+ embedBuilder.setTitle("Unknown RSN's");
+ int i = 1;
+ for (String unknownRsn : unknownList) {
+ embedBuilder.addField(new Field(unknownRsn, "", true));
+ if (JdaUtil.requiresBuild(embedBuilder, unknownList.size(), i)) {
+ channel.sendMessageEmbeds(embedBuilder.build()).queue();
+ embedBuilder.clearFields();
+ }
+ i++;
+ }
+ if (!embedBuilder.getFields().isEmpty())
+ channel.sendMessageEmbeds(embedBuilder.build()).queue();
+ }
+}