vote_mute #2
|
|
@ -113,6 +113,7 @@ public class VelocityChat {
|
||||||
new Reload(server);
|
new Reload(server);
|
||||||
new MailCommand(server);
|
new MailCommand(server);
|
||||||
new Report(server);
|
new Report(server);
|
||||||
|
new VoteToMute(server);
|
||||||
server.getCommandManager().register("party", new PartyCommand());
|
server.getCommandManager().register("party", new PartyCommand());
|
||||||
// all (proxy)commands go here
|
// all (proxy)commands go here
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
package com.alttd.velocitychat.commands;
|
||||||
|
|
||||||
|
import com.alttd.chat.config.Config;
|
||||||
|
import com.alttd.chat.util.Utility;
|
||||||
|
import com.alttd.velocitychat.commands.vote_to_mute.ActiveVoteToMute;
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||||
|
import com.mojang.brigadier.suggestion.Suggestions;
|
||||||
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import com.velocitypowered.api.command.BrigadierCommand;
|
||||||
|
import com.velocitypowered.api.command.CommandMeta;
|
||||||
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
|
import com.velocitypowered.api.proxy.ServerConnection;
|
||||||
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class VoteToMute {
|
||||||
|
|
||||||
|
public VoteToMute(ProxyServer proxyServer) {
|
||||||
|
RequiredArgumentBuilder<CommandSource, String> playerNode = RequiredArgumentBuilder
|
||||||
|
.<CommandSource, String>argument("player", StringArgumentType.string())
|
||||||
|
.suggests((context, builder) -> {
|
||||||
|
List<Player> possiblePlayers;
|
||||||
|
if (context.getSource() instanceof Player player) {
|
||||||
|
Optional<ServerConnection> currentServer = player.getCurrentServer();
|
||||||
|
if (currentServer.isPresent()) {
|
||||||
|
possiblePlayers = getEligiblePlayers(currentServer.get().getServer());
|
||||||
|
} else {
|
||||||
|
possiblePlayers = getEligiblePlayers(proxyServer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
possiblePlayers = getEligiblePlayers(proxyServer);
|
||||||
|
}
|
||||||
|
Collection<String> possibleValues = possiblePlayers.stream()
|
||||||
|
.map(Player::getUsername)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (possibleValues.isEmpty())
|
||||||
|
return Suggestions.empty();
|
||||||
|
|
||||||
|
String remaining = builder.getRemaining().toLowerCase();
|
||||||
|
possibleValues.stream()
|
||||||
|
.filter(str -> str.toLowerCase().startsWith(remaining))
|
||||||
|
.map(StringArgumentType::escapeIfRequired)
|
||||||
|
.forEach(builder::suggest);
|
||||||
|
return builder.buildFuture();
|
||||||
|
})
|
||||||
|
.executes(context -> {
|
||||||
|
sendHelpMessage(context.getSource());
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
RequiredArgumentBuilder<CommandSource, String> yesNoNode = RequiredArgumentBuilder.
|
||||||
|
<CommandSource, String>argument("vote", StringArgumentType.string())
|
||||||
|
.suggests(((commandContext, suggestionsBuilder) -> {
|
||||||
|
List<String> yesNoValues = Arrays.asList("yes", "no");
|
||||||
|
String remaining = suggestionsBuilder.getRemaining().toLowerCase();
|
||||||
|
yesNoValues.stream()
|
||||||
|
.filter((String str) -> str.toLowerCase().startsWith(remaining))
|
||||||
|
.map(StringArgumentType::escapeIfRequired)
|
||||||
|
.forEach(suggestionsBuilder::suggest);
|
||||||
|
return suggestionsBuilder.buildFuture();
|
||||||
|
}));
|
||||||
|
|
||||||
|
LiteralArgumentBuilder<CommandSource> voteNode = LiteralArgumentBuilder
|
||||||
|
.<CommandSource>literal("vote")
|
||||||
|
.then(playerNode
|
||||||
|
.then(yesNoNode
|
||||||
|
.executes(commandContext -> {
|
||||||
|
if (!(commandContext.getSource() instanceof Player)) {
|
||||||
|
commandContext.getSource().sendMessage(Utility.parseMiniMessage(
|
||||||
|
"<red>Only players are allowed to vote</red>"));
|
||||||
|
}
|
||||||
|
Player source = (Player) commandContext.getSource();
|
||||||
|
String playerName = commandContext.getArgument("player", String.class);
|
||||||
|
Optional<ActiveVoteToMute> optionalActiveVoteToMute = ActiveVoteToMute.getInstance(playerName);
|
||||||
|
if (optionalActiveVoteToMute.isEmpty()) {
|
||||||
|
commandContext.getSource().sendMessage(Utility.parseMiniMessage(
|
||||||
|
"<red>This player does not have an active vote to mute them</red>"));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ActiveVoteToMute activeVoteToMute = optionalActiveVoteToMute.get();
|
||||||
|
String vote = commandContext.getArgument("vote", String.class);
|
||||||
|
switch (vote.toLowerCase()) {
|
||||||
|
case "yes" -> activeVoteToMute.vote(source.getUniqueId(), true);
|
||||||
|
case "no" -> activeVoteToMute.vote(source.getUniqueId(), false);
|
||||||
|
default -> commandContext.getSource().sendMessage(Utility.parseMiniMessage(
|
||||||
|
"<red><vote> is not a valid vote option</red>", Placeholder.parsed("vote", vote)));
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
})).executes(context -> {
|
||||||
|
sendHelpMessage(context.getSource());
|
||||||
|
return 1;
|
||||||
|
})).executes(context -> {
|
||||||
|
sendHelpMessage(context.getSource());
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
LiteralCommandNode<CommandSource> command = LiteralArgumentBuilder
|
||||||
|
.<CommandSource>literal("votetomute")
|
||||||
|
.requires(commandSource -> commandSource.hasPermission("chat.vote-to-mute"))
|
||||||
|
.requires(commandSource -> commandSource instanceof Player)
|
||||||
|
.then(playerNode
|
||||||
|
.executes(commandContext -> {
|
||||||
|
String playerName = commandContext.getArgument("player", String.class);
|
||||||
|
Optional<Player> optionalPlayer = proxyServer.getPlayer(playerName);
|
||||||
|
if (optionalPlayer.isEmpty()) {
|
||||||
|
commandContext.getSource().sendMessage(Utility.parseMiniMessage(
|
||||||
|
"<red>Player <player> is not online</red>",
|
||||||
|
Placeholder.parsed("player", playerName)));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}))
|
||||||
|
.then(voteNode)
|
||||||
|
.executes(context -> {
|
||||||
|
sendHelpMessage(context.getSource());
|
||||||
|
return 1;
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
//TODO test command
|
||||||
|
//TODO add command to pick out the messages
|
||||||
|
//TODO add command to go to the next page
|
||||||
|
|
||||||
|
BrigadierCommand brigadierCommand = new BrigadierCommand(command);
|
||||||
|
|
||||||
|
CommandMeta.Builder metaBuilder = proxyServer.getCommandManager().metaBuilder(brigadierCommand);
|
||||||
|
|
||||||
|
CommandMeta meta = metaBuilder.build();
|
||||||
|
|
||||||
|
proxyServer.getCommandManager().register(meta, brigadierCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendHelpMessage(CommandSource commandSource) {
|
||||||
|
commandSource.sendMessage(Utility.parseMiniMessage("<red>Use: <gold>/votetomute <player></gold>.</red>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Player> getEligiblePlayers(ProxyServer proxyServer) {
|
||||||
|
return proxyServer.getAllPlayers().stream()
|
||||||
|
.filter(player -> player.hasPermission("chat.affected-by-vote-to-mute"))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Player> getEligiblePlayers(RegisteredServer registeredServer) {
|
||||||
|
return registeredServer.getPlayersConnected().stream()
|
||||||
|
.filter(player -> player.hasPermission("chat.affected-by-vote-to-mute"))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
package com.alttd.velocitychat.commands.vote_to_mute;
|
||||||
|
|
||||||
|
import com.alttd.chat.util.Utility;
|
||||||
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ActiveVoteToMute {
|
||||||
|
|
||||||
|
private static final HashMap<String, ActiveVoteToMute> instances = new HashMap<>();
|
||||||
|
private static final Component prefix = Utility.parseMiniMessage("<gold>[VoteMute]</gold>");
|
||||||
|
|
||||||
|
private final Instant start;
|
||||||
|
private final Player votedPlayer;
|
||||||
|
private final Duration duration;
|
||||||
|
private HashSet<UUID> votedFor = new HashSet<>();
|
||||||
|
private HashSet<UUID> votedAgainst = new HashSet<>();
|
||||||
|
private int totalEligibleVoters;
|
||||||
|
|
||||||
|
public static Optional<ActiveVoteToMute> getInstance(String username) {
|
||||||
|
if (!instances.containsKey(username))
|
||||||
|
return Optional.empty();
|
||||||
|
return Optional.of(instances.get(username));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActiveVoteToMute(@NotNull Player votedPlayer, Duration duration, int totalEligibleVoters) {
|
||||||
|
this.start = Instant.now();
|
||||||
|
this.votedPlayer = votedPlayer;
|
||||||
|
this.duration = duration;
|
||||||
|
this.totalEligibleVoters = totalEligibleVoters;
|
||||||
|
instances.put(votedPlayer.getUsername(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(@NotNull RegisteredServer registeredServer, Component chatLogs) {
|
||||||
|
Component message = Utility.parseMiniMessage(
|
||||||
|
String.format("""
|
||||||
|
<prefix> <gold>[VoteMute]</gold> <green>A vote to mute <player> for one hour has been started, please read the logs below before voting.</green>
|
||||||
|
<logs>
|
||||||
|
<prefix> Click: <click:run_command:'/votetomute vote %s yes'><red>Mute</red></click> --- <click:run_command:'/votetomute vote %s no'><yellow>Don't mute</yellow></click>""",
|
||||||
|
votedPlayer.getUsername(), votedPlayer.getUsername()),
|
||||||
|
Placeholder.component("prefix", prefix),
|
||||||
|
Placeholder.parsed("player", votedPlayer.getUsername()),
|
||||||
|
Placeholder.component("logs", chatLogs));
|
||||||
|
registeredServer.getPlayersConnected().stream()
|
||||||
|
.filter(player -> player.hasPermission("chat.vote-to-mute"))
|
||||||
|
.forEach(player -> player.sendMessage(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void vote(UUID uuid, boolean votedToMute) {
|
||||||
|
if (votedToMute) {
|
||||||
|
votedFor.add(uuid);
|
||||||
|
votedAgainst.remove(uuid);
|
||||||
|
} else {
|
||||||
|
votedAgainst.add(uuid);
|
||||||
|
votedFor.remove(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean votePassed() {
|
||||||
|
double totalVotes = (votedFor.size() + votedAgainst.size());
|
||||||
|
if (totalVotes / totalEligibleVoters < 0.6) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return votedFor.size() / totalVotes > 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean voteEnded() {
|
||||||
|
if (votedFor.size() + votedAgainst.size() == totalEligibleVoters)
|
||||||
|
return true;
|
||||||
|
return duration.minus(Duration.between(start, Instant.now())).isNegative();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
package com.alttd.velocitychat.commands.vote_to_mute;
|
||||||
|
|
||||||
|
import com.alttd.chat.objects.chat_log.ChatLog;
|
||||||
|
import com.alttd.chat.objects.chat_log.ChatLogHandler;
|
||||||
|
import com.alttd.chat.util.Utility;
|
||||||
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.JoinConfiguration;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
public class VoteToMuteStarter {
|
||||||
|
|
||||||
|
private static final HashMap<UUID, VoteToMuteStarter> instanceMap = new HashMap<>();
|
||||||
|
|
||||||
|
private static final Component prefix = Utility.parseMiniMessage("<gold>[VoteMute]</gold>");
|
||||||
|
private final ChatLogHandler chatLogHandler;
|
||||||
|
private final Player votedPlayer;
|
||||||
|
private final Player commandSource;
|
||||||
|
private final String serverName;
|
||||||
|
private List<Component> parsedChatLogs;
|
||||||
|
|
||||||
|
public static Optional<VoteToMuteStarter> getInstance(UUID uuid) {
|
||||||
|
if (!instanceMap.containsKey(uuid))
|
||||||
|
return Optional.empty();
|
||||||
|
return Optional.of(instanceMap.get(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
public VoteToMuteStarter(ChatLogHandler chatLogHandler, Player votedPlayer, Player commandSource, String serverName) {
|
||||||
|
this.chatLogHandler = chatLogHandler;
|
||||||
|
this.votedPlayer = votedPlayer;
|
||||||
|
this.commandSource = commandSource;
|
||||||
|
this.serverName = serverName;
|
||||||
|
instanceMap.put(commandSource.getUniqueId(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
chatLogHandler.retrieveChatLogs(votedPlayer.getUniqueId(), Duration.ofMinutes(5), serverName).whenCompleteAsync((chatLogs, throwable) -> {
|
||||||
|
if (throwable != null) {
|
||||||
|
commandSource.sendMessage(Utility.parseMiniMessage("<prefix> <red>Unable to retrieve messages</red> for player <player>",
|
||||||
|
Placeholder.component("prefix", prefix),
|
||||||
|
Placeholder.parsed("player", votedPlayer.getUsername())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parseChatLogs(chatLogs);
|
||||||
|
commandSource.sendMessage(Utility.parseMiniMessage(
|
||||||
|
"<prefix> <green>Please select up to 10 messages other players should see to decide their vote, seperated by comma's. " +
|
||||||
|
"Example: <gold>/votetomute messages 1, 2, 5, 8</gold></green>"));
|
||||||
|
showPage(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseChatLogs(List<ChatLog> chatLogs) {
|
||||||
|
TagResolver.Single playerTag = Placeholder.parsed("player", votedPlayer.getUsername());
|
||||||
|
chatLogs.sort(Comparator.comparing(ChatLog::getTimestamp));
|
||||||
|
parsedChatLogs = IntStream.range(0, chatLogs.size())
|
||||||
|
.mapToObj(i -> Utility.parseMiniMessage(
|
||||||
|
"<number>. [ChatLog] <player>: <message>",
|
||||||
|
TagResolver.resolver(
|
||||||
|
Placeholder.unparsed("message", chatLogs.get(i).getMessage()),
|
||||||
|
Placeholder.parsed("number", String.valueOf(i + 1)),
|
||||||
|
playerTag
|
||||||
|
))
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showPage(int page) {
|
||||||
|
List<Component> collect = parsedChatLogs.stream().skip(page * 10L).limit(page).toList();
|
||||||
|
Component chatLogsComponent = Component.join(JoinConfiguration.newlines(), collect);
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.append("<prefix> ChatLogs for <player>\n<logs>\n");
|
||||||
|
if (page != 0) {
|
||||||
|
stringBuilder.append("<click:run_command:/votetomute page ")
|
||||||
|
.append(page - 1)
|
||||||
|
.append("><hover:show_text:'<gold>Click to go to previous page'><gold><previous page></gold></hover></click> ");
|
||||||
|
}
|
||||||
|
if (parsedChatLogs.size() > page * 10) {
|
||||||
|
stringBuilder.append("<click:run_command:/votetomute page ")
|
||||||
|
.append(page + 1)
|
||||||
|
.append("><hover:show_text:'<gold>Click to go to next page'><gold><previous page></gold></hover></click> ");
|
||||||
|
}
|
||||||
|
commandSource.sendMessage(Utility.parseMiniMessage(stringBuilder.toString(),
|
||||||
|
Placeholder.parsed("player", votedPlayer.getUsername()),
|
||||||
|
Placeholder.component("prefix", prefix),
|
||||||
|
Placeholder.component("logs", chatLogsComponent)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the chat logs for the given list of IDs. It removes 1 from the IDs before using them
|
||||||
|
* It removes the instance from the hashmap after this function call
|
||||||
|
*
|
||||||
|
* @param ids A list of integers representing the IDs of the chat logs to retrieve.
|
||||||
|
* @return A Component object containing the selected chat logs joined by newlines.
|
||||||
|
*/
|
||||||
|
public Component getChatLogsAndClose(List<Integer> ids) {
|
||||||
|
List<Component> selectedChatLogs = ids.stream()
|
||||||
|
.filter(id -> id >= 1 && id <= parsedChatLogs.size())
|
||||||
|
.map(id -> parsedChatLogs.get(id - 1))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
instanceMap.remove(commandSource.getUniqueId());
|
||||||
|
return Component.join(JoinConfiguration.newlines(), selectedChatLogs);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user