From db0f6d6841d3786d30801bfa315de2bfc57f588e Mon Sep 17 00:00:00 2001 From: bignutty <3515180-bignutty@users.noreply.gitlab.com> Date: Thu, 30 Jan 2025 23:52:39 +0100 Subject: [PATCH] add omnitranslate to lc.tr --- commands/message/utils/translate.js | 150 +++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 15 deletions(-) diff --git a/commands/message/utils/translate.js b/commands/message/utils/translate.js index ea3ed15..a192476 100644 --- a/commands/message/utils/translate.js +++ b/commands/message/utils/translate.js @@ -1,4 +1,4 @@ -const { googleTranslate } = require('#api'); +const { googleTranslate, googleTranslateMulti} = require('#api'); const { TRANSLATE_LANGUAGES, TRANSLATE_DISPLAY_MAPPINGS, PERMISSION_GROUPS } = require('#constants'); const { createEmbed } = require('#utils/embed'); @@ -8,6 +8,107 @@ const { editOrReply } = require('#utils/message') const { STATICS } = require('#utils/statics'); const { isSupported, getCodeFromAny } = require('#utils/translate'); +// TODO(unity): interaction/context/translate.js +async function translateMessage(context, message, to, from){ + let mappings = {}; + + if(message.content){ + // Special Case - this is (very very very likely) a 1p translated message + if(message.content.startsWith(`-# ${icon("subtext_translate")}`)) { + let cnt = message.content.split("\n"); + cnt.shift(); + if(cnt.length >= 1){ mappings.content = cnt.join("\n") } + } else mappings.content = message.content + } + if(message.embeds) { + let i = 0; + // Message Translation supports Descriptions and Fields + for(const e of message.embeds){ + let emb = e[1] + if(emb.description) mappings["embeds/" + i + "/description"] = emb.description; + if(emb.title) mappings["embeds/" + i + "/title"] = emb.title; + if(emb.author?.name) mappings["embeds/" + i + "/author/name"] = emb.author.name; + if(emb.footer?.text) mappings["embeds/" + i + "/footer/text"] = emb.footer.text; + + if(emb.fields){ + let fi = 0; + for(const f of emb.fields){ + mappings["embeds/" + i + "/fields/" + fi + "/name"] = f[1].name; + mappings["embeds/" + i + "/fields/" + fi + "/value"] = f[1].value; + fi++; + } + } + } + } + + // Cancel if we don't have anything that can be translated + if(Object.keys(mappings).length === 0) return {}; + + // Translate message via multitranslate endpoint on 1p + try{ + let translation = await googleTranslateMulti(context, mappings, to, from) + + let tr = translation.response.body.translations + + // Deserialize message + let result = {}; + + // This relies on mappings.content to handle the special case + if(mappings.content) result.content = tr["content"]; + + if(message.embeds) { + let i = 0; + result.embeds = []; + // Message Translation supports Descriptions and Fields + for(const e of message.embeds){ + let emb = e[1] + let newEmbed = { + fields: [] + }; + + // Elements we don't translate + if(emb.color) newEmbed.color = emb.color; + if(emb.thumbnail) newEmbed.thumbnail = emb.thumbnail; + if(emb.image) newEmbed.image = emb.image; + if(emb.url) newEmbed.url = emb.url; + + if(emb.title) newEmbed.title = stringwrap(tr["embeds/" + i + "/title"], 256); + + if(emb.description) newEmbed.description = stringwrap(tr["embeds/" + i + "/description"], 4096); + + if(emb.author) newEmbed.author = Object.assign({}, emb.author); + if(emb.author?.name) newEmbed.author.name = stringwrap(tr["embeds/" + i + "/author/name"], 256); + + if(emb.footer) newEmbed.footer = Object.assign({}, emb.footer); + if(emb.footer?.text) newEmbed.footer.text = stringwrap(tr["embeds/" + i + "/footer/text"], 2048); + + if(emb.fields){ + let fi = 0; + for(const f of emb.fields){ + newEmbed.fields[fi] = { + inline: f[1].inline + } + + newEmbed.fields[fi].name = stringwrap(tr["embeds/" + i + "/fields/" + fi + "/name"], 256); + newEmbed.fields[fi].value = stringwrap(tr["embeds/" + i + "/fields/" + fi + "/value"], 1024) + fi++; + } + } + result.embeds[i] = Object.assign({}, newEmbed); + } + } + + return { + message: result, + metadata: translation.response.body + }; + }catch(e){ + console.log(e) + console.log(mappings) + throw "Translation Failed." + } +} + module.exports = { name: 'translate', label: 'text', @@ -30,24 +131,43 @@ module.exports = { let content = args.text; - // TODO: Turn this into a reply helper - if(context.message.messageReference) { - let msg = await context.message.channel.fetchMessage(context.message.messageReference.messageId); - if(msg.content && msg.content.length) content = msg.content - if(msg.embeds?.length) for(const e of msg.embeds) if(e[1].description?.length) { content += '\n' + e[1].description; break; } - - // Controls ctx-based translations - if(args.text.length >= 1 && getCodeFromAny(args.text)) args.to = args.text; - else if(args.text.length >= 1) content = args.text; - } - - if(!content.length) return editOrReply(context, createEmbed("warning", context, "No text supplied.")) - if(!isSupported(args.to)) return editOrReply(context, createEmbed("warning", context, `Invalid target language (${stringwrap(args.to, 10, false)}).`)) if(!isSupported(args.from)) return editOrReply(context, createEmbed("warning", context, `Invalid source language (${stringwrap(args.from, 10, false)}).`)) let targetLanguage = getCodeFromAny(args.to) let sourceLanguage = getCodeFromAny(args.from) + + // Invoke Message Translation + if(context.message.messageReference) { + try{ + // Quick language shortcut - lc.tr de + if(args.text.length >= 1 && getCodeFromAny(args.text)) args.to = args.text; + let targetLanguage = getCodeFromAny(args.to) + + let message = await context.message.channel.fetchMessage(context.message.messageReference.messageId); + + let translate = await translateMessage(context, message, targetLanguage, sourceLanguage); + + if(!translate.message) return editOrReply(context, createEmbed("warning", context, "No translatable content found.")) + + let fromFlag = TRANSLATE_DISPLAY_MAPPINGS[translate.metadata.language.from || sourceLanguage] || '' + let toFlag = TRANSLATE_DISPLAY_MAPPINGS[translate.metadata.language.to] || '' + + let newMessage = translate.message; + let newMessageContent = ""; + if(newMessage.content) newMessageContent += "\n" + newMessage.content + + return editOrReply(context, createEmbed("default", context, { + content: stringwrap(`-# ${icon("subtext_translate")} Translated from ${fromFlag} **${TRANSLATE_LANGUAGES[translate.metadata.language.from || sourceLanguage] || translate.metadata.language.from || args.from}** to ${toFlag} **${TRANSLATE_LANGUAGES[translate.metadata.language.to] || translate.metadata.language.to}** • Google Translate${newMessageContent}`, 2000), + embeds: newMessage.embeds + })); + }catch(e){ + console.log(e) + return editOrReply(context, createEmbed("error", context, "Unable to translate message.")) + } + } + + if(!content.length) return editOrReply(context, createEmbed("warning", context, "No text supplied.")) if(!targetLanguage) return editOrReply(context, createEmbed("warning", context, `Invalid target language (${stringwrap(args.to, 10, false)}).`)) if(!sourceLanguage) return editOrReply(context, createEmbed("warning", context, `Invalid source language (${stringwrap(args.from, 10, false)}).`)) @@ -66,7 +186,7 @@ module.exports = { } })) }catch(e){ - if(e.response?.body?.status && e.response.body.status == 2) return editOrReply(context, createEmbed("error", context, `Unable to translate text.`)) + if(e.response?.body?.status && e.response.body.status === 2) return editOrReply(context, createEmbed("error", context, `Unable to translate text.`)) console.log(e) return editOrReply(context, createEmbed("error", context, `Something went wrong.`)) }