Skip to content
Draft
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
8 changes: 7 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ repositories {
}

dependencies {
implementation(libs.discord4j)
implementation(libs.jda) {
exclude(module = "opus-java")
exclude(module = "tink")
}
implementation(libs.bundles.netty)
implementation(libs.bundles.jackson)
implementation(libs.jedis)
implementation(libs.aikar.taskchain)
implementation(libs.semver4j)
Expand Down Expand Up @@ -83,6 +88,7 @@ tasks.build {
}

tasks.shadowJar {
minimize()
isEnableRelocation = true
relocate("io.netty.buffer", "io.netty.buffer")
relocate("io.netty.util", "io.netty.util")
Expand Down
22 changes: 22 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ luckperms-api = "5.5"
jetbrains-annotation = "26.1.0"
junit = "5.14.4"
junit-platform = "1.14.4"
jda = "6.4.1"
netty = "4.2.12.Final"
jackson-core = "2.21.2"
jackson-databind = "2.21.2"
jackson-annotations = "2.21"

[plugins]
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
Expand All @@ -39,3 +44,20 @@ jetbrains-annotation = { module = "org.jetbrains:annotations", version.ref = "je
junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junit-platform" }
jda = { module = "net.dv8tion:JDA", version.ref = "jda" }
netty-buffer = { module = "io.netty:netty-buffer", version.ref = "netty"}
netty-codec = { module = "io.netty:netty-codec", version.ref = "netty"}
jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jackson-core" }
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson-databind" }
jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson-annotations" }

[bundles]
jackson = [
"jackson-core",
"jackson-databind",
"jackson-annotations"
]
netty = [
"netty-buffer",
"netty-codec"
]
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ private void setupRedisConnections() {
private void setupDiscordConnection() {
try {
vcLunaChatChannelSharer.setLunaChatChannelName(rpcConfig.getVcCommandLunaChatChannel());
discordHandler = new DiscordHandler(this, rpcConfig.getDiscordBotToken());
boolean initResult = discordHandler.init();
discordHandler = new DiscordHandler(this);
boolean initResult = discordHandler.init(rpcConfig.getDiscordBotToken());

if (!initResult) {
getLogger().warning("Failed to login to Discord Bot. Is that the correct Token?");
Expand Down
15 changes: 7 additions & 8 deletions src/main/java/net/azisaba/ryuzupluginchat/config/RPCConfig.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package net.azisaba.ryuzupluginchat.config;

import discord4j.common.util.Snowflake;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.azisaba.ryuzupluginchat.RyuZUPluginChat;
Expand All @@ -16,6 +11,11 @@
import org.bukkit.configuration.file.FileConfiguration;
import redis.clients.jedis.HostAndPort;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

@Getter
@RequiredArgsConstructor
public class RPCConfig {
Expand Down Expand Up @@ -131,9 +131,8 @@ private DiscordMessageConnection importConnectionDataFromConfig(
.warning("Invalid discord channel id ( " + section + ".discord-channel-id )");
return null;
}
Snowflake discordChannelId = Snowflake.of(discordChIdLong);

GlobalChatSyncData globalData;
GlobalChatSyncData globalData;
ChannelChatSyncData channelData;
PrivateChatSyncData privateData;

Expand Down Expand Up @@ -173,6 +172,6 @@ private DiscordMessageConnection importConnectionDataFromConfig(
privateData = new PrivateChatSyncData(false, false);
}

return new DiscordMessageConnection(id, discordChannelId, globalData, channelData, privateData);
return new DiscordMessageConnection(id, discordChIdLong, globalData, channelData, privateData);
}
}
258 changes: 114 additions & 144 deletions src/main/java/net/azisaba/ryuzupluginchat/discord/DiscordHandler.java
Original file line number Diff line number Diff line change
@@ -1,175 +1,145 @@
package net.azisaba.ryuzupluginchat.discord;

import discord4j.common.util.Snowflake;
import discord4j.core.DiscordClient;
import discord4j.core.GatewayDiscordClient;
import discord4j.core.event.domain.message.MessageCreateEvent;
import discord4j.core.object.entity.Message;
import discord4j.core.object.entity.User;
import discord4j.rest.entity.RestChannel;
import lombok.RequiredArgsConstructor;
import net.azisaba.ryuzupluginchat.RyuZUPluginChat;
import net.azisaba.ryuzupluginchat.discord.data.ChannelChatSyncData;
import net.azisaba.ryuzupluginchat.discord.data.GlobalChatSyncData;
import net.azisaba.ryuzupluginchat.discord.data.PrivateChatSyncData;
import net.azisaba.ryuzupluginchat.discord.deliverer.DiscordMessageDeliverer;
import net.azisaba.ryuzupluginchat.discord.deliverer.ServerChatMessageDeliverer;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.requests.GatewayIntent;
import org.bukkit.Bukkit;

@RequiredArgsConstructor
public class DiscordHandler {
import java.util.concurrent.ConcurrentHashMap;

private final RyuZUPluginChat plugin;
private final String token;
@RequiredArgsConstructor
public class DiscordHandler extends ListenerAdapter {
private JDA jda;
private final RyuZUPluginChat plugin;
private DiscordMessageDeliverer discordMessageDeliverer;
private ServerChatMessageDeliverer serverChatMessageDeliverer;

// どのチャンネルIDがどの処理(Global, Channel, Private)に紐付いているかを管理
private final ConcurrentHashMap<Long, DiscordInputType> channelConfigurations = new ConcurrentHashMap<>();

public boolean init(String token) {
try {
// JDAの構築
jda = JDABuilder.createDefault(token)
.enableIntents(GatewayIntent.GUILD_MESSAGES, GatewayIntent.MESSAGE_CONTENT)
.addEventListeners(this)
.build();

// 接続完了まで待機
jda.awaitReady();

this.discordMessageDeliverer = new DiscordMessageDeliverer(plugin);
this.serverChatMessageDeliverer = new ServerChatMessageDeliverer(plugin, jda);

return true;
} catch (Exception e) {
plugin.getLogger().severe("Discordの初期化に失敗しました: " + e.getMessage());
return false;
}
}

private DiscordClient client;
private GatewayDiscordClient gateway;
// DiscordからMinecraftへの入力イベント
@Override
public void onMessageReceived(MessageReceivedEvent event) {
if (event.getAuthor().isBot()) return;

private DiscordMessageDeliverer discordMessageDeliverer;
private ServerChatMessageDeliverer serverChatMessageDeliverer;
long channelId = event.getChannel().getIdLong();
DiscordInputType type = channelConfigurations.get(channelId);

public boolean init() {
if (type == null) return;

client = DiscordClient.create(token);
gateway = client.login().block();
switch (type) {
case GLOBAL:
discordMessageDeliverer.sendToGlobal(event);
break;
case CHANNEL:
// syncDataが必要な場合は、別途Mapなどで管理して渡す
discordMessageDeliverer.sendToChannel(event, type.getSyncData());
break;
}
}

if (gateway == null) {
return false;
public void connectUsing(DiscordMessageConnection connectionData) {
long channelId = connectionData.getDiscordChannelId();

// Global Chat Sync
if (connectionData.getGlobalChatSyncData().isEnabled()) {
GlobalChatSyncData data = connectionData.getGlobalChatSyncData();
if (data.isDiscordInputEnabled()) {
channelConfigurations.put(channelId, DiscordInputType.GLOBAL);
}
registerGlobalToDiscord(channelId, data.isVoiceChatMode());
}

// Channel Chat Sync
if (connectionData.getChannelChatSyncData().isEnabled()) {
ChannelChatSyncData data = connectionData.getChannelChatSyncData();
if (data.isDiscordInputEnabled()) {
// 必要に応じてsyncDataを保持するロジックを追加
channelConfigurations.put(channelId, DiscordInputType.CHANNEL);
}
registerLunaChatChannelToDiscord(data, channelId, data.isVoiceChatMode());
}

// Private Chat Sync
if (connectionData.getPrivateChatSyncData().isEnabled()) {
PrivateChatSyncData data = connectionData.getPrivateChatSyncData();
registerPrivateToDiscord(channelId, data.isVoiceChatMode());
}
}

discordMessageDeliverer = new DiscordMessageDeliverer(plugin);
serverChatMessageDeliverer = new ServerChatMessageDeliverer(plugin, client);
private void registerGlobalToDiscord(long chId, boolean vcMode) {
MessageChannel targetChannel = jda.getTextChannelById(chId);
if (targetChannel == null) return;

// TODO これ要らなくない?
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> gateway.onDisconnect().block());
return true;
}
plugin.getSubscriber().registerPublicConsumer((data) -> {
if (data.isFromDiscord()) return;
Bukkit.getScheduler().runTaskAsynchronously(plugin,
() -> serverChatMessageDeliverer.sendToDiscord(data, targetChannel, vcMode));
});
}

public void connectUsing(DiscordMessageConnection connectionData) {
if (connectionData.getGlobalChatSyncData().isEnabled()) {
GlobalChatSyncData data = connectionData.getGlobalChatSyncData();
if (data.isDiscordInputEnabled()) {
registerDiscordToGlobal(connectionData.getDiscordChannelId());
}
private void registerLunaChatChannelToDiscord(ChannelChatSyncData syncData, long chId, boolean vcMode) {
MessageChannel targetChannel = jda.getTextChannelById(chId);
if (targetChannel == null) return;

registerGlobalToDiscord(connectionData.getDiscordChannelId(), data.isVoiceChatMode());
}
plugin.getSubscriber().registerChannelChatConsumer((data) -> {
if (data.isFromDiscord()) return;
if (!syncData.isMatch(data.getLunaChatChannelName())) return;

if (connectionData.getChannelChatSyncData().isEnabled()) {
ChannelChatSyncData data = connectionData.getChannelChatSyncData();
Bukkit.getScheduler().runTaskAsynchronously(plugin,
() -> serverChatMessageDeliverer.sendToDiscord(data, targetChannel, vcMode));
});
}

if (data.isDiscordInputEnabled()) {
registerDiscordToChannels(connectionData.getDiscordChannelId(), data);
}
private void registerPrivateToDiscord(long chId, boolean vcMode) {
MessageChannel targetChannel = jda.getTextChannelById(chId);
if (targetChannel == null) return;

registerLunaChatChannelToDiscord(
data, connectionData.getDiscordChannelId(), data.isVoiceChatMode());
plugin.getSubscriber().registerTellConsumer((data) ->
Bukkit.getScheduler().runTaskAsynchronously(plugin,
() -> serverChatMessageDeliverer.sendToDiscord(data, targetChannel, vcMode)));
}

if (connectionData.getPrivateChatSyncData().isEnabled()) {
PrivateChatSyncData data = connectionData.getPrivateChatSyncData();
registerPrivateToDiscord(connectionData.getDiscordChannelId(), data.isVoiceChatMode());
public void disconnect() {
if (jda != null) jda.shutdown();
}
}

private void registerDiscordToGlobal(Snowflake discordChannelId) {
gateway
.on(MessageCreateEvent.class)
.subscribe(
event -> {
try {
Message message = event.getMessage();
User user = message.getAuthor().orElse(null);
if (user == null || user.isBot()) {
return;
}
if (!message.getChannelId().equals(discordChannelId)) {
return;
}

discordMessageDeliverer.sendToGlobal(event);
} catch (Exception e) {
e.printStackTrace();
}
});
}

private void registerDiscordToChannels(Snowflake discordChannelId, ChannelChatSyncData syncData) {
gateway
.on(MessageCreateEvent.class)
.subscribe(
event -> {
try {
Message message = event.getMessage();
User user = message.getAuthor().orElse(null);
if (user == null || user.isBot()) {
return;
}
if (!message.getChannelId().equals(discordChannelId)) {
return;
}

discordMessageDeliverer.sendToChannel(event, syncData);
} catch (Exception e) {
e.printStackTrace();
}
});
}

private void registerGlobalToDiscord(Snowflake chId, boolean vcMode) {
RestChannel targetChannel = client.getChannelById(chId);
plugin
.getSubscriber()
.registerPublicConsumer(
(data) -> {
if (data.isFromDiscord()) {
return;
}

Bukkit.getScheduler()
.runTaskAsynchronously(
plugin,
() -> serverChatMessageDeliverer.sendToDiscord(data, targetChannel, vcMode));
});
}

private void registerLunaChatChannelToDiscord(
ChannelChatSyncData channelChatSyncData, Snowflake discordChannelId, boolean vcMode) {
RestChannel targetChannel = client.getChannelById(discordChannelId);
plugin
.getSubscriber()
.registerChannelChatConsumer(
(data) -> {
if (data.isFromDiscord()) {
return;
}
if (!channelChatSyncData.isMatch(data.getLunaChatChannelName())) {
return;
}

Bukkit.getScheduler()
.runTaskAsynchronously(
plugin,
() -> serverChatMessageDeliverer.sendToDiscord(data, targetChannel, vcMode));
});
}

private void registerPrivateToDiscord(Snowflake discordChannelId, boolean vcMode) {
RestChannel targetChannel = client.getChannelById(discordChannelId);
plugin
.getSubscriber()
.registerTellConsumer(
(data) ->
Bukkit.getScheduler()
.runTaskAsynchronously(
plugin,
() ->
serverChatMessageDeliverer.sendtoDiscord(data, targetChannel, vcMode)));
}

public void disconnect() {
if (gateway != null) {
gateway.logout().block();
// 内部判別用
private enum DiscordInputType {
GLOBAL, CHANNEL;
private ChannelChatSyncData syncData;
public void setSyncData(ChannelChatSyncData data) { this.syncData = data; }
public ChannelChatSyncData getSyncData() { return syncData; }
}
}
}
Loading