commit 5c775c5d6d8da04041180e1d88be1469017c5c4a Author: JFronny Date: Thu Dec 30 22:16:37 2021 +0100 Add files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c37caf --- /dev/null +++ b/.gitignore @@ -0,0 +1,118 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ + +# Common working directory +run/ + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..a121a25 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,2 @@ +include: + - remote: 'https://jfmods.gitlab.io/scripts/jfmod.yml' \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..91ef586 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2021 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..e9c368d --- /dev/null +++ b/build.gradle @@ -0,0 +1,14 @@ +apply from: "https://jfmods.gitlab.io/scripts/jfmod.gradle" + +repositories { +} + +dependencies { + include modImplementation("io.gitlab.jfronny.libjf:libjf-config-v0:${project.jfapi_version}") + include("io.gitlab.jfronny.libjf:libjf-unsafe-v0:${project.jfapi_version}") + include("io.gitlab.jfronny.libjf:libjf-base:${project.jfapi_version}") + runtimeOnly("io.gitlab.jfronny.libjf:libjf-devutil-v0:${project.jfapi_version}") + + // https://maven.terraformersmc.com/releases/com/terraformersmc/modmenu + modImplementation "com.terraformersmc:modmenu:3.0.1" +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..41347a5 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,15 @@ +org.gradle.jvmargs=-Xmx1G +# https://fabricmc.net/versions.html +minecraft_version=1.18.1 +yarn_mappings=build.12 +loader_version=0.12.12 +maven_group=io.gitlab.jfronny +archives_base_name=GoogleChat + +jfapi_version=2.2.1 +modrinth_io= +# modrinth_required_dependencies= +# modrinth_optional_dependencies= +curseforge_id=none +# curseforge_required_dependencies= +# curseforge_optional_dependencies= \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..329f10c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..f91a4fe --- /dev/null +++ b/settings.gradle @@ -0,0 +1,9 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + } +} diff --git a/src/main/java/io/gitlab/jfronny/googlechat/GoogleChat.java b/src/main/java/io/gitlab/jfronny/googlechat/GoogleChat.java new file mode 100644 index 0000000..fa42fad --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/googlechat/GoogleChat.java @@ -0,0 +1,17 @@ +package io.gitlab.jfronny.googlechat; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Environment(EnvType.CLIENT) +public class GoogleChat implements ClientModInitializer { + public static final String MOD_ID = "google-chat"; + public static final Logger LOGGER = LogManager.getFormatterLogger(MOD_ID); + @Override + public void onInitializeClient() { + + } +} diff --git a/src/main/java/io/gitlab/jfronny/googlechat/GoogleChatConfig.java b/src/main/java/io/gitlab/jfronny/googlechat/GoogleChatConfig.java new file mode 100644 index 0000000..c9db257 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/googlechat/GoogleChatConfig.java @@ -0,0 +1,10 @@ +package io.gitlab.jfronny.googlechat; + +import io.gitlab.jfronny.libjf.config.api.Entry; +import io.gitlab.jfronny.libjf.config.api.JfConfig; + +public class GoogleChatConfig implements JfConfig { + @Entry public static Boolean enabled = true; + @Entry public static String serverLanguage = "auto"; + @Entry public static String clientLanguage = "en"; +} diff --git a/src/main/java/io/gitlab/jfronny/googlechat/api/GoogleService.java b/src/main/java/io/gitlab/jfronny/googlechat/api/GoogleService.java new file mode 100644 index 0000000..9bce2eb --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/googlechat/api/GoogleService.java @@ -0,0 +1,79 @@ +package io.gitlab.jfronny.googlechat.api; + +import io.gitlab.jfronny.googlechat.GoogleChat; +import org.apache.commons.lang3.StringEscapeUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class GoogleService { + private static final Pattern TRANSLATION_RESULT = Pattern.compile("class=\"result-container\">([^<]*)", Pattern.MULTILINE); + + public static String translate(String textToTranslate, Language translateFrom, Language translateTo) { + if (textToTranslate == null) + return null; + String pageSource = ""; + try { + pageSource = getPageSource(textToTranslate, translateFrom.value, translateTo.value); + Matcher matcher = TRANSLATION_RESULT.matcher(pageSource); + if (matcher.find()) { + String match = matcher.group(1); + if (match != null && !match.isEmpty()) { + return StringEscapeUtils.unescapeHtml4(match); + } + } + GoogleChat.LOGGER.error("Could not translate \"" + textToTranslate + "\": result page couldn't be parsed"); + return null; + } catch (Exception e) { + try { + Path p = Files.createTempFile("translater-pagedump-", ".html").toAbsolutePath(); + Files.writeString(p, pageSource); + GoogleChat.LOGGER.error("Could not translate string, see dumped page at " + p, e); + } catch (IOException ioe) { + GoogleChat.LOGGER.error("Could not translate string and the page could not be dumped", ioe); + } + return null; + } + } + + private static String getPageSource(String textToTranslate, String translateFrom, String translateTo) + throws Exception { + if (textToTranslate == null) + return null; + String pageUrl = String.format("https://translate.google.com/m?hl=en&sl=%s&tl=%s&ie=UTF-8&prev=_m&q=%s", + translateFrom, translateTo, URLEncoder.encode(textToTranslate.trim(), StandardCharsets.UTF_8)); + URL url = new URL(pageUrl); + HttpURLConnection connection = null; + BufferedReader bufferedReader = null; + StringBuilder pageSource = new StringBuilder(); + try { + connection = (HttpURLConnection) url.openConnection(); + connection.setConnectTimeout(5000); + connection.setRequestProperty("User-Agent", + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11"); + bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = bufferedReader.readLine()) != null) { + pageSource.append(line).append('\n'); + } + return pageSource.toString(); + } catch (Exception e) { + GoogleChat.LOGGER.error("Could not load translation from google", e); + return null; + } finally { + if (connection != null) + connection.disconnect(); + if (bufferedReader != null) + bufferedReader.close(); + } + } +} diff --git a/src/main/java/io/gitlab/jfronny/googlechat/api/Language.java b/src/main/java/io/gitlab/jfronny/googlechat/api/Language.java new file mode 100644 index 0000000..5bc378c --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/googlechat/api/Language.java @@ -0,0 +1,39 @@ +package io.gitlab.jfronny.googlechat.api; + +import java.util.HashMap; +import java.util.Map; + +public enum Language { + AUTO_DETECT("AUTO_DETECT", "auto"), ARABIC("ARABIC", "ar"), CHINESE_SIMPLIFIED("CHINESE_SIMPLIFIED", "zh-CN"), + CHINESE_TRADITIONAL("CHINESE_TRADITIONAL", "zh-TW"), ENGLISH("ENGLISH", "en"), FILIPINO("FILIPINO", "tl"), + FRENCH("FRENCH", "fr"), GERMAN("GERMAN", "de"), GREEK("GREEK", "el"), INDONESIAN("INDONESIAN", "id"), + IRISH("IRISH", "ga"), ITALIAN("ITALIAN", "it"), JAPANESE("JAPANESE", "ja"), JAVANESE("JAVANESE", "jw"), + KOREAN("KOREAN", "ko"), LATIN("LATIN", "la"), POLISH("POLISH", "pl"), PORTUGUESE("PORTUGUESE", "pt"), + RUSSIAN("RUSSIAN", "ru"), SPANISH("SPANISH", "es"), SWEDISH("SWEDISH", "sv"), THAI("THAI", "th"), + VIETNAMESE("VIETNAMESE", "vi"); + + private static final Map LANGUAGE_BY_VALUE = new HashMap<>(); + + static { + for (Language language : Language.values()) { + LANGUAGE_BY_VALUE.put(language.value, language); + } + } + + public static Language byValue(String value) { + return LANGUAGE_BY_VALUE.getOrDefault(value, AUTO_DETECT); + } + + public final String name; + public final String value; + + Language(String name, String value) { + this.name = name; + this.value = value; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/io/gitlab/jfronny/googlechat/mixin/ChatMessageC2SPacketMixin.java b/src/main/java/io/gitlab/jfronny/googlechat/mixin/ChatMessageC2SPacketMixin.java new file mode 100644 index 0000000..9fdd145 --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/googlechat/mixin/ChatMessageC2SPacketMixin.java @@ -0,0 +1,31 @@ +package io.gitlab.jfronny.googlechat.mixin; + +import io.gitlab.jfronny.googlechat.GoogleChatConfig; +import io.gitlab.jfronny.googlechat.api.GoogleService; +import io.gitlab.jfronny.googlechat.api.Language; +import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ChatMessageC2SPacket.class) +public class ChatMessageC2SPacketMixin { + @Mutable + @Final + @Shadow + private String chatMessage; + + @Inject(at = @At("RETURN"), method = "(Ljava/lang/String;)V") + public void init(String chatMessage, CallbackInfo info) { + if (!GoogleChatConfig.enabled) return; + Language server = Language.byValue(GoogleChatConfig.serverLanguage); + if (server == Language.AUTO_DETECT || chatMessage.startsWith("/")) return; + chatMessage = GoogleService.translate(chatMessage, Language.byValue(GoogleChatConfig.clientLanguage), server); + if (chatMessage.length() > 256) chatMessage = chatMessage.substring(0, 256); + this.chatMessage = chatMessage; + } +} diff --git a/src/main/java/io/gitlab/jfronny/googlechat/mixin/GameMessageS2CPacketMixin.java b/src/main/java/io/gitlab/jfronny/googlechat/mixin/GameMessageS2CPacketMixin.java new file mode 100644 index 0000000..64ed50c --- /dev/null +++ b/src/main/java/io/gitlab/jfronny/googlechat/mixin/GameMessageS2CPacketMixin.java @@ -0,0 +1,41 @@ +package io.gitlab.jfronny.googlechat.mixin; + +import io.gitlab.jfronny.googlechat.GoogleChatConfig; +import io.gitlab.jfronny.googlechat.api.GoogleService; +import io.gitlab.jfronny.googlechat.api.Language; +import net.minecraft.client.MinecraftClient; +import net.minecraft.network.MessageType; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.packet.s2c.play.GameMessageS2CPacket; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.UUID; + +@Mixin(GameMessageS2CPacket.class) +public class GameMessageS2CPacketMixin { + @Shadow @Final @Mutable private Text message; + @Shadow @Final private UUID sender; + + @Inject(at = @At("RETURN"), method = "(Lnet/minecraft/network/PacketByteBuf;)V") + private void init(PacketByteBuf buf, CallbackInfo ci) { + if (!GoogleChatConfig.enabled) return; + MinecraftClient mc = MinecraftClient.getInstance(); + if (mc == null || mc.player == null || sender.equals(mc.player.getUuid())) return; + Language client = Language.byValue(GoogleChatConfig.clientLanguage); + if (client == Language.AUTO_DETECT) return; + StringBuilder sb = new StringBuilder(); + message.asOrderedText().accept((index, style, codePoint) -> { + sb.append((char)codePoint); + return true; + }); + message = new LiteralText(GoogleService.translate(sb.toString(), Language.byValue(GoogleChatConfig.serverLanguage), client)); + } +} diff --git a/src/main/resources/GoogleChat.mixins.json b/src/main/resources/GoogleChat.mixins.json new file mode 100644 index 0000000..62f2cf0 --- /dev/null +++ b/src/main/resources/GoogleChat.mixins.json @@ -0,0 +1,15 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "io.gitlab.jfronny.googlechat.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [ + "ChatMessageC2SPacketMixin", + "GameMessageS2CPacketMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..65e08c2 --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "google-chat", + "version": "${version}", + "name": "GoogleChat", + "description": "", + "authors": [], + "contact": {}, + "license": "MIT", + "icon": "assets/GoogleChat/icon.png", + "environment": "client", + "entrypoints": { + "client": [ + "io.gitlab.jfronny.googlechat.GoogleChat" + ], + "libjf:config": [ + "io.gitlab.jfronny.googlechat.GoogleChatConfig" + ] + }, + "mixins": [ + "GoogleChat.mixins.json" + ], + "depends": { + "fabricloader": ">=0.12.12", + "minecraft": "1.18.1" + } +}