2025-02-11 20:49:53 +01:00

144 lines
3.8 KiB
TypeScript

import {
AutocompleteInteraction,
hyperlink,
inlineCode,
SlashCommandBuilder,
type InteractionEditReplyOptions,
} from "discord.js";
import { defineCommand } from "..";
import {
getSourceLanguages,
getTargetLanguages,
isSourceLanguageSupported,
isTargetLanguageSupported,
languageCodeToName,
translate,
} from "../../utils/deepl";
import { abort } from "../../utils/error";
import type { OCRResult } from "../../types/ocr";
import { capitalize } from "../../utils/functions";
export async function translateAutocompleteImpl(
interaction: AutocompleteInteraction
) {
const option = interaction.options.getFocused(true);
const languages =
option.name === "source"
? await getSourceLanguages()
: await getTargetLanguages();
const choices = languages
.filter((language) =>
language.name.toLowerCase().includes(option.value.toLowerCase())
)
.map((language) => ({
name: language.name,
value: language.code,
}))
.slice(0, 25);
await interaction.respond(choices);
}
export async function translateImpl(
text: string,
source: string | null,
target: string,
ocrModel?: OCRResult["model"],
imageUrl?: string
): Promise<InteractionEditReplyOptions> {
const {
text: translatedText,
detectedSourceLang,
billedCharacters,
} = await translate({
text,
source,
target,
});
const displaySource = await languageCodeToName(detectedSourceLang);
const displayTarget = await languageCodeToName(target);
if (translatedText.length > 4096) {
return {
content: ocrModel
? `OCR: ${inlineCode(capitalize(ocrModel))}`
: "" + (imageUrl ? `\n${hyperlink("Image", imageUrl)}` : ""),
files: [
{
name: `${displaySource}-${displayTarget}.txt`,
attachment: Buffer.from(
`--- From ${displaySource} to ${displayTarget} ---\n${translatedText}`
),
},
],
};
}
return {
embeds: [
{
title: `From ${displaySource} to ${displayTarget}`,
description: translatedText,
color: 0x0f2b46,
...(imageUrl ? { image: { url: imageUrl } } : {}),
author: {
name: "DeepL",
icon_url: "https://www.google.com/s2/favicons?domain=deepl.com&sz=64",
},
footer: {
text: ocrModel
? `OCR: ${capitalize(ocrModel)}`
: `Billed characters: ${billedCharacters}`,
icon_url: ocrModel
? `https://www.google.com/s2/favicons?domain=${ocrModel}.com&sz=64`
: undefined,
},
},
],
};
}
export default defineCommand({
data: new SlashCommandBuilder()
.setName("translate")
.setDescription("Translates text using DeepL")
.addStringOption((option) =>
option
.setName("text")
.setDescription("The text to translate")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("source")
.setDescription("Source language of the text")
.setAutocomplete(true)
)
.addStringOption((option) =>
option
.setName("target")
.setDescription("Target language of the text")
.setAutocomplete(true)
),
autocomplete: translateAutocompleteImpl,
async execute(interaction) {
const text = interaction.options.getString("text", true);
const source = interaction.options.getString("source") ?? null;
const target = interaction.options.getString("target") ?? "en-US";
await interaction.deferReply();
if (source && !(await isSourceLanguageSupported(source))) {
abort("Source language not supported");
}
if (target && !(await isTargetLanguageSupported(target))) {
abort("Target language not supported");
}
const payload = await translateImpl(text, source, target);
await interaction.editReply(payload);
},
});