diff --git a/package.json b/package.json
index a57bf8a8..582ce411 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "equicord",
"private": "true",
- "version": "1.9.6",
+ "version": "1.9.7",
"description": "The other cutest Discord client mod",
"homepage": "https://github.com/Equicord/Equicord#readme",
"bugs": {
diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx
index d0e8cf34..072f4ee6 100644
--- a/src/plugins/betterFolders/index.tsx
+++ b/src/plugins/betterFolders/index.tsx
@@ -249,6 +249,10 @@ export default definePlugin({
dispatchingFoldersClose = false;
});
}
+ },
+
+ LOGOUT() {
+ closeFolders();
}
},
diff --git a/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx b/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx
index 5fbe165c..6501e0fe 100644
--- a/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx
+++ b/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx
@@ -8,7 +8,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { openInviteModal } from "@utils/discord";
import { Margins } from "@utils/margins";
-import { classes } from "@utils/misc";
+import { classes, copyWithToast } from "@utils/misc";
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { findComponentByCodeLazy } from "@webpack";
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
@@ -45,7 +45,11 @@ interface Section {
authorIds?: string[];
}
-function SectionHeader({ section }: { section: Section; }) {
+interface SectionHeaderProps {
+ section: Section;
+}
+
+function SectionHeader({ section }: SectionHeaderProps) {
const hasSubtitle = typeof section.subtitle !== "undefined";
const hasAuthorIds = typeof section.authorIds !== "undefined";
@@ -62,6 +66,7 @@ function SectionHeader({ section }: { section: Section; }) {
})();
}, [section.authorIds]);
+
return
{section.title}
@@ -74,8 +79,7 @@ function SectionHeader({ section }: { section: Section; }) {
size={16}
showUserPopout
className={Margins.bottom8}
- />
- }
+ />}
{hasSubtitle &&
@@ -204,7 +208,16 @@ function ChangeDecorationModal(props: ModalProps) {
{activeSelectedDecoration?.alt}
}
- {activeDecorationHasAuthor && Created by {Parser.parse(`<@${activeSelectedDecoration.authorId}>`)}}
+ {activeDecorationHasAuthor && (
+
+ Created by {Parser.parse(`<@${activeSelectedDecoration.authorId}>`)}
+
+ )}
+ {isActiveDecorationPreset && (
+
+ )}
diff --git a/src/plugins/mentionAvatars/index.tsx b/src/plugins/mentionAvatars/index.tsx
index 7454e367..d45425c6 100644
--- a/src/plugins/mentionAvatars/index.tsx
+++ b/src/plugins/mentionAvatars/index.tsx
@@ -13,10 +13,10 @@ import definePlugin, { OptionType } from "@utils/types";
import { SelectedGuildStore, useState } from "@webpack/common";
import { User } from "discord-types/general";
-export const settings = definePluginSettings({
- hideAtSymbol: {
+const settings = definePluginSettings({
+ showAtSymbol: {
type: OptionType.BOOLEAN,
- description: "Whether the the @-symbol should be hidden.",
+ description: "Whether the the @ symbol should be displayed",
default: true
}
});
@@ -32,7 +32,9 @@ export default definePlugin({
replace: "children:$self.renderUsername({username:$1,user:$2})"
}
}],
+
settings,
+
renderUsername: ErrorBoundary.wrap((props: { user: User, username: string; }) => {
const { user, username } = props;
const [isHovering, setIsHovering] = useState(false);
@@ -49,9 +51,11 @@ export default definePlugin({
);
}, { noop: true })
+
});
function getUsernameString(username: string) {
- if (settings.store.hideAtSymbol) return username;
- return `@${username}`;
+ return settings.store.showAtSymbol
+ ? `@${username}`
+ : username;
}
diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx
index 9181306a..a08aeccc 100644
--- a/src/plugins/messageLogger/index.tsx
+++ b/src/plugins/messageLogger/index.tsx
@@ -151,6 +151,7 @@ export default definePlugin({
contextMenus: {
"message": patchMessageContextMenu,
"channel-context": patchChannelContextMenu,
+ "thread-context": patchChannelContextMenu,
"user-context": patchChannelContextMenu,
"gdm-context": patchChannelContextMenu
},
diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx
index 0fbf41e9..27850143 100644
--- a/src/plugins/mutualGroupDMs/index.tsx
+++ b/src/plugins/mutualGroupDMs/index.tsx
@@ -49,7 +49,7 @@ export default definePlugin({
find: ".Messages.MUTUAL_GUILDS_WITH_END_COUNT", // Note: the module is lazy-loaded
replacement: {
match: /(?<=\.tabBarItem.{0,50}MUTUAL_GUILDS.+?}\),)(?=.+?(\(0,\i\.jsxs?\)\(.{0,100}id:))/,
- replace: '$self.isBotOrSelf(arguments[0].user)?null:$1"MUTUAL_GDMS",children:"Mutual Groups"}),'
+ replace: '$self.isBotOrSelf(arguments[0].user)?null:$1"MUTUAL_GDMS",children:$self.getMutualGDMCountText(arguments[0].user)}),'
}
},
{
@@ -64,7 +64,7 @@ export default definePlugin({
replacement: [
{
match: /(?<=onItemSelect:\i,children:)(\i)\.map/,
- replace: "[...$1, ...($self.isBotOrSelf(arguments[0].user) ? [] : [{section:'MUTUAL_GDMS',text:'Mutual Groups'}])].map"
+ replace: "[...$1, ...($self.isBotOrSelf(arguments[0].user) ? [] : [{section:'MUTUAL_GDMS',text:$self.getMutualGDMCountText(arguments[0].user)}])].map"
},
{
match: /\(0,\i\.jsx\)\(\i,\{items:\i,section:(\i)/,
@@ -76,6 +76,11 @@ export default definePlugin({
isBotOrSelf: (user: User) => user.bot || user.id === UserStore.getCurrentUser().id,
+ getMutualGDMCountText: (user: User) => {
+ const count = ChannelStore.getSortedPrivateChannels().filter(c => c.isGroupDM() && c.recipients.includes(user.id)).length;
+ return `${count === 0 ? "No" : count} Mutual Group${count !== 1 ? "s" : ""}`;
+ },
+
renderMutualGDMs: ErrorBoundary.wrap(({ user, onClose }: { user: User, onClose: () => void; }) => {
const entries = ChannelStore.getSortedPrivateChannels().filter(c => c.isGroupDM() && c.recipients.includes(user.id)).map(c => (
settings.store.showModView,
replacement: {
match: /(role:)\i(?=,guildId.{0,100}role:(\i\[))/,
diff --git a/src/plugins/translate/TranslateIcon.tsx b/src/plugins/translate/TranslateIcon.tsx
index b22c488e..fa1d9abf 100644
--- a/src/plugins/translate/TranslateIcon.tsx
+++ b/src/plugins/translate/TranslateIcon.tsx
@@ -17,10 +17,9 @@
*/
import { ChatBarButton } from "@api/ChatButtons";
-import { Margins } from "@utils/margins";
import { classes } from "@utils/misc";
import { openModal } from "@utils/modal";
-import { Alerts, Forms } from "@webpack/common";
+import { Alerts, Forms, Tooltip, useEffect, useState } from "@webpack/common";
import { settings } from "./settings";
import { TranslateModal } from "./TranslateModal";
@@ -39,9 +38,17 @@ export function TranslateIcon({ height = 24, width = 24, className }: { height?:
);
}
+export let setShouldShowTranslateEnabledTooltip: undefined | ((show: boolean) => void);
+
export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
const { autoTranslate, showChatBarButton } = settings.use(["autoTranslate", "showChatBarButton"]);
+ const [shouldShowTranslateEnabledTooltip, setter] = useState(false);
+ useEffect(() => {
+ setShouldShowTranslateEnabledTooltip = setter;
+ return () => setShouldShowTranslateEnabledTooltip = undefined;
+ }, []);
+
if (!isMainChat || !showChatBarButton) return null;
const toggle = () => {
@@ -52,21 +59,20 @@ export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
title: "Vencord Auto-Translate Enabled",
body: <>
- You just enabled auto translate (by right clicking the Translate icon). Any message you send will automatically be translated before being sent.
-
-
- If this was an accident, disable it again, or it will change your message content before sending.
+ You just enabled Auto Translate! Any message will automatically be translated before being sent.
>,
- cancelText: "Disable Auto-Translate",
- confirmText: "Got it",
+ confirmText: "Disable Auto-Translate",
+ cancelText: "Got it",
secondaryConfirmText: "Don't show again",
onConfirmSecondary: () => settings.store.showAutoTranslateAlert = false,
- onCancel: () => settings.store.autoTranslate = false
+ onConfirm: () => settings.store.autoTranslate = false,
+ // troll
+ confirmColor: "vc-notification-log-danger-btn",
});
};
- return (
+ const button = (
{
@@ -76,7 +82,7 @@ export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
));
}}
- onContextMenu={() => toggle()}
+ onContextMenu={toggle}
buttonProps={{
"aria-haspopup": "dialog"
}}
@@ -84,4 +90,13 @@ export const TranslateChatBarIcon: ChatBarButton = ({ isMainChat }) => {
);
+
+ if (shouldShowTranslateEnabledTooltip && settings.store.showAutoTranslateTooltip)
+ return (
+
+ {() => button}
+
+ );
+
+ return button;
};
diff --git a/src/plugins/translate/TranslateModal.tsx b/src/plugins/translate/TranslateModal.tsx
index 7628a31e..7a32d1b7 100644
--- a/src/plugins/translate/TranslateModal.tsx
+++ b/src/plugins/translate/TranslateModal.tsx
@@ -20,9 +20,8 @@ import { Margins } from "@utils/margins";
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
import { Forms, SearchableSelect, Switch, useMemo } from "@webpack/common";
-import { Languages } from "./languages";
import { settings } from "./settings";
-import { cl } from "./utils";
+import { cl, getLanguages } from "./utils";
const LanguageSettingKeys = ["receivedInput", "receivedOutput", "sentInput", "sentOutput"] as const;
@@ -31,7 +30,7 @@ function LanguageSelect({ settingsKey, includeAuto }: { settingsKey: typeof Lang
const options = useMemo(
() => {
- const options = Object.entries(Languages).map(([value, label]) => ({ value, label }));
+ const options = Object.entries(getLanguages()).map(([value, label]) => ({ value, label }));
if (!includeAuto)
options.shift();
diff --git a/src/plugins/translate/TranslationAccessory.tsx b/src/plugins/translate/TranslationAccessory.tsx
index 72b35940..8e8f4c17 100644
--- a/src/plugins/translate/TranslationAccessory.tsx
+++ b/src/plugins/translate/TranslationAccessory.tsx
@@ -19,7 +19,6 @@
import { Parser, useEffect, useState } from "@webpack/common";
import { Message } from "discord-types/general";
-import { Languages } from "./languages";
import { TranslateIcon } from "./TranslateIcon";
import { cl, TranslationValue } from "./utils";
@@ -59,7 +58,7 @@ export function TranslationAccessory({ message }: { message: Message; }) {
{Parser.parse(translation.text)}
{" "}
- (translated from {Languages[translation.src] ?? translation.src} - setTranslation(undefined)} />)
+ (translated from {translation.sourceLanguage} - setTranslation(undefined)} />)
);
}
diff --git a/src/plugins/translate/index.tsx b/src/plugins/translate/index.tsx
index f602d125..de61cef9 100644
--- a/src/plugins/translate/index.tsx
+++ b/src/plugins/translate/index.tsx
@@ -28,7 +28,7 @@ import definePlugin from "@utils/types";
import { ChannelStore, Menu } from "@webpack/common";
import { settings } from "./settings";
-import { TranslateChatBarIcon, TranslateIcon } from "./TranslateIcon";
+import { setShouldShowTranslateEnabledTooltip, TranslateChatBarIcon, TranslateIcon } from "./TranslateIcon";
import { handleTranslate, TranslationAccessory } from "./TranslationAccessory";
import { translate } from "./utils";
@@ -53,8 +53,8 @@ const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }) =>
export default definePlugin({
name: "Translate",
- description: "Translate messages with Google Translate",
- authors: [Devs.Ven],
+ description: "Translate messages with Google Translate or DeepL",
+ authors: [Devs.Ven, Devs.AshtonMemer],
dependencies: ["MessageAccessoriesAPI", "MessagePopoverAPI", "MessageEventsAPI", "ChatInputButtonAPI"],
settings,
contextMenus: {
@@ -83,11 +83,18 @@ export default definePlugin({
};
});
+ let tooltipTimeout: any;
this.preSend = addPreSendListener(async (_, message) => {
if (!settings.store.autoTranslate) return;
if (!message.content) return;
- message.content = (await translate("sent", message.content)).text;
+ setShouldShowTranslateEnabledTooltip?.(true);
+ clearTimeout(tooltipTimeout);
+ tooltipTimeout = setTimeout(() => setShouldShowTranslateEnabledTooltip?.(false), 2000);
+
+ const trans = await translate("sent", message.content);
+ message.content = trans.text;
+
});
},
diff --git a/src/plugins/translate/languages.ts b/src/plugins/translate/languages.ts
index 4bf370b5..3e2e7c71 100644
--- a/src/plugins/translate/languages.ts
+++ b/src/plugins/translate/languages.ts
@@ -31,9 +31,10 @@ copy(Object.fromEntries(
))
*/
-export type Language = keyof typeof Languages;
+export type GoogleLanguage = keyof typeof GoogleLanguages;
+export type DeeplLanguage = keyof typeof DeeplLanguages;
-export const Languages = {
+export const GoogleLanguages = {
"auto": "Detect language",
"af": "Afrikaans",
"sq": "Albanian",
@@ -169,3 +170,57 @@ export const Languages = {
"yo": "Yoruba",
"zu": "Zulu"
} as const;
+
+export const DeeplLanguages = {
+ "": "Detect language",
+ "ar": "Arabic",
+ "bg": "Bulgarian",
+ "zh-hans": "Chinese (Simplified)",
+ "zh-hant": "Chinese (Traditional)",
+ "cs": "Czech",
+ "da": "Danish",
+ "nl": "Dutch",
+ "en-us": "English (American)",
+ "en-gb": "English (British)",
+ "et": "Estonian",
+ "fi": "Finnish",
+ "fr": "French",
+ "de": "German",
+ "el": "Greek",
+ "hu": "Hungarian",
+ "id": "Indonesian",
+ "it": "Italian",
+ "ja": "Japanese",
+ "ko": "Korean",
+ "lv": "Latvian",
+ "lt": "Lithuanian",
+ "nb": "Norwegian",
+ "pl": "Polish",
+ "pt-br": "Portuguese (Brazilian)",
+ "pt-pt": "Portuguese (European)",
+ "ro": "Romanian",
+ "ru": "Russian",
+ "sk": "Slovak",
+ "sl": "Slovenian",
+ "es": "Spanish",
+ "sv": "Swedish",
+ "tr": "Turkish",
+ "uk": "Ukrainian"
+} as const;
+
+export function deeplLanguageToGoogleLanguage(language: string) {
+ switch (language) {
+ case "": return "auto";
+ case "nb": return "no";
+ case "zh-hans": return "zh-CN";
+ case "zh-hant": return "zh-TW";
+ case "en-us":
+ case "en-gb":
+ return "en";
+ case "pt-br":
+ case "pt-pt":
+ return "pt";
+ default:
+ return language;
+ }
+}
diff --git a/src/plugins/translate/native.ts b/src/plugins/translate/native.ts
new file mode 100644
index 00000000..3415e95e
--- /dev/null
+++ b/src/plugins/translate/native.ts
@@ -0,0 +1,29 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { IpcMainInvokeEvent } from "electron";
+
+export async function makeDeeplTranslateRequest(_: IpcMainInvokeEvent, pro: boolean, apiKey: string, payload: string) {
+ const url = pro
+ ? "https://api.deepl.com/v2/translate"
+ : "https://api-free.deepl.com/v2/translate";
+
+ try {
+ const res = await fetch(url, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Authorization": `DeepL-Auth-Key ${apiKey}`
+ },
+ body: payload
+ });
+
+ const data = await res.text();
+ return { status: res.status, data };
+ } catch (e) {
+ return { status: -1, data: String(e) };
+ }
+}
diff --git a/src/plugins/translate/settings.ts b/src/plugins/translate/settings.ts
index 65d84535..916c70bd 100644
--- a/src/plugins/translate/settings.ts
+++ b/src/plugins/translate/settings.ts
@@ -22,38 +22,76 @@ import { OptionType } from "@utils/types";
export const settings = definePluginSettings({
receivedInput: {
type: OptionType.STRING,
- description: "Input language for received messages",
+ description: "Language that received messages should be translated from",
default: "auto",
hidden: true
},
receivedOutput: {
type: OptionType.STRING,
- description: "Output language for received messages",
+ description: "Language that received messages should be translated to",
default: "en",
hidden: true
},
sentInput: {
type: OptionType.STRING,
- description: "Input language for sent messages",
+ description: "Language that your own messages should be translated from",
default: "auto",
hidden: true
},
sentOutput: {
type: OptionType.STRING,
- description: "Output language for sent messages",
+ description: "Language that your own messages should be translated to",
default: "en",
hidden: true
},
+
+ showChatBarButton: {
+ type: OptionType.BOOLEAN,
+ description: "Show translate button in chat bar",
+ default: true
+ },
+ service: {
+ type: OptionType.SELECT,
+ description: IS_WEB ? "Translation service (Not supported on Web!)" : "Translation service",
+ disabled: () => IS_WEB,
+ options: [
+ { label: "Google Translate", value: "google", default: true },
+ { label: "DeepL Free", value: "deepl" },
+ { label: "DeepL Pro", value: "deepl-pro" }
+ ] as const,
+ onChange: resetLanguageDefaults
+ },
+ deeplApiKey: {
+ type: OptionType.STRING,
+ description: "DeepL API key",
+ default: "",
+ placeholder: "Get your API key from https://deepl.com/your-account",
+ disabled: () => IS_WEB
+ },
autoTranslate: {
type: OptionType.BOOLEAN,
description: "Automatically translate your messages before sending. You can also shift/right click the translate button to toggle this",
default: false
},
- showChatBarButton: {
+ showAutoTranslateTooltip: {
type: OptionType.BOOLEAN,
- description: "Show translate button in chat bar",
+ description: "Show a tooltip on the ChatBar button whenever a message is automatically translated",
default: true
- }
+ },
}).withPrivateSettings<{
showAutoTranslateAlert: boolean;
}>();
+
+export function resetLanguageDefaults() {
+ if (IS_WEB || settings.store.service === "google") {
+ settings.store.receivedInput = "auto";
+ settings.store.receivedOutput = "en";
+ settings.store.sentInput = "auto";
+ settings.store.sentOutput = "en";
+ } else {
+ settings.store.receivedInput = "";
+ settings.store.receivedOutput = "en-us";
+ settings.store.sentInput = "";
+ settings.store.sentOutput = "en-us";
+ }
+}
diff --git a/src/plugins/translate/utils.ts b/src/plugins/translate/utils.ts
index 493fb2ca..aff64e8a 100644
--- a/src/plugins/translate/utils.ts
+++ b/src/plugins/translate/utils.ts
@@ -17,12 +17,18 @@
*/
import { classNameFactory } from "@api/Styles";
+import { onlyOnce } from "@utils/onlyOnce";
+import { PluginNative } from "@utils/types";
+import { showToast, Toasts } from "@webpack/common";
-import { settings } from "./settings";
+import { DeeplLanguages, deeplLanguageToGoogleLanguage, GoogleLanguages } from "./languages";
+import { resetLanguageDefaults, settings } from "./settings";
export const cl = classNameFactory("vc-trans-");
-interface TranslationData {
+const Native = VencordNative.pluginHelpers.Translate as PluginNative;
+
+interface GoogleData {
src: string;
sentences: {
// 🏳️⚧️
@@ -30,15 +36,47 @@ interface TranslationData {
}[];
}
+interface DeeplData {
+ translations: {
+ detected_source_language: string;
+ text: string;
+ }[];
+}
+
export interface TranslationValue {
- src: string;
+ sourceLanguage: string;
text: string;
}
-export async function translate(kind: "received" | "sent", text: string): Promise {
- const sourceLang = settings.store[kind + "Input"];
- const targetLang = settings.store[kind + "Output"];
+export const getLanguages = () => IS_WEB || settings.store.service === "google"
+ ? GoogleLanguages
+ : DeeplLanguages;
+export async function translate(kind: "received" | "sent", text: string): Promise {
+ const translate = IS_WEB || settings.store.service === "google"
+ ? googleTranslate
+ : deeplTranslate;
+
+ try {
+ return await translate(
+ text,
+ settings.store[`${kind}Input`],
+ settings.store[`${kind}Output`]
+ );
+ } catch (e) {
+ const userMessage = typeof e === "string"
+ ? e
+ : "Something went wrong. If this issue persists, please check the console or ask for help in the support server.";
+
+ showToast(userMessage, Toasts.Type.FAILURE);
+
+ throw e instanceof Error
+ ? e
+ : new Error(userMessage);
+ }
+}
+
+async function googleTranslate(text: string, sourceLang: string, targetLang: string): Promise {
const url = "https://translate.googleapis.com/translate_a/single?" + new URLSearchParams({
// see https://stackoverflow.com/a/29537590 for more params
// holy shidd nvidia
@@ -63,13 +101,69 @@ export async function translate(kind: "received" | "sent", text: string): Promis
+ `\n${res.status} ${res.statusText}`
);
- const { src, sentences }: TranslationData = await res.json();
+ const { src, sentences }: GoogleData = await res.json();
return {
- src,
+ sourceLanguage: GoogleLanguages[src] ?? src,
text: sentences.
map(s => s?.trans).
filter(Boolean).
join("")
};
}
+
+function fallbackToGoogle(text: string, sourceLang: string, targetLang: string): Promise {
+ return googleTranslate(
+ text,
+ deeplLanguageToGoogleLanguage(sourceLang),
+ deeplLanguageToGoogleLanguage(targetLang)
+ );
+}
+
+const showDeeplApiQuotaToast = onlyOnce(
+ () => showToast("Deepl API quota exceeded. Falling back to Google Translate", Toasts.Type.FAILURE)
+);
+
+async function deeplTranslate(text: string, sourceLang: string, targetLang: string): Promise {
+ if (!settings.store.deeplApiKey) {
+ showToast("DeepL API key is not set. Resetting to Google", Toasts.Type.FAILURE);
+
+ settings.store.service = "google";
+ resetLanguageDefaults();
+
+ return fallbackToGoogle(text, sourceLang, targetLang);
+ }
+
+ // CORS jumpscare
+ const { status, data } = await Native.makeDeeplTranslateRequest(
+ settings.store.service === "deepl-pro",
+ settings.store.deeplApiKey,
+ JSON.stringify({
+ text: [text],
+ target_lang: targetLang,
+ source_lang: sourceLang.split("-")[0]
+ })
+ );
+
+ switch (status) {
+ case 200:
+ break;
+ case -1:
+ throw "Failed to connect to DeepL API: " + data;
+ case 403:
+ throw "Invalid DeepL API key or version";
+ case 456:
+ showDeeplApiQuotaToast();
+ return fallbackToGoogle(text, sourceLang, targetLang);
+ default:
+ throw new Error(`Failed to translate "${text}" (${sourceLang} -> ${targetLang})\n${status} ${data}`);
+ }
+
+ const { translations }: DeeplData = JSON.parse(data);
+ const src = translations[0].detected_source_language;
+
+ return {
+ sourceLanguage: DeeplLanguages[src] ?? src,
+ text: translations[0].text
+ };
+}
diff --git a/src/plugins/viewRaw/index.tsx b/src/plugins/viewRaw/index.tsx
index 56c285ec..83c560df 100644
--- a/src/plugins/viewRaw/index.tsx
+++ b/src/plugins/viewRaw/index.tsx
@@ -153,6 +153,7 @@ export default definePlugin({
contextMenus: {
"guild-context": MakeContextCallback("Guild"),
"channel-context": MakeContextCallback("Channel"),
+ "thread-context": MakeContextCallback("Channel"),
"user-context": MakeContextCallback("User")
},
diff --git a/src/plugins/watchTogetherAdblock.desktop/adguard.js b/src/plugins/youtubeAdblock.desktop/adguard.js
similarity index 100%
rename from src/plugins/watchTogetherAdblock.desktop/adguard.js
rename to src/plugins/youtubeAdblock.desktop/adguard.js
diff --git a/src/plugins/watchTogetherAdblock.desktop/index.ts b/src/plugins/youtubeAdblock.desktop/index.ts
similarity index 53%
rename from src/plugins/watchTogetherAdblock.desktop/index.ts
rename to src/plugins/youtubeAdblock.desktop/index.ts
index 2dbc13d4..708b908d 100644
--- a/src/plugins/watchTogetherAdblock.desktop/index.ts
+++ b/src/plugins/youtubeAdblock.desktop/index.ts
@@ -4,12 +4,14 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
+import { migratePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
// The entire code of this plugin can be found in native.ts
+migratePluginSettings("YoutubeAdblock", "WatchTogetherAdblock");
export default definePlugin({
- name: "WatchTogetherAdblock",
- description: "Block ads in the YouTube WatchTogether activity via AdGuard",
- authors: [Devs.ImLvna],
+ name: "YoutubeAdblock",
+ description: "Block ads in YouTube embeds and the WatchTogether activity via AdGuard",
+ authors: [Devs.ImLvna, Devs.Ven],
});
diff --git a/src/plugins/watchTogetherAdblock.desktop/native.ts b/src/plugins/youtubeAdblock.desktop/native.ts
similarity index 69%
rename from src/plugins/watchTogetherAdblock.desktop/native.ts
rename to src/plugins/youtubeAdblock.desktop/native.ts
index c4106c34..8cc6a323 100644
--- a/src/plugins/watchTogetherAdblock.desktop/native.ts
+++ b/src/plugins/youtubeAdblock.desktop/native.ts
@@ -11,9 +11,9 @@ import adguard from "file://adguard.js?minify";
app.on("browser-window-created", (_, win) => {
win.webContents.on("frame-created", (_, { frame }) => {
frame.once("dom-ready", () => {
- if (frame.url.includes("discordsays") && frame.url.includes("youtube.com")) {
- if (!RendererSettings.store.plugins?.WatchTogetherAdblock?.enabled) return;
+ if (!RendererSettings.store.plugins?.YoutubeAdblock?.enabled) return;
+ if (frame.url.includes("youtube.com/embed/") || (frame.url.includes("discordsays") && frame.url.includes("youtube.com"))) {
frame.executeJavaScript(adguard);
}
});
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 8face04c..d58aea2d 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -540,6 +540,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "Joona",
id: 297410829589020673n
},
+ AshtonMemer: {
+ name: "AshtonMemer",
+ id: 373657230530052099n
+ },
surgedevs: {
name: "Chloe",
id: 1084592643784331324n