/[sudobot]/branches/8.x/src/commands/tools/TranslateCommand.ts
ViewVC logotype

Contents of /branches/8.x/src/commands/tools/TranslateCommand.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (show annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 7758 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 /**
2 * This file is part of SudoBot.
3 *
4 * Copyright (C) 2021-2023 OSN Developers.
5 *
6 * SudoBot is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * SudoBot is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with SudoBot. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 import {
21 ApplicationCommandOptionChoiceData,
22 ApplicationCommandType,
23 CacheType,
24 ContextMenuCommandBuilder,
25 EmbedBuilder,
26 Interaction,
27 MessageContextMenuCommandInteraction,
28 SlashCommandBuilder,
29 User
30 } from "discord.js";
31 import { readFileSync } from "fs";
32 import JSON5 from "json5";
33 import path from "path";
34 import Command, { AnyCommandContext, ArgumentType, CommandMessage, CommandReturn, ValidationRule } from "../../core/Command";
35 import { GatewayEventListener } from "../../decorators/GatewayEventListener";
36 import { ChatInputCommandContext } from "../../services/CommandManager";
37 import { HasEventListeners } from "../../types/HasEventListeners";
38
39 export default class TranslateCommand extends Command implements HasEventListeners {
40 public readonly name = "translate";
41 public readonly validationRules: ValidationRule[] = [
42 {
43 types: [ArgumentType.StringRest],
44 errors: {
45 required: "Please specify the text to translate!",
46 "type:invalid": "Invalid input given"
47 },
48 name: "text"
49 }
50 ];
51 public readonly permissions = [];
52 public readonly aliases = ["Translate to English"];
53 public readonly languages: Record<string, string> = JSON5.parse(
54 readFileSync(path.resolve(__dirname, "../../../resources/languages.json"), { encoding: "utf-8" })
55 );
56
57 public readonly slashCommandBuilder = new SlashCommandBuilder()
58 .addStringOption(option => option.setName("text").setDescription("The text to translate").setRequired(true))
59 .addStringOption(option => option.setName("from").setDescription("The language of the input text").setAutocomplete(true))
60 .addStringOption(option => option.setName("to").setDescription("The language of the output text").setAutocomplete(true));
61
62 public readonly otherApplicationCommandBuilders = [
63 new ContextMenuCommandBuilder()
64 .setName("Translate to English")
65 .setType(ApplicationCommandType.Message)
66 .setDMPermission(false)
67 ];
68
69 public readonly description = "Translates the given text.";
70
71 protected readonly displayNames = new Intl.DisplayNames(["en"], {
72 type: "language"
73 });
74
75 protected readonly supportedLocales = Intl.DisplayNames.supportedLocalesOf();
76
77 @GatewayEventListener("interactionCreate")
78 onInteractionCreate(interaction: Interaction<CacheType>) {
79 if (!interaction.isAutocomplete() || interaction.commandName !== this.name) {
80 return;
81 }
82
83 const focused = interaction.options.getFocused();
84 const matches: ApplicationCommandOptionChoiceData[] = [];
85
86 for (const code in this.languages) {
87 if (matches.length >= 25) {
88 break;
89 }
90
91 if (code === focused || this.languages[code].includes(focused)) {
92 matches.push({
93 name: this.languages[code],
94 value: code
95 });
96 }
97 }
98
99 if (matches.length < 25) {
100 for (const locale of this.supportedLocales) {
101 if (matches.length >= 25) {
102 break;
103 }
104
105 if (this.languages[locale]) {
106 continue;
107 }
108
109 const displayName = this.displayNames.of(locale);
110
111 if (!displayName) {
112 continue;
113 }
114
115 if (locale === focused || displayName.includes(focused)) {
116 matches.push({
117 name: displayName,
118 value: locale
119 });
120 }
121 }
122 }
123
124 console.log(matches);
125 interaction.respond(matches).catch(console.error);
126 }
127
128 async execute(message: CommandMessage, context: AnyCommandContext): Promise<CommandReturn> {
129 await this.deferIfInteraction(message);
130
131 const text = context.isLegacy
132 ? context.parsedNamedArgs.text
133 : message instanceof MessageContextMenuCommandInteraction && context.isContextMenu
134 ? message.targetMessage.content
135 : (context as ChatInputCommandContext).options.getString("text", true);
136
137 if (!text) {
138 await this.error(message, "Invalid text content provided.");
139 return;
140 }
141
142 const from = context.isLegacy ? "auto" : (context.isContextMenu ? "auto" : context.options.getString("from")) ?? "auto";
143 const to = context.isLegacy ? "en" : (context.isContextMenu ? "en" : context.options.getString("to")) ?? "en";
144
145 const toString = this.displayNames.of(to);
146
147 if (from !== "auto" && !this.languages[from] && !this.displayNames.of(from)) {
148 await this.error(message, "Invalid language specified in the `from` option");
149 return;
150 }
151
152 if (to !== "auto" && !this.languages[to] && !this.displayNames.of(to)) {
153 await this.error(message, "Invalid language specified in the `to` option");
154 return;
155 }
156
157 const { error, translation, response } = await this.client.translator.translate(text, from, to);
158
159 if (error) {
160 await this.deferredReply(message, {
161 embeds: [
162 new EmbedBuilder({
163 color: 0xf14a60,
164 author: {
165 name: "Translation Failed"
166 },
167 description: `${this.emoji("error")} Couldn't translate that due to an internal error.`,
168 footer: {
169 text: "Powered by Google Translate"
170 }
171 }).setTimestamp()
172 ]
173 });
174
175 return;
176 }
177
178 const fromString = this.displayNames.of(response!.data.src);
179
180 await this.deferredReply(message, {
181 embeds: [
182 new EmbedBuilder({
183 color: 0x007bff,
184 author: {
185 name:
186 message instanceof MessageContextMenuCommandInteraction
187 ? (message.targetMessage.author as User).username
188 : "Translation",
189 iconURL:
190 message instanceof MessageContextMenuCommandInteraction
191 ? (message.targetMessage.author as User).displayAvatarURL()
192 : undefined
193 },
194 description: translation,
195 footer: {
196 text: `Translated from ${fromString ?? this.languages[response!.data.src] ?? response!.data.src} to ${
197 toString ?? this.languages[to] ?? to
198 } • Powered by Google Translate`
199 }
200 })
201 ]
202 });
203 }
204 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26