Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
acrylic-style committed Jul 30, 2024
1 parent 8dd25f3 commit 6878dd6
Show file tree
Hide file tree
Showing 10 changed files with 280 additions and 34 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
<dependency>
<groupId>net.azisaba</groupId>
<artifactId>lunachatplus</artifactId>
<version>3.3.0</version>
<version>3.3.1</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down
16 changes: 4 additions & 12 deletions src/main/java/net/azisaba/ryuzupluginchat/RyuZUPluginChat.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,7 @@
import net.azisaba.ryuzupluginchat.listener.PrivateChatDebugListener;
import net.azisaba.ryuzupluginchat.localization.Messages;
import net.azisaba.ryuzupluginchat.message.*;
import net.azisaba.ryuzupluginchat.redis.HideAllInfoController;
import net.azisaba.ryuzupluginchat.redis.HideInfoController;
import net.azisaba.ryuzupluginchat.redis.MessagePublisher;
import net.azisaba.ryuzupluginchat.redis.MessageSubscriber;
import net.azisaba.ryuzupluginchat.redis.PlayerUUIDMapContainer;
import net.azisaba.ryuzupluginchat.redis.PrivateChatIDGetter;
import net.azisaba.ryuzupluginchat.redis.PrivateChatReachedSubscriber;
import net.azisaba.ryuzupluginchat.redis.PrivateChatResponseWaiter;
import net.azisaba.ryuzupluginchat.redis.ReplyTargetFetcher;
import net.azisaba.ryuzupluginchat.redis.RyuZUPrefixSuffixContainer;
import net.azisaba.ryuzupluginchat.redis.VCLunaChatChannelSharer;
import net.azisaba.ryuzupluginchat.redis.VanishController;
import net.azisaba.ryuzupluginchat.redis.*;
import net.azisaba.ryuzupluginchat.task.SubscriberPingTask;
import net.azisaba.ryuzupluginchat.taskchain.BukkitTaskChainFactory;
import net.azisaba.ryuzupluginchat.updater.GitHubPluginUpdater;
Expand Down Expand Up @@ -70,6 +59,7 @@ public final class RyuZUPluginChat extends JavaPlugin {
private HideInfoController hideInfoController;
private HideAllInfoController hideAllInfoController;
private VanishController vanishController;
private LanguageController languageController;
private final PrivateChatInspectHandler privateChatInspectHandler =
new PrivateChatInspectHandler();
private final ChannelChatInspectHandler channelChatInspectHandler =
Expand Down Expand Up @@ -214,6 +204,8 @@ private void setupRedisConnections() {
hideAllInfoController.refreshAllAsync();
vanishController = new VanishController(jedisPool, rpcConfig.getGroupName());
vanishController.refreshAllAsync();
languageController = new LanguageController(jedisPool, rpcConfig.getGroupName());
languageController.refreshAllAsync();

// Populate (reverse)hideMap
hideInfoController.updateCache();
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/net/azisaba/ryuzupluginchat/config/RPCConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
@Getter
@RequiredArgsConstructor
public class RPCConfig {
private static final String TRANSLATOR_DEFAULT_PROMPT =
"Translate the input into %language%." +
"Everything other than English translation must not be in the output." +
"Punctuations should be omitted if not in the original text.";

private final RyuZUPluginChat plugin;

Expand All @@ -39,6 +43,10 @@ public class RPCConfig {
private boolean defaultDisablePrivateChatInspect;
private boolean defaultDisableChannelChatInspect;

private String translatorPrompt;
private String translatorOpenAIApiKey;
private String translatorOpenAIOrganization;

private final List<DiscordMessageConnection> messageConnections = new ArrayList<>();

public void load() {
Expand All @@ -62,6 +70,10 @@ public void load() {

groupName = conf.getString("redis.group");

translatorPrompt = conf.getString("translator.prompt", TRANSLATOR_DEFAULT_PROMPT);
translatorOpenAIApiKey = conf.getString("translator.openai-api-key");
translatorOpenAIOrganization = conf.getString("translator.openai-organization");

defaultDisablePrivateChatInspect = conf.getBoolean("default-disable-private-chat-inspect", false);
defaultDisableChannelChatInspect = conf.getBoolean("default-disable-channel-chat-inspect", false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;

import java.util.Locale;

@RequiredArgsConstructor
public class JoinQuitListener implements Listener {

Expand All @@ -27,6 +29,18 @@ public void onJoin(PlayerJoinEvent e) {
}
},
20L * 3L);
Bukkit.getScheduler()
.runTaskAsynchronously(
plugin,
() -> {
if (plugin.getLanguageController().getLanguage(p.getUniqueId()) == null) {
String locale = p.getLocale().replaceAll("(.+)_.+", "$1");
if (Locale.forLanguageTag(locale).toLanguageTag().equalsIgnoreCase("und")) {
throw new IllegalArgumentException("Invalid locale received from client: " + p.getLocale());
}
plugin.getLanguageController().setLanguage(p.getUniqueId(), locale);
}
});

plugin.getHideAllInfoController().refreshHideAllInfoAsync(p.getUniqueId());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,48 @@
import net.azisaba.ryuzupluginchat.event.AsyncGlobalMessageEvent;
import net.azisaba.ryuzupluginchat.event.AsyncPrivateMessageEvent;
import net.azisaba.ryuzupluginchat.event.AsyncPrivateMessageNotifyEvent;
import net.azisaba.ryuzupluginchat.message.data.ChannelChatMessageData;
import net.azisaba.ryuzupluginchat.message.data.GlobalMessageData;
import net.azisaba.ryuzupluginchat.message.data.PrivateMessageData;
import net.azisaba.ryuzupluginchat.message.data.SystemMessageData;
import net.azisaba.ryuzupluginchat.message.data.*;
import net.azisaba.ryuzupluginchat.util.Chat;
import net.azisaba.ryuzupluginchat.util.OpenAIUtils;
import net.azisaba.ryuzupluginchat.util.TaskSchedulingUtils;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@RequiredArgsConstructor
public class MessageProcessor {

private final RyuZUPluginChat plugin;

private void translate(
@NotNull MessageData data,
@NotNull String originalMessage,
@NotNull String from,
@NotNull String to,
@Nullable UUID senderUUID
) {
if (from.equals(to)) {
data.setMessage(originalMessage);
} else {
try {
String displayLanguage = Locale.forLanguageTag(to).getDisplayLanguage(Locale.ENGLISH);
data.setMessage(OpenAIUtils.ask(plugin.getRpcConfig(), displayLanguage, String.valueOf(senderUUID), originalMessage));
} catch (Exception e) {
plugin.getSLF4JLogger().error("Error retrieving translation", e);
data.setMessage(originalMessage);
}
}
}

public void processGlobalMessage(GlobalMessageData data) {
String message = data.format();

UUID senderUUID = data.getPlayerUuid();
if (senderUUID == null) {
senderUUID = plugin.getPlayerUUIDMapContainer().getUUID(data.getPlayerName());
}
UUID senderUUID =
data.getPlayerUuid() == null
? plugin.getPlayerUUIDMapContainer().getUUID(data.getPlayerName())
: data.getPlayerUuid();
final Set<UUID> deafenPlayers;
if (senderUUID != null) {
deafenPlayers = plugin.getHideInfoController().getPlayersWhoHide(senderUUID);
Expand All @@ -45,21 +65,26 @@ public void processGlobalMessage(GlobalMessageData data) {
deafenPlayers = Collections.emptySet();
}

Set<Player> recipients = Bukkit.getOnlinePlayers().stream()
.filter(p -> !deafenPlayers.contains(p.getUniqueId()))
.collect(Collectors.toCollection(HashSet::new));

AsyncGlobalMessageEvent event = new AsyncGlobalMessageEvent(data, recipients);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
plugin.getLogger().info("[Global-Chat] " + ChatColor.stripColor(message));

for (Player player : event.getRecipients()) {
player.sendMessage(message);
}
String senderLanguage = plugin.getLanguageController().getLanguageOrDefault(senderUUID, Locale.JAPANESE.toLanguageTag());
Map<String, Set<Player>> recipientsMap = new HashMap<>();
Bukkit.getOnlinePlayers().stream()
.filter(p -> !deafenPlayers.contains(p.getUniqueId()))
.forEach(player -> recipientsMap.computeIfAbsent(plugin.getLanguageController().getLanguage(player), k -> new HashSet<>()).add(player));
String originalMessage = data.getMessage();
recipientsMap.entrySet().parallelStream().forEach(entry -> {
translate(data, originalMessage, senderLanguage, entry.getKey(), senderUUID);
AsyncGlobalMessageEvent event = new AsyncGlobalMessageEvent(data, entry.getValue());
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}

plugin.getLogger().info("[Global-Chat] " + ChatColor.stripColor(message));
for (Player player : event.getRecipients()) {
player.sendMessage(message);
}
});
}

public void processChannelChatMessage(ChannelChatMessageData data) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package net.azisaba.ryuzupluginchat.message.data;

public interface MessageData {
void setMessage(String message);
String getMessage();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package net.azisaba.ryuzupluginchat.redis;

import lombok.RequiredArgsConstructor;
import net.azisaba.ryuzupluginchat.RyuZUPluginChat;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

@RequiredArgsConstructor
public class LanguageController {

private final Map<UUID, String> languages = new HashMap<>();
private final ReentrantLock lock = new ReentrantLock();

private final JedisPool jedisPool;
private final String groupName;

public @NotNull String getLanguage(@NotNull Player player) {
String language = getLanguage(player.getUniqueId());
if (language == null) {
String locale = player.getLocale().replaceAll("(.+)_.+", "$1");
if (Locale.forLanguageTag(locale).toLanguageTag().equalsIgnoreCase("und")) {
language = "ja";
} else {
language = locale;
}
}
return language;
}

public @Nullable String getLanguage(UUID uuid) {
if (uuid == null) return Locale.JAPANESE.toLanguageTag();
lock.lock();
try {
return languages.get(uuid);
} finally {
lock.unlock();
}
}

public @NotNull String getLanguageOrDefault(UUID uuid, String defaultValue) {
if (uuid == null) return Locale.JAPANESE.toLanguageTag();
lock.lock();
try {
return languages.getOrDefault(uuid, Locale.JAPANESE.toLanguageTag());
} finally {
lock.unlock();
}
}

public void refreshAllAsync() {
RyuZUPluginChat.newChain()
.async(
() -> {
try (Jedis jedis = jedisPool.getResource()) {
String keyPrefix =
"rpc:" + groupName + ":language:";

try {
lock.lock();
languages.clear();
for (String s : jedis.keys(keyPrefix + "*")) {
try {
UUID uuid = UUID.fromString(s.replace(keyPrefix, ""));
languages.put(uuid, jedis.get(s));
} catch (IllegalArgumentException ignored) {
}
}
} finally {
lock.unlock();
}
}
})
.execute();
}

public void setLanguage(@NotNull UUID uuid, @NotNull String language) {
lock.lock();
try {
languages.put(uuid, language);
RyuZUPluginChat.newChain()
.async(
() -> {
try (Jedis jedis = jedisPool.getResource()) {
String key =
"rpc:" + groupName + ":language:" + uuid;
jedis.set(key, language);
}
})
.execute();
} finally {
lock.unlock();
}
}
}
52 changes: 52 additions & 0 deletions src/main/java/net/azisaba/ryuzupluginchat/util/OpenAIUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package net.azisaba.ryuzupluginchat.util;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import net.azisaba.ryuzupluginchat.config.RPCConfig;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;

public class OpenAIUtils {
public static @NotNull Map<String, String> getOpenAIRequestHeaders(@NotNull RPCConfig config) {
Map<String, String> map = new HashMap<>();
map.put("Content-Type", "application/json");
map.put("Authorization", "Bearer " + config.getTranslatorOpenAIApiKey());
if (config.getTranslatorOpenAIOrganization() != null) {
map.put("OpenAI-Organization", config.getTranslatorOpenAIOrganization());
}
return map;
}

public static @NotNull String ask(@NotNull RPCConfig config, @NotNull String language, @Nullable String user, @NotNull String message) {
JsonObject obj = getRequestBody(config.getTranslatorPrompt().replace("%language%", language), user, message);
JsonObject responseObject = new Gson().fromJson(Reqwest.post(obj.toString(), "https://api.openai.com/v1/chat/completions", getOpenAIRequestHeaders(config)), JsonObject.class);
return responseObject.getAsJsonArray("choices")
.get(0)
.getAsJsonObject()
.getAsJsonObject("message")
.get("content")
.getAsString();
}

private static @NotNull JsonObject getRequestBody(@NotNull String prompt, @Nullable String user, @NotNull String message) {
JsonObject systemMessage = new JsonObject();
systemMessage.addProperty("role", "system");
systemMessage.addProperty("content", prompt);
JsonObject userMessage = new JsonObject();
userMessage.addProperty("role", "user");
userMessage.addProperty("content", message);
JsonArray messages = new JsonArray();
messages.add(systemMessage);
messages.add(userMessage);
JsonObject obj = new JsonObject();
obj.addProperty("model", "gpt-4o");
obj.addProperty("user", user);
obj.addProperty("max_tokens", 250);
obj.add("messages", messages);
return obj;
}
}
Loading

0 comments on commit 6878dd6

Please sign in to comment.