From d61de1b208a733e412d08e6b492860205ea01942 Mon Sep 17 00:00:00 2001 From: marcos Date: Thu, 6 Aug 2026 22:22:25 +0000 Subject: [PATCH] Add zoacao chat gag: bare 'f' replaced with a random line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New module keyed 'zoacao'. An AsyncPlayerChatEvent listener swaps any bare 'f'/'F' (trimmed, nothing else) for a random line from the configurable zoacao.mensagens list (default Sou gay + 5 others). Pure chat swap — player name still prefixes it; luto tribute untouched (still needs the [F] button or /f command). Gated by modulos.zoacao (default on). Zoacao.replace is pure for unit tests; ZoacaoTest covers bare f, case, whitespace, f-with-extra-text, empty/null lists. 124 tests green. Co-Authored-By: Claude --- .../marcospaulo/canalhandia/Canalhandia.java | 18 ++++++ .../dev/marcospaulo/canalhandia/Module.java | 1 + .../dev/marcospaulo/canalhandia/Settings.java | 21 +++++++ .../dev/marcospaulo/canalhandia/Zoacao.java | 37 ++++++++++++ src/main/resources/config.yml | 13 ++++ .../marcospaulo/canalhandia/ZoacaoTest.java | 60 +++++++++++++++++++ 6 files changed, 150 insertions(+) create mode 100644 src/main/java/dev/marcospaulo/canalhandia/Zoacao.java create mode 100644 src/test/java/dev/marcospaulo/canalhandia/ZoacaoTest.java diff --git a/src/main/java/dev/marcospaulo/canalhandia/Canalhandia.java b/src/main/java/dev/marcospaulo/canalhandia/Canalhandia.java index e0184a0..dbba016 100644 --- a/src/main/java/dev/marcospaulo/canalhandia/Canalhandia.java +++ b/src/main/java/dev/marcospaulo/canalhandia/Canalhandia.java @@ -18,6 +18,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.inventory.ItemStack; @@ -478,6 +479,23 @@ public final class Canalhandia extends JavaPlugin implements Listener { }, settings.joinDelaySeconds() * 20L); } + /** + * Chat gag: a bare "f" (or "F", trimmed, nothing else) gets replaced with a + * random line from {@code zoacao.mensagens}. Pure chat swap — the player's + * name still prefixes it as normal. Independent of {@code luto}: paying + * respects still needs the {@code [F]} button (Java) or {@code /f} command. + */ + @EventHandler + public void onChatF(AsyncPlayerChatEvent event) { + if (!settings.moduleEnabled(Module.ZOACAO)) { + return; + } + String gag = Zoacao.replace(event.getMessage(), settings.zoacaoMessages(), random); + if (gag != null) { + event.setMessage(gag); + } + } + /** "Press F" — a mourning button under each death message. */ @EventHandler public void onDeath(PlayerDeathEvent event) { diff --git a/src/main/java/dev/marcospaulo/canalhandia/Module.java b/src/main/java/dev/marcospaulo/canalhandia/Module.java index 606ac14..3c599ed 100644 --- a/src/main/java/dev/marcospaulo/canalhandia/Module.java +++ b/src/main/java/dev/marcospaulo/canalhandia/Module.java @@ -10,6 +10,7 @@ enum Module { RANKING("ranking", "Rankings"), MARCOS("marcos", "Marcos e conquistas"), MORTES("mortes", "Mortes com humor e coordenadas"), + ZOACAO("zoacao", "Zoa de quem manda só 'f' no chat"), IA("ia", "Perguntas para a IA"); private final String key; diff --git a/src/main/java/dev/marcospaulo/canalhandia/Settings.java b/src/main/java/dev/marcospaulo/canalhandia/Settings.java index 220230e..9835401 100644 --- a/src/main/java/dev/marcospaulo/canalhandia/Settings.java +++ b/src/main/java/dev/marcospaulo/canalhandia/Settings.java @@ -344,6 +344,27 @@ final class Settings { set("ia.estatisticas-jogador", value); } + // --- zoacao (f-gag) ----------------------------------------------------- + + /** + * Lines a bare "f" in chat gets replaced with, picked at random. Defaults + * to a small built-in list if unset/empty so the feature works out of the + * box; operators edit {@code zoacao.mensagens} in config.yml to customise. + */ + List zoacaoMessages() { + List messages = plugin.getConfig().getStringList("zoacao.mensagens"); + if (messages == null || messages.isEmpty()) { + return List.of( + "Sou gay", + "Gosto de anime", + "Jogo no celular", + "Tenho 12 anos", + "Sou noob", + "Uso Windows"); + } + return messages; + } + // --- content ------------------------------------------------------------ boolean categoryEnabled(Category category) { diff --git a/src/main/java/dev/marcospaulo/canalhandia/Zoacao.java b/src/main/java/dev/marcospaulo/canalhandia/Zoacao.java new file mode 100644 index 0000000..75ac72d --- /dev/null +++ b/src/main/java/dev/marcospaulo/canalhandia/Zoacao.java @@ -0,0 +1,37 @@ +package dev.marcospaulo.canalhandia; + +import java.util.List; +import java.util.Random; + +/** + * Pure logic for the {@code zoacao} chat gag: when a player sends a bare + * {@code "f"} (or {@code "F"}, trimmed, nothing else) their message is swapped + * for a random line from a configurable list. Extracted so the trigger rule is + * unit-testable without a server. + * + *

This is a standalone chat gag. It does not interact with the {@code luto} + * tribute — paying respects still happens via the {@code [F]} button (Java) or + * the {@code /f} command (Bedrock), neither of which is a chat message. + */ +final class Zoacao { + + private Zoacao() { + } + + /** + * @param message the chat message as sent by the player + * @param gags the configured replacement lines; if null/empty, no gag + * @param random shared random used to pick a line + * @return the replacement line if the message is a bare "f", otherwise null + * (meaning "leave the message alone") + */ + static String replace(String message, List gags, Random random) { + if (message == null || gags == null || gags.isEmpty()) { + return null; + } + if (!message.trim().equalsIgnoreCase("f")) { + return null; + } + return gags.get(random.nextInt(gags.size())); + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index c68ad79..61ac83b 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -17,6 +17,8 @@ modulos: # Mensagem de morte engraçada + coordenadas da morte mandadas em privado # só para quem morreu (para correr buscar os itens). mortes: true + # Quem manda só "f" no chat (sem mais nada) leva uma zoada no lugar da mensagem. + zoacao: true ia: true # /ia — só para quem tem canalhandia.ia # --- Curiosidades ------------------------------------------------------------ @@ -69,6 +71,17 @@ janela-reacao-segundos: 90 luto: cabeca: true +# Frases que substituem um "f" sozinho no chat (módulo zoacao). Uma é sorteada +# por mensagem. Edite à vontade — a graça é ser inesperado. +zoacao: + mensagens: + - "Sou gay" + - "Gosto de anime" + - "Jogo no celular" + - "Tenho 12 anos" + - "Sou noob" + - "Uso Windows" + # Quantos nomes cabem no resumo de reações antes do resto virar "+N". # O resumo é sempre UMA linha, por mais gente que reaja; para ver a lista # completa use /reacoes (privado, não polui o chat). diff --git a/src/test/java/dev/marcospaulo/canalhandia/ZoacaoTest.java b/src/test/java/dev/marcospaulo/canalhandia/ZoacaoTest.java new file mode 100644 index 0000000..3229b0a --- /dev/null +++ b/src/test/java/dev/marcospaulo/canalhandia/ZoacaoTest.java @@ -0,0 +1,60 @@ +package dev.marcospaulo.canalhandia; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import java.util.Random; +import org.junit.jupiter.api.Test; + +class ZoacaoTest { + + private static final List GAGS = List.of( + "Sou gay", "Gosto de anime", "Jogo no celular"); + + @Test + void bareLowercaseFIsReplacedWithAGagFromTheList() { + String gag = Zoacao.replace("f", GAGS, new Random(0L)); + assertTrue(GAGS.contains(gag), "expected a gag from the list, got " + gag); + } + + @Test + void bareUppercaseFIsReplaced() { + // A single-element list makes the random pick deterministic. + String gag = Zoacao.replace("F", List.of("alvo"), new Random(0L)); + assertEquals("alvo", gag); + } + + @Test + void surroundingWhitespaceStillCountsAsBareF() { + assertEquals("alvo", Zoacao.replace(" f ", List.of("alvo"), new Random(0L))); + assertEquals("alvo", Zoacao.replace("\tF\n", List.of("alvo"), new Random(0L))); + } + + @Test + void fWithAnythingElseIsLeftAlone() { + assertNull(Zoacao.replace("f lol", GAGS, new Random(0L))); + assertNull(Zoacao.replace("ff", GAGS, new Random(0L))); + assertNull(Zoacao.replace("f.", GAGS, new Random(0L))); + assertNull(Zoacao.replace("pra você f", GAGS, new Random(0L))); + } + + @Test + void nonFMessagesAreLeftAlone() { + assertNull(Zoacao.replace("oi", GAGS, new Random(0L))); + assertNull(Zoacao.replace("", GAGS, new Random(0L))); + assertNull(Zoacao.replace("F para o morto", GAGS, new Random(0L))); + } + + @Test + void emptyOrNullGagsLeaveMessageAlone() { + assertNull(Zoacao.replace("f", List.of(), new Random(0L))); + assertNull(Zoacao.replace("f", null, new Random(0L))); + } + + @Test + void nullMessageLeftAlone() { + assertNull(Zoacao.replace(null, GAGS, new Random(0L))); + } +} \ No newline at end of file