Make Discord join messages medieval-themed, randomized, and configurable with custom server name#10
Conversation
Add discord.join-message config option with {player} and {server}
placeholders. Default message uses a fun medieval theme:
"⚔️ Hear ye, hear ye! **{player}** hath entered the realm of **{server}**! ⚔️"
Falls back to default when config value is null or empty.
Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
|
@copilot That's great! However, I think it would be even more fun if you were able to specify more than one message and they were chosen at random. Provide 10 medieval-themed messages by default. |
…efaults Changed discord.join-message (single string) to discord.join-messages (list). A random message is picked each time a player joins. Ships with 10 medieval-themed defaults. Falls back to defaults when the list is null or empty. Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Done in ba0d56f.
Users can add, remove, or replace entries freely. Falls back to the 10 defaults if the list is empty or missing. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
@copilot Currently {server} is always "CraftBukkit" ; this should be configurable also |
Previously {server} always resolved to "CraftBukkit" (from getServer().getName()).
Now reads from the `server-name` config key. Defaults to "Minecraft" when empty.
Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Added a server-name: "My Awesome Server"Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
There was a problem hiding this comment.
Pull request overview
This PR updates Herald’s Discord notifications to support randomized, medieval-themed join messages that are configurable via discord.join-messages, using {player} and {server} placeholders.
Changes:
- Add default medieval-themed join message templates and randomized selection in
DiscordNotifier. - Load
discord.join-messagesfrom config and pass it intoDiscordNotifier. - Update default
config.yml, README documentation, and tests to reflect the new message templating behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/java/com/dansplugins/herald/DiscordNotifier.java | Adds default message templates, configurable template list, and random selection with placeholder substitution. |
| src/main/java/com/dansplugins/herald/Herald.java | Loads discord.join-messages from config and wires it into DiscordNotifier. |
| src/main/resources/config.yml | Documents and provides default discord.join-messages list with placeholders. |
| README.md | Documents discord.join-messages and shows default examples/placeholder usage. |
| src/test/java/com/dansplugins/herald/DiscordNotifierTest.java | Updates constructor usage and adds coverage for defaults/custom templates/random selection. |
| src/test/java/com/dansplugins/herald/HeraldIntegrationTest.java | Updates message-format expectations to align with templated defaults. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @DisplayName("Default Discord message format should use medieval theme with player and server names") | ||
| void testDiscordMessageFormat() { | ||
| String playerName = "TestPlayer"; | ||
| String serverName = "TestServer"; | ||
| String expectedFormat = "**" + playerName + "** joined the **" + serverName + "** server"; | ||
|
|
||
| assertTrue(expectedFormat.startsWith("**")); | ||
| assertTrue(expectedFormat.contains("** joined the **")); | ||
| assertTrue(expectedFormat.endsWith("** server")); | ||
| for (String template : DiscordNotifier.DEFAULT_JOIN_MESSAGES) { | ||
| String formatted = template.replace("{player}", playerName).replace("{server}", serverName); | ||
| assertTrue(formatted.contains("**" + playerName + "**")); | ||
| assertTrue(formatted.contains("**" + serverName + "**")); | ||
| } |
There was a problem hiding this comment.
This test’s display name claims it verifies a “medieval theme”, but the assertions only check that the formatted message contains the bolded player and server names. Either adjust the display name to match what’s being asserted, or add a concrete assertion that actually validates the intended theme (e.g., that each default template contains an expected keyword/emoji).
| static final List<String> DEFAULT_JOIN_MESSAGES = List.of( | ||
| "\u2694\uFE0F Hear ye, hear ye! **{player}** hath entered the realm of **{server}**! \u2694\uFE0F", | ||
| "\uD83C\uDFF0 The gates of **{server}** open wide for **{player}**! Welcome, brave soul!", | ||
| "\uD83D\uDCDC By royal decree, **{player}** hath been granted passage into **{server}**!", | ||
| "\uD83D\uDDE1\uFE0F A new champion approaches! **{player}** rides into **{server}**!", | ||
| "\uD83C\uDF1F The bards shall sing of this day! **{player}** hath arrived in **{server}**!", | ||
| "\uD83D\uDC51 All hail **{player}**, who now graces the lands of **{server}**!", | ||
| "\uD83D\uDD25 The torches flicker as **{player}** strides into **{server}**!", | ||
| "\uD83C\uDFBA Sound the trumpets! **{player}** hath joined the kingdom of **{server}**!", | ||
| "\uD83D\uDEE1\uFE0F The defenders of **{server}** welcome **{player}** to their ranks!", | ||
| "\u2728 By the stars above, **{player}** hath made their presence known in **{server}**!" | ||
| ); |
There was a problem hiding this comment.
DEFAULT_JOIN_MESSAGES is duplicated (same 10 strings also appear in src/main/resources/config.yml and README.md). This creates a drift risk where the documented/configured defaults diverge from the fallback used when the config key is missing/empty. Consider establishing a single source of truth (e.g., generate config defaults from the Java list on first run, or keep defaults only in config and remove/derive the Java list).
| this.joinMessages = (joinMessages != null && !joinMessages.isEmpty()) | ||
| ? Collections.unmodifiableList(new ArrayList<>(joinMessages)) | ||
| : DEFAULT_JOIN_MESSAGES; | ||
| this.random = random; | ||
| } | ||
|
|
||
| /** | ||
| * Send a player-join notification to Discord. | ||
| * Formats the message using Discord Markdown bold syntax and sends it via webhook. | ||
| * Picks a random message from the configured templates and sends it via webhook. | ||
| * Each template supports {player} and {server} placeholders. | ||
| * | ||
| * @param playerName the name of the player who joined | ||
| * @param serverName the name of the server they joined | ||
| * @throws IOException if there's an error sending the message | ||
| */ | ||
| @Override | ||
| public void notifyPlayerJoin(String playerName, String serverName) throws IOException { | ||
| String content = "**" + playerName + "** joined the **" + serverName + "** server"; | ||
| String template = joinMessages.get(random.nextInt(joinMessages.size())); | ||
| String content = template.replace("{player}", playerName).replace("{server}", serverName); |
There was a problem hiding this comment.
notifyPlayerJoin assumes every entry in joinMessages is non-null. If a caller passes a list containing null entries (or config deserialization ever yields nulls), template.replace(...) will throw a NullPointerException. Consider filtering out null/blank templates in the constructor and falling back to DEFAULT_JOIN_MESSAGES if the filtered list ends up empty.
Adds a configurable
discord.join-messageslist of templates with{player}and{server}placeholders. A random message is chosen each time a player joins. Ships with 10 medieval-themed defaults. The server name used in{server}is now configurable viaserver-nameinstead of always showing "CraftBukkit".Changes
DiscordNotifier: Constructor now accepts aList<String>of join message templates. Randomly picks one on each player join. Falls back toDEFAULT_JOIN_MESSAGES(10 medieval-themed entries) when the list is null/empty. Package-private constructor accepts an injectableRandomfor deterministic testing.Herald.java: Readsdiscord.join-messages(list) from config, passes toDiscordNotifier. Readsserver-namefrom config for the{server}placeholder instead of usinggetServer().getName()(which always returned "CraftBukkit"). Defaults to"Minecraft"when empty.config.yml: Newdiscord.join-messageslist key with 10 default entries. New top-levelserver-namekey.README.md: Documents the new list-based config option, placeholder usage, random selection behavior, and theserver-namesetting.Config example
Users can add, remove, or replace messages freely. To restore the old single-message style, use a list with one entry, e.g.
["**{player}** joined **{server}**"]. Setserver-nameto your server's name so{server}displays correctly instead of "CraftBukkit".✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.