package cc.fascinated.bat.service; import cc.fascinated.bat.common.TimerUtils; import cc.fascinated.bat.event.EventListener; import cc.fascinated.bat.model.BatGuild; import cc.fascinated.bat.model.BatUser; import com.mongodb.client.model.Filters; import lombok.Getter; import lombok.NonNull; import lombok.extern.log4j.Log4j2; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent; import net.dv8tion.jda.api.events.user.update.UserUpdateGlobalNameEvent; import net.jodah.expiringmap.ExpirationPolicy; import net.jodah.expiringmap.ExpiringMap; import org.bson.Document; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * @author Fascinated (fascinated7) */ @Service @Log4j2(topic = "User Service") @Getter @DependsOn({"discordService", "mongoService"}) public class UserService implements EventListener { private static final long SAVE_INTERVAL = TimeUnit.MINUTES.toMillis(5); /** * The cached users */ private final Map users = ExpiringMap.builder() .expiration(1, TimeUnit.HOURS) .expirationPolicy(ExpirationPolicy.ACCESSED) // Expire after not being accessed for 1 hour .expirationListener((key, cachedUser) -> { BatUser user = (BatUser) cachedUser; log.info("Removed user \"{}\" - \"{}\" from cache", user.getName(), user.getId()); user.save(); }) .build(); @Autowired public UserService() { TimerUtils.scheduleRepeating(() -> { long before = System.currentTimeMillis(); for (BatUser user : users.values()) { user.save(); } log.info("Saved {} users in {}ms", users.size(), System.currentTimeMillis() - before); }, SAVE_INTERVAL, SAVE_INTERVAL); } /** * Gets a user by its ID * * @param id The ID of the user * @return The user */ public BatUser getUser(@NonNull String id, User user) { long before = System.currentTimeMillis(); // User is cached if (users.containsKey(id)) { return users.get(id); } // User is not cached Document document = MongoService.INSTANCE.getUsersCollection().find(Filters.eq("_id", id)).first(); if (document != null) { BatUser batUser = new BatUser(id, user, document); users.put(id, batUser); return batUser; } // New user BatUser batUser = new BatUser(id, user, new Document()); users.put(id, batUser); log.info("Created user \"{}\" - \"{}\" in {}ms", batUser.getName(), batUser.getId(), System.currentTimeMillis() - before); return batUser; } /** * Gets a user by its ID * * @param id The ID of the user * @return The user */ public BatUser getUser(@NonNull String id) { User user = DiscordService.JDA.retrieveUserById(id).complete(); if (user == null || user.isBot()) { return null; } return getUser(id, null); } @Override public void onShutdown() { log.info("Saving all users before shutdown..."); for (BatUser user : users.values()) { user.save(); } log.info("Saved all users."); } @Override public void onGuildMemberJoin(@NonNull BatGuild guild, @NonNull BatUser user, @NonNull GuildMemberJoinEvent event) { user.setGlobalName(event.getUser().getGlobalName()); // Ensure the user's name is up-to-date } @Override public void onUserUpdateGlobalName(@NonNull BatUser user, String oldName, String newName, @NonNull UserUpdateGlobalNameEvent event) { user.setGlobalName(newName); } }