Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,12 @@
import ti4.discord.interactions.commands.transaction.TransactionCommand;
import ti4.discord.interactions.commands.uncategorized.AllInfoCommand;
import ti4.discord.interactions.commands.uncategorized.CardsInfoCommand;
import ti4.discord.interactions.commands.uncategorized.RemindMeCommand;
import ti4.discord.interactions.commands.uncategorized.SelectionBoxDemoCommand;
import ti4.discord.interactions.commands.uncategorized.ShowDistancesCommand;
import ti4.discord.interactions.commands.uncategorized.ShowGameCommand;
import ti4.discord.interactions.commands.uncategorized.WCommand;
import ti4.discord.interactions.commands.uncategorized.WhisperCommand;
import ti4.discord.interactions.commands.units.AddUnitDamage;
import ti4.discord.interactions.commands.units.AddUnits;
import ti4.discord.interactions.commands.units.CaptureCommand;
Expand All @@ -84,6 +87,9 @@ public class SlashCommandManager {
new RemoveAllUnits(),
new AllInfoCommand(),
new CardsInfoCommand(),
new WhisperCommand(),
new WCommand(),
new RemindMeCommand(),
new ShowGameCommand(),
new ShowDistancesCommand(),
new AddCCCommand(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package ti4.discord.interactions.commands.uncategorized;

import java.util.List;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import ti4.discord.interactions.commands.GameStateCommand;
import ti4.game.Player;
import ti4.helpers.Constants;
import ti4.message.MessageHelper;
import ti4.service.fow.WhisperService;

public class RemindMeCommand extends GameStateCommand {

public RemindMeCommand() {
super(true, true);
}

@Override
public String getName() {
return Constants.REMIND_ME;
}

@Override
public String getDescription() {
return "Send a reminder message to yourself at start of your next turn";
}

@Override
public List<OptionData> getOptions() {
return List.of(new OptionData(OptionType.STRING, Constants.MESSAGE, "Message to send").setRequired(true));
}

@Override
public void execute(SlashCommandInteractionEvent event) {
Player player = getPlayer();
if (!player.isRealPlayer()) {
MessageHelper.sendEphemeralMessageToEventChannel(event, "This command only works on real players.");
return;
}

String message = event.getOption(Constants.MESSAGE, "", OptionMapping::getAsString);
WhisperService.whisperToFutureMe(getGame(), player, message, event.getChannel());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ti4.discord.interactions.commands.uncategorized;

import ti4.helpers.Constants;

public class WCommand extends WhisperCommandBase {

public WCommand() {
super(Constants.W, "Alias for /whisper");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ti4.discord.interactions.commands.uncategorized;

import ti4.helpers.Constants;

public class WhisperCommand extends WhisperCommandBase {

public WhisperCommand() {
super(Constants.WHISPER, "Send a private whisper to a player");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package ti4.discord.interactions.commands.uncategorized;

import java.util.List;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import ti4.discord.interactions.commands.GameStateCommand;
import ti4.game.Game;
import ti4.game.Player;
import ti4.helpers.Constants;
import ti4.message.MessageHelper;
import ti4.service.fow.WhisperService;

abstract class WhisperCommandBase extends GameStateCommand {

private final String name;
private final String description;

protected WhisperCommandBase(String name, String description) {
super(true, true);
this.name = name;
this.description = description;
}

@Override
public String getName() {
return name;
}

@Override
public String getDescription() {
return description;
}

@Override
public List<OptionData> getOptions() {
return List.of(
new OptionData(OptionType.STRING, Constants.TO, "Faction or color to whisper to")
.setAutoComplete(true)
.setRequired(true),
new OptionData(OptionType.STRING, Constants.MESSAGE, "Message to send"),
new OptionData(OptionType.BOOLEAN, Constants.FUTURE, "When true, deliver at start of target's turn"));
}

@Override
public void execute(SlashCommandInteractionEvent event) {
Player sender = getPlayer();
if (!sender.isRealPlayer()) {
MessageHelper.sendEphemeralMessageToEventChannel(event, "This command only works on real players.");
return;
}

Game game = getGame();
String to =
event.getOption(Constants.TO, "", OptionMapping::getAsString).toLowerCase();
Player receiver = WhisperService.getPlayerByColorOrFaction(game, to);
if (receiver == null) {
MessageHelper.sendEphemeralMessageToEventChannel(event, "Player not found: " + to);
return;
}

String message = event.getOption(Constants.MESSAGE, "", OptionMapping::getAsString);
boolean future = event.getOption(Constants.FUTURE, false, OptionMapping::getAsBoolean);
if (future) {
WhisperService.whisperToFutureColorOrFaction(game, message, sender, receiver, event.getChannel());
return;
}

WhisperService.sendWhisper(game, sender, receiver, message, "n", event.getMessageChannel(), event.getGuild());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,7 @@ private static void handleOptions(
.toList();
event.replyChoices(options).queue(Consumers.nop(), BotLogger::catchRestError);
}
case Constants.FACTION_COLOR, Constants.TARGET_FACTION_OR_COLOR -> {
case Constants.FACTION_COLOR, Constants.TARGET_FACTION_OR_COLOR, Constants.TO -> {
if (!GameManager.isValid(gameName)) return;
Game game = GameManager.getManagedGame(gameName).getGame();
String enteredValue = event.getFocusedOption().getValue().toLowerCase();
Expand Down
134 changes: 1 addition & 133 deletions src/main/java/ti4/discord/interactions/listeners/MessageListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import net.dv8tion.jda.api.components.buttons.Button;
import net.dv8tion.jda.api.entities.Message;
Expand All @@ -25,10 +23,8 @@
import ti4.game.Player;
import ti4.game.persistence.GameManager;
import ti4.game.persistence.ManagedGame;
import ti4.helpers.AliasHandler;
import ti4.helpers.Constants;
import ti4.helpers.async.RoundSummaryHelper;
import ti4.image.Mapper;
import ti4.logging.BotLogger;
import ti4.message.MessageHelper;
import ti4.service.emoji.CardEmojis;
Expand All @@ -42,8 +38,6 @@
class MessageListener extends ListenerAdapter {

private static final int EXECUTION_TIME_WARNING_THRESHOLD_SECONDS = 1;
private static final Pattern FUTURE = Pattern.compile("future");
private static final Pattern PATTERN = Pattern.compile("[^a-zA-Z0-9]+$");
// The mention itself is 23 characters long
private static final int BOTHELPER_MENTION_REMINDER_MESSAGE_LENGTH_THRESHOLD = 53;
private static final String BOTHELPER_MENTION_REMINDER_TEXT = """
Expand Down Expand Up @@ -115,7 +109,7 @@ private static void processMessage(@Nonnull MessageReceivedEvent event, Message
reportInterestingMessages(message);

if (isValidGameMessage) {
if (handleWhispers(event, message, gameName)) return;
if (WhisperService.handleWhispers(event, message, gameName)) return;
if (endOfRoundSummary(event, message, gameName)) return;
if (addFactionEmojiReactionsToMessages(event, gameName)) return;
}
Expand Down Expand Up @@ -244,132 +238,6 @@ private static boolean endOfRoundSummary(MessageReceivedEvent event, Message mes
return true;
}

private static boolean handleWhispers(MessageReceivedEvent event, Message message, String gameName) {
if (message.getContentRaw().contains("used /fow whisper")) {
message.delete().queue(Consumers.nop(), BotLogger::catchRestError);
}

String messageText = message.getContentRaw();
if (!messageText.toLowerCase().startsWith("to") || !messageText.contains(" ")) {
return false;
}

ManagedGame managedGame = GameManager.getManagedGame(gameName);
if (managedGame == null) {
return true;
}

// Prevent whispers from fow combat threads
if (managedGame.isFowMode()
&& event.getChannel() instanceof ThreadChannel
&& event.getChannel().getName().contains("vs")
&& event.getChannel().getName().contains("private")) {
return false;
}

ExecutionLockManager.wrapWithLockAndRelease(gameName, ExecutionLockType.WRITE, () -> {
Game game = managedGame.getGame();
Player sender = getPlayer(event, game);
if (sender == null || !sender.isRealPlayer()) {
return;
}

String messageLowerCase = messageText.toLowerCase();
String receivingColorOrFaction = PATTERN.matcher(
StringUtils.substringBetween(messageLowerCase, "to", " "))
.replaceAll("");

if ("futureme".equals(receivingColorOrFaction)) {
whisperToFutureMe(event, game, sender);
GameManager.save(game, "Whisper to future by " + sender.getUserName());
return;
}

boolean future = receivingColorOrFaction.startsWith("future");
receivingColorOrFaction =
FUTURE.matcher(receivingColorOrFaction).replaceFirst("");
if (receivingColorOrFaction.isEmpty()) {
return;
}

receivingColorOrFaction = AliasHandler.resolveFaction(receivingColorOrFaction);
if (!Mapper.isValidColor(receivingColorOrFaction)
&& !Mapper.isValidFaction(receivingColorOrFaction)) {
return;
}

if (game.isWhispersDisabled()) {
MessageHelper.sendMessageToChannel(
event.getChannel(),
"Whispers are disabled in this game. To reenable them, use `/game setup whispers_enabled:true`.");
message.delete().queue(Consumers.nop(), BotLogger::catchRestError);
return;
}

String messageContent = StringUtils.substringAfter(messageText, " ");
if (messageContent.isEmpty()) {
message.reply("No message content?").queue(Consumers.nop(), BotLogger::catchRestError);
return;
}

Player receiver = getPlayer(game, receivingColorOrFaction);
if (receiver == null) {
MessageHelper.sendMessageToChannel(
event.getChannel(), "Player not found: " + receivingColorOrFaction);
return;
}

if (future) {
whisperToFutureColorOrFaction(event, game, messageContent, sender, receiver);
} else {
WhisperService.sendWhisper(
game, sender, receiver, messageContent, "n", event.getChannel(), event.getGuild());
message.delete().queue(Consumers.nop(), BotLogger::catchRestError);
}
GameManager.save(game, "Whisper");
})
.run();

return true;
}

private static Player getPlayer(Game game, String receivingColorOrFaction) {
for (Player player : game.getRealPlayers()) {
if (Objects.equals(receivingColorOrFaction, player.getFaction())
|| Objects.equals(receivingColorOrFaction, player.getColor())) {
return player;
}
}
return null;
}

private static void whisperToFutureColorOrFaction(
MessageReceivedEvent event, Game game, String messageContent, Player sender, Player receiver) {
String futureMsgKey = "futureMessageFor_" + receiver.getFaction() + "_" + sender.getFaction();
game.setStoredValue(futureMsgKey, game.getStoredValue(futureMsgKey) + "\n\n" + messageContent);
MessageHelper.sendMessageToChannel(
event.getChannel(), sender.getFactionEmoji() + " sent someone else a future message");
MessageHelper.sendMessageToPlayerCardsInfoThread(
sender,
"You sent a future message to " + receiver.getRepresentationNoPing() + ":\n>>> " + messageContent);
event.getMessage().delete().queue(Consumers.nop(), BotLogger::catchRestError);
}

private static void whisperToFutureMe(MessageReceivedEvent event, Game game, Player player) {
String messageContent = StringUtils.substringAfter(event.getMessage().getContentRaw(), " ");

String previousThoughts = "";
if (!game.getStoredValue("futureMessageFor" + player.getFaction()).isEmpty()) {
previousThoughts = game.getStoredValue("futureMessageFor" + player.getFaction()) + "\n\n";
}
game.setStoredValue("futureMessageFor" + player.getFaction(), previousThoughts + messageContent);
MessageHelper.sendMessageToChannel(
event.getChannel(), player.getFactionEmoji() + " sent themselves a future message");
MessageHelper.sendMessageToPlayerCardsInfoThread(
player, "You sent yourself a future message:\n>>> " + messageContent);
event.getMessage().delete().queue(Consumers.nop(), BotLogger::catchRestError);
}

private static boolean addFactionEmojiReactionsToMessages(MessageReceivedEvent event, String gameName) {
ManagedGame managedGame = GameManager.getManagedGame(gameName);
if (managedGame.getGame().isHiddenAgendaMode()
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/ti4/helpers/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,10 @@ public static String privateStaticVoidPing() {
public static final String PING = "ping";
public static final String PING_ACTIVE_PLAYER = "ping_active_player";
public static final String WHISPER = "whisper";
public static final String W = "w";
public static final String TO = "to";
public static final String FUTURE = "future";
public static final String REMIND_ME = "remind_me";
public static final String MSG = "msg";
public static final String OVERRIDE_FOW = "override_fow";
public static final String OVERRIDE_EXPLORE_OWNERSHIP_REQ = "override_explore_ownership_req";
Expand Down
Loading
Loading