Developer API
AstralChat exposes its services through the main plugin instance and fires a cancellable Bukkit event for every channel message. Other plugins can integrate by depending on com.astralrealms:chat.
Maven coordinates
<dependency>
<groupId>com.astralrealms</groupId>
<artifactId>chat</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
Repository: https://maven.astralrealms.fr/repository/maven-public/.
Plugin handle
import com.astralrealms.chat.AstralChat;
AstralChat plugin = AstralChat.getPlugin(AstralChat.class);
Services
Every service is exposed via a Lombok-generated getter on the plugin instance.
Getter | Type | Use |
|---|
plugin.channels()
| ChannelService
| Broadcast into a channel, pause/resume, network packet helpers. |
plugin.chat()
| PaperChatService
| Render and broadcast components to channel subscribers. Implements the shared ChatService interface. |
plugin.privateMessages()
| PrivateMessageService
| Programmatically send a PM. |
plugin.ignores()
| IgnoreService (impl: IgnoreServiceImpl)
| Read and mutate ignore relationships. Shared via the AstralCore service repository. |
plugin.filters()
| FilterService
| Run filters; register custom filters. |
plugin.mentions()
| MentionService
| Dispatch a mention notification programmatically. |
plugin.tags()
| TagService
| Look up a tag by id; iterate all tags. |
plugin.players()
| PlayerService
| Subscription state, cached PlayerChatData. |
plugin.moderation()
| ModerationService
| Per-channel cooldown bookkeeping. |
plugin.configuration()
| MainConfiguration
| Loaded config.yml. Call findChannel, defaultChannel, isEnabled. |
plugin.groupsConfiguration()
| GroupsConfiguration
| Loaded groups.yml. |
The chat service and ignore service are also registered with AstralCore's service repository so any other plugin can resolve them without a hard dependency on AstralChat:
AstralPaperAPI.getService(ChatService.class)
.ifPresent(chat -> chat.broadcastChatMessage(player, "global", component, raw));
AstralPaperAPI.getService(IgnoreService.class)
.ifPresent(ignores -> ignores.isIgnoring(viewer, candidate));
Broadcasting
ChatChannel channel = plugin.configuration().findChannel("global").orElseThrow();
Component message = Component.text("Server restart in 5 minutes", NamedTextColor.RED);
plugin.channels().broadcast(channel, message);
For a fully-rendered chat message that respects pause and cooldown, use the chat service:
plugin.chat().broadcastChatMessage(sender, "global", component, rawString);
Sending a private message
plugin.privateMessages()
.sendPrivateMessage(sender, recipient, "Hello!");
The call is network-aware and returns asynchronously via the RabbitMQ pipeline. Failure reasons surface as status codes which are mapped to message keys (see Private Messages).
Listening to chat events
ChannelChatEvent is an async cancellable Bukkit event fired by DefaultChannelChatListener after rendering and before viewer fan-out.
import com.astralrealms.chat.event.ChannelChatEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import net.kyori.adventure.text.Component;
public class ExampleListener implements Listener {
@EventHandler
public void onChannelChat(ChannelChatEvent event) {
Player sender = event.getPlayer();
String raw = event.getMessage();
Component component = event.getComponent();
ChatChannel channel = event.getChannel();
// Rewrite — replace the broadcast component
event.setComponent(component.append(Component.text(" *edited*")));
// Or cancel — sender's message never reaches subscribers
if (raw.contains("forbidden")) {
event.setCancelled(true);
}
}
}
Method | Notes |
|---|
getPlayer()
| Sender. |
getMessage()
| Raw string the player typed (pre-filter). |
getComponent()/setComponent(Component)
| The rendered component about to be broadcast. |
getChannel()
| The ChatChannel. |
isCancelled()/setCancelled(boolean)
| Inherited. |
Always check Bukkit.isPrimaryThread() before touching Bukkit APIs from inside the handler — the event fires async when AstralChat handles the message off the main thread.
Registering a custom filter
import com.astralrealms.chat.filters.ChatFilter;
import com.astralrealms.chat.filters.FilterResult;
import org.bukkit.entity.Player;
public class CustomFilter extends ChatFilter {
public CustomFilter() {
super("custom", List.of());
}
@Override
public FilterResult filter(String message, Player player) {
if (message.contains("blocked"))
return FilterResult.match("custom filter matched");
return FilterResult.noMatch();
}
}
// During onEnable, after AstralChat is loaded
plugin.filters().registerFilter(new CustomFilter());
FilterResult factories:
Factory | Meaning |
|---|
FilterResult.noMatch()
| Pass through. |
FilterResult.match()
| Match without a reason. |
FilterResult.match(String)
| Match with a reason string (used by notification-channel templates). |
FilterResult.match(String, Component)
| Match with a replacement component for the broadcast. |
Reading per-player state
PlayerChatData is persisted by AstralSync — pull it through SyncAPI:
import com.astralrealms.chat.snapshot.PlayerChatData;
import com.astralrealms.sync.SyncAPI;
SyncAPI.findData(player.getUniqueId(), PlayerChatData.class)
.ifPresent(data -> {
boolean pmOn = data.privateMessagesEnabled();
String tag = data.chatTag();
Set<String> subs = data.subscribedChannels();
});
Mutating these flags directly bypasses the user-facing confirmation messages — use the action types from Actions for normal flows; only touch the snapshot directly for admin tooling.
Last modified: 25 July 2026