2025-02-09 19:09:01 +01:00

88 lines
2.1 KiB
TypeScript

import {
Attachment,
codeBlock,
inlineCode,
SlashCommandBuilder,
type InteractionEditReplyOptions,
} from "discord.js";
import { defineCommand } from "..";
import { downloadFile } from "../../utils/http";
import { abort } from "../../utils/error";
import { yandexOcr } from "../../utils/ocr";
import sharp from "sharp";
export function buildOcrPayload(
text: string,
detected_lang: string,
attachment?: Attachment
): InteractionEditReplyOptions {
const languageName =
new Intl.DisplayNames(["en"], { type: "language" }).of(detected_lang) ??
"unknown";
const content = `Detected language: ${inlineCode(languageName)}\n${codeBlock(
text
)}`;
if (content.length > 2000) {
return {
content: `Detected language: ${inlineCode(languageName)}`,
files: [
{
name: "ocr.txt",
attachment: text,
},
...(attachment ? [attachment] : []),
],
};
}
return {
content,
files: attachment ? [attachment] : [],
};
}
export async function ocrImpl(attachment: Attachment) {
const { data, type } = await downloadFile(attachment.url);
if (!type?.mime.startsWith("image/")) {
abort("The file must be an image!");
}
const compressed = await sharp(data)
.resize(1000)
.jpeg({ quality: 90 })
.toBuffer();
return yandexOcr(compressed, type.mime);
}
export default defineCommand({
data: new SlashCommandBuilder()
.setName("ocr")
.setDescription("OCR an image using Yandex")
.addAttachmentOption((option) =>
option
.setName("image")
.setDescription("The image to OCR")
.setRequired(true)
),
async execute(interaction) {
const attachment = interaction.options.getAttachment("image", true);
if (!attachment.contentType?.startsWith("image/")) {
abort("The file must be an image!");
}
await interaction.deferReply();
const result = await ocrImpl(attachment);
const payload = buildOcrPayload(
result.text,
result.detected_lang,
attachment
);
await interaction.editReply(payload);
},
});