diff --git a/pom.xml b/pom.xml
index 9db32ab..137ddae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,6 +38,17 @@
fastjson2
2.0.21
+
+ net.dv8tion
+ JDA
+ 5.0.0-beta.18
+
+
+ club.minnced
+ opus-java
+
+
+
diff --git a/src/main/java/com/zhanganzhi/chathub/adaptors/discord/DiscordAdaptor.java b/src/main/java/com/zhanganzhi/chathub/adaptors/discord/DiscordAdaptor.java
new file mode 100644
index 0000000..7abe5bc
--- /dev/null
+++ b/src/main/java/com/zhanganzhi/chathub/adaptors/discord/DiscordAdaptor.java
@@ -0,0 +1,118 @@
+package com.zhanganzhi.chathub.adaptors.discord;
+
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.JDABuilder;
+import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
+import net.dv8tion.jda.api.interactions.commands.build.Commands;
+import net.dv8tion.jda.api.requests.GatewayIntent;
+
+import com.zhanganzhi.chathub.ChatHub;
+import com.zhanganzhi.chathub.adaptors.IAdaptor;
+import com.zhanganzhi.chathub.core.Config;
+import com.zhanganzhi.chathub.entity.Platform;
+import com.zhanganzhi.chathub.event.MessageEvent;
+import com.zhanganzhi.chathub.event.ServerChangeEvent;
+
+public class DiscordAdaptor implements IAdaptor {
+ private static final Platform platform = Platform.DISCORD;
+ private final Config config = Config.getInstance();
+ private final ChatHub chatHub;
+ private JDA jda;
+ private TextChannel channel;
+
+ public DiscordAdaptor(ChatHub chatHub) {
+ this.chatHub = chatHub;
+ }
+
+ @Override
+ public Platform getPlatform() {
+ return platform;
+ }
+
+ @Override
+ public void onUserChat(MessageEvent event) {
+ sendMessage(config.getDiscordChatMessage(
+ event.getServerName(),
+ event.user(),
+ event.content()
+ ));
+ }
+
+ @Override
+ public void onJoinServer(ServerChangeEvent event) {
+ sendMessage(config.getDiscordJoinMessage(
+ event.server,
+ event.player.getUsername()
+ ));
+ }
+
+ @Override
+ public void onLeaveServer(ServerChangeEvent event) {
+ sendMessage(config.getDiscordLeaveMessage(
+ event.player.getUsername()
+ ));
+ }
+
+ @Override
+ public void onSwitchServer(ServerChangeEvent event) {
+ sendMessage(config.getDiscordSwitchMessage(
+ event.player.getUsername(),
+ event.serverPrev,
+ event.server
+ ));
+ }
+
+ @Override
+ public void sendListMessage(String source) {
+ }
+
+ void sendMessage(String message) {
+ if (config.isDiscordEnabled()) {
+ new Thread(() -> channel.sendMessage(message).queue()).start();
+ }
+ }
+
+ @Override
+ public void start() {
+ if (config.isDiscordEnabled()) {
+ // logger
+ chatHub.getLogger().info("Discord enabled");
+
+ // build jda
+ jda = JDABuilder.createLight(config.getDiscordToken())
+ .enableIntents(
+ GatewayIntent.MESSAGE_CONTENT
+ )
+ .addEventListeners(new DiscordReceiver(chatHub))
+ .build();
+
+ // commands
+ jda.updateCommands().addCommands(
+ Commands
+ .slash("list", "List online players")
+ .setGuildOnly(true)
+ ).queue();
+
+ // await jda ready
+ try {
+ jda.awaitReady();
+ } catch (InterruptedException e) {
+ chatHub.getLogger().error("Discord jda failed to start: " + e.getMessage());
+ }
+
+ // get channel
+ channel = jda.getTextChannelById(config.getDiscordChannelId());
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (config.isDiscordEnabled() && jda != null) {
+ jda.shutdownNow();
+ }
+ }
+
+ @Override
+ public void restart() {
+ }
+}
diff --git a/src/main/java/com/zhanganzhi/chathub/adaptors/discord/DiscordReceiver.java b/src/main/java/com/zhanganzhi/chathub/adaptors/discord/DiscordReceiver.java
new file mode 100644
index 0000000..4ab1a62
--- /dev/null
+++ b/src/main/java/com/zhanganzhi/chathub/adaptors/discord/DiscordReceiver.java
@@ -0,0 +1,65 @@
+package com.zhanganzhi.chathub.adaptors.discord;
+
+import com.velocitypowered.api.proxy.Player;
+import com.velocitypowered.api.proxy.server.RegisteredServer;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
+
+import com.zhanganzhi.chathub.ChatHub;
+import com.zhanganzhi.chathub.core.Config;
+import com.zhanganzhi.chathub.core.EventHub;
+import com.zhanganzhi.chathub.entity.Platform;
+import com.zhanganzhi.chathub.event.MessageEvent;
+
+public class DiscordReceiver extends ListenerAdapter {
+ private static final Platform PLATFORM = Platform.DISCORD;
+ private final Config config = Config.getInstance();
+ private final ChatHub chatHub;
+ private final EventHub eventHub;
+
+ public DiscordReceiver(ChatHub chatHub) {
+ this.chatHub = chatHub;
+ this.eventHub = chatHub.getEventHub();
+ }
+
+ @Override
+ public void onMessageReceived(MessageReceivedEvent messageReceivedEvent) {
+ // ignore bot message
+ if (messageReceivedEvent.getAuthor().isBot()) {
+ return;
+ }
+
+ // check channel id
+ if (!messageReceivedEvent.getChannel().getId().equals(config.getDiscordChannelId())) {
+ return;
+ }
+
+ // handle message
+ String content = messageReceivedEvent.getMessage().getContentStripped();
+ eventHub.onUserChat(new MessageEvent(
+ PLATFORM,
+ null,
+ messageReceivedEvent.getAuthor().getEffectiveName(),
+ content
+ ));
+ }
+
+ @Override
+ public void onSlashCommandInteraction(SlashCommandInteractionEvent slashCommandInteractionEvent) {
+ if (slashCommandInteractionEvent.getName().equals("list")) {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (RegisteredServer registeredServer : chatHub.getProxyServer().getAllServers()) {
+ if (!registeredServer.getPlayersConnected().isEmpty()) {
+ stringBuilder.append(config.getDiscordListMessage(
+ registeredServer.getServerInfo().getName(),
+ registeredServer.getPlayersConnected().size(),
+ registeredServer.getPlayersConnected().stream().map(Player::getUsername).toArray(String[]::new)
+ ));
+ stringBuilder.append("\n");
+ }
+ }
+ slashCommandInteractionEvent.reply(stringBuilder.isEmpty() ? config.getDiscordListEmptyMessage() : stringBuilder.toString()).queue();
+ }
+ }
+}
diff --git a/src/main/java/com/zhanganzhi/chathub/core/Config.java b/src/main/java/com/zhanganzhi/chathub/core/Config.java
index e127d3a..b4bdd98 100644
--- a/src/main/java/com/zhanganzhi/chathub/core/Config.java
+++ b/src/main/java/com/zhanganzhi/chathub/core/Config.java
@@ -95,6 +95,59 @@ public String getMinecraftListEmptyMessage() {
return configToml.getString("minecraft.message.listEmpty");
}
+ public boolean isDiscordEnabled() {
+ return configToml.getBoolean("discord.enable");
+ }
+
+ public String getDiscordToken() {
+ return configToml.getString("discord.token");
+ }
+
+ public String getDiscordChannelId() {
+ return configToml.getString("discord.channelId");
+ }
+
+ public String getDiscordChatMessage(String server, String name, String message) {
+ return configToml
+ .getString("discord.message.chat")
+ .replace("{server}", getPlainServername(server))
+ .replace("{name}", name)
+ .replace("{message}", message);
+ }
+
+ public String getDiscordJoinMessage(String server, String name) {
+ return configToml
+ .getString("discord.message.join")
+ .replace("{server}", getPlainServername(server))
+ .replace("{name}", name);
+ }
+
+ public String getDiscordLeaveMessage(String name) {
+ return configToml
+ .getString("discord.message.leave")
+ .replace("{name}", name);
+ }
+
+ public String getDiscordSwitchMessage(String name, String serverFrom, String serverTo) {
+ return configToml
+ .getString("discord.message.switch")
+ .replace("{name}", name)
+ .replace("{serverFrom}", getPlainServername(serverFrom))
+ .replace("{serverTo}", getPlainServername(serverTo));
+ }
+
+ public String getDiscordListMessage(String server, int count, String[] playerList) {
+ return configToml
+ .getString("discord.message.list")
+ .replace("{server}", getPlainServername(server))
+ .replace("{count}", String.valueOf(count))
+ .replace("{playerList}", String.join(", ", playerList));
+ }
+
+ public String getDiscordListEmptyMessage() {
+ return configToml.getString("discord.message.listEmpty");
+ }
+
public boolean isKookEnabled() {
return tempIsKookEnabled;
}
diff --git a/src/main/java/com/zhanganzhi/chathub/core/EventHub.java b/src/main/java/com/zhanganzhi/chathub/core/EventHub.java
index fc64ebb..0237b5c 100644
--- a/src/main/java/com/zhanganzhi/chathub/core/EventHub.java
+++ b/src/main/java/com/zhanganzhi/chathub/core/EventHub.java
@@ -4,6 +4,7 @@
import com.zhanganzhi.chathub.ChatHub;
import com.zhanganzhi.chathub.adaptors.IAdaptor;
+import com.zhanganzhi.chathub.adaptors.discord.DiscordAdaptor;
import com.zhanganzhi.chathub.adaptors.kook.KookAdaptor;
import com.zhanganzhi.chathub.adaptors.velocity.VelocityAdaptor;
import com.zhanganzhi.chathub.entity.Platform;
@@ -15,6 +16,7 @@ public class EventHub {
public EventHub(ChatHub chatHub) {
adaptors = List.of(
+ new DiscordAdaptor(chatHub),
new KookAdaptor(chatHub, this),
new VelocityAdaptor(chatHub, this)
);
diff --git a/src/main/java/com/zhanganzhi/chathub/entity/Platform.java b/src/main/java/com/zhanganzhi/chathub/entity/Platform.java
index b01ed1c..02756dc 100644
--- a/src/main/java/com/zhanganzhi/chathub/entity/Platform.java
+++ b/src/main/java/com/zhanganzhi/chathub/entity/Platform.java
@@ -1,6 +1,7 @@
package com.zhanganzhi.chathub.entity;
public enum Platform {
+ DISCORD("discord"),
KOOK("kook"),
VELOCITY("velocity");
diff --git a/src/main/resources/config.toml b/src/main/resources/config.toml
index c5a029b..ca96321 100644
--- a/src/main/resources/config.toml
+++ b/src/main/resources/config.toml
@@ -2,8 +2,9 @@
lobby = '§f§l大厅服'
survival = '§2§l生存服'
creative = '§6§l创造服'
-kook = '§a§lKOOK'
-qq = '§5§l群聊天'
+discord = '§a§lDiscord'
+kook = '§a§lKook'
+qq = '§a§l群聊天'
[minecraft]
completeTakeoverMode = false
@@ -18,6 +19,19 @@ msgTarget = '§7§o{sender}悄悄地对你说: {message}'
list = '§8§l» §7[{server}§7] 当前共有§6{count}§7名玩家在线: §e{playerList}'
listEmpty = '当前没有玩家在线'
+[discord]
+enable = false
+token = ''
+channelId = ''
+
+[discord.message]
+chat = '[{server}] <{name}>: {message}'
+join = '[+] [{server}] {name}'
+leave = '[-] {name}'
+switch = '<{name}>: [{serverFrom}] ➟ [{serverTo}]'
+list = '- [{server}] 当前共有{count}名玩家在线: {playerList}'
+listEmpty = '当前没有玩家在线'
+
[kook]
enable = false
token = ''