diff --git a/README.md b/README.md index 0e2266f3e..854bebc38 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Get the latest development builds from our [GitHub Actions](https://github.com/E - Chat On/Off Switch - Chat Slow Mode - /ignore and /unignore (with -all option) - - /msg, /socialspy, and /reply commands + - /msg, /msgtoggle, /socialspy, and /reply commands - /helpop command - Advanced Notification System allowing you to customize every message to your liking (Title, Subtitle, Actionbar, Chat, etc.) - :hammer: Open Utility Blocks with simple commands like `/workbench` diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatState.java b/eternalcore-api/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatState.java new file mode 100644 index 000000000..8c55b6203 --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatState.java @@ -0,0 +1,17 @@ +package com.eternalcode.core.feature.privatechat.toggle; + +/** + * Enum representing state of blocking incoming private messages by the player. + */ +public enum PrivateChatState { + + /** + * Player can receive private messages. + */ + ENABLE, + + /** + * Player cannot receive private messages. + */ + DISABLE +} diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggle.java b/eternalcore-api/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggle.java new file mode 100644 index 000000000..b783529f4 --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggle.java @@ -0,0 +1,25 @@ +package com.eternalcode.core.feature.privatechat.toggle; + +import java.util.UUID; + +/** + * Represents a player's private chat toggle state. + */ +public class PrivateChatToggle { + + UUID uuid; + PrivateChatState state; + + public PrivateChatToggle(UUID uuid, PrivateChatState toggle) { + this.uuid = uuid; + this.state = toggle; + } + + public UUID getUuid() { + return uuid; + } + + public PrivateChatState getState() { + return state; + } +} diff --git a/eternalcore-api/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleService.java b/eternalcore-api/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleService.java new file mode 100644 index 000000000..3328a5865 --- /dev/null +++ b/eternalcore-api/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleService.java @@ -0,0 +1,27 @@ +package com.eternalcode.core.feature.privatechat.toggle; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +/** + * This Service manages the state of player's private chat messages blocking + */ +public interface PrivateChatToggleService { + + /** + * Checks status of player's private chat messages blocking. + * + * @param uuid player's UUID. + * @return state of player's private chat messages blocking. + */ + CompletableFuture getPrivateChatToggleState(UUID uuid); + + /** + * Sets blocking of incoming private messages. + * + * @param uuid player's UUID. + * @param toggle desired state of player's private chat messages blocking. + */ + void togglePrivateChat(UUID uuid, PrivateChatState toggle); + +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/PrivateChatServiceImpl.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/PrivateChatServiceImpl.java index 46e11a569..6fda06d0d 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/PrivateChatServiceImpl.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/PrivateChatServiceImpl.java @@ -2,6 +2,8 @@ import com.eternalcode.core.event.EventCaller; import com.eternalcode.core.feature.ignore.IgnoreService; +import com.eternalcode.core.feature.privatechat.toggle.PrivateChatToggleService; +import com.eternalcode.core.feature.privatechat.toggle.PrivateChatState; import com.eternalcode.core.injector.annotations.Inject; import com.eternalcode.core.injector.annotations.component.Service; import com.eternalcode.core.notice.NoticeService; @@ -24,6 +26,7 @@ class PrivateChatServiceImpl implements PrivateChatService { private final UserManager userManager; private final PrivateChatPresenter presenter; private final EventCaller eventCaller; + private final PrivateChatToggleService privateChatToggleService; private final Cache replies = CacheBuilder.newBuilder() .expireAfterWrite(Duration.ofHours(1)) @@ -36,12 +39,14 @@ class PrivateChatServiceImpl implements PrivateChatService { NoticeService noticeService, IgnoreService ignoreService, UserManager userManager, - EventCaller eventCaller + EventCaller eventCaller, + PrivateChatToggleService privateChatToggleService ) { this.noticeService = noticeService; this.ignoreService = ignoreService; this.userManager = userManager; this.eventCaller = eventCaller; + this.privateChatToggleService = privateChatToggleService; this.presenter = new PrivateChatPresenter(noticeService); } @@ -53,15 +58,25 @@ void privateMessage(User sender, User target, String message) { return; } - this.ignoreService.isIgnored(target.getUniqueId(), sender.getUniqueId()).thenAccept(isIgnored -> { - if (!isIgnored) { - this.replies.put(target.getUniqueId(), sender.getUniqueId()); - this.replies.put(sender.getUniqueId(), target.getUniqueId()); + UUID uniqueId = target.getUniqueId(); + + this.privateChatToggleService.getPrivateChatToggleState(uniqueId).thenAccept(privateChatToggleState -> { + if (privateChatToggleState == PrivateChatState.DISABLE) { + this.noticeService.player(sender.getUniqueId(), translation -> translation.privateChat().receiverDisabledMessages()); + + return; } - PrivateChatEvent event = new PrivateChatEvent(sender.getUniqueId(), target.getUniqueId(), message); - this.eventCaller.callEvent(event); - this.presenter.onPrivate(new PrivateMessage(sender, target, event.getContent(), this.socialSpy, isIgnored)); + this.ignoreService.isIgnored(uniqueId, sender.getUniqueId()).thenAccept(isIgnored -> { + if (!isIgnored) { + this.replies.put(uniqueId, sender.getUniqueId()); + this.replies.put(sender.getUniqueId(), uniqueId); + } + + PrivateChatEvent event = new PrivateChatEvent(sender.getUniqueId(), uniqueId, message); + this.eventCaller.callEvent(event); + this.presenter.onPrivate(new PrivateMessage(sender, target, event.getContent(), this.socialSpy, isIgnored)); + }); }); } diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleCommand.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleCommand.java new file mode 100644 index 000000000..ed9ada389 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleCommand.java @@ -0,0 +1,134 @@ +package com.eternalcode.core.feature.privatechat.toggle; + +import com.eternalcode.annotations.scan.command.DescriptionDocs; +import com.eternalcode.core.injector.annotations.Inject; +import com.eternalcode.core.notice.NoticeService; +import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.context.Context; +import dev.rollczi.litecommands.annotations.execute.Execute; +import dev.rollczi.litecommands.annotations.optional.OptionalArg; +import dev.rollczi.litecommands.annotations.permission.Permission; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +@Command(name = "msgtoggle") +@Permission("eternalcore.msgtoggle") +public class PrivateChatToggleCommand { + + private final PrivateChatToggleService privateChatToggleService; + private final NoticeService noticeService; + + @Inject + public PrivateChatToggleCommand(PrivateChatToggleService privateChatToggleService, NoticeService noticeService) { + this.privateChatToggleService = privateChatToggleService; + this.noticeService = noticeService; + } + + @Execute + @DescriptionDocs(description = "Switch receiving private messages", arguments = "") + public void execute(@Context Player sender, @OptionalArg PrivateChatState state) { + UUID uniqueId = sender.getUniqueId(); + + if (state == null) { + CompletableFuture completablePresentState = this.privateChatToggleService.getPrivateChatToggleState(sender.getUniqueId()); + + completablePresentState.thenAccept(presentState -> { + if (presentState == PrivateChatState.DISABLE) { + this.enable(uniqueId); + } + else { + this.disable(uniqueId); + } + }); + + return; + } + + if (state == PrivateChatState.DISABLE) { + this.disable(uniqueId); + } + else { + this.enable(uniqueId); + } + } + + @Execute + @Permission("eternalcore.msgtoggle.other") + @DescriptionDocs(description = "Switch receiving private messages for other player", arguments = " ") + public void other(@Context CommandSender sender, @Arg("player") Player target, @OptionalArg PrivateChatState state) { + + UUID uniqueId = target.getUniqueId(); + + if (state == null) { + CompletableFuture completablePresentState = this.privateChatToggleService.getPrivateChatToggleState(uniqueId); + completablePresentState.thenAccept(presentState -> { + if (presentState == PrivateChatState.DISABLE) { + handleToggle(sender, target, PrivateChatState.ENABLE); + } + else { + handleToggle(sender, target, PrivateChatState.DISABLE); + } + }); + + return; + } + + handleToggle(sender, target, state); + } + + private void handleToggle(CommandSender sender, Player target, @NotNull PrivateChatState desiredState) { + UUID uniqueId = target.getUniqueId(); + + if (desiredState == PrivateChatState.DISABLE) { + this.disable(uniqueId); + + if (!this.isCommandSenderSameAsTarget(sender, target)) { + this.noticeService.create() + .notice(translation -> translation.privateChat().otherMessagesDisabled()) + .sender(sender) + .placeholder("{PLAYER}", target.getName()) + .send(); + } + } + else { + this.enable(uniqueId); + + if (!this.isCommandSenderSameAsTarget(sender, target)) { + this.noticeService.create() + .notice(translation -> translation.privateChat().otherMessagesEnabled()) + .sender(sender) + .placeholder("{PLAYER}", target.getName()) + .send(); + } + } + } + + private void enable(UUID uniqueId) { + this.privateChatToggleService.togglePrivateChat(uniqueId, PrivateChatState.ENABLE); + + this.noticeService.create() + .notice(translation -> translation.privateChat().selfMessagesEnabled()) + .player(uniqueId) + .send(); + } + + private void disable(UUID uniqueId) { + this.privateChatToggleService.togglePrivateChat(uniqueId, PrivateChatState.DISABLE); + + this.noticeService.create() + .notice(translation -> translation.privateChat().selfMessagesDisabled()) + .player(uniqueId) + .send(); + } + + private boolean isCommandSenderSameAsTarget(CommandSender context, Player player) { + if (context instanceof Player commandSender) { + return commandSender.getUniqueId().equals(player.getUniqueId()); + } + return false; + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleRepository.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleRepository.java new file mode 100644 index 000000000..08950a700 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleRepository.java @@ -0,0 +1,13 @@ +package com.eternalcode.core.feature.privatechat.toggle; + + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +interface PrivateChatToggleRepository { + + CompletableFuture getPrivateChatToggleState(UUID uuid); + + CompletableFuture setPrivateChatToggle(UUID uuid, PrivateChatState toggledOff); + +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleRepositoryOrmLite.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleRepositoryOrmLite.java new file mode 100644 index 000000000..c2bf9c860 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleRepositoryOrmLite.java @@ -0,0 +1,35 @@ +package com.eternalcode.core.feature.privatechat.toggle; + +import com.eternalcode.commons.scheduler.Scheduler; +import com.eternalcode.core.database.DatabaseManager; +import com.eternalcode.core.database.wrapper.AbstractRepositoryOrmLite; +import com.eternalcode.core.injector.annotations.Inject; +import com.eternalcode.core.injector.annotations.component.Repository; +import com.j256.ormlite.table.TableUtils; +import java.sql.SQLException; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +@Repository +class PrivateChatToggleRepositoryOrmLite extends AbstractRepositoryOrmLite implements PrivateChatToggleRepository { + + @Inject + private PrivateChatToggleRepositoryOrmLite(DatabaseManager databaseManager, Scheduler scheduler) throws SQLException { + super(databaseManager, scheduler); + TableUtils.createTableIfNotExists(databaseManager.connectionSource(), PrivateChatToggleWrapper.class); + } + + @Override + public CompletableFuture getPrivateChatToggleState(UUID uuid) { + return this.selectSafe(PrivateChatToggleWrapper.class, uuid) + .thenApply( + optional -> optional.map(PrivateChatToggleWrapper::isEnabled).orElse(PrivateChatState.ENABLE) + ); + } + + @Override + public CompletableFuture setPrivateChatToggle(UUID uuid, PrivateChatState toggledOff) { + return this.save(PrivateChatToggleWrapper.class, new PrivateChatToggleWrapper(uuid, toggledOff)) + .thenApply(status -> null); + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleServiceImpl.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleServiceImpl.java new file mode 100644 index 000000000..81d55b9d9 --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleServiceImpl.java @@ -0,0 +1,36 @@ +package com.eternalcode.core.feature.privatechat.toggle; + +import com.eternalcode.core.injector.annotations.Inject; +import com.eternalcode.core.injector.annotations.component.Service; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +@Service +public class PrivateChatToggleServiceImpl implements PrivateChatToggleService { + + private final PrivateChatToggleRepository msgToggleRepository; + private final ConcurrentHashMap cachedToggleStates; + + @Inject + public PrivateChatToggleServiceImpl(PrivateChatToggleRepository msgToggleRepository) { + this.cachedToggleStates = new ConcurrentHashMap<>(); + this.msgToggleRepository = msgToggleRepository; + } + + + @Override + public CompletableFuture getPrivateChatToggleState(UUID uuid) { + if (this.cachedToggleStates.containsKey(uuid)) { + return CompletableFuture.completedFuture(this.cachedToggleStates.get(uuid)); + } + + return this.msgToggleRepository.getPrivateChatToggleState(uuid); + } + + @Override + public void togglePrivateChat(UUID uuid, PrivateChatState toggle) { + this.cachedToggleStates.put(uuid, toggle); + this.msgToggleRepository.setPrivateChatToggle(uuid, toggle); + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleWrapper.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleWrapper.java new file mode 100644 index 000000000..0b028726c --- /dev/null +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/privatechat/toggle/PrivateChatToggleWrapper.java @@ -0,0 +1,34 @@ +package com.eternalcode.core.feature.privatechat.toggle; + +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; +import java.util.UUID; + +@DatabaseTable(tableName = "eternal_core_private_chat_toggle") +class PrivateChatToggleWrapper { + + @DatabaseField(columnName = "id", id = true) + private UUID uniqueId; + + @DatabaseField(columnName = "enabled") + private PrivateChatState state; + + PrivateChatToggleWrapper(UUID id, PrivateChatState enabled) { + this.uniqueId = id; + this.state = enabled; + } + + PrivateChatToggleWrapper() {} + + static PrivateChatToggleWrapper from(PrivateChatToggle msgToggle) { + return new PrivateChatToggleWrapper(msgToggle.getUuid(), msgToggle.getState()); + } + + PrivateChatState isEnabled() { + return this.state; + } + + void setState(PrivateChatState state) { + this.state = state; + } +} diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/translation/Translation.java b/eternalcore-core/src/main/java/com/eternalcode/core/translation/Translation.java index 0fbd21922..386154506 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/translation/Translation.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/translation/Translation.java @@ -260,6 +260,12 @@ interface PrivateChatSection { Notice socialSpyEnable(); Notice socialSpyDisable(); + Notice receiverDisabledMessages(); + Notice selfMessagesDisabled(); + Notice selfMessagesEnabled(); + Notice otherMessagesDisabled(); + Notice otherMessagesEnabled(); + Notice ignorePlayer(); Notice ignoreAll(); Notice unIgnorePlayer(); diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/ENTranslation.java b/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/ENTranslation.java index 3a7446ce3..e3413fdcd 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/ENTranslation.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/ENTranslation.java @@ -496,6 +496,18 @@ public static class ENPrivateSection implements PrivateChatSection { public Notice socialSpyEnable = Notice.chat("SocialSpy has been {STATE}!"); public Notice socialSpyDisable = Notice.chat("SocialSpy has been {STATE}!"); + + public Notice receiverDisabledMessages = Notice.chat("This player has disabled private messages!"); + + public Notice selfMessagesDisabled = Notice.chat("Private messages have been disabled!"); + public Notice selfMessagesEnabled = Notice.chat("Private messages have been enabled!"); + + @Description("# {PLAYER} - Player") + public Notice otherMessagesDisabled = Notice.chat("Private messages have been disabled for {PLAYER}!"); + public Notice otherMessagesEnabled = Notice.chat("Private messages have been enabled for {PLAYER}!"); + + + @Description({" ", "# {PLAYER} - Ignored player"}) public Notice ignorePlayer = Notice.chat("► {PLAYER} player has been ignored!"); diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/PLTranslation.java b/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/PLTranslation.java index c1cfa5529..4bf276d2c 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/PLTranslation.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/translation/implementation/PLTranslation.java @@ -505,6 +505,15 @@ public static class PLPrivateChatSection implements PrivateChatSection { public Notice socialSpyEnable = Notice.chat("SocialSpy został {STATE}!"); public Notice socialSpyDisable = Notice.chat("SocialSpy został {STATE}!"); + public Notice receiverDisabledMessages = Notice.chat("Wiadomości prywatne zostały wyłączone!"); + + public Notice selfMessagesDisabled = Notice.chat("Wiadomości prywatne zostały wyłączone!"); + public Notice selfMessagesEnabled = Notice.chat("Wiadomości prywatne zostały włączone!"); + + @Description({" ", "# {PLAYER} - Gracz któremu wyłączono wiadomości prywatne"}) + public Notice otherMessagesDisabled = Notice.chat("Wiadomości prywatne zostały wyłączone dla gracza {PLAYER}!"); + public Notice otherMessagesEnabled = Notice.chat("Wiadomości prywatne zostały włączone dla gracza {PLAYER}!"); + @Description({" ", "# {PLAYER} - Gracz który jest zignorowany"}) public Notice ignorePlayer = Notice.chat("Zignorowano gracza {PLAYER}!");