From c23ced479ff2db81100824d9deaf2bed372da37b Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 29 Mar 2022 10:45:41 +0100 Subject: [PATCH 01/26] feat(service): Implemented barebones XP system (#87) --- .../voidtech/gerald/entities/Experience.java | 89 +++++++++++++++++++ .../voidtech/gerald/entities/LevelUpRole.java | 38 ++++++++ .../entities/ServerExperienceConfig.java | 34 +++++++ .../routines/utils/ExperienceRoutine.java | 49 ++++++++++ .../gerald/service/AutoroleService.java | 8 +- .../gerald/service/ExperienceService.java | 78 ++++++++++++++++ 6 files changed, 292 insertions(+), 4 deletions(-) create mode 100644 src/main/java/de/voidtech/gerald/entities/Experience.java create mode 100644 src/main/java/de/voidtech/gerald/entities/LevelUpRole.java create mode 100644 src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java create mode 100644 src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java create mode 100644 src/main/java/de/voidtech/gerald/service/ExperienceService.java diff --git a/src/main/java/de/voidtech/gerald/entities/Experience.java b/src/main/java/de/voidtech/gerald/entities/Experience.java new file mode 100644 index 00000000..a96f9867 --- /dev/null +++ b/src/main/java/de/voidtech/gerald/entities/Experience.java @@ -0,0 +1,89 @@ +package main.java.de.voidtech.gerald.entities; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "experience") +public class Experience { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column + private String userID; + + @Column + private long serverID; + + @Column + private long messageCount; + + @Column + private long experienceGainedToNextLevel; + + @Column + private long level; + + @Column + private long lastMessageTime; + + @Deprecated + //ONLY FOR HIBERNATE, DO NOT USE + Experience() { + } + + public Experience(String userID, long serverID) { + this.userID = userID; + this.serverID = serverID; + this.messageCount = 0; + this.level = 1; + this.lastMessageTime = 0; + this.experienceGainedToNextLevel = 0; + } + + public void setLevel(long level) { + this.level = level; + } + + public void setCurrentXP(long xp) { + this.experienceGainedToNextLevel = xp; + } + + public void setLastMessageTime(long lastMessageTime) { + this.lastMessageTime = lastMessageTime; + } + + public void incrementMessageCount() { + this.messageCount++; + } + + public long getLevel() { + return this.level; + } + + public long getCurrentExperience() { + return this.experienceGainedToNextLevel; + } + + public long getLastMessageTime() { + return this.lastMessageTime; + } + + public long getMessageCount() { + return this.messageCount; + } + + public long getServerID() { + return this.serverID; + } + + public String getUserID() { + return this.userID; + } +} diff --git a/src/main/java/de/voidtech/gerald/entities/LevelUpRole.java b/src/main/java/de/voidtech/gerald/entities/LevelUpRole.java new file mode 100644 index 00000000..35caa18a --- /dev/null +++ b/src/main/java/de/voidtech/gerald/entities/LevelUpRole.java @@ -0,0 +1,38 @@ +package main.java.de.voidtech.gerald.entities; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "leveluprole") +public class LevelUpRole { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column + private String roleID; + + @Column + private long serverID; + + @Column + private long level; + + @Deprecated + //ONLY FOR HIBERNATE, DO NOT USE + LevelUpRole() { + } + + public LevelUpRole(String roleID, long serverID, long level) { + this.roleID = roleID; + this.serverID = serverID; + this.level = level; + } + +} diff --git a/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java b/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java new file mode 100644 index 00000000..50c51c6a --- /dev/null +++ b/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java @@ -0,0 +1,34 @@ +package main.java.de.voidtech.gerald.entities; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "serverexperienceconfig") +public class ServerExperienceConfig { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column + private long serverID; + + @Column + private boolean levelUpMessagesEnabled; + + @Deprecated + //ONLY FOR HIBERNATE, DO NOT USE + ServerExperienceConfig() { + } + + public ServerExperienceConfig(long serverID) { + this.serverID = serverID; + this.levelUpMessagesEnabled = false; + } + +} \ No newline at end of file diff --git a/src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java b/src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java new file mode 100644 index 00000000..1d4c330e --- /dev/null +++ b/src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java @@ -0,0 +1,49 @@ +package main.java.de.voidtech.gerald.routines.utils; + +import org.springframework.beans.factory.annotation.Autowired; + +import main.java.de.voidtech.gerald.annotations.Routine; +import main.java.de.voidtech.gerald.routines.AbstractRoutine; +import main.java.de.voidtech.gerald.routines.RoutineCategory; +import main.java.de.voidtech.gerald.service.ExperienceService; +import net.dv8tion.jda.api.entities.ChannelType; +import net.dv8tion.jda.api.entities.Message; + +@Routine +public class ExperienceRoutine extends AbstractRoutine { + + @Autowired + private ExperienceService xpService; + + @Override + public void executeInternal(Message message) { + if (message.getChannel().getType().equals(ChannelType.PRIVATE)) return; + xpService.updateUserExperience(message.getAuthor().getId(), message.getGuild().getId(), message.getChannel().getId()); + } + + @Override + public String getName() { + return "r-xp"; + } + + @Override + public String getDescription() { + return "Allows Gerald to update user experience and levels"; + } + + @Override + public RoutineCategory getRoutineCategory() { + return RoutineCategory.UTILS; + } + + @Override + public boolean allowsBotResponses() { + return false; + } + + @Override + public boolean canBeDisabled() { + return true; + } + +} diff --git a/src/main/java/de/voidtech/gerald/service/AutoroleService.java b/src/main/java/de/voidtech/gerald/service/AutoroleService.java index 9ae5eda3..b4340cdc 100644 --- a/src/main/java/de/voidtech/gerald/service/AutoroleService.java +++ b/src/main/java/de/voidtech/gerald/service/AutoroleService.java @@ -26,8 +26,8 @@ public List getAutoroleConfigs(long serverID) { { return (List) session.createQuery("FROM AutoroleConfig WHERE serverID = :serverID") -.setParameter("serverID", serverID) -.list(); + .setParameter("serverID", serverID) + .list(); } } @@ -35,8 +35,8 @@ public AutoroleConfig getAutoroleConfigByRoleID(String roleID) { try(Session session = sessionFactory.openSession()) { return (AutoroleConfig) session.createQuery("FROM AutoroleConfig WHERE roleID = :roleID") -.setParameter("roleID", roleID) -.uniqueResult(); + .setParameter("roleID", roleID) + .uniqueResult(); } } diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java new file mode 100644 index 00000000..24d68ee2 --- /dev/null +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -0,0 +1,78 @@ +package main.java.de.voidtech.gerald.service; + +import java.time.Instant; +import java.util.Random; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import main.java.de.voidtech.gerald.entities.Experience; +import main.java.de.voidtech.gerald.entities.Server; + +@Service +public class ExperienceService { + + @Autowired + private SessionFactory sessionFactory; + + @Autowired + private ServerService serverService; + + private static final int EXPERIENCE_DELAY = 60; //Delay between incrementing XP in seconds + + private Experience getUserExperience(String userID, long serverID) { + try(Session session = sessionFactory.openSession()) + { + Experience xp = (Experience) session.createQuery("FROM Experience WHERE userID = :userID AND serverID = :serverID") + .setParameter("userID", userID) + .setParameter("serverID", serverID) + .uniqueResult(); + if (xp == null) xp = new Experience(userID, serverID); + + return xp; + } + } + + private void saveUserExperience(Experience userXP) { + try(Session session = sessionFactory.openSession()) + { + session.getTransaction().begin(); + session.saveOrUpdate(userXP); + session.getTransaction().commit(); + } + } + + private long xpToNextLevel(long currentLevel, long currentXP) { + long nextLevel = currentLevel + 1; + return 5 * (nextLevel ^ 2) + (50 * nextLevel) + 100 - currentXP; + } + + private int generateExperience() { + return new Random().nextInt(16); + } + + public void updateUserExperience(String userID, String guildID, String channelID) { + Server server = serverService.getServer(guildID); + Experience userXP = getUserExperience(userID, server.getId()); + userXP.incrementMessageCount(); + + if ((userXP.getLastMessageTime() + EXPERIENCE_DELAY) > Instant.now().getEpochSecond()) return; + + long currentExperience = userXP.getCurrentExperience() + generateExperience(); + long xpToNextLevel = xpToNextLevel(userXP.getLevel(), currentExperience); + + if (xpToNextLevel < 0) { + userXP.setLevel(userXP.getLevel() + 1); + userXP.setCurrentXP(-1 * xpToNextLevel); + } else { + userXP.setCurrentXP(currentExperience); + } + + userXP.setLastMessageTime(Instant.now().getEpochSecond()); + + saveUserExperience(userXP); + } + +} From 0d1cb167bd0dda32ab745c89f44417db85c3b2e4 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 29 Mar 2022 12:48:49 +0100 Subject: [PATCH 02/26] feat(service): Finished level up roles and experience system, commands are still needed and output formatting is not done (#87) --- .../voidtech/gerald/entities/LevelUpRole.java | 12 +++ .../entities/ServerExperienceConfig.java | 8 ++ .../routines/utils/ExperienceRoutine.java | 2 +- .../gerald/service/ExperienceService.java | 98 +++++++++++++++++-- 4 files changed, 111 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/entities/LevelUpRole.java b/src/main/java/de/voidtech/gerald/entities/LevelUpRole.java index 35caa18a..8b520b74 100644 --- a/src/main/java/de/voidtech/gerald/entities/LevelUpRole.java +++ b/src/main/java/de/voidtech/gerald/entities/LevelUpRole.java @@ -34,5 +34,17 @@ public LevelUpRole(String roleID, long serverID, long level) { this.serverID = serverID; this.level = level; } + + public String getRoleID() { + return this.roleID; + } + + public long getServerID() { + return this.serverID; + } + + public long getLevel() { + return this.level; + } } diff --git a/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java b/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java index 50c51c6a..9a445b54 100644 --- a/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java +++ b/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java @@ -31,4 +31,12 @@ public ServerExperienceConfig(long serverID) { this.levelUpMessagesEnabled = false; } + public boolean levelUpMessagesEnabled() { + return this.levelUpMessagesEnabled; + } + + public void setLevelUpMessagesEnabled(boolean enabled) { + this.levelUpMessagesEnabled = enabled; + } + } \ No newline at end of file diff --git a/src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java b/src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java index 1d4c330e..1f9da189 100644 --- a/src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java +++ b/src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java @@ -18,7 +18,7 @@ public class ExperienceRoutine extends AbstractRoutine { @Override public void executeInternal(Message message) { if (message.getChannel().getType().equals(ChannelType.PRIVATE)) return; - xpService.updateUserExperience(message.getAuthor().getId(), message.getGuild().getId(), message.getChannel().getId()); + xpService.updateUserExperience(message.getMember(), message.getGuild().getId(), message.getChannel().getId()); } @Override diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index 24d68ee2..f050d356 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -1,6 +1,7 @@ package main.java.de.voidtech.gerald.service; import java.time.Instant; +import java.util.List; import java.util.Random; import org.hibernate.Session; @@ -9,7 +10,11 @@ import org.springframework.stereotype.Service; import main.java.de.voidtech.gerald.entities.Experience; +import main.java.de.voidtech.gerald.entities.LevelUpRole; import main.java.de.voidtech.gerald.entities.Server; +import main.java.de.voidtech.gerald.entities.ServerExperienceConfig; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Role; @Service public class ExperienceService { @@ -20,7 +25,7 @@ public class ExperienceService { @Autowired private ServerService serverService; - private static final int EXPERIENCE_DELAY = 60; //Delay between incrementing XP in seconds + private static final int EXPERIENCE_DELAY = 0; //Delay between incrementing XP in seconds private Experience getUserExperience(String userID, long serverID) { try(Session session = sessionFactory.openSession()) @@ -29,12 +34,39 @@ private Experience getUserExperience(String userID, long serverID) { .setParameter("userID", userID) .setParameter("serverID", serverID) .uniqueResult(); + if (xp == null) xp = new Experience(userID, serverID); - return xp; } } + private ServerExperienceConfig getServerExperienceConfig(long serverID) { + try(Session session = sessionFactory.openSession()) + { + ServerExperienceConfig xpConf = (ServerExperienceConfig) session.createQuery("FROM ServerExperienceConfig WHERE serverID = :serverID") + .setParameter("serverID", serverID) + .uniqueResult(); + + if (xpConf == null) { + xpConf = new ServerExperienceConfig(serverID); + saveServerExperienceConfig(xpConf); + } + return xpConf; + } + } + + private List getRolesForLevel(long id, long level) { + try(Session session = sessionFactory.openSession()) + { + @SuppressWarnings("unchecked") + List roles = (List) session.createQuery("FROM LevelUpRole WHERE serverID = :serverID AND level <= :level") + .setParameter("serverID", id) + .setParameter("level", level) + .list(); + return roles; + } + } + private void saveUserExperience(Experience userXP) { try(Session session = sessionFactory.openSession()) { @@ -44,6 +76,28 @@ private void saveUserExperience(Experience userXP) { } } + private void saveServerExperienceConfig(ServerExperienceConfig config) { + try(Session session = sessionFactory.openSession()) + { + session.getTransaction().begin(); + session.saveOrUpdate(config); + session.getTransaction().commit(); + } + } + + + private void removeLevelUpRole(LevelUpRole role) { + try(Session session = sessionFactory.openSession()) + { + session.getTransaction().begin(); + session.createQuery("DELETE FROM LevelUpRole WHERE roleID = :roleID AND serverID = :serverID") + .setParameter("roleID", role.getRoleID()) + .setParameter("serverID", role.getServerID()) + .executeUpdate(); + session.getTransaction().commit(); + } + } + private long xpToNextLevel(long currentLevel, long currentXP) { long nextLevel = currentLevel + 1; return 5 * (nextLevel ^ 2) + (50 * nextLevel) + 100 - currentXP; @@ -53,12 +107,15 @@ private int generateExperience() { return new Random().nextInt(16); } - public void updateUserExperience(String userID, String guildID, String channelID) { + public void updateUserExperience(Member member, String guildID, String channelID) { Server server = serverService.getServer(guildID); - Experience userXP = getUserExperience(userID, server.getId()); + Experience userXP = getUserExperience(member.getId(), server.getId()); userXP.incrementMessageCount(); - if ((userXP.getLastMessageTime() + EXPERIENCE_DELAY) > Instant.now().getEpochSecond()) return; + if ((userXP.getLastMessageTime() + EXPERIENCE_DELAY) > Instant.now().getEpochSecond()) { + saveUserExperience(userXP); + return; + } long currentExperience = userXP.getCurrentExperience() + generateExperience(); long xpToNextLevel = xpToNextLevel(userXP.getLevel(), currentExperience); @@ -66,13 +123,38 @@ public void updateUserExperience(String userID, String guildID, String channelID if (xpToNextLevel < 0) { userXP.setLevel(userXP.getLevel() + 1); userXP.setCurrentXP(-1 * xpToNextLevel); - } else { - userXP.setCurrentXP(currentExperience); - } + performLevelUpActions(userXP, server, member, channelID); + } else userXP.setCurrentXP(currentExperience); userXP.setLastMessageTime(Instant.now().getEpochSecond()); saveUserExperience(userXP); } + private void performLevelUpActions(Experience userXP, Server server, Member member, String channelID) { + ServerExperienceConfig config = getServerExperienceConfig(server.getId()); + + List roles = getRolesForLevel(server.getId(), userXP.getLevel()); + if (roles.isEmpty()) return; + + List memberRoles = member.getRoles(); + + for (LevelUpRole role : roles) { + Role roleToBeGiven = member.getGuild().getRoleById(role.getRoleID()); + if (roleToBeGiven == null) removeLevelUpRole(role); + else { + if (!memberRoles.contains(roleToBeGiven)) { + member.getGuild().addRoleToMember(member, roleToBeGiven).complete(); + if (config.levelUpMessagesEnabled()) sendLevelUpMessage(role, member, channelID); + } + } + } + } + + private void sendLevelUpMessage(LevelUpRole role, Member member, String channelID) { + member.getGuild().getTextChannelById(channelID).sendMessage(member.getAsMention() + " reached level " + + role.getLevel() + " and was given the role " + + role.getRoleID()).queue(); + } + } From 0bd4c6907490130ed8c851e04cbe3a6c34243fae Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 29 Mar 2022 12:55:10 +0100 Subject: [PATCH 03/26] feat(service): Level up messages look a bit less scuffed now (#87) --- .../gerald/service/ExperienceService.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index f050d356..1b0b195f 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -13,7 +13,9 @@ import main.java.de.voidtech.gerald.entities.LevelUpRole; import main.java.de.voidtech.gerald.entities.Server; import main.java.de.voidtech.gerald.entities.ServerExperienceConfig; +import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.Role; @Service @@ -145,16 +147,20 @@ private void performLevelUpActions(Experience userXP, Server server, Member memb else { if (!memberRoles.contains(roleToBeGiven)) { member.getGuild().addRoleToMember(member, roleToBeGiven).complete(); - if (config.levelUpMessagesEnabled()) sendLevelUpMessage(role, member, channelID); + if (config.levelUpMessagesEnabled()) sendLevelUpMessage(role, member, roleToBeGiven, channelID); } } } } - private void sendLevelUpMessage(LevelUpRole role, Member member, String channelID) { - member.getGuild().getTextChannelById(channelID).sendMessage(member.getAsMention() + " reached level " - + role.getLevel() + " and was given the role " - + role.getRoleID()).queue(); + private void sendLevelUpMessage(LevelUpRole role, Member member, Role roleToBeGiven, String channelID) { + MessageEmbed levelUpEmbed = new EmbedBuilder() + .setColor(roleToBeGiven.getColor()) + .setTitle(member.getUser().getName() + " levelled up!") + .setDescription(member.getAsMention() + " reached level `" + role.getLevel() + + "` and received the role " + roleToBeGiven.getAsMention()) + .build(); + member.getGuild().getTextChannelById(channelID).sendMessageEmbeds(levelUpEmbed).queue(); } } From e4d1e1fcaf36c6ab50ac896e35e31a99cb80d85c Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 30 Mar 2022 14:35:38 +0100 Subject: [PATCH 04/26] refac(entity): Enabled XP messages by default (#87) --- .../de/voidtech/gerald/entities/ServerExperienceConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java b/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java index 9a445b54..86794a63 100644 --- a/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java +++ b/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java @@ -28,7 +28,7 @@ public class ServerExperienceConfig { public ServerExperienceConfig(long serverID) { this.serverID = serverID; - this.levelUpMessagesEnabled = false; + this.levelUpMessagesEnabled = true; } public boolean levelUpMessagesEnabled() { From 47d0e32876d007affd567737753684b4a0379039 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 30 Mar 2022 14:36:17 +0100 Subject: [PATCH 05/26] feat(service): XP service now has a half decent level up message (#87) --- src/main/java/de/voidtech/gerald/service/ExperienceService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index 1b0b195f..d7f131e6 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -162,5 +162,4 @@ private void sendLevelUpMessage(LevelUpRole role, Member member, Role roleToBeGi .build(); member.getGuild().getTextChannelById(channelID).sendMessageEmbeds(levelUpEmbed).queue(); } - } From e2636e408608ad0adfe19ff0d6bff7a5e80bac43 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Apr 2022 20:37:30 +0100 Subject: [PATCH 06/26] feat(command): Completed LevelUpRole implementation (#87) --- .../commands/utils/ExperienceCommand.java | 208 ++++++++++++++++++ .../voidtech/gerald/entities/Experience.java | 2 +- .../gerald/service/ExperienceService.java | 53 ++++- 3 files changed, 253 insertions(+), 10 deletions(-) create mode 100644 src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java diff --git a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java new file mode 100644 index 00000000..b4f1eff5 --- /dev/null +++ b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java @@ -0,0 +1,208 @@ +package main.java.de.voidtech.gerald.commands.utils; + +import java.awt.Color; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; + +import main.java.de.voidtech.gerald.annotations.Command; +import main.java.de.voidtech.gerald.commands.AbstractCommand; +import main.java.de.voidtech.gerald.commands.CommandCategory; +import main.java.de.voidtech.gerald.commands.CommandContext; +import main.java.de.voidtech.gerald.entities.Experience; +import main.java.de.voidtech.gerald.entities.LevelUpRole; +import main.java.de.voidtech.gerald.entities.Server; +import main.java.de.voidtech.gerald.service.ExperienceService; +import main.java.de.voidtech.gerald.service.ServerService; +import main.java.de.voidtech.gerald.util.ParsingUtils; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.MessageChannel; +import net.dv8tion.jda.api.entities.MessageEmbed; + +@Command +public class ExperienceCommand extends AbstractCommand { + + @Autowired + private ServerService serverService; + + @Autowired + private ExperienceService xpService; + + @Override + public void executeInternal(CommandContext context, List args) { + Server server = serverService.getServer(context.getGuild().getId()); + if (args.isEmpty()) sendLevelCard(context.getChannel(), context.getMember(), server.getId()); + else { + String ID = ParsingUtils.filterSnowflake(args.get(0)); + if (ParsingUtils.isSnowflake(ID)) { + Member mentionedMember = context.getGuild().getMemberById(ID); + if (mentionedMember == null) context.getChannel().sendMessage("**You need to mention another member to see their XP!**").queue(); + else sendLevelCard(context.getChannel(), mentionedMember, server.getId()); + } else { + switch (args.get(0)) { + case "levels": + showAllLevelRoles(context.getChannel(), server, context.getGuild()); + break; + case "addrole": + addLevelUpRole(context.getChannel(), args, server); + break; + case "removerole": + removeLevelUpRole(context.getChannel(), args, server); + break; + } + } + } + } + + private void addLevelUpRole(MessageChannel channel, List args, Server server) { + if (args.size() < 3) { + channel.sendMessage("**You need to supply more arguments! Make sure you have a level and a role mention or ID!**").queue(); + return; + } + + String level = args.get(1); + if (!ParsingUtils.isInteger(level)) { + channel.sendMessage("**You need to provide a valid number for the level!**").queue(); + return; + } + + if (xpService.serverHasRoleForLevel(server.getId(), Integer.parseInt(level))) { + channel.sendMessage("**There is already a role set up for this level!**").queue(); + return; + } + + String roleID = ParsingUtils.filterSnowflake(args.get(2)); + if (!ParsingUtils.isSnowflake(roleID)) { + channel.sendMessage("**Please provide a valid role mention or role ID**").queue(); + return; + } + + Guild guild = channel.getJDA().getGuildById(server.getGuildID()); + if (guild.getRoleById(roleID) == null) { + channel.sendMessage("**Please provide a valid role mention or role ID**").queue(); + return; + } + + LevelUpRole role = new LevelUpRole(roleID, server.getId(), Integer.parseInt(level)); + xpService.saveLevelUpRole(role); + MessageEmbed newRoleEmbed = new EmbedBuilder() + .setColor(Color.ORANGE) + .setTitle("Added Level Up Role!") + .setDescription("Level: `" + level + "`\nRole: <@&" + roleID + ">") + .build(); + channel.sendMessageEmbeds(newRoleEmbed).queue(); + } + + private void removeLevelUpRole(MessageChannel channel, List args, Server server) { + if (args.size() < 2) { + channel.sendMessage("**You need to provide a level to remove the role from!**").queue(); + return; + } + + String level = args.get(1); + if (!ParsingUtils.isInteger(level)) { + channel.sendMessage("**You need to provide a valid number for the level!**").queue(); + return; + } + + if (!xpService.serverHasRoleForLevel(server.getId(), Integer.parseInt(level))) { + channel.sendMessage("**There isn't a role set up for this level!**").queue(); + return; + } + + xpService.removeLevelUpRole(Integer.parseInt(level), server.getId()); + MessageEmbed roleDeletedEmbed = new EmbedBuilder() + .setColor(Color.ORANGE) + .setTitle("Removed Level Up Role!") + .setDescription("Role for level `" + level + "` has been removed") + .build(); + channel.sendMessageEmbeds(roleDeletedEmbed).queue(); + } + + private void showAllLevelRoles(MessageChannel channel, Server server, Guild guild) { + List levelUpRoles = xpService.getAllLevelUpRolesForServer(server.getId()); + if (levelUpRoles.isEmpty()) + channel.sendMessage("**There are no level roles set up in this server! See the help page for more info!**").queue(); + else { + String roleMessage = ""; + for (LevelUpRole role : levelUpRoles) { + roleMessage += "`" + role.getLevel() + "` - " + "<@&" + role.getRoleID() + ">\n"; + } + MessageEmbed levelRolesEmbed = new EmbedBuilder() + .setColor(Color.ORANGE) + .setTitle("Level up roles for " + guild.getName()) + .setDescription(roleMessage) + .build(); + channel.sendMessageEmbeds(levelRolesEmbed).queue(); + } + } + + private void sendLevelCard(MessageChannel channel, Member member, long serverID) { + Experience userXP = xpService.getUserExperience(member.getId(), serverID); + MessageEmbed experienceEmbed = new EmbedBuilder() + .setColor(member.getColorRaw()) + .setThumbnail(member.getEffectiveAvatarUrl()) + .setTitle(member.getUser().getName() + "'s Experience") + .setDescription("**Level:** `" + userXP.getLevel() + + "`\n**XP towards next level:** `" + userXP.getCurrentExperience() + + "`\n**XP Needed for next level:** `" + xpService.xpNeededForLevel(userXP.getLevel() + 1) + "`") + .build(); + channel.sendMessageEmbeds(experienceEmbed).queue(); + } + + @Override + public String getDescription() { + return "You know the drill, the more messages you send, the more experience you gain!\n" + + "You can gain up to 15 experience per minute.\n" + + "Server admins can configure roles that are given to you when you reach a certain level.\n" + + "To stop people from checking their XP, you can disable the XP command.\n" + + "If you want to stop people from gaining XP, disable the r-xp routine."; + } + + @Override + public String getUsage() { + return "xp\n" + + "xp levels\n" + + "xp addrole [level] [role]\n" + + "xp removerole [level]\n" + + "xp leaderboard"; + } + + @Override + public String getName() { + return "xp"; + } + + @Override + public CommandCategory getCommandCategory() { + return CommandCategory.UTILS; + } + + @Override + public boolean isDMCapable() { + return false; + } + + @Override + public boolean requiresArguments() { + return false; + } + + @Override + public String[] getCommandAliases() { + return new String[] {"level", "rank"}; + } + + @Override + public boolean canBeDisabled() { + return true; + } + + @Override + public boolean isSlashCompatible() { + return false; + } + +} diff --git a/src/main/java/de/voidtech/gerald/entities/Experience.java b/src/main/java/de/voidtech/gerald/entities/Experience.java index a96f9867..a60b5d05 100644 --- a/src/main/java/de/voidtech/gerald/entities/Experience.java +++ b/src/main/java/de/voidtech/gerald/entities/Experience.java @@ -42,7 +42,7 @@ public Experience(String userID, long serverID) { this.userID = userID; this.serverID = serverID; this.messageCount = 0; - this.level = 1; + this.level = 0; this.lastMessageTime = 0; this.experienceGainedToNextLevel = 0; } diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index d7f131e6..09494ce5 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -29,7 +29,7 @@ public class ExperienceService { private static final int EXPERIENCE_DELAY = 0; //Delay between incrementing XP in seconds - private Experience getUserExperience(String userID, long serverID) { + public Experience getUserExperience(String userID, long serverID) { try(Session session = sessionFactory.openSession()) { Experience xp = (Experience) session.createQuery("FROM Experience WHERE userID = :userID AND serverID = :serverID") @@ -57,7 +57,7 @@ private ServerExperienceConfig getServerExperienceConfig(long serverID) { } } - private List getRolesForLevel(long id, long level) { + private List getRolesForLevelFromServer(long id, long level) { try(Session session = sessionFactory.openSession()) { @SuppressWarnings("unchecked") @@ -69,6 +69,28 @@ private List getRolesForLevel(long id, long level) { } } + public List getAllLevelUpRolesForServer(long id) { + try(Session session = sessionFactory.openSession()) + { + @SuppressWarnings("unchecked") + List roles = (List) session.createQuery("FROM LevelUpRole WHERE serverID = :serverID") + .setParameter("serverID", id) + .list(); + return roles; + } + } + + public boolean serverHasRoleForLevel(long id, long level) { + try(Session session = sessionFactory.openSession()) + { + LevelUpRole role = (LevelUpRole) session.createQuery("FROM LevelUpRole WHERE serverID = :serverID AND level = :level") + .setParameter("serverID", id) + .setParameter("level", level) + .uniqueResult(); + return role != null; + } + } + private void saveUserExperience(Experience userXP) { try(Session session = sessionFactory.openSession()) { @@ -87,22 +109,35 @@ private void saveServerExperienceConfig(ServerExperienceConfig config) { } } + public void saveLevelUpRole(LevelUpRole role) { + try(Session session = sessionFactory.openSession()) + { + session.getTransaction().begin(); + session.saveOrUpdate(role); + session.getTransaction().commit(); + } + } + - private void removeLevelUpRole(LevelUpRole role) { + public void removeLevelUpRole(long level, long serverID) { try(Session session = sessionFactory.openSession()) { session.getTransaction().begin(); - session.createQuery("DELETE FROM LevelUpRole WHERE roleID = :roleID AND serverID = :serverID") - .setParameter("roleID", role.getRoleID()) - .setParameter("serverID", role.getServerID()) + session.createQuery("DELETE FROM LevelUpRole WHERE level = :level AND serverID = :serverID") + .setParameter("level", level) + .setParameter("serverID", serverID) .executeUpdate(); session.getTransaction().commit(); } } + public long xpNeededForLevel(long level) { + return 5 * (level ^ 2) + (50 * level) + 100; + } + private long xpToNextLevel(long currentLevel, long currentXP) { long nextLevel = currentLevel + 1; - return 5 * (nextLevel ^ 2) + (50 * nextLevel) + 100 - currentXP; + return xpNeededForLevel(nextLevel) - currentXP; } private int generateExperience() { @@ -136,14 +171,14 @@ public void updateUserExperience(Member member, String guildID, String channelID private void performLevelUpActions(Experience userXP, Server server, Member member, String channelID) { ServerExperienceConfig config = getServerExperienceConfig(server.getId()); - List roles = getRolesForLevel(server.getId(), userXP.getLevel()); + List roles = getRolesForLevelFromServer(server.getId(), userXP.getLevel()); if (roles.isEmpty()) return; List memberRoles = member.getRoles(); for (LevelUpRole role : roles) { Role roleToBeGiven = member.getGuild().getRoleById(role.getRoleID()); - if (roleToBeGiven == null) removeLevelUpRole(role); + if (roleToBeGiven == null) removeLevelUpRole(role.getLevel(), role.getServerID()); else { if (!memberRoles.contains(roleToBeGiven)) { member.getGuild().addRoleToMember(member, roleToBeGiven).complete(); From 69a83a422b93df23559cfbab9854bfbfba3f28bb Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 13 Apr 2022 21:30:50 +0100 Subject: [PATCH 07/26] feat(command): Implemented XP card API. Issue found with levelling up. Big brain needed. (#87) --- .../commands/utils/ExperienceCommand.java | 15 ++++---- .../gerald/service/ExperienceService.java | 36 ++++++++++++++++++- .../voidtech/gerald/service/GeraldConfig.java | 5 +++ 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java index b4f1eff5..5688b4be 100644 --- a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java @@ -141,15 +141,12 @@ private void showAllLevelRoles(MessageChannel channel, Server server, Guild guil private void sendLevelCard(MessageChannel channel, Member member, long serverID) { Experience userXP = xpService.getUserExperience(member.getId(), serverID); - MessageEmbed experienceEmbed = new EmbedBuilder() - .setColor(member.getColorRaw()) - .setThumbnail(member.getEffectiveAvatarUrl()) - .setTitle(member.getUser().getName() + "'s Experience") - .setDescription("**Level:** `" + userXP.getLevel() + - "`\n**XP towards next level:** `" + userXP.getCurrentExperience() + - "`\n**XP Needed for next level:** `" + xpService.xpNeededForLevel(userXP.getLevel() + 1) + "`") - .build(); - channel.sendMessageEmbeds(experienceEmbed).queue(); + + byte[] xpCard = xpService.getExperienceCard(member.getUser().getAvatarUrl(), + userXP.getCurrentExperience(), xpService.xpNeededForLevel(userXP.getLevel()), + userXP.getLevel(), 1, member.getUser().getName() + "#" + member.getUser().getDiscriminator(), + "#FF0000", "#2E2E2E"); + channel.sendFile(xpCard, "xpcard.png").queue(); } @Override diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index 09494ce5..dcb122d2 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -1,11 +1,19 @@ package main.java.de.voidtech.gerald.service; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.List; import java.util.Random; +import javax.xml.bind.DatatypeConverter; + import org.hibernate.Session; import org.hibernate.SessionFactory; +import org.jsoup.Jsoup; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -27,8 +35,34 @@ public class ExperienceService { @Autowired private ServerService serverService; + @Autowired + private GeraldConfig config; + private static final int EXPERIENCE_DELAY = 0; //Delay between incrementing XP in seconds + public byte[] getExperienceCard(String avatarURL, long xpAchieved, long xpNeeded, + long level, long rank, String username, String barColour, String background) { + try { + String cardURL = config.getExperienceCardApiURL() + "?avatar_url=" + avatarURL + + "&xp=" + xpAchieved + "&xp_needed=" + xpNeeded + "&level=" + level + "&rank=" + rank + + "&username=" + URLEncoder.encode(username, StandardCharsets.UTF_8.toString()) + + "&bar_colour=" + URLEncoder.encode(barColour, StandardCharsets.UTF_8.toString()) + + "&bg_colour=" + URLEncoder.encode(background, StandardCharsets.UTF_8.toString()); + URL url = new URL(cardURL); + //Remove the data:image/png;base64 part + String response = Jsoup.connect(url.toString()).get().toString().split(",")[1]; + byte[] imageBytes = DatatypeConverter.parseBase64Binary(response); + return imageBytes; + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + + } + public Experience getUserExperience(String userID, long serverID) { try(Session session = sessionFactory.openSession()) { @@ -157,7 +191,7 @@ public void updateUserExperience(Member member, String guildID, String channelID long currentExperience = userXP.getCurrentExperience() + generateExperience(); long xpToNextLevel = xpToNextLevel(userXP.getLevel(), currentExperience); - if (xpToNextLevel < 0) { + if (xpToNextLevel <= 0) { userXP.setLevel(userXP.getLevel() + 1); userXP.setCurrentXP(-1 * xpToNextLevel); performLevelUpActions(userXP, server, member, channelID); diff --git a/src/main/java/de/voidtech/gerald/service/GeraldConfig.java b/src/main/java/de/voidtech/gerald/service/GeraldConfig.java index e53691a1..cd39c8fc 100644 --- a/src/main/java/de/voidtech/gerald/service/GeraldConfig.java +++ b/src/main/java/de/voidtech/gerald/service/GeraldConfig.java @@ -97,4 +97,9 @@ public String getTwitchSecret() { public String getMemeApiURL() { return config.getProperty("api.meme_url"); } + + public String getExperienceCardApiURL() { + String url = config.getProperty("api.gavin_url"); + return url != null ? url : "http://localhost:3000/api/"; + } } \ No newline at end of file From 41140c4f7b17c9e42c70199b3db2519b0e9b1dd2 Mon Sep 17 00:00:00 2001 From: Seb Date: Thu, 14 Apr 2022 08:58:02 +0100 Subject: [PATCH 08/26] fix(command): Fixed broken XP issue (#87) --- .../de/voidtech/gerald/commands/utils/ExperienceCommand.java | 2 +- src/main/java/de/voidtech/gerald/service/ExperienceService.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java index 5688b4be..8cf25b2c 100644 --- a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java @@ -143,7 +143,7 @@ private void sendLevelCard(MessageChannel channel, Member member, long serverID) Experience userXP = xpService.getUserExperience(member.getId(), serverID); byte[] xpCard = xpService.getExperienceCard(member.getUser().getAvatarUrl(), - userXP.getCurrentExperience(), xpService.xpNeededForLevel(userXP.getLevel()), + userXP.getCurrentExperience(), xpService.xpNeededForLevel(userXP.getLevel() + 1), userXP.getLevel(), 1, member.getUser().getName() + "#" + member.getUser().getDiscriminator(), "#FF0000", "#2E2E2E"); channel.sendFile(xpCard, "xpcard.png").queue(); diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index dcb122d2..10ca2047 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -126,6 +126,7 @@ public boolean serverHasRoleForLevel(long id, long level) { } private void saveUserExperience(Experience userXP) { + System.out.println("Saving user XP"); try(Session session = sessionFactory.openSession()) { session.getTransaction().begin(); From a11b6e6aef5bc011d783b3d8f7b3693f6a91d713 Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 18 Apr 2022 20:53:23 +0100 Subject: [PATCH 09/26] feat(service): Added a total xp count to the xp system (#87) --- .../gerald/commands/utils/ExperienceCommand.java | 4 ++-- .../de/voidtech/gerald/entities/Experience.java | 14 +++++++++++++- .../voidtech/gerald/service/ExperienceService.java | 13 ++++++------- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java index 8cf25b2c..7fa9f312 100644 --- a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java @@ -143,8 +143,8 @@ private void sendLevelCard(MessageChannel channel, Member member, long serverID) Experience userXP = xpService.getUserExperience(member.getId(), serverID); byte[] xpCard = xpService.getExperienceCard(member.getUser().getAvatarUrl(), - userXP.getCurrentExperience(), xpService.xpNeededForLevel(userXP.getLevel() + 1), - userXP.getLevel(), 1, member.getUser().getName() + "#" + member.getUser().getDiscriminator(), + userXP.getCurrentExperience(), xpService.xpNeededForLevel(userXP.getNextLevel()), + userXP.getCurrentLevel(), 1, member.getUser().getName() + "#" + member.getUser().getDiscriminator(), "#FF0000", "#2E2E2E"); channel.sendFile(xpCard, "xpcard.png").queue(); } diff --git a/src/main/java/de/voidtech/gerald/entities/Experience.java b/src/main/java/de/voidtech/gerald/entities/Experience.java index a60b5d05..8ac23cab 100644 --- a/src/main/java/de/voidtech/gerald/entities/Experience.java +++ b/src/main/java/de/voidtech/gerald/entities/Experience.java @@ -33,6 +33,9 @@ public class Experience { @Column private long lastMessageTime; + @Column + private long totalExperience; + @Deprecated //ONLY FOR HIBERNATE, DO NOT USE Experience() { @@ -51,6 +54,11 @@ public void setLevel(long level) { this.level = level; } + public void incrementExperience(long xp) { + this.experienceGainedToNextLevel = this.experienceGainedToNextLevel + xp; + this.totalExperience = this.totalExperience + xp; + } + public void setCurrentXP(long xp) { this.experienceGainedToNextLevel = xp; } @@ -63,10 +71,14 @@ public void incrementMessageCount() { this.messageCount++; } - public long getLevel() { + public long getCurrentLevel() { return this.level; } + public long getNextLevel() { + return this.level + 1; + } + public long getCurrentExperience() { return this.experienceGainedToNextLevel; } diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index 10ca2047..57ffe146 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -126,7 +126,6 @@ public boolean serverHasRoleForLevel(long id, long level) { } private void saveUserExperience(Experience userXP) { - System.out.println("Saving user XP"); try(Session session = sessionFactory.openSession()) { session.getTransaction().begin(); @@ -170,8 +169,7 @@ public long xpNeededForLevel(long level) { return 5 * (level ^ 2) + (50 * level) + 100; } - private long xpToNextLevel(long currentLevel, long currentXP) { - long nextLevel = currentLevel + 1; + private long xpToNextLevel(long nextLevel, long currentXP) { return xpNeededForLevel(nextLevel) - currentXP; } @@ -189,11 +187,12 @@ public void updateUserExperience(Member member, String guildID, String channelID return; } - long currentExperience = userXP.getCurrentExperience() + generateExperience(); - long xpToNextLevel = xpToNextLevel(userXP.getLevel(), currentExperience); + userXP.incrementExperience(generateExperience()); + long currentExperience = userXP.getCurrentExperience(); + long xpToNextLevel = xpToNextLevel(userXP.getNextLevel(), currentExperience); if (xpToNextLevel <= 0) { - userXP.setLevel(userXP.getLevel() + 1); + userXP.setLevel(userXP.getNextLevel()); userXP.setCurrentXP(-1 * xpToNextLevel); performLevelUpActions(userXP, server, member, channelID); } else userXP.setCurrentXP(currentExperience); @@ -206,7 +205,7 @@ public void updateUserExperience(Member member, String guildID, String channelID private void performLevelUpActions(Experience userXP, Server server, Member member, String channelID) { ServerExperienceConfig config = getServerExperienceConfig(server.getId()); - List roles = getRolesForLevelFromServer(server.getId(), userXP.getLevel()); + List roles = getRolesForLevelFromServer(server.getId(), userXP.getCurrentLevel()); if (roles.isEmpty()) return; List memberRoles = member.getRoles(); From ba813846f1ea41197d86746305308e968d2ecb8e Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 19 Apr 2022 20:46:18 +0100 Subject: [PATCH 10/26] feat(command): Added leaderboard to XP system (#87) --- .../commands/utils/ExperienceCommand.java | 120 ++++++++++++++---- .../voidtech/gerald/entities/Experience.java | 4 + .../gerald/service/ExperienceService.java | 41 +++++- 3 files changed, 138 insertions(+), 27 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java index 7fa9f312..1dc9a608 100644 --- a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java @@ -1,6 +1,7 @@ package main.java.de.voidtech.gerald.commands.utils; import java.awt.Color; +import java.util.Arrays; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -18,7 +19,6 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.entities.MessageEmbed; @Command @@ -33,55 +33,87 @@ public class ExperienceCommand extends AbstractCommand { @Override public void executeInternal(CommandContext context, List args) { Server server = serverService.getServer(context.getGuild().getId()); - if (args.isEmpty()) sendLevelCard(context.getChannel(), context.getMember(), server.getId()); + if (args.isEmpty()) sendLevelCard(context, context.getMember(), server.getId()); else { String ID = ParsingUtils.filterSnowflake(args.get(0)); if (ParsingUtils.isSnowflake(ID)) { - Member mentionedMember = context.getGuild().getMemberById(ID); + Member mentionedMember = context.getGuild().retrieveMemberById(ID).complete(); if (mentionedMember == null) context.getChannel().sendMessage("**You need to mention another member to see their XP!**").queue(); - else sendLevelCard(context.getChannel(), mentionedMember, server.getId()); + else sendLevelCard(context, mentionedMember, server.getId()); } else { switch (args.get(0)) { case "levels": - showAllLevelRoles(context.getChannel(), server, context.getGuild()); + showAllLevelRoles(context, server, context.getGuild()); break; case "addrole": - addLevelUpRole(context.getChannel(), args, server); + addLevelUpRole(context, args, server); break; case "removerole": - removeLevelUpRole(context.getChannel(), args, server); + removeLevelUpRole(context, args, server); + break; + case "leaderboard": + showServerLeaderboard(context, server); + break; + case "lb": + showServerLeaderboard(context, server); break; } } } } - private void addLevelUpRole(MessageChannel channel, List args, Server server) { + private void showServerLeaderboard(CommandContext context, Server server) { + List topTenMembers = xpService.getServerLeaderboardChunk(server.getId(), 10, 0); + int userPosition = xpService.getUserLeaderboardPosition(server.getId(), context.getAuthor().getId()); + Experience userXP = xpService.getUserExperience(context.getAuthor().getId(), server.getId()); + + String leaderboard = ""; + int rank = 1; + + for (Experience xp : topTenMembers) { + leaderboard += numberToEmoji(rank) + " <@" + xp.getUserID() + ">\n"; + leaderboard += "```js\nLevel " + xp.getCurrentLevel() + " | XP " + xp.getTotalExperience() + "\n```"; + rank++; + } + + leaderboard += "\n**Your Position**\n"; + leaderboard += numberToEmoji(userPosition) + " <@" + userXP.getUserID() + ">\n"; + leaderboard += "```js\nLevel " + userXP.getCurrentLevel() + " | XP " + userXP.getTotalExperience() + "\n```"; + + MessageEmbed leaderboardEmbed = new EmbedBuilder() + .setColor(Color.ORANGE) + .setDescription(leaderboard) + .setTitle(context.getGuild().getName() + "'s Most Active Members") + .build(); + context.reply(leaderboardEmbed); + } + + private void addLevelUpRole(CommandContext context, List args, Server server) { if (args.size() < 3) { - channel.sendMessage("**You need to supply more arguments! Make sure you have a level and a role mention or ID!**").queue(); + context.reply("**You need to supply more arguments! Make sure you have a level and a role mention or ID!**"); return; } String level = args.get(1); if (!ParsingUtils.isInteger(level)) { - channel.sendMessage("**You need to provide a valid number for the level!**").queue(); + context.reply("**You need to provide a valid number for the level!**"); return; } if (xpService.serverHasRoleForLevel(server.getId(), Integer.parseInt(level))) { - channel.sendMessage("**There is already a role set up for this level!**").queue(); + context.reply("**There is already a role set up for this level!**"); return; } String roleID = ParsingUtils.filterSnowflake(args.get(2)); if (!ParsingUtils.isSnowflake(roleID)) { - channel.sendMessage("**Please provide a valid role mention or role ID**").queue(); + context.reply("**Please provide a valid role mention or role ID**"); return; } - Guild guild = channel.getJDA().getGuildById(server.getGuildID()); + Guild guild = context.getJDA().getGuildById(server.getGuildID()); if (guild.getRoleById(roleID) == null) { - channel.sendMessage("**Please provide a valid role mention or role ID**").queue(); + context.reply("**Please provide a valid role mention or role ID**"); return; } @@ -92,23 +124,61 @@ private void addLevelUpRole(MessageChannel channel, List args, Server se .setTitle("Added Level Up Role!") .setDescription("Level: `" + level + "`\nRole: <@&" + roleID + ">") .build(); - channel.sendMessageEmbeds(newRoleEmbed).queue(); + context.reply(newRoleEmbed); + } + + private String numberToEmoji(int number) { + List digits = Arrays.asList(String.valueOf(number).split("")); //Wowzer + String finalNumber = ""; + for (String digit : digits) { + finalNumber += convertSingleDigitToEmoji(digit); + } + return finalNumber; + } + + private String convertSingleDigitToEmoji(String digit) { + switch (digit) { + case "0": + return ":zero"; + case "1": + return ":one:"; + case "2": + return ":two:"; + case "3": + return ":three:"; + case "4": + return ":four:"; + case "5": + return ":five:"; + case "6": + return ":six:"; + case "7": + return ":seven:"; + case "8": + return ":eight:"; + case "9": + return ":nine:"; + case "10": + return ":ten:"; + default: + return ":zero:"; + } } - private void removeLevelUpRole(MessageChannel channel, List args, Server server) { + private void removeLevelUpRole(CommandContext context, List args, Server server) { if (args.size() < 2) { - channel.sendMessage("**You need to provide a level to remove the role from!**").queue(); + context.reply("**You need to provide a level to remove the role from!**"); return; } String level = args.get(1); if (!ParsingUtils.isInteger(level)) { - channel.sendMessage("**You need to provide a valid number for the level!**").queue(); + context.reply("**You need to provide a valid number for the level!**"); return; } if (!xpService.serverHasRoleForLevel(server.getId(), Integer.parseInt(level))) { - channel.sendMessage("**There isn't a role set up for this level!**").queue(); + context.reply("**There isn't a role set up for this level!**"); return; } @@ -118,13 +188,13 @@ private void removeLevelUpRole(MessageChannel channel, List args, Server .setTitle("Removed Level Up Role!") .setDescription("Role for level `" + level + "` has been removed") .build(); - channel.sendMessageEmbeds(roleDeletedEmbed).queue(); + context.reply(roleDeletedEmbed); } - private void showAllLevelRoles(MessageChannel channel, Server server, Guild guild) { + private void showAllLevelRoles(CommandContext context, Server server, Guild guild) { List levelUpRoles = xpService.getAllLevelUpRolesForServer(server.getId()); if (levelUpRoles.isEmpty()) - channel.sendMessage("**There are no level roles set up in this server! See the help page for more info!**").queue(); + context.reply("**There are no level roles set up in this server! See the help page for more info!**"); else { String roleMessage = ""; for (LevelUpRole role : levelUpRoles) { @@ -135,18 +205,18 @@ private void showAllLevelRoles(MessageChannel channel, Server server, Guild guil .setTitle("Level up roles for " + guild.getName()) .setDescription(roleMessage) .build(); - channel.sendMessageEmbeds(levelRolesEmbed).queue(); + context.reply(levelRolesEmbed); } } - private void sendLevelCard(MessageChannel channel, Member member, long serverID) { + private void sendLevelCard(CommandContext context, Member member, long serverID) { Experience userXP = xpService.getUserExperience(member.getId(), serverID); byte[] xpCard = xpService.getExperienceCard(member.getUser().getAvatarUrl(), userXP.getCurrentExperience(), xpService.xpNeededForLevel(userXP.getNextLevel()), userXP.getCurrentLevel(), 1, member.getUser().getName() + "#" + member.getUser().getDiscriminator(), "#FF0000", "#2E2E2E"); - channel.sendFile(xpCard, "xpcard.png").queue(); + context.replyWithFile(xpCard, "xpcard.png"); } @Override diff --git a/src/main/java/de/voidtech/gerald/entities/Experience.java b/src/main/java/de/voidtech/gerald/entities/Experience.java index 8ac23cab..d569fbb4 100644 --- a/src/main/java/de/voidtech/gerald/entities/Experience.java +++ b/src/main/java/de/voidtech/gerald/entities/Experience.java @@ -83,6 +83,10 @@ public long getCurrentExperience() { return this.experienceGainedToNextLevel; } + public long getTotalExperience() { + return this.totalExperience; + } + public long getLastMessageTime() { return this.lastMessageTime; } diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index 57ffe146..cd67f694 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -43,7 +43,7 @@ public class ExperienceService { public byte[] getExperienceCard(String avatarURL, long xpAchieved, long xpNeeded, long level, long rank, String username, String barColour, String background) { try { - String cardURL = config.getExperienceCardApiURL() + "?avatar_url=" + avatarURL + + String cardURL = config.getExperienceCardApiURL() + "xpcard/?avatar_url=" + avatarURL + "&xp=" + xpAchieved + "&xp_needed=" + xpNeeded + "&level=" + level + "&rank=" + rank + "&username=" + URLEncoder.encode(username, StandardCharsets.UTF_8.toString()) + "&bar_colour=" + URLEncoder.encode(barColour, StandardCharsets.UTF_8.toString()) @@ -91,6 +91,43 @@ private ServerExperienceConfig getServerExperienceConfig(long serverID) { } } + public List getServerLeaderboard(long serverID) { + try(Session session = sessionFactory.openSession()) + { + @SuppressWarnings("unchecked") + List leaderboard = session.createQuery("FROM Experience WHERE serverID = :serverID ORDER BY totalExperience DESC") + .setParameter("serverID", serverID) + .list(); + return leaderboard; + } + } + + public List getServerLeaderboardChunk(long serverID, int limit, int offset) { + try(Session session = sessionFactory.openSession()) + { + @SuppressWarnings("unchecked") + List leaderboard = session.createQuery("FROM Experience WHERE serverID = :serverID ORDER BY totalExperience DESC") + .setParameter("serverID", serverID) + .setMaxResults(limit) + .setFirstResult(offset) + .list(); + return leaderboard; + } + } + + public int getUserLeaderboardPosition(long serverID, String userID) { + List leaderboard = getServerLeaderboard(serverID); + int position = 0; + boolean found = false; + + while (!found & leaderboard.iterator().hasNext()) { + position++; + if (leaderboard.iterator().next().getUserID().equals(userID)) found = true; + } + + return position; + } + private List getRolesForLevelFromServer(long id, long level) { try(Session session = sessionFactory.openSession()) { @@ -166,7 +203,7 @@ public void removeLevelUpRole(long level, long serverID) { } public long xpNeededForLevel(long level) { - return 5 * (level ^ 2) + (50 * level) + 100; + return (long) Math.ceil(Math.sqrt(5000 * (Math.pow(level, 3)))); } private long xpToNextLevel(long nextLevel, long currentXP) { From e9a4ad0869e840af756426a4bde76afe73c85cc4 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 19 Apr 2022 21:00:18 +0100 Subject: [PATCH 11/26] refac(service): Added proper error logging to XP card getter (#87) --- .../de/voidtech/gerald/service/ExperienceService.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index cd67f694..89037962 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -1,13 +1,14 @@ package main.java.de.voidtech.gerald.service; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.List; import java.util.Random; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.xml.bind.DatatypeConverter; @@ -38,6 +39,7 @@ public class ExperienceService { @Autowired private GeraldConfig config; + private static final Logger LOGGER = Logger.getLogger(ExperienceService.class.getName()); private static final int EXPERIENCE_DELAY = 0; //Delay between incrementing XP in seconds public byte[] getExperienceCard(String avatarURL, long xpAchieved, long xpNeeded, @@ -53,14 +55,10 @@ public byte[] getExperienceCard(String avatarURL, long xpAchieved, long xpNeeded String response = Jsoup.connect(url.toString()).get().toString().split(",")[1]; byte[] imageBytes = DatatypeConverter.parseBase64Binary(response); return imageBytes; - } catch (MalformedURLException e) { - e.printStackTrace(); } catch (IOException e) { - e.printStackTrace(); + LOGGER.log(Level.SEVERE, e.getMessage()); } - return null; - } public Experience getUserExperience(String userID, long serverID) { From c7baa97be32a9c08c63dceae6b51c37c61c6ffe0 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 20 Apr 2022 10:36:46 +0100 Subject: [PATCH 12/26] refac(service): Added support for codec design in XP api (#87) --- .../voidtech/gerald/commands/utils/ExperienceCommand.java | 4 ++-- .../java/de/voidtech/gerald/service/ExperienceService.java | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java index 1dc9a608..6995b9b7 100644 --- a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java @@ -214,8 +214,8 @@ private void sendLevelCard(CommandContext context, Member member, long serverID) byte[] xpCard = xpService.getExperienceCard(member.getUser().getAvatarUrl(), userXP.getCurrentExperience(), xpService.xpNeededForLevel(userXP.getNextLevel()), - userXP.getCurrentLevel(), 1, member.getUser().getName() + "#" + member.getUser().getDiscriminator(), - "#FF0000", "#2E2E2E"); + userXP.getCurrentLevel(), 1, member.getUser().getName(), member.getUser().getDiscriminator(), "#F24548", + "#3B43D5", "#2F3136"); context.replyWithFile(xpCard, "xpcard.png"); } diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index 89037962..1c21adac 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -43,12 +43,15 @@ public class ExperienceService { private static final int EXPERIENCE_DELAY = 0; //Delay between incrementing XP in seconds public byte[] getExperienceCard(String avatarURL, long xpAchieved, long xpNeeded, - long level, long rank, String username, String barColour, String background) { + long level, long rank, String username, String discriminator, String barFromColour, + String barToColour, String background) { try { String cardURL = config.getExperienceCardApiURL() + "xpcard/?avatar_url=" + avatarURL + "&xp=" + xpAchieved + "&xp_needed=" + xpNeeded + "&level=" + level + "&rank=" + rank + "&username=" + URLEncoder.encode(username, StandardCharsets.UTF_8.toString()) - + "&bar_colour=" + URLEncoder.encode(barColour, StandardCharsets.UTF_8.toString()) + + "&discriminator=" + URLEncoder.encode(discriminator, StandardCharsets.UTF_8.toString()) + + "&bar_colour_from=" + URLEncoder.encode(barFromColour, StandardCharsets.UTF_8.toString()) + + "&bar_colour_to=" + URLEncoder.encode(barToColour, StandardCharsets.UTF_8.toString()) + "&bg_colour=" + URLEncoder.encode(background, StandardCharsets.UTF_8.toString()); URL url = new URL(cardURL); //Remove the data:image/png;base64 part From 8af09d5487ea5a3c003366a93d3436cd971ea1dc Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 20 Apr 2022 12:48:24 +0100 Subject: [PATCH 13/26] meta(service): Set XP delay to 1 minute (#87) --- src/main/java/de/voidtech/gerald/service/ExperienceService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index 1c21adac..55429c1b 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -40,7 +40,7 @@ public class ExperienceService { private GeraldConfig config; private static final Logger LOGGER = Logger.getLogger(ExperienceService.class.getName()); - private static final int EXPERIENCE_DELAY = 0; //Delay between incrementing XP in seconds + private static final int EXPERIENCE_DELAY = 60; //Delay between incrementing XP in seconds public byte[] getExperienceCard(String avatarURL, long xpAchieved, long xpNeeded, long level, long rank, String username, String discriminator, String barFromColour, From 8dd34fbb70f68ee6d2caf96d9daa737a97c1a066 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 20 Apr 2022 13:06:00 +0100 Subject: [PATCH 14/26] refac(service): Fixed a config file issue (#87) --- src/main/java/de/voidtech/gerald/service/GeraldConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/voidtech/gerald/service/GeraldConfig.java b/src/main/java/de/voidtech/gerald/service/GeraldConfig.java index cd39c8fc..04d80091 100644 --- a/src/main/java/de/voidtech/gerald/service/GeraldConfig.java +++ b/src/main/java/de/voidtech/gerald/service/GeraldConfig.java @@ -99,7 +99,7 @@ public String getMemeApiURL() { } public String getExperienceCardApiURL() { - String url = config.getProperty("api.gavin_url"); + String url = config.getProperty("api.xp_url"); return url != null ? url : "http://localhost:3000/api/"; } } \ No newline at end of file From a1e029875a7e7c325b7e560d043a42c5e4f8edc2 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 20 Apr 2022 16:37:43 +0100 Subject: [PATCH 15/26] refac(command): XP cards now show the correct rank instead of just "1" (#87) --- .../de/voidtech/gerald/commands/utils/ExperienceCommand.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java index 6995b9b7..c3118124 100644 --- a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java @@ -214,7 +214,8 @@ private void sendLevelCard(CommandContext context, Member member, long serverID) byte[] xpCard = xpService.getExperienceCard(member.getUser().getAvatarUrl(), userXP.getCurrentExperience(), xpService.xpNeededForLevel(userXP.getNextLevel()), - userXP.getCurrentLevel(), 1, member.getUser().getName(), member.getUser().getDiscriminator(), "#F24548", + userXP.getCurrentLevel(), xpService.getUserLeaderboardPosition(serverID, member.getId()), + member.getUser().getName(), member.getUser().getDiscriminator(), "#F24548", "#3B43D5", "#2F3136"); context.replyWithFile(xpCard, "xpcard.png"); } From e542c7d39933959617f6f56083ea56d0556cee4b Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 20 Apr 2022 18:46:30 +0100 Subject: [PATCH 16/26] fix(xp): Fixed my horrible, awful mistakes. (#87) --- .../commands/utils/ExperienceCommand.java | 20 ++++++++++++++----- .../gerald/service/ExperienceService.java | 10 ++++++++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java index c3118124..0454ee80 100644 --- a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java @@ -212,11 +212,21 @@ private void showAllLevelRoles(CommandContext context, Server server, Guild guil private void sendLevelCard(CommandContext context, Member member, long serverID) { Experience userXP = xpService.getUserExperience(member.getId(), serverID); - byte[] xpCard = xpService.getExperienceCard(member.getUser().getAvatarUrl(), - userXP.getCurrentExperience(), xpService.xpNeededForLevel(userXP.getNextLevel()), - userXP.getCurrentLevel(), xpService.getUserLeaderboardPosition(serverID, member.getId()), - member.getUser().getName(), member.getUser().getDiscriminator(), "#F24548", - "#3B43D5", "#2F3136"); + if (userXP == null) { + context.reply("**User is not ranked yet!**"); + return; + } + + String avatarURL = member.getUser().getAvatarUrl(); + long xpAchieved = userXP.getCurrentExperience(); + long xpNeeded = xpService.xpNeededForLevel(userXP.getNextLevel()); + long level = userXP.getCurrentLevel(); + long rank = xpService.getUserLeaderboardPosition(serverID, userXP.getUserID()); + String username = member.getUser().getName(); + String discriminator = member.getUser().getDiscriminator(); + + byte[] xpCard = xpService.getExperienceCard(avatarURL, + xpAchieved, xpNeeded, level, rank, username, discriminator, "#F24548", "#3B43D5", "#2F3136"); context.replyWithFile(xpCard, "xpcard.png"); } diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index 55429c1b..dbd6dc1a 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -71,8 +71,6 @@ public Experience getUserExperience(String userID, long serverID) { .setParameter("userID", userID) .setParameter("serverID", serverID) .uniqueResult(); - - if (xp == null) xp = new Experience(userID, serverID); return xp; } } @@ -100,7 +98,10 @@ public List getServerLeaderboard(long serverID) { .setParameter("serverID", serverID) .list(); return leaderboard; + } catch (Exception e) { + e.printStackTrace(); } + return null; } public List getServerLeaderboardChunk(long serverID, int limit, int offset) { @@ -218,6 +219,11 @@ private int generateExperience() { public void updateUserExperience(Member member, String guildID, String channelID) { Server server = serverService.getServer(guildID); Experience userXP = getUserExperience(member.getId(), server.getId()); + + if (userXP == null) { + userXP = new Experience(member.getId(), server.getId()); + } + userXP.incrementMessageCount(); if ((userXP.getLastMessageTime() + EXPERIENCE_DELAY) > Instant.now().getEpochSecond()) { From f0f5c87e3a8cae53e7a4bb5ce3eca8bd0da095ed Mon Sep 17 00:00:00 2001 From: Seb Date: Thu, 21 Apr 2022 11:45:50 +0100 Subject: [PATCH 17/26] test: Removed user rank search algorithm to determine XP issue (#87) --- .../de/voidtech/gerald/service/ExperienceService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index dbd6dc1a..82e4f984 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -120,12 +120,12 @@ public List getServerLeaderboardChunk(long serverID, int limit, int public int getUserLeaderboardPosition(long serverID, String userID) { List leaderboard = getServerLeaderboard(serverID); int position = 0; - boolean found = false; + //boolean found = false; - while (!found & leaderboard.iterator().hasNext()) { - position++; - if (leaderboard.iterator().next().getUserID().equals(userID)) found = true; - } + //while (!found & leaderboard.iterator().hasNext()) { + //position++; + //if (leaderboard.iterator().next().getUserID().equals(userID)) found = true; + //} return position; } From 80525f5a7c7178cae48d9590a0e3aafcb8c20619 Mon Sep 17 00:00:00 2001 From: Seb Date: Thu, 21 Apr 2022 11:58:07 +0100 Subject: [PATCH 18/26] fix(service): Changed the searching algorithm, it may be less broken now (#87) --- .../de/voidtech/gerald/service/ExperienceService.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index 82e4f984..c0483828 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -120,13 +120,12 @@ public List getServerLeaderboardChunk(long serverID, int limit, int public int getUserLeaderboardPosition(long serverID, String userID) { List leaderboard = getServerLeaderboard(serverID); int position = 0; - //boolean found = false; - - //while (!found & leaderboard.iterator().hasNext()) { - //position++; - //if (leaderboard.iterator().next().getUserID().equals(userID)) found = true; - //} + for (Experience xp : leaderboard) { + position++; + if (xp.getUserID().equals(userID)) break; + } + return position; } From e7b239438791f7d73f72f8d841eb86bc5f405c18 Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 25 Apr 2022 12:00:10 +0100 Subject: [PATCH 19/26] refac(command): Reduced XP leaderboard chunk size to 5 (#87) --- .../voidtech/gerald/commands/utils/ExperienceCommand.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java index 0454ee80..62a2cd8e 100644 --- a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java @@ -57,13 +57,16 @@ public void executeInternal(CommandContext context, List args) { case "lb": showServerLeaderboard(context, server); break; + default: + context.reply("**That's not a valid subcommand!**\n" + this.getUsage()); + break; } } } } private void showServerLeaderboard(CommandContext context, Server server) { - List topTenMembers = xpService.getServerLeaderboardChunk(server.getId(), 10, 0); + List topTenMembers = xpService.getServerLeaderboardChunk(server.getId(), 5, 0); int userPosition = xpService.getUserLeaderboardPosition(server.getId(), context.getAuthor().getId()); Experience userXP = xpService.getUserExperience(context.getAuthor().getId(), server.getId()); @@ -139,7 +142,7 @@ private String numberToEmoji(int number) { private String convertSingleDigitToEmoji(String digit) { switch (digit) { case "0": - return ":zero"; + return ":zero:"; case "1": return ":one:"; case "2": From 3ac06e5559c3ca3e0b60f58d5e3ed25971549c20 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 26 Apr 2022 12:20:59 +0100 Subject: [PATCH 20/26] feat(command): Added level up message toggle (#87) --- .../commands/utils/ExperienceCommand.java | 67 ++++++++++++------- .../gerald/service/ExperienceService.java | 8 +++ 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java index 62a2cd8e..71377bd1 100644 --- a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java @@ -17,6 +17,7 @@ import main.java.de.voidtech.gerald.service.ServerService; import main.java.de.voidtech.gerald.util.ParsingUtils; import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageEmbed; @@ -57,6 +58,9 @@ public void executeInternal(CommandContext context, List args) { case "lb": showServerLeaderboard(context, server); break; + case "togglemsg": + toggleLevelUpMessages(context, server); + break; default: context.reply("**That's not a valid subcommand!**\n" + this.getUsage()); break; @@ -65,6 +69,15 @@ public void executeInternal(CommandContext context, List args) { } } + private void toggleLevelUpMessages(CommandContext context, Server server) { + boolean userCanToggle = context.getMember().hasPermission(Permission.MANAGE_SERVER); + if (!userCanToggle) context.reply("**You need the Manage Server permission to toggle level up messages!**"); + else { + boolean enabled = xpService.toggleLevelUpMessages(server.getId()); + context.reply("**Level up messages are now " + (enabled ? "enabled**" : "disabled**")); + } + } + private void showServerLeaderboard(CommandContext context, Server server) { List topTenMembers = xpService.getServerLeaderboardChunk(server.getId(), 5, 0); int userPosition = xpService.getUserLeaderboardPosition(server.getId(), context.getAuthor().getId()); @@ -141,31 +154,31 @@ private String numberToEmoji(int number) { private String convertSingleDigitToEmoji(String digit) { switch (digit) { - case "0": - return ":zero:"; - case "1": - return ":one:"; - case "2": - return ":two:"; - case "3": - return ":three:"; - case "4": - return ":four:"; - case "5": - return ":five:"; - case "6": - return ":six:"; - case "7": - return ":seven:"; - case "8": - return ":eight:"; - case "9": - return ":nine:"; - case "10": - return ":ten:"; - default: - return ":zero:"; - } + case "0": + return ":zero:"; + case "1": + return ":one:"; + case "2": + return ":two:"; + case "3": + return ":three:"; + case "4": + return ":four:"; + case "5": + return ":five:"; + case "6": + return ":six:"; + case "7": + return ":seven:"; + case "8": + return ":eight:"; + case "9": + return ":nine:"; + case "10": + return ":ten:"; + default: + return ":zero:"; + } } private void removeLevelUpRole(CommandContext context, List args, Server server) { @@ -239,7 +252,8 @@ public String getDescription() { + "You can gain up to 15 experience per minute.\n" + "Server admins can configure roles that are given to you when you reach a certain level.\n" + "To stop people from checking their XP, you can disable the XP command.\n" - + "If you want to stop people from gaining XP, disable the r-xp routine."; + + "If you want to stop people from gaining XP, disable the r-xp routine.\n" + + "To disable the level up messages, use the togglemsg subcommand."; } @Override @@ -248,6 +262,7 @@ public String getUsage() { + "xp levels\n" + "xp addrole [level] [role]\n" + "xp removerole [level]\n" + + "xp togglemsg\n" + "xp leaderboard"; } diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index c0483828..d3c84801 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -274,4 +274,12 @@ private void sendLevelUpMessage(LevelUpRole role, Member member, Role roleToBeGi .build(); member.getGuild().getTextChannelById(channelID).sendMessageEmbeds(levelUpEmbed).queue(); } + + public boolean toggleLevelUpMessages(long id) { + ServerExperienceConfig config = getServerExperienceConfig(id); + boolean nowEnabled = !config.levelUpMessagesEnabled(); + config.setLevelUpMessagesEnabled(nowEnabled); + saveServerExperienceConfig(config); + return nowEnabled; + } } From 5420c8000c0f2bb29f7607b4a597a9a7a9d9b14c Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 4 May 2022 10:41:19 +0100 Subject: [PATCH 21/26] feat(service): Roles are automatically re-added upon member join (#87) --- .../gerald/listeners/MemberListener.java | 10 ++++++++++ .../gerald/service/ExperienceService.java | 16 +++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/voidtech/gerald/listeners/MemberListener.java b/src/main/java/de/voidtech/gerald/listeners/MemberListener.java index fc43379b..2156ca1d 100644 --- a/src/main/java/de/voidtech/gerald/listeners/MemberListener.java +++ b/src/main/java/de/voidtech/gerald/listeners/MemberListener.java @@ -3,7 +3,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import main.java.de.voidtech.gerald.service.ExperienceService; import main.java.de.voidtech.gerald.service.JoinLeaveMessageService; +import main.java.de.voidtech.gerald.service.ServerService; import net.dv8tion.jda.api.events.GenericEvent; import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent; import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent; @@ -15,11 +17,19 @@ public class MemberListener implements EventListener { @Autowired private JoinLeaveMessageService JLMService; + @Autowired + private ExperienceService xpService; + + @Autowired + private ServerService serverService; + @Override public void onEvent(GenericEvent event) { if (event instanceof GuildMemberJoinEvent) { GuildMemberJoinEvent joinEvent = (GuildMemberJoinEvent) event; JLMService.sendJoinMessage(joinEvent); + xpService.addRolesOnServerJoin(serverService.getServer(joinEvent.getGuild().getId()), joinEvent.getMember()); + } if (event instanceof GuildMemberRemoveEvent) { diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index d3c84801..0da7a956 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -40,7 +40,7 @@ public class ExperienceService { private GeraldConfig config; private static final Logger LOGGER = Logger.getLogger(ExperienceService.class.getName()); - private static final int EXPERIENCE_DELAY = 60; //Delay between incrementing XP in seconds + private static final int EXPERIENCE_DELAY = 0; //Delay between incrementing XP in seconds public byte[] getExperienceCard(String avatarURL, long xpAchieved, long xpNeeded, long level, long rank, String username, String discriminator, String barFromColour, @@ -264,6 +264,20 @@ private void performLevelUpActions(Experience userXP, Server server, Member memb } } } + + public void addRolesOnServerJoin(Server server, Member member) { + Experience userXP = getUserExperience(member.getId(), server.getId()); + List roles = getRolesForLevelFromServer(server.getId(), userXP.getCurrentLevel()); + + if (roles.isEmpty()) return; + + List memberRoles = member.getRoles(); + for (LevelUpRole role : roles) { + Role roleToBeGiven = member.getGuild().getRoleById(role.getRoleID()); + if (roleToBeGiven == null) removeLevelUpRole(role.getLevel(), role.getServerID()); + else if (!memberRoles.contains(roleToBeGiven)) member.getGuild().addRoleToMember(member, roleToBeGiven).complete(); + } + } private void sendLevelUpMessage(LevelUpRole role, Member member, Role roleToBeGiven, String channelID) { MessageEmbed levelUpEmbed = new EmbedBuilder() From ba6b1a01f4c0943222668d2eaca5195e99f32f01 Mon Sep 17 00:00:00 2001 From: Seb Date: Wed, 4 May 2022 11:19:29 +0100 Subject: [PATCH 22/26] refac(service): Set the XP delay back up to 60 seconds because i forgor (#87) --- src/main/java/de/voidtech/gerald/service/ExperienceService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index 0da7a956..7c1075dd 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -40,7 +40,7 @@ public class ExperienceService { private GeraldConfig config; private static final Logger LOGGER = Logger.getLogger(ExperienceService.class.getName()); - private static final int EXPERIENCE_DELAY = 0; //Delay between incrementing XP in seconds + private static final int EXPERIENCE_DELAY = 60; //Delay between incrementing XP in seconds public byte[] getExperienceCard(String avatarURL, long xpAchieved, long xpNeeded, long level, long rank, String username, String discriminator, String barFromColour, From dc1063bac9b19982a49aa642f4c5e499ca179d44 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 31 May 2022 01:45:06 +0100 Subject: [PATCH 23/26] feat(command): Added noxp to xp system (#87) --- .../commands/utils/ExperienceCommand.java | 138 ++++++++++++++---- .../entities/ServerExperienceConfig.java | 30 ++++ .../routines/utils/ExperienceRoutine.java | 10 ++ .../gerald/service/ExperienceService.java | 32 ++++ .../de/voidtech/gerald/util/ParsingUtils.java | 29 ++++ 5 files changed, 207 insertions(+), 32 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java index 71377bd1..86f4ca11 100644 --- a/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/utils/ExperienceCommand.java @@ -3,6 +3,7 @@ import java.awt.Color; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; @@ -21,6 +22,7 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.TextChannel; @Command public class ExperienceCommand extends AbstractCommand { @@ -61,6 +63,9 @@ public void executeInternal(CommandContext context, List args) { case "togglemsg": toggleLevelUpMessages(context, server); break; + case "noxp": + handleNoXpChannelSettings(context, server); + break; default: context.reply("**That's not a valid subcommand!**\n" + this.getUsage()); break; @@ -68,6 +73,102 @@ public void executeInternal(CommandContext context, List args) { } } } + + private void showNoXpHelp(CommandContext context) { + context.reply("**No XP Settings:**\n" + + "list - shows all channels where xp will not be gained\n" + + "add - add a channel that will not gain xp\n" + + "clear - remove all no xp channels\n" + + "remove - remove a channel that will not gain xp\n\n" + + "When adding or removing a channel from the no xp list, you will be prompted to enter a channel mention or ID."); + } + + private void handleNoXpChannelSettings(CommandContext context, Server server) { + String mode; + if (context.getArgs().size() < 2) mode = "help"; + else mode = context.getArgs().get(1); + + switch (mode) { + case "list": + showNoXPChannels(context, server); + break; + case "add": + addNoXPChannel(context, server); + break; + case "remove": + removeNoXPChannel(context, server); + break; + case "clear": + clearNoXPChannels(context, server); + break; + case "help": + showNoXpHelp(context); + break; + default: + showNoXpHelp(context); + break; + } + } + + private void clearNoXPChannels(CommandContext context, Server server) { + if (!context.getMember().getPermissions().contains(Permission.MANAGE_SERVER)) { + context.reply("**You need the Manage Server Permission to do that!**"); + return; + } + xpService.clearNoXpChannels(server.getId()); + context.reply("**No XP Channels have been cleared!**"); + } + + private void removeNoXPChannel(CommandContext context, Server server) { + if (!context.getMember().getPermissions().contains(Permission.MANAGE_SERVER)) { + context.reply("**You need the Manage Server Permission to do that!**"); + return; + } + if (context.getArgs().size() < 3) { + context.reply("**You need to specify a channel to remove!**"); + return; + } + String channelID = ParsingUtils.filterSnowflake(context.getArgs().get(2)); + TextChannel channel = context.getGuild().getTextChannelById(channelID); + if (channel == null) { + context.reply("**You need to specify a valid text channel!**"); + return; + } + xpService.deleteNoXpChannel(channelID, server.getId()); + context.reply("**No XP channel** <#" + channelID + "> **has been removed!**"); + } + + private void addNoXPChannel(CommandContext context, Server server) { + if (!context.getMember().getPermissions().contains(Permission.MANAGE_SERVER)) { + context.reply("**You need the Manage Server Permission to do that!**"); + return; + } + if (context.getArgs().size() < 3) { + context.reply("**You need to specify a channel to add!**"); + return; + } + String channelID = ParsingUtils.filterSnowflake(context.getArgs().get(2)); + TextChannel channel = context.getGuild().getTextChannelById(channelID); + if (channel == null) { + context.reply("**You need to specify a valid text channel!**"); + return; + } + xpService.addNoXpChannel(channelID, server.getId()); + context.reply("**No XP channel** <#" + channelID + "> **has been added!**"); + } + + private void showNoXPChannels(CommandContext context, Server server) { + List channels = xpService.getNoExperienceChannelsForServer(server.getId(), context.getJDA()) + .stream().map(channel -> "<#" + channel + ">").collect(Collectors.toList()); + + String messageText = channels.isEmpty() ? "No channels to show!" : String.join("\n", channels); + + MessageEmbed noXPChannelsEmbed = new EmbedBuilder() + .setDescription(messageText) + .setColor(Color.ORANGE) + .build(); + context.reply(noXPChannelsEmbed); + } private void toggleLevelUpMessages(CommandContext context, Server server) { boolean userCanToggle = context.getMember().hasPermission(Permission.MANAGE_SERVER); @@ -147,40 +248,11 @@ private String numberToEmoji(int number) { List digits = Arrays.asList(String.valueOf(number).split("")); //Wowzer String finalNumber = ""; for (String digit : digits) { - finalNumber += convertSingleDigitToEmoji(digit); + finalNumber += ParsingUtils.convertSingleDigitToEmoji(digit); } return finalNumber; } - private String convertSingleDigitToEmoji(String digit) { - switch (digit) { - case "0": - return ":zero:"; - case "1": - return ":one:"; - case "2": - return ":two:"; - case "3": - return ":three:"; - case "4": - return ":four:"; - case "5": - return ":five:"; - case "6": - return ":six:"; - case "7": - return ":seven:"; - case "8": - return ":eight:"; - case "9": - return ":nine:"; - case "10": - return ":ten:"; - default: - return ":zero:"; - } - } - private void removeLevelUpRole(CommandContext context, List args, Server server) { if (args.size() < 2) { context.reply("**You need to provide a level to remove the role from!**"); @@ -253,7 +325,8 @@ public String getDescription() { + "Server admins can configure roles that are given to you when you reach a certain level.\n" + "To stop people from checking their XP, you can disable the XP command.\n" + "If you want to stop people from gaining XP, disable the r-xp routine.\n" - + "To disable the level up messages, use the togglemsg subcommand."; + + "To disable the level up messages, use the togglemsg subcommand.\n" + + "To control which channels will not allow members to gain XP, use 'xp noxp help' to see how to set it up!"; } @Override @@ -263,7 +336,8 @@ public String getUsage() { + "xp addrole [level] [role]\n" + "xp removerole [level]\n" + "xp togglemsg\n" - + "xp leaderboard"; + + "xp leaderboard\n" + + "xp noxp [help/list/add/remove/clear]"; } @Override diff --git a/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java b/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java index 86794a63..303ad448 100644 --- a/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java +++ b/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java @@ -1,12 +1,20 @@ package main.java.de.voidtech.gerald.entities; +import java.util.HashSet; +import java.util.Set; + import javax.persistence.Column; +import javax.persistence.ElementCollection; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; + @Entity @Table(name = "serverexperienceconfig") public class ServerExperienceConfig { @@ -21,11 +29,33 @@ public class ServerExperienceConfig { @Column private boolean levelUpMessagesEnabled; + //TODO: Don't fetch EAGER for this. + @ElementCollection(fetch = FetchType.EAGER) + @Cascade(CascadeType.REMOVE) + private Set noExperienceChannels; + @Deprecated //ONLY FOR HIBERNATE, DO NOT USE ServerExperienceConfig() { } + public Set getNoXPChannels() { + if(this.noExperienceChannels == null) return new HashSet(); + else return this.noExperienceChannels; + } + + public void addNoExperienceChannel(String ID) { + this.noExperienceChannels.add(ID); + } + + public void removeNoExperienceChannel(String ID) { + this.noExperienceChannels.remove(ID); + } + + public void clearNoExperienceChannels() { + this.noExperienceChannels.clear(); + } + public ServerExperienceConfig(long serverID) { this.serverID = serverID; this.levelUpMessagesEnabled = true; diff --git a/src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java b/src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java index 1f9da189..97ab9723 100644 --- a/src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java +++ b/src/main/java/de/voidtech/gerald/routines/utils/ExperienceRoutine.java @@ -1,11 +1,15 @@ package main.java.de.voidtech.gerald.routines.utils; +import java.util.List; + import org.springframework.beans.factory.annotation.Autowired; import main.java.de.voidtech.gerald.annotations.Routine; +import main.java.de.voidtech.gerald.entities.Server; import main.java.de.voidtech.gerald.routines.AbstractRoutine; import main.java.de.voidtech.gerald.routines.RoutineCategory; import main.java.de.voidtech.gerald.service.ExperienceService; +import main.java.de.voidtech.gerald.service.ServerService; import net.dv8tion.jda.api.entities.ChannelType; import net.dv8tion.jda.api.entities.Message; @@ -15,9 +19,15 @@ public class ExperienceRoutine extends AbstractRoutine { @Autowired private ExperienceService xpService; + @Autowired + private ServerService serverService; + @Override public void executeInternal(Message message) { if (message.getChannel().getType().equals(ChannelType.PRIVATE)) return; + Server server = serverService.getServer(message.getGuild().getId()); + List noxp = xpService.getNoExperienceChannelsForServer(server.getId(), message.getJDA()); + if (noxp.contains(message.getChannel().getId())) return; xpService.updateUserExperience(message.getMember(), message.getGuild().getId(), message.getChannel().getId()); } diff --git a/src/main/java/de/voidtech/gerald/service/ExperienceService.java b/src/main/java/de/voidtech/gerald/service/ExperienceService.java index 7c1075dd..4ee98ea0 100644 --- a/src/main/java/de/voidtech/gerald/service/ExperienceService.java +++ b/src/main/java/de/voidtech/gerald/service/ExperienceService.java @@ -5,10 +5,12 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.xml.bind.DatatypeConverter; @@ -23,6 +25,7 @@ import main.java.de.voidtech.gerald.entities.Server; import main.java.de.voidtech.gerald.entities.ServerExperienceConfig; import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.Role; @@ -75,6 +78,17 @@ public Experience getUserExperience(String userID, long serverID) { } } + public List getNoExperienceChannelsForServer(long serverID, JDA jda) { + List channels = new ArrayList(); + ServerExperienceConfig config = getServerExperienceConfig(serverID); + config.getNoXPChannels() + .stream() + .filter(channel -> jda.getTextChannelById(channel) == null) + .forEach(channel -> config.removeNoExperienceChannel(channel)); + channels = config.getNoXPChannels().stream().collect(Collectors.toList()); + return channels; + } + private ServerExperienceConfig getServerExperienceConfig(long serverID) { try(Session session = sessionFactory.openSession()) { @@ -203,6 +217,24 @@ public void removeLevelUpRole(long level, long serverID) { } } + public void deleteNoXpChannel(String channelID, long serverID) { + ServerExperienceConfig config = getServerExperienceConfig(serverID); + config.removeNoExperienceChannel(channelID); + saveServerExperienceConfig(config); + } + + public void clearNoXpChannels(long serverID) { + ServerExperienceConfig config = getServerExperienceConfig(serverID); + config.clearNoExperienceChannels(); + saveServerExperienceConfig(config); + } + + public void addNoXpChannel(String channelID, long serverID) { + ServerExperienceConfig config = getServerExperienceConfig(serverID); + config.addNoExperienceChannel(channelID); + saveServerExperienceConfig(config); + } + public long xpNeededForLevel(long level) { return (long) Math.ceil(Math.sqrt(5000 * (Math.pow(level, 3)))); } diff --git a/src/main/java/de/voidtech/gerald/util/ParsingUtils.java b/src/main/java/de/voidtech/gerald/util/ParsingUtils.java index 65120a16..2a5789f5 100644 --- a/src/main/java/de/voidtech/gerald/util/ParsingUtils.java +++ b/src/main/java/de/voidtech/gerald/util/ParsingUtils.java @@ -11,6 +11,35 @@ public class ParsingUtils { private static final Pattern HEX_PATTERN = Pattern.compile("^([a-fA-F0-9]{6})$"); + public static String convertSingleDigitToEmoji(String digit) { + switch (digit) { + case "0": + return ":zero:"; + case "1": + return ":one:"; + case "2": + return ":two:"; + case "3": + return ":three:"; + case "4": + return ":four:"; + case "5": + return ":five:"; + case "6": + return ":six:"; + case "7": + return ":seven:"; + case "8": + return ":eight:"; + case "9": + return ":nine:"; + case "10": + return ":ten:"; + default: + return ":zero:"; + } + } + public static boolean isInteger(String str) { if (str == null) { return false; From 6ec502bec8449acdcd3cfc665af4ab7929815925 Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 31 May 2022 01:45:39 +0100 Subject: [PATCH 24/26] refac(service): Added message reply embed to webhooks --- .../commands/fun/ImpersonateCommand.java | 2 +- .../gerald/routines/utils/TunnelRoutine.java | 9 +--- .../gerald/service/NitroliteService.java | 2 +- .../gerald/service/WebhookManager.java | 53 ++++++++++++------- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/commands/fun/ImpersonateCommand.java b/src/main/java/de/voidtech/gerald/commands/fun/ImpersonateCommand.java index 19a1243a..2c35c1b6 100644 --- a/src/main/java/de/voidtech/gerald/commands/fun/ImpersonateCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/fun/ImpersonateCommand.java @@ -49,7 +49,7 @@ private void sendWebhookMessage(CommandContext context, List args, Membe messageToBeSent.append(args.get(i)).append(" "); } Webhook impersonateHook = webhookManager.getOrCreateWebhook((TextChannel) context.getChannel(), "BGImpersonate", context.getJDA().getSelfUser().getId()); - webhookManager.postMessage(messageToBeSent.toString(), memberToBeImpersonated.getUser().getAvatarUrl(), memberToBeImpersonated.getUser().getName(), impersonateHook); + webhookManager.postMessage(messageToBeSent.toString(), null, memberToBeImpersonated.getUser().getAvatarUrl(), memberToBeImpersonated.getUser().getName(), impersonateHook); //TODO (from: Franziska): Message needs to be deleted, message context does not have a message object. Should we add one? Do we do this somehow else? Should this command be available through slashes at all!? //context.delete().queue(); diff --git a/src/main/java/de/voidtech/gerald/routines/utils/TunnelRoutine.java b/src/main/java/de/voidtech/gerald/routines/utils/TunnelRoutine.java index a508b5b2..d64d7496 100644 --- a/src/main/java/de/voidtech/gerald/routines/utils/TunnelRoutine.java +++ b/src/main/java/de/voidtech/gerald/routines/utils/TunnelRoutine.java @@ -68,15 +68,8 @@ private void sendWebhookMessage(Webhook webhook, String content, Message message } content = contentBuilder.toString(); } - - if (message.getReferencedMessage() != null) { - content = "> " + (message.getReferencedMessage().getContentDisplay().length() > 100 ? - message.getReferencedMessage().getContentDisplay().substring(0, 100) + "..." : - message.getReferencedMessage().getContentDisplay()) - + "\n" + content; - } - webhookManager.postMessage(content, message.getAuthor().getAvatarUrl(), message.getAuthor().getName(), webhook); + webhookManager.postMessage(content, message.getReferencedMessage(), message.getAuthor().getAvatarUrl(), message.getAuthor().getName(), webhook); } private void sendTunnelMessage(Tunnel tunnel, Message message) { diff --git a/src/main/java/de/voidtech/gerald/service/NitroliteService.java b/src/main/java/de/voidtech/gerald/service/NitroliteService.java index 22f5eba2..5d773898 100644 --- a/src/main/java/de/voidtech/gerald/service/NitroliteService.java +++ b/src/main/java/de/voidtech/gerald/service/NitroliteService.java @@ -109,7 +109,7 @@ public String constructEmoteString(NitroliteEmote emote) { private void sendWebhookMessage(Message message, String content) { Webhook webhook = webhookManager.getOrCreateWebhook((TextChannel) message.getChannel(), "BGNitrolite", message.getJDA().getSelfUser().getId()); - webhookManager.postMessage(content, message.getAuthor().getAvatarUrl(), message.getMember().getEffectiveName(), webhook); + webhookManager.postMessage(content, message.getReferencedMessage(), message.getAuthor().getAvatarUrl(), message.getMember().getEffectiveName(), webhook); } public List processNitroliteMessage(Message message) { diff --git a/src/main/java/de/voidtech/gerald/service/WebhookManager.java b/src/main/java/de/voidtech/gerald/service/WebhookManager.java index 82725de0..dec1c0ac 100644 --- a/src/main/java/de/voidtech/gerald/service/WebhookManager.java +++ b/src/main/java/de/voidtech/gerald/service/WebhookManager.java @@ -1,15 +1,6 @@ package main.java.de.voidtech.gerald.service; -import main.java.de.voidtech.gerald.commands.CommandContext; -import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.GuildChannel; -import net.dv8tion.jda.api.entities.TextChannel; -import net.dv8tion.jda.api.entities.Webhook; -import org.json.JSONObject; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import javax.net.ssl.HttpsURLConnection; +import java.awt.Color; import java.io.OutputStream; import java.net.URL; import java.util.EnumSet; @@ -17,6 +8,22 @@ import java.util.logging.Level; import java.util.logging.Logger; +import javax.net.ssl.HttpsURLConnection; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import main.java.de.voidtech.gerald.commands.CommandContext; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.GuildChannel; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.Webhook; + @Service public class WebhookManager { @@ -36,7 +43,7 @@ public Webhook getOrCreateWebhook(TextChannel targetChannel, String webhookName, return targetChannel.createWebhook(webhookName).complete(); } - private void executeWebhookPost(String content, String avatarUrl, String username, Webhook webhook) { + private void executeWebhookPost(String content, Message referencedMessage, String avatarUrl, String username, Webhook webhook) { String messageToBeSent = content.replaceAll("@everyone", "``@``everyone").replaceAll("@here", "``@``here"); JSONObject webhookPayload = new JSONObject(); @@ -44,8 +51,19 @@ private void executeWebhookPost(String content, String avatarUrl, String usernam webhookPayload.put("username", username); webhookPayload.put("avatar_url", avatarUrl); webhookPayload.put("tts", false); + + + if (referencedMessage != null) { + MessageEmbed replyTextEmbed = new EmbedBuilder() + .setTitle("Replying to this message", referencedMessage.getJumpUrl()) + .setDescription(referencedMessage.getContentRaw()) + .setColor(new Color(47,49,54)) + .setFooter("Original message from " + referencedMessage.getAuthor().getName(), referencedMessage.getAuthor().getAvatarUrl()) + .build(); + webhookPayload.put("embeds", new JSONArray().put(new JSONObject(replyTextEmbed.toData().toString()))); + } + try { - URL url = new URL(webhook.getUrl()); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); @@ -53,7 +71,7 @@ private void executeWebhookPost(String content, String avatarUrl, String usernam connection.addRequestProperty("User-Agent", "Barista-Gerald"); connection.setDoOutput(true); connection.setRequestMethod("POST"); - + OutputStream stream = connection.getOutputStream(); stream.write(webhookPayload.toString().getBytes()); stream.flush(); @@ -68,8 +86,8 @@ private void executeWebhookPost(String content, String avatarUrl, String usernam } } - public void postMessage(String content, String avatarUrl, String username, Webhook webhook) { - Runnable webhookThreadRunnable = () -> executeWebhookPost(content, avatarUrl, username, webhook); + public void postMessage(String content, Message referencedMessage, String avatarUrl, String username, Webhook webhook) { + Runnable webhookThreadRunnable = () -> executeWebhookPost(content, referencedMessage, avatarUrl, username, webhook); threadManager.getThreadByName("T-Webhook").execute(webhookThreadRunnable); } @@ -77,10 +95,7 @@ public void postMessageWithFallback(CommandContext context, String content, Stri EnumSet perms = context.getGuild().getSelfMember().getPermissions((GuildChannel) context.getChannel()); if (perms.contains(Permission.MANAGE_WEBHOOKS)) { - postMessage( - content, - avatarUrl, - username, + postMessage(content, null, avatarUrl, username, getOrCreateWebhook((TextChannel) context.getChannel(), webhookName, context.getJDA().getSelfUser().getId()) From cafecd9f96911a134140d29d51b5d847b9c5562c Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 31 May 2022 01:45:56 +0100 Subject: [PATCH 25/26] feat(command): Added action stats leaderboard --- .../commands/actions/ActionsCommand.java | 136 +++++++++++++----- 1 file changed, 104 insertions(+), 32 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/commands/actions/ActionsCommand.java b/src/main/java/de/voidtech/gerald/commands/actions/ActionsCommand.java index fe50038b..0841aa75 100644 --- a/src/main/java/de/voidtech/gerald/commands/actions/ActionsCommand.java +++ b/src/main/java/de/voidtech/gerald/commands/actions/ActionsCommand.java @@ -1,27 +1,31 @@ package main.java.de.voidtech.gerald.commands.actions; -import main.java.de.voidtech.gerald.commands.AbstractCommand; -import main.java.de.voidtech.gerald.commands.CommandContext; -import main.java.de.voidtech.gerald.entities.ActionStats; -import main.java.de.voidtech.gerald.service.ServerService; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.entities.MessageEmbed; -import org.hibernate.Session; -import org.hibernate.SessionFactory; -import org.json.JSONException; -import org.json.JSONObject; -import org.springframework.beans.factory.annotation.Autowired; - -import java.awt.*; +import java.awt.Color; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; + +import main.java.de.voidtech.gerald.commands.AbstractCommand; +import main.java.de.voidtech.gerald.commands.CommandContext; +import main.java.de.voidtech.gerald.entities.ActionStats; +import main.java.de.voidtech.gerald.entities.Server; +import main.java.de.voidtech.gerald.service.ServerService; +import main.java.de.voidtech.gerald.util.ParsingUtils; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageEmbed; + public abstract class ActionsCommand extends AbstractCommand { private static final String API_URL = "http://api.nekos.fun:8080/api/"; @@ -63,6 +67,32 @@ private ActionStats getOrCreateProfile(String id, ActionType action, long server } return stats; } + + @SuppressWarnings("unchecked") + private List getTopGivenInServer(ActionType type, long serverID) { + try(Session session = sessionFactory.openSession()) + { + return (List) session + .createQuery("FROM ActionStats WHERE type = :type AND serverID = :serverID AND givenCount > 0 ORDER BY givenCount DESC") + .setMaxResults(5) + .setParameter("type", type.getType()) + .setParameter("serverID", serverID) + .list(); + } + } + + @SuppressWarnings("unchecked") + private List getTopReceivedInServer(ActionType type, long serverID) { + try(Session session = sessionFactory.openSession()) + { + return (List) session + .createQuery("FROM ActionStats WHERE type = :type AND serverID = :serverID AND receivedCount > 0 ORDER BY receivedCount DESC") + .setMaxResults(5) + .setParameter("type", type.getType()) + .setParameter("serverID", serverID) + .list(); + } + } private void updateStatsProfile(ActionStats stats) { try(Session session = sessionFactory.openSession()) @@ -103,29 +133,71 @@ private String getStatsString(String giver, String receiver, ActionType action, } public void sendAction(CommandContext context, ActionType action) { - if(context.getMentionedMembers().isEmpty()) { + if (context.getArgs().get(0).equals("leaderboard")) { + sendActionLeaderboard(context, action); + return; + } + if (context.getMentionedMembers().isEmpty()) { context.reply("You need to mention someone to " + action.getType() + "!"); - } else { - String gifURL = getActionGif(action.getType()); - if (gifURL != null) - { - updateActionStats(context.getAuthor().getId(), context.getMentionedMembers().get(0).getId(), action, context); - String phrase = String.format("%s %s %s", context.getMember().getEffectiveName(), conjugateAction(action.getType()), - context.getMentionedMembers().get(0).getId().equals(context.getAuthor().getId()) ? "themself" : context.getMentionedMembers().get(0).getEffectiveName()); - - EmbedBuilder actionEmbedBuilder = new EmbedBuilder(); - actionEmbedBuilder.setTitle(phrase); - actionEmbedBuilder.setColor(Color.ORANGE); - if (!gifURL.equals("")) { - actionEmbedBuilder.setImage(gifURL); - } - actionEmbedBuilder.setFooter(getStatsString(context.getAuthor().getId(), context.getMentionedMembers().get(0).getId(), action, context)); - MessageEmbed actionEmbed = actionEmbedBuilder.build(); - context.reply(actionEmbed); - } + return; + } + String gifURL = getActionGif(action.getType()); + if (gifURL != null) { + updateActionStats(context.getAuthor().getId(), context.getMentionedMembers().get(0).getId(), action, context); + String phrase = String.format("%s %s %s", + context.getMember().getEffectiveName(), + conjugateAction(action.getType()), + context.getMentionedMembers().get(0).getId().equals(context.getAuthor().getId()) ? + "themself" : context.getMentionedMembers().get(0).getEffectiveName()); + //Wow that was a big one + EmbedBuilder actionEmbedBuilder = new EmbedBuilder(); + actionEmbedBuilder.setTitle(phrase); + actionEmbedBuilder.setColor(Color.ORANGE); + if (!gifURL.equals("")) actionEmbedBuilder.setImage(gifURL); + actionEmbedBuilder.setFooter(getStatsString(context.getAuthor().getId(), context.getMentionedMembers().get(0).getId(), action, context)); + MessageEmbed actionEmbed = actionEmbedBuilder.build(); + context.reply(actionEmbed); } } + private void sendActionLeaderboard(CommandContext context, ActionType action) { + Server server = serverService.getServer(context.getGuild().getId()); + List topGiven = getTopGivenInServer(action, server.getId()); + List topReceived = getTopReceivedInServer(action, server.getId()); + + StringBuilder leaderboardBuilder = new StringBuilder(); + int i = 1; + leaderboardBuilder.append("**Top 5 " + action.getType() + " givers**\n\n"); + for (ActionStats stat : topGiven) { + leaderboardBuilder.append(ParsingUtils.convertSingleDigitToEmoji(String.valueOf(i))); + leaderboardBuilder.append(" "); + leaderboardBuilder.append(context.getGuild().retrieveMemberById(stat.getMember()).complete().getAsMention()); + leaderboardBuilder.append(" - `"); + leaderboardBuilder.append(stat.getGivenCount()); + leaderboardBuilder.append("`\n"); + i++; + } + if (i == 1) leaderboardBuilder.append("Nobody to show! Go " + action.getType() + " someone!\n"); + i = 1; + leaderboardBuilder.append("**\nTop 5 " + action.getType() + " receivers**\n\n"); + for (ActionStats stat : topReceived) { + leaderboardBuilder.append(ParsingUtils.convertSingleDigitToEmoji(String.valueOf(i))); + leaderboardBuilder.append(" "); + leaderboardBuilder.append(context.getGuild().retrieveMemberById(stat.getMember()).complete().getAsMention()); + leaderboardBuilder.append(" - `"); + leaderboardBuilder.append(stat.getReceivedCount()); + leaderboardBuilder.append("`\n"); + i++; + } + if (i == 1) leaderboardBuilder.append("Nobody to show! Go " + action.getType() + " someone!"); + MessageEmbed leaderboardEmbed = new EmbedBuilder() + .setColor(Color.ORANGE) + .setTitle(context.getGuild().getName() + "'s " + action.getType() + " leaderboard") + .setDescription(leaderboardBuilder.toString()) + .build(); + context.reply(leaderboardEmbed); + } + private String conjugateAction(String action) { String conjugatedAction = action; From 4fb943b5f1c55fa3cc9152e85619749f6913bf3a Mon Sep 17 00:00:00 2001 From: Seb Date: Tue, 31 May 2022 21:05:53 +0100 Subject: [PATCH 26/26] Incremented version and disabled level up messages by default --- src/main/java/de/voidtech/gerald/GlobalConstants.java | 2 +- .../de/voidtech/gerald/entities/ServerExperienceConfig.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/voidtech/gerald/GlobalConstants.java b/src/main/java/de/voidtech/gerald/GlobalConstants.java index 4689873d..84352543 100644 --- a/src/main/java/de/voidtech/gerald/GlobalConstants.java +++ b/src/main/java/de/voidtech/gerald/GlobalConstants.java @@ -4,5 +4,5 @@ public class GlobalConstants { public static final String STREAM_URL = "https://twitch.tv/elementalmp4"; public static final String LINKTREE_URL = "https://linktr.ee/GeraldBot"; public static final String INVITE_URL = "https://discord.com/api/oauth2/authorize?client_id=555816892141404163&permissions=805694544&scope=bot%20applications.commands"; - public static final String VERSION = "1.2.8 - Mucho Mocha"; + public static final String VERSION = "1.3.0 - Delightful Doppio"; } diff --git a/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java b/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java index 303ad448..1b3602e2 100644 --- a/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java +++ b/src/main/java/de/voidtech/gerald/entities/ServerExperienceConfig.java @@ -58,7 +58,7 @@ public void clearNoExperienceChannels() { public ServerExperienceConfig(long serverID) { this.serverID = serverID; - this.levelUpMessagesEnabled = true; + this.levelUpMessagesEnabled = false; } public boolean levelUpMessagesEnabled() {