Skip to content

Commit

Permalink
feat(Dislate Lite): temp fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Rico040 committed May 27, 2024
1 parent ce0460d commit 307d060
Show file tree
Hide file tree
Showing 10 changed files with 374 additions and 0 deletions.
22 changes: 22 additions & 0 deletions plugins/dislate/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "Dislate Lite",
"description": "Translates text into a desired language.",
"authors": [
{
"name": "Acquite <3",
"id": "581573474296791211"
},
{
"name": "sapphire :>",
"id": "757982547861962752"
},
{
"name": "Rico040 ;)",
"id": "619474349845643275"
}
],
"main": "src/index.ts",
"vendetta": {
"icon": "ic_locale_24px"
}
}
29 changes: 29 additions & 0 deletions plugins/dislate/src/api/DeepL.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { DeepLResponse } from "../type"

// TODO: Change API link when it'll be down
const API_URL = "https://deeplx.vercel.app/translate"

const translate = async (text: string, source_lang: string = "auto", target_lang: string, original: boolean = false) => {
try {
if (original) return { source_lang, text }
const data: DeepLResponse = await (await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
text,
source_lang,
target_lang
})
})).json()
if (data.code !== 200) throw Error(`Failed to translate text from DeepL: ${data.message}`)
return { source_lang, text: data.data }
} catch (e) {
throw Error(`Failed to fetch from DeepL: ${e}`)
}
}

export default { translate }


5 changes: 5 additions & 0 deletions plugins/dislate/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import DeepL from "./DeepL"

export {
DeepL
}
22 changes: 22 additions & 0 deletions plugins/dislate/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { storage } from "@vendetta/plugin"
import patchActionSheet from "./patches/ActionSheet"
import patchCommands from "./patches/Commands"
import Settings from "./settings"

export const settings: {
source_lang?: string
target_lang?: string
} = storage

settings.target_lang ??= "EN"

let patches = []

export default {
onLoad: () => patches = [
patchActionSheet(),
patchCommands()
],
onUnload: () => { for (const unpatch of patches) unpatch() },
settings: Settings
}
32 changes: 32 additions & 0 deletions plugins/dislate/src/lang.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export default {
"arabic": "AR",
"bulgarian": "BG",
"czech": "CS",
"danish": "DA",
"german": "DE",
"greek": "EL",
"english": "EN", // EN-GB, EN-US
"spanish": "ES",
"estonian": "ET",
"finnish": "FI",
"french": "FR",
"hungarian": "HU",
"indonesian": "ID",
"italian": "IT",
"japanese": "JA",
"korean": "KO",
"lithuanian": "LT",
"latvian": "LV",
"norwegian": "NO",
"dutch": "NL",
"polish": "PL",
"portuguese": "PT", // PT-BR, PT-PT
"romanian": "RO",
"russian": "RU",
"slovak": "SK",
"slovenian": "SL",
"swedish": "SV",
"turkish": "TR",
"ukrainian": "UK",
"chinese-simplified": "ZH"
}
106 changes: 106 additions & 0 deletions plugins/dislate/src/patches/ActionSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { findByProps, findByStoreName } from "@vendetta/metro"
import { FluxDispatcher, React, ReactNative, i18n, stylesheet } from "@vendetta/metro/common"
import { before, after } from "@vendetta/patcher"
import { semanticColors } from "@vendetta/ui"
import { getAssetIDByName } from "@vendetta/ui/assets"
import { Forms } from "@vendetta/ui/components"
import { findInReactTree } from "@vendetta/utils"
import { settings } from ".."

import { DeepL } from "../api"
import { showToast } from "@vendetta/ui/toasts"
import { logger } from "@vendetta"

const LazyActionSheet = findByProps("openLazy", "hideActionSheet")
const ActionSheetRow = findByProps("ActionSheetRow")?.ActionSheetRow ?? Forms.FormRow // no icon if legacy
const MessageStore = findByStoreName("MessageStore")
const ChannelStore = findByStoreName("ChannelStore")

const styles = stylesheet.createThemedStyleSheet({
iconComponent: {
width: 24,
height: 24,
tintColor: semanticColors.INTERACTIVE_NORMAL
}
})

let cachedData: object[] = []

export default () => before("openLazy", LazyActionSheet, ([component, key, msg]) => {
const message = msg?.message
if (key !== "MessageLongPressActionSheet" || !message) return
component.then(instance => {
const unpatch = after("default", instance, (_, component) => {
React.useEffect(() => () => { unpatch() }, [])

const buttons = findInReactTree(component, x => x?.[0]?.type?.name === "ButtonRow")
if (!buttons) return
const position = Math.max(buttons.findIndex((x: any) => x.props.message === i18n.Messages.MARK_UNREAD), 0)

const originalMessage = MessageStore.getMessage(
message.channel_id,
message.id
)
if (!originalMessage?.content && !message.content) return

const messageId = originalMessage?.id ?? message.id
const messageContent = originalMessage?.content ?? message.content
const existingCachedObject = cachedData.find((o: any) => Object.keys(o)[0] === messageId, "cache object")

const translateType = existingCachedObject ? "Revert" : "Translate"
const icon = translateType === "Translate" ? getAssetIDByName("ic_locale_24px") : getAssetIDByName("ic_highlight")

const translate = async () => {
try {
const target_lang = settings.target_lang
const isTranslated = translateType === "Translate"

const translate = await DeepL.translate(originalMessage.content, undefined, target_lang, !isTranslated)

FluxDispatcher.dispatch({
type: "MESSAGE_UPDATE",
message: {
...originalMessage,
content: `${isTranslated ? translate.text : (existingCachedObject as object)[messageId]}`
+ ` ${isTranslated ? `\`[${target_lang?.toLowerCase()}]\``
: ""}`,
guild_id: ChannelStore.getChannel(
originalMessage.channel_id
).guild_id,
},
log_edit: false
})

isTranslated
? cachedData.unshift({ [messageId]: messageContent })
: cachedData = cachedData.filter((e: any) => e !== existingCachedObject, "cached data override")
} catch (e) {
showToast("Failed to translate message. Please check Debug Logs for more info.", getAssetIDByName("Small"))
logger.error(e)
} finally {
return LazyActionSheet.hideActionSheet()
}
}


buttons.splice(position, 0, (
<ActionSheetRow
label={`${translateType} Message`}
icon={
<ActionSheetRow.Icon
source={icon}
IconComponent={() => (
<ReactNative.Image
resizeMode="cover"
style={styles.iconComponent}
source={icon}
/>
)}
/>
}
onPress={translate}
/>
))
})
})
})
68 changes: 68 additions & 0 deletions plugins/dislate/src/patches/Commands.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { logger } from "@vendetta"
import { registerCommand } from "@vendetta/commands"
import { ApplicationCommandInputType, ApplicationCommandType, ApplicationCommandOptionType } from "../../../../ApplicationCommandTypes"
import { showToast } from "@vendetta/ui/toasts"
import { getAssetIDByName } from "@vendetta/ui/assets"
import { Codeblock } from "@vendetta/ui/components"
import { showConfirmationAlert } from "@vendetta/ui/alerts"
import { findByProps } from "@vendetta/metro"

import lang from "../lang"
import { DeepL } from "../api"

const ClydeUtils = findByProps("sendBotMessage")
const langOptions = Object.entries(lang).map(([key, value]) => ({
name: key,
displayName: key,
value: value
}))

export default () => registerCommand({
name: "translate",
displayName: "translate",
description: "Send a message using Dislate in any language chosen, using the DeepL Translate API.",
displayDescription: "Send a message using Dislate in any language chosen, using the DeepL Translate API.",
applicationId: "-1",
type: ApplicationCommandType.CHAT as number,
inputType: ApplicationCommandInputType.BUILT_IN_TEXT as number,
options: [
{
name: "text",
displayName: "text",
description: "The text/message for Dislate to translate. Please note some formatting of mentions and emojis may break due to the API.",
displayDescription: "The text/message for Dislate to translate. Please note some formatting of mentions and emojis may break due to the API.",
type: ApplicationCommandOptionType.STRING as number,
required: true
},
{
name: "language",
displayName: "language",
description: "The language that Dislate will translate the text into. This can be any language from the list.",
displayDescription: "The language that Dislate will translate the text into. This can be any language from the list.",
type: ApplicationCommandOptionType.STRING as number,
// @ts-ignore
choices: [...langOptions],
required: true
}
],
async execute(args, ctx) {
const [text, lang] = args
try {
const content = await DeepL.translate(text.value, null, lang.value)
return await new Promise((resolve): void => showConfirmationAlert({
title: "Are you sure you want to send it?",
content: (
<Codeblock>
{content.text}
</Codeblock>
),
confirmText: "Yep, send it!",
onConfirm: () => resolve({ content: content.text }),
cancelText: "Nope, don't send it"
}))
} catch (e) {
logger.error(e)
return ClydeUtils.sendBotMessage(ctx.channel.id, "Failed to translate message. Please check Debug Logs for more info.", getAssetIDByName("Small"))
}
}
})
35 changes: 35 additions & 0 deletions plugins/dislate/src/settings/TargetLang.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { getAssetIDByName } from "@vendetta/ui/assets"
import { React, ReactNative } from "@vendetta/metro/common"
import { Forms, Search } from "@vendetta/ui/components"
import { showToast } from "@vendetta/ui/toasts"
import { useProxy } from "@vendetta/storage"
import { settings } from ".."
import Lang from "../lang"

const { FormRow } = Forms
const { ScrollView } = ReactNative

export default () => {
useProxy(settings)
const [query, setQuery] = React.useState("")
return (<ScrollView style={{ flex: 1 }}>
<Search
style={{ padding: 15 }}
placeholder="Search Language"
onChangeText={(text: string) => {
setQuery(text)
}}
/>
{
Object.entries(Lang).filter(([key, value]) => key.includes(query)).map(([key, value]) => <FormRow
label={key}
trailing={() => <FormRow.Arrow />}
onPress={() => {
if (settings.target_lang == value) return
settings.target_lang = value
showToast(`Saved ToLang to ${key}`, getAssetIDByName("check"))
}}
/>)
}
</ScrollView>)
}
48 changes: 48 additions & 0 deletions plugins/dislate/src/settings/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { getAssetIDByName } from "@vendetta/ui/assets"
import { React, ReactNative, stylesheet, constants, NavigationNative, url } from "@vendetta/metro/common"
import { semanticColors } from "@vendetta/ui"
import { Forms } from "@vendetta/ui/components"
import { manifest } from "@vendetta/plugin"
import { useProxy } from "@vendetta/storage"

import { settings } from ".."
import TargetLang from "./TargetLang"

const { ScrollView, Text } = ReactNative
const { FormRow } = Forms

const styles = stylesheet.createThemedStyleSheet({
subheaderText: {
color: semanticColors.HEADER_SECONDARY,
textAlign: 'center',
margin: 10,
marginBottom: 50,
letterSpacing: 0.25,
fontFamily: constants.Fonts.PRIMARY_BOLD,
fontSize: 14
}
})

export default () => {
const navigation = NavigationNative.useNavigation()
useProxy(settings)

return (
<ScrollView>
<FormRow
label={"Translate to"}
subLabel={settings.target_lang?.toLowerCase()}
leading={<FormRow.Icon source={getAssetIDByName("ic_activity_24px")} />}
trailing={() => <FormRow.Arrow />}
onPress={() => navigation.push("VendettaCustomPage", {
title: "Translate to",
render: TargetLang,
})}
/>

<Text style={styles.subheaderText} onPress={() => url.openURL("https://github.com/aeongdesu/vdplugins")}>
{`Build: (${manifest.hash.substring(0, 7)})`}
</Text>
</ScrollView>
)
}
7 changes: 7 additions & 0 deletions plugins/dislate/src/type.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface DeepLResponse {
alternatives?: string[]
code?: number
message?: string
data?: string
id?: number
}

0 comments on commit 307d060

Please sign in to comment.