diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4cc6ef1f9..5e4a15145 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,7 +41,8 @@ jobs: cd dist mkdir release cp browser/browser.* release - cp Vencord.user.{js,js.LEGAL.txt} release + cp Equicord.user.{js,js.LEGAL.txt} release + # copy the plugin data jsons, the extension zips and the desktop/vesktop asars cp *.{json,zip,asar} release # legacy un-asared files diff --git a/.github/workflows/nixosBuild.yml b/.github/workflows/nixosBuild.yml index d99577ec0..e4cb7a88c 100644 --- a/.github/workflows/nixosBuild.yml +++ b/.github/workflows/nixosBuild.yml @@ -39,7 +39,8 @@ jobs: cd dist mkdir release cp browser/browser.* release - cp Vencord.user.{js,js.LEGAL.txt} release + cp Equicord.user.{js,js.LEGAL.txt} release + # copy the plugin data jsons, the extension zips and the desktop/vesktop asars cp *.{json,zip,asar} release # legacy un-asared files diff --git a/browser/content.js b/browser/content.js index 2b51ebabf..cd1f59793 100644 --- a/browser/content.js +++ b/browser/content.js @@ -5,7 +5,7 @@ if (typeof browser === "undefined") { const style = document.createElement("link"); style.type = "text/css"; style.rel = "stylesheet"; -style.href = browser.runtime.getURL("dist/Vencord.css"); +style.href = browser.runtime.getURL("dist/Equicord.css"); document.addEventListener( "DOMContentLoaded", diff --git a/browser/manifest.json b/browser/manifest.json index 96fcdc571..cb594c1a7 100644 --- a/browser/manifest.json +++ b/browser/manifest.json @@ -3,7 +3,7 @@ "minimum_chrome_version": "111", "name": "Equicord Web", "description": "The other cutest Discord mod now in your browser", - "author": "Thororen", + "author": "Equicord", "homepage_url": "https://github.com/Equicord/Equicord", "icons": { "128": "icon.png" @@ -33,7 +33,7 @@ "*://*.discord.com/*" ], "js": [ - "dist/Vencord.js" + "dist/Equicord.js" ], "all_frames": true, "world": "MAIN" diff --git a/browser/manifestv2.json b/browser/manifestv2.json index 172a42e50..b95080d54 100644 --- a/browser/manifestv2.json +++ b/browser/manifestv2.json @@ -3,7 +3,7 @@ "minimum_chrome_version": "91", "name": "Equicord Web", "description": "The other cutest Discord mod now in your browser", - "author": "Thororen", + "author": "Equicord", "homepage_url": "https://github.com/Equicord/Equicord", "icons": { "128": "icon.png" @@ -32,7 +32,7 @@ "*://*.discord.com/*" ], "js": [ - "dist/Vencord.js" + "dist/Equicord.js" ], "all_frames": true, "world": "MAIN" @@ -44,13 +44,13 @@ ] }, "web_accessible_resources": [ - "dist/Vencord.js", - "dist/Vencord.css" + "dist/Equicord.js", + "dist/Equicord.css" ], "browser_specific_settings": { "gecko": { - "id": "vencord-firefox@vendicated.dev", + "id": "equicord-firefox@equicord.org", "strict_min_version": "128.0" } } -} \ No newline at end of file +} diff --git a/packages/discord-types/src/common/Guild.d.ts b/packages/discord-types/src/common/Guild.d.ts index 5b5c3c745..65e973ffe 100644 --- a/packages/discord-types/src/common/Guild.d.ts +++ b/packages/discord-types/src/common/Guild.d.ts @@ -47,7 +47,7 @@ export class Guild extends DiscordRecord { }; premiumProgressBarEnabled: boolean; premiumSubscriberCount: number; - premiumTier: number; + premiumTier: 0 | 1 | 2 | 3; profile: { badge: string | undefined; tag: string | undefined; diff --git a/packages/discord-types/src/stores/MediaEngineStore.d.ts b/packages/discord-types/src/stores/MediaEngineStore.d.ts index d44f5aa58..4ef81639d 100644 --- a/packages/discord-types/src/stores/MediaEngineStore.d.ts +++ b/packages/discord-types/src/stores/MediaEngineStore.d.ts @@ -1,10 +1,37 @@ import { FluxStore } from ".."; export class MediaEngineStore extends FluxStore { - isSelfMute(): boolean; - isSelfDeaf(): boolean; + isAdvancedVoiceActivitySupported(): boolean; + isAecDumpSupported(): boolean; + isAnyLocalVideoAutoDisabled(): boolean; + isAutomaticGainControlSupported(): boolean; + isDeaf(): boolean; + isEnableHardwareMuteNotice(): boolean; + isEnabled(): boolean; + isExperimentalEncodersSupported(): boolean; + isHardwareMute(): boolean; + isInputProfileCustom(): boolean; + isInteractionRequired(): boolean; + isLocalMute(): boolean; isLocalMute(userId): boolean; + isLocalVideoAutoDisabled(userId): boolean; isLocalVideoDisabled(userId): boolean; + isLocalVideoDisabled(): boolean; + isMediaFilterSettingLoading(): boolean; + isMute(): boolean; + isNativeAudioPermissionReady(): boolean; + isNoiseCancellationError(): boolean; + isNoiseCancellationSupported(): boolean; + isNoiseSuppressionSupported(): boolean; + isScreenSharing(): boolean; + isSelfDeaf(): boolean; + isSelfMute(): boolean; + isSelfMutedTemporarily(): boolean; + isSimulcastSupported(): boolean; + isSoundSharing(): boolean; + isSupported(): boolean; + isVideoAvailable(): boolean; + isVideoEnabled(): boolean; setAv1Enabled(AV1: boolean): void; setH265Enabled(H265: boolean): void; diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index 86eec848d..7d7d5518e 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -105,7 +105,7 @@ const buildConfigs = [ IS_USERSCRIPT: "true", window: "unsafeWindow", }, - outfile: "dist/Vencord.user.js", + outfile: "dist/Equicord.user.js", banner: { js: readFileSync("browser/userscript.meta.js", "utf-8").replace("%version%", `${VERSION}.${new Date().getTime()}`) }, @@ -155,8 +155,8 @@ async function loadDir(dir, basePath = "") { */ async function buildExtension(target, files) { const entries = { - "dist/Vencord.js": await readFile("dist/browser/extension.js"), - "dist/Vencord.css": await readFile("dist/browser/extension.css"), + "dist/Equicord.js": await readFile("dist/browser/extension.js"), + "dist/Equicord.css": await readFile("dist/browser/extension.css"), ...await loadDir("dist/browser/vendor/monaco", "dist/browser/"), ...Object.fromEntries(await Promise.all(files.map(async f => { let content = await readFile(join("browser", f)); @@ -184,7 +184,7 @@ async function buildExtension(target, files) { console.info("Unpacked Extension written to dist/browser/" + target); } -const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content => { +const appendCssRuntime = readFile("dist/Equicord.user.css", "utf-8").then(content => { const cssRuntime = ` ;document.addEventListener("DOMContentLoaded", () => document.body.insertAdjacentElement("afterend", Object.assign(document.createElement("style"), { @@ -194,7 +194,7 @@ const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content ), { once: true }); `; - return appendFile("dist/Vencord.user.js", cssRuntime); + return appendFile("dist/Equicord.user.js", cssRuntime); }); if (!process.argv.includes("--skip-extension")) { diff --git a/src/components/settings/tabs/changelog/index.tsx b/src/components/settings/tabs/changelog/index.tsx index d349178c6..ac24023b0 100644 --- a/src/components/settings/tabs/changelog/index.tsx +++ b/src/components/settings/tabs/changelog/index.tsx @@ -13,13 +13,15 @@ import { DeleteIcon } from "@components/Icons"; import { Link } from "@components/Link"; import { Paragraph } from "@components/Paragraph"; import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab"; +import { gitHashShort } from "@shared/vencordUserAgent"; import { Margins } from "@utils/margins"; import { useAwaiter } from "@utils/react"; -import { getRepo, shortGitHash, UpdateLogger } from "@utils/updater"; +import { getRepo, UpdateLogger } from "@utils/updater"; import { Alerts, Button, Card, React, Toasts } from "@webpack/common"; import gitHash from "~git-hash"; +import { HashLink } from "../updater/Components"; import { ChangelogEntry, ChangelogHistory, @@ -40,22 +42,6 @@ import { } from "./changelogManager"; import { NewPluginsCompact, NewPluginsSection } from "./NewPluginsSection"; -function HashLink({ - repo, - hash, - disabled = false, -}: { - repo: string; - hash: string; - disabled?: boolean; -}) { - return ( - - {hash} - - ); -} - function ChangelogCard({ entry, repo, @@ -78,7 +64,7 @@ function ChangelogCard({ @@ -131,7 +117,7 @@ function UpdateLogCard({ - {shortGitHash()} + {gitHashShort} ) diff --git a/src/components/settings/tabs/plugins/styles.css b/src/components/settings/tabs/plugins/styles.css index f9d12e05f..70288b64a 100644 --- a/src/components/settings/tabs/plugins/styles.css +++ b/src/components/settings/tabs/plugins/styles.css @@ -58,6 +58,7 @@ line-height: 16px; color: var(--white-500); text-align: center; + margin-right: 8px; } .vc-plugins-dep-name { diff --git a/src/components/settings/tabs/updater/Components.tsx b/src/components/settings/tabs/updater/Components.tsx index 9354e0219..935c24e46 100644 --- a/src/components/settings/tabs/updater/Components.tsx +++ b/src/components/settings/tabs/updater/Components.tsx @@ -24,7 +24,7 @@ export interface CommonProps { export function HashLink({ repo, hash, disabled = false }: { repo: string, hash: string, disabled?: boolean; }) { return ( - {hash} + {hash.slice(0, 7)} ); } diff --git a/src/components/settings/tabs/updater/index.tsx b/src/components/settings/tabs/updater/index.tsx index eb20d79ab..4bca119fe 100644 --- a/src/components/settings/tabs/updater/index.tsx +++ b/src/components/settings/tabs/updater/index.tsx @@ -30,11 +30,13 @@ import { classes } from "@utils/misc"; import { ModalCloseButton, ModalContent, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { relaunch } from "@utils/native"; import { useAwaiter } from "@utils/react"; -import { changes, checkForUpdates, getRepo, isNewer, shortGitHash, update, updateError, UpdateLogger } from "@utils/updater"; -import { Alerts, Button, Card, Parser, React, Toasts } from "@webpack/common"; +import { changes, checkForUpdates, getRepo, isNewer, update, updateError, UpdateLogger } from "@utils/updater"; +import { Alerts, Button, Parser, React, Toasts } from "@webpack/common"; import gitHash from "~git-hash"; +import { Changes, HashLink } from "./Components"; + function withDispatcher(dispatcher: React.Dispatch>, action: () => any) { return async () => { dispatcher(true); @@ -80,31 +82,6 @@ interface CommonProps { repoPending: boolean; } -function HashLink({ repo, hash, disabled = false, longHash = hash }: { repo: string, hash: string, disabled?: boolean; longHash?: string; }) { - return - {hash} - ; -} - -function Changes({ updates, repo, repoPending }: CommonProps & { updates: typeof changes; }) { - return ( - - {updates.map(({ hash, author, message }) => ( - - - {message} - {author} - - ))} - - ); -} - function Updatable(props: CommonProps) { const [updates, setUpdates] = React.useState(changes); const [isChecking, setIsChecking] = React.useState(false); @@ -235,7 +212,7 @@ function Updater() { ) } - {" "}() + {" "}() diff --git a/src/equicordplugins/anammox/index.ts b/src/equicordplugins/anammox/index.ts index acf93b585..463351649 100644 --- a/src/equicordplugins/anammox/index.ts +++ b/src/equicordplugins/anammox/index.ts @@ -53,7 +53,7 @@ export default definePlugin({ find: 'tutorialId:"direct-messages"', replacement: [ { - match: /NAVIGATION_LINK\}\}\}.{0,15}\)(?=,\i&&\()/, + match: /NAVIGATION_LINK\}\}\}\)(?=,\i(&&\(|\]))/, replace: "$&&&undefined", }, { diff --git a/src/equicordplugins/arRPCBun.web/index.tsx b/src/equicordplugins/arRPCBun.web/index.tsx new file mode 100644 index 000000000..012c4f45d --- /dev/null +++ b/src/equicordplugins/arRPCBun.web/index.tsx @@ -0,0 +1,230 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { popNotice, showNotice } from "@api/Notices"; +import { definePluginSettings } from "@api/Settings"; +import { EquicordDevs } from "@utils/constants"; +import { Logger } from "@utils/Logger"; +import { isAnyPluginDev, isEquicordGuild } from "@utils/misc"; +import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; +import { findByCodeLazy } from "@webpack"; +import { ApplicationAssetUtils, FluxDispatcher, Toasts, UserStore } from "@webpack/common"; + +const fetchApplicationsRPC = findByCodeLazy('"Invalid Origin"', ".application"); +const logger = new Logger("arRPCBun"); + +async function lookupAsset(applicationId: string, key: string): Promise { + return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0]; +} + +const apps: any = {}; +async function lookupApp(applicationId: string): Promise { + const socket: any = {}; + await fetchApplicationsRPC(socket, applicationId); + return socket.application; +} + +let ws: WebSocket; + +export const settings = definePluginSettings({ + oldVerNotice: { + type: OptionType.BOOLEAN, + description: "old version notice for why arrpc bun doesnt work", + default: false, + hidden: true + }, + oneTimeNotice: { + type: OptionType.BOOLEAN, + description: "One time notice for showing arrpc disabled", + default: false, + hidden: true + }, +}); + + +export default definePlugin({ + name: "arRPCBun", + description: "arRPCBun integration", + authors: [EquicordDevs.creations], + reporterTestable: ReporterTestable.None, + enabledByDefault: IS_EQUIBOP, + settings, + + commands: [ + { + name: "arrpc-debug", + description: "Show arRPCBun debug information", + predicate: ctx => { + const result = isAnyPluginDev(UserStore.getCurrentUser()?.id) || isEquicordGuild(ctx?.guild?.id, true); + return result; + }, + execute: () => { + const arrpcStatus = IS_EQUIBOP ? VesktopNative.arrpc?.getStatus?.() : null; + + let content = ""; + + if (IS_EQUIBOP) { + const version = VesktopNative.app.getVersion(); + const gitHash = VesktopNative.app.getGitHash?.(); + const shortHash = gitHash?.slice(0, 7); + + content += `Equibop: v${version}`; + if (shortHash) { + content += ` • [${shortHash}]()`; + } + content += "\n"; + } + + if (arrpcStatus) { + content += `Running: ${arrpcStatus.running ? "Yes" : "No"}\n`; + content += `Enabled: ${arrpcStatus.enabled ? "Yes" : "No"}\n`; + + if (arrpcStatus.running) { + content += `Port: ${arrpcStatus.port}\n`; + content += `PID: ${arrpcStatus.pid}\n`; + + if (arrpcStatus.uptime) { + const seconds = Math.floor(arrpcStatus.uptime / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + + if (hours > 0) { + content += `Uptime: ${hours}h ${minutes % 60}m ${seconds % 60}s\n`; + } else if (minutes > 0) { + content += `Uptime: ${minutes}m ${seconds % 60}s\n`; + } else { + content += `Uptime: ${seconds}s\n`; + } + } + } + + const info = [ + arrpcStatus.restartCount > 0 && ["Restarts", arrpcStatus.restartCount], + arrpcStatus.bunPath && ["Bun", arrpcStatus.bunPath], + arrpcStatus.warnings?.length && ["Warnings", arrpcStatus.warnings.join(", ")], + arrpcStatus.lastError && ["Last Error", arrpcStatus.lastError], + arrpcStatus.lastExitCode && arrpcStatus.lastExitCode !== 0 && ["Exit Code", arrpcStatus.lastExitCode] + ].filter(Boolean); + + content += info.map(([type, value]) => `${type}: ${value}`).join("\n"); + } else { + if (ws) { + content += ws.readyState === WebSocket.OPEN + ? "WebSocket: Connected to external arRPCBun server\n" + : `WebSocket: ${["Connecting", "Open", "Closing", "Closed"][ws.readyState]}\n`; + } else { + content += "WebSocket: Not connected\n"; + } + } + + return { content }; + }, + }, + ], + + async handleEvent(e: MessageEvent) { + const data = JSON.parse(e.data); + + const { activity } = data; + const assets = activity?.assets; + + if (assets?.large_image) assets.large_image = await lookupAsset(activity.application_id, assets.large_image); + if (assets?.small_image) assets.small_image = await lookupAsset(activity.application_id, assets.small_image); + + if (activity) { + const appId = activity.application_id; + apps[appId] ||= await lookupApp(appId); + + const app = apps[appId]; + activity.name ||= app.name; + } + + FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", ...data }); + }, + + async start() { + // only works on 3.0.8+ + if (IS_EQUIBOP) { + const version = VesktopNative.app.getVersion(); + const [major, minor, patch] = version.split(".").map(Number); + + if (major < 3 || (major === 3 && minor === 0 && patch < 8) && !settings.store.oldVerNotice) { + logger.error(`Equibop ${version} is too old. Requires 3.0.8+ for arRPCBun fix.`); + showNotice(`arRPCBun requires Equibop 3.0.8+. You have ${version}. Update Equibop to use this plugin.`, "OK", () => { + settings.store.oldVerNotice = true; + popNotice(); + }); + return; + } + } + + // disable WebRichPresence to avoid conflicts + const webRPC = Vencord.Plugins.plugins.WebRichPresence; + if (webRPC && Vencord.Plugins.isPluginEnabled("WebRichPresence")) { + logger.info("Disabling WebRichPresence to avoid conflicts"); + Vencord.Plugins.stopPlugin(webRPC); + } + + // get arRPC status from Equibop if available, otherwise use defaults + const arrpcStatus = IS_EQUIBOP ? VesktopNative.arrpc?.getStatus?.() : null; + + // if on Equibop and arRPC is disabled AND not running, warn user + if (IS_EQUIBOP && !arrpcStatus?.enabled && !arrpcStatus?.running && !settings.store.oneTimeNotice) { + logger.warn("Equibop's built-in arRPC is disabled and not running"); + showNotice("arRPC is not running. Enable it in Equibop settings, or run your own arRPCBun server.", "OK", () => { + settings.store.oneTimeNotice = true; + popNotice(); + }); + return; + } + + const host = arrpcStatus?.host || "127.0.0.1"; + const port = arrpcStatus?.port || 1337; + + const wsUrl = `ws://${host}:${port}`; + logger.info(`Connecting to arRPCBun at ${wsUrl}${arrpcStatus?.host ? "" : " (using defaults)"}`); + + if (ws) ws.close(); + ws = new WebSocket(wsUrl); + + ws.onmessage = this.handleEvent; + + ws.onerror = error => { + logger.error("WebSocket error:", error); + }; + + ws.onclose = () => { + logger.info("WebSocket closed"); + }; + + const connectionSuccessful = await new Promise(res => setTimeout(() => res(ws.readyState === WebSocket.OPEN), 5000)); + if (!connectionSuccessful) { + logger.error("Failed to connect to arRPCBun"); + showNotice("Failed to connect to arRPCBun, is it running?", "Retry", () => { + popNotice(); + this.start(); + }); + return; + } + + logger.info("Successfully connected to arRPCBun"); + Toasts.show({ + message: "Connected to arRPCBun", + type: Toasts.Type.SUCCESS, + id: Toasts.genId(), + options: { + duration: 1000, + position: Toasts.Position.BOTTOM + } + }); + }, + + stop() { + FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", activity: null }); + ws?.close(); + logger.info("Stopped arRPCBun connection"); + } +}); diff --git a/src/equicordplugins/autoZipper/index.ts b/src/equicordplugins/autoZipper/index.ts index 692084fad..ab0f2335f 100644 --- a/src/equicordplugins/autoZipper/index.ts +++ b/src/equicordplugins/autoZipper/index.ts @@ -75,7 +75,7 @@ async function readDirectoryEntry(entry: FileSystemDirectoryEntry): Promise => { return new Promise((resolve, reject) => { - reader.readEntries(async (entries) => { + reader.readEntries(async entries => { if (entries.length === 0) { resolve(); return; diff --git a/src/equicordplugins/betterBanReasons/index.tsx b/src/equicordplugins/betterBanReasons/index.tsx index f43c6fc93..749d7791f 100644 --- a/src/equicordplugins/betterBanReasons/index.tsx +++ b/src/equicordplugins/betterBanReasons/index.tsx @@ -42,7 +42,7 @@ function ReasonsComponent() { reasons.splice(i, 1); settings.store.reasons = reasons; }} - look={Button.Looks.LINK} + look={Button.Looks.FILLED} size={Button.Sizes.MIN} > diff --git a/src/equicordplugins/channelTabs/components/ContextMenus.tsx b/src/equicordplugins/channelTabs/components/ContextMenus.tsx index 459cf5efe..e4f25deaa 100644 --- a/src/equicordplugins/channelTabs/components/ContextMenus.tsx +++ b/src/equicordplugins/channelTabs/components/ContextMenus.tsx @@ -82,7 +82,7 @@ export function EditModal({ modalProps, modalKey, bookmark, onSave }: { >Save closeModal(modalKey)} >Cancel @@ -128,7 +128,7 @@ function AddToFolderModal({ modalProps, modalKey, bookmarks, onSave }: { >Save closeModal(modalKey)} >Cancel @@ -156,7 +156,7 @@ function DeleteFolderConfirmationModal({ modalProps, modalKey, onConfirm }) { closeModal(modalKey)} > Cancel diff --git a/src/equicordplugins/chatButtonsPlus/index.tsx b/src/equicordplugins/chatButtonsPlus/index.tsx index 32b82a781..46270e316 100644 --- a/src/equicordplugins/chatButtonsPlus/index.tsx +++ b/src/equicordplugins/chatButtonsPlus/index.tsx @@ -192,7 +192,7 @@ function ButtonEntries() { /> removeButtonEntry(entry.id, update)} - look={Button.Looks.LINK} + look={Button.Looks.FILLED} color={Button.Colors.RED} size={Button.Sizes.SMALL} > @@ -245,7 +245,7 @@ function ButtonEntries() { addButtonEntry(update)}>Add Button resetAllButtons(update)} - look={Button.Looks.LINK} + look={Button.Looks.FILLED} color={Button.Colors.RED} > Reset All diff --git a/src/equicordplugins/contentWarning/index.tsx b/src/equicordplugins/contentWarning/index.tsx index 46336fb2d..cdebb1bdb 100644 --- a/src/equicordplugins/contentWarning/index.tsx +++ b/src/equicordplugins/contentWarning/index.tsx @@ -102,7 +102,7 @@ function FlaggedInput({ index, forceUpdate }) { {)(.{0,60}?)showDownload:(\i),(.{0,1250}?)href:\i,/, - replace: "$1const downloadifyHoverItem=$2;$3downloadifyShowDownload:$4=!0,$5onClick:()=>{$self.handleHoverDownloadButtonClicked(downloadifyHoverItem);}," - }, - }, - { - // Overwrites the default download button behavior for expanded image & video modals. - // This patch is lazy loaded. You must open an image or video modal for it to resolve. - find: "SAVE_MEDIA_PRESSED),", - group: true, - replacement: [ - { - // Make use of the download function. - match: /(let{item:\i}=(\i).{0,450}?)(\i\(!0\);try{.{0,400}?finally{\i\(!1\)})/, - replace: "$1await $self.handleExpandedModalDownloadButtonClicked($2,async()=>{$3});", - }, - { - // Prevent videos from opening in browser. - match: /(let{item:\i}=\i.{0,350}?)(SAVE_MEDIA_PRESSED\)).{0,100}?type\){/, - replace: "$1$2,true){" - } - ], - } - ] -}); diff --git a/src/equicordplugins/downloadify.desktop/native.ts b/src/equicordplugins/downloadify.desktop/native.ts deleted file mode 100644 index 1cc48e27a..000000000 --- a/src/equicordplugins/downloadify.desktop/native.ts +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2025 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { dialog } from "electron"; -import fs from "fs"; -import { Readable } from "stream"; -import { finished } from "stream/promises"; - -/** - * Check if a file exists at a given path. - */ -export async function fileExists(_: Electron.IpcMainInvokeEvent, filePath: string): Promise { - try { - await fs.promises.access(filePath, fs.constants.F_OK); - return true; - } catch { - return false; - } -} - -/** - * Ask the user to select a directory to save files. - */ -export async function getDirectory(_: Electron.IpcMainInvokeEvent): Promise { - const result = await dialog.showOpenDialog({ - properties: ["openDirectory"] - }); - - return result.filePaths[0] ?? null; -} - -/** - * Ask the user to select a file name and location to save a file. - */ -export async function getFilePath(_: Electron.IpcMainInvokeEvent, fileName: string, fileTypes: string[] | null): Promise { - const filters = fileTypes?.map(ext => ({ - name: ext.toUpperCase(), - extensions: [ext] - })) ?? []; - - if (!fileTypes?.length) { - filters.push({ name: "All Files", extensions: ["*"] }); - } - - const result = await dialog.showSaveDialog({ - title: "Save File", - defaultPath: fileName, - buttonLabel: "Save", - properties: ["createDirectory"], - filters - }); - - return result.filePath ?? null; -} - -/** - * Get the content type of a file at a URL. - * - * @returns The content type of the file if it exists, undefined if no content type is available, or null if the file doesn't exist. - */ -export async function queryURL(_: Electron.IpcMainInvokeEvent, url: string): Promise { - const response = await fetch(url, { method: "HEAD" }); - - if (!response.ok) { - return null; - } - - return response.headers.get("content-type") ?? undefined; -} - -/** - * Download a file from a URL to a local file path. - */ -export async function downloadURL(_: Electron.IpcMainInvokeEvent, url: string, filePath: string): Promise { - const response = await fetch(url); - - if (!response.ok) { - return false; - } - - if (!response.body) { - return false; - } - - const readableStream = Readable.fromWeb(response.body as any); - const fileWriteStream = fs.createWriteStream(filePath); - readableStream.pipe(fileWriteStream); - await finished(fileWriteStream); - - return true; -} diff --git a/src/equicordplugins/downloadify.desktop/settings.tsx b/src/equicordplugins/downloadify.desktop/settings.tsx deleted file mode 100644 index 97a74d9d8..000000000 --- a/src/equicordplugins/downloadify.desktop/settings.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2025 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import "./style.css"; - -import { definePluginSettings } from "@api/Settings"; -import ErrorBoundary from "@components/ErrorBoundary"; -import { Heading } from "@components/Heading"; -import { Paragraph } from "@components/Paragraph"; -import { OptionType } from "@utils/types"; -import { Button, showToast, Toasts, useState } from "@webpack/common"; -import { JSX } from "react"; - -import { d, DownloadifyLogger, DownloadifyNative } from "./utils/definitions"; -import { getFormattedNow } from "./utils/misc"; - -function DefaultDirectorySetting(): JSX.Element { - const { defaultDirectory } = settings.use(["defaultDirectory"]); - const [isDialogueOpen, setDialogueOpen] = useState(false); - - const handlePickDirectory = async () => { - try { - setDialogueOpen(true); - const directory = await DownloadifyNative.getDirectory(); - directory && (settings.store.defaultDirectory = directory); - } catch (error) { - DownloadifyLogger.error(`[${getFormattedNow()}] [FAILED TO SET DOWNLOAD DIRECTORY]`, error); - showToast("Failed to set download directory.", Toasts.Type.FAILURE, { duration: 3000 }); - } finally { - setDialogueOpen(false); - } - }; - - const handleClearDirectory = () => { - settings.store.defaultDirectory = ""; - }; - - return ( - - - - Default Directory - - - Default download location. If set, the file will always be downloaded in its original format even if alternatives are available. Leave empty to pick a folder and file type each time. - - - - {defaultDirectory || "No Directory Set"} - - - - {isDialogueOpen ? "Browsing..." : "Browse"} - - - Clear - - - - - - ); -} - -export const settings = definePluginSettings({ - displayStatus: { - description: "Display a status notification when downloads start, finish, or error.", - type: OptionType.BOOLEAN, - default: true, - }, - statusDuration: { - type: OptionType.SLIDER, - description: "The number of seconds to display status notifications.", - markers: [1, 3, 5], - default: 2.5, - stickToMarkers: false, - }, - voiceMessages: { - description: "Add a download button to voice messages.", - type: OptionType.BOOLEAN, - default: true, - }, - allowUnicode: { - description: "Allow non-ASCII characters in file names.", - type: OptionType.BOOLEAN, - default: true, - }, - overwriteFiles: { - description: "If a default directory is set and a download file name matches an existing file, overwrite the file. If disabled, a number will be appended to the file name.", - type: OptionType.BOOLEAN, - default: false, - }, - defaultDirectory: { - component: DefaultDirectorySetting, - type: OptionType.COMPONENT, - default: "", - }, -}); diff --git a/src/equicordplugins/downloadify.desktop/style.css b/src/equicordplugins/downloadify.desktop/style.css deleted file mode 100644 index 2b7f3df5e..000000000 --- a/src/equicordplugins/downloadify.desktop/style.css +++ /dev/null @@ -1,77 +0,0 @@ -.downloadify-voice-message-container { - flex-shrink: 0; - align-items: center; - display: flex; - flex-direction: column; - justify-content: flex-end; - position: relative; - margin: 0 -2px; -} - -.downloadify-voice-message-button { - cursor: pointer; - line-height: 0; - border: none; - width: auto; - background: transparent; - color: var(--interactive-normal); - margin: 0; - padding: 0; - align-items: center; - border-radius: 8px; - box-sizing: border-box; - display: flex; - font-size: 14px; - font-weight: var(--font-weight-medium); - justify-content: center; - position: relative; - transition-duration: .2s; - user-select: none; -} - -.downloadify-voice-message-button:hover { - color: var(--interactive-hover); -} - -.downloadify-directory-container { - margin-top: 10px; -} - -.downloadify-directory-buttons { - margin-top: 10px; - display: flex; - gap: 10px; -} - -.downloadify-directory-button { - border-radius: 4px; - min-height: 34px; - color: white; - height: 34px; - flex: 1; -} - -.downloadify-directory-button:not(.downloadify-disabled):hover { - filter: brightness(0.75); - transition: filter 0.15s ease-in-out; -} - -.downloadify-clear-button { - background-color: rgb(180 71 78); -} - -.downloadify-browse-button { - background-color: rgb(87 100 240); -} - -.downloadify-directory-display { - background-color: var(--background-base-lower); - overflow-wrap: break-word; - font-family: monospace; - word-wrap: break-word; - white-space: pre-wrap; - border-radius: 4px; - user-select: text; - font-size: 14px; - padding: 8px; -} \ No newline at end of file diff --git a/src/equicordplugins/downloadify.desktop/utils/definitions.tsx b/src/equicordplugins/downloadify.desktop/utils/definitions.tsx deleted file mode 100644 index 8f65a964a..000000000 --- a/src/equicordplugins/downloadify.desktop/utils/definitions.tsx +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2025 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { classNameFactory } from "@api/Styles"; -import { Logger } from "@utils/Logger"; -import { PluginNative } from "@utils/types"; -import { Channel, Guild, GuildMember, Message, User } from "@vencord/discord-types"; -import { findByPropsLazy } from "@webpack"; -import { JSX } from "react"; - -export const d = classNameFactory("downloadify-"); -export const CDN_BASE = "https://cdn.discordapp.com"; -export const MEDIA_PROXY_BASE = "https://media.discordapp.net"; -export const PRIMARY_DOMAIN_BASE = "https://discord.com"; -export const IMAGE_EXT_1_DOMAIN_BASE = "https://images-ext-1.discordapp.net"; -export const IMAGE_EXT_2_DOMAIN_BASE = "https://images-ext-2.discordapp.net"; -export const TENOR_BASE = "https://c.tenor.com"; -export const TENOR_GIF_ID = "Ad"; -export const TENOR_MP4_ID = "Po"; -export const ASSET_TYPE_EXTRACTOR = new RegExp(/^https:\/\/(?:cdn\.discordapp\.com|media.discordapp.net)\/([a-z-]+(?:\/[a-z-]+){0,2})(?:(?:\/\d+\/[a-z-]+\/\d+\/([a-z-]+))|(?:\/([a-z-]+(?:\/[a-z-_]+)?)))?/i); -export const DownloadifyLogger = new Logger("Downloadify"); -export const DownloadifyNative = VencordNative.pluginHelpers.Downloadify as PluginNative; -export const defaultAssets = findByPropsLazy("DEFAULT_GROUP_DM_AVATARS") as DefaultAssets; - -interface DefaultAssets { - BOT_AVATARS: Record; - DEFAULT_AVATARS: string[]; - DEFAULT_CHANNEL_ICON: string; - DEFAULT_GROUP_DM_AVATARS: string[]; - DEFAULT_PROVISIONAL_AVATARS: string[]; -} - -export interface AssetInfo { - source: AssetSource; - static: string[]; - animated: string[]; - type?: string; - forceAnimated?: boolean; -} - -export enum AssetSource { - CDN = "cdn", - EXTERNAL_IMAGE_PROXY = "external_image_proxy", - ATTACHMENT_MEDIA_PROXY = "attachment_media_proxy", - ASSET_MEDIA_PROXY = "asset_media_proxy", - PRIMARY_DOMAIN = "primary_domain", - EXTERNAL = "external", - TENOR = "tenor" -} - -export const unknownExternal = { - source: AssetSource.EXTERNAL, - static: [], - animated: [] -}; - -export const unknownPrimaryDomain = { - source: AssetSource.PRIMARY_DOMAIN, - static: [], - animated: [] -}; - -export const unknownCDN = { - source: AssetSource.CDN, - static: [], - animated: [] -}; - -export const unknownAssetMediaProxy = { - source: AssetSource.ASSET_MEDIA_PROXY, - static: [], - animated: [] -}; - -export const unknownAttachmentMediaProxy = { - source: AssetSource.ATTACHMENT_MEDIA_PROXY, - static: [], - animated: [] -}; - -export const unknownExternalImageProxy = { - source: AssetSource.EXTERNAL_IMAGE_PROXY, - static: [], - animated: [] -}; - -export const defaultResourceAvailability = { - source: AssetSource.PRIMARY_DOMAIN, - static: ["png"], - animated: [] -}; - -export const APNGAvailability = { - source: AssetSource.ASSET_MEDIA_PROXY, - static: ["png", "webp", "jpg"], - animated: ["apng", "png", "webp", "jpg"] -}; - -export const hashableAvailability = { - source: AssetSource.ASSET_MEDIA_PROXY, - static: ["png", "webp", "jpg"], - animated: ["gif", "awebp", "png", "webp", "jpg"] -}; - -// Defines known asset type availability. Unknown types which may be -// encountered are AssetSource.EXTERNAL which are present in some embeds, -// as well as OTHER attachment types. -export const assetAvailability = { - "user-avatar": hashableAvailability, - "user-banner": hashableAvailability, - "avatar-decoration": APNGAvailability, - "clan-badge": { - source: AssetSource.ASSET_MEDIA_PROXY, - static: ["png"], - animated: [] - }, - "nameplate": { - source: AssetSource.CDN, - static: ["png"], - animated: ["webm", "apng", "png"], - type: "nameplate" - }, - "default-user-avatar": defaultResourceAvailability, - "default-group-icon": defaultResourceAvailability, - "role-icon-custom": { - source: AssetSource.ASSET_MEDIA_PROXY, - static: ["png", "webp", "jpg"], - animated: [], - }, - "role-icon-unicode-emoji": { - source: AssetSource.PRIMARY_DOMAIN, - static: ["svg"], - animated: [], - }, - "guild-icon": hashableAvailability, - "guild-banner": hashableAvailability, - "guild-invite-splash": hashableAvailability, - "guild-discovery-splash": hashableAvailability, - "custom-emoji": hashableAvailability, - "unicode-emoji": { - source: AssetSource.PRIMARY_DOMAIN, - static: ["svg"], - animated: [] - }, - "sticker": { - "APNG": APNGAvailability, - "PNG": { - source: AssetSource.ASSET_MEDIA_PROXY, - static: ["png", "webp", "jpg"], - animated: [] - }, - "GIF": { - source: AssetSource.ASSET_MEDIA_PROXY, - static: [], - animated: ["gif", "awebp", "png", "webp", "jpg"], - forceAnimated: true - }, - "LOTTIE": { - source: AssetSource.CDN, - static: ["json"], - animated: ["json"] - }, - }, - "tenor": { - source: AssetSource.TENOR, - static: [], - animated: ["gif", "mp4"], - forceAnimated: true - }, - "external": { - "image/png": { - source: AssetSource.EXTERNAL_IMAGE_PROXY, - static: ["png", "webp", "jpg"], - animated: [] - }, - "image/webp": { - source: AssetSource.EXTERNAL_IMAGE_PROXY, - static: ["png", "webp", "jpg"], - animated: [] - }, - "image/jpeg": { - source: AssetSource.EXTERNAL_IMAGE_PROXY, - static: ["png", "webp", "jpg"], - animated: [] - }, - "video/mp4": { - source: AssetSource.EXTERNAL, - static: [], - animated: ["mp4"], - forceAnimated: true - }, - }, - "attachment": { - // Image attachments over a certain size and/or resolution are treated as - // non-image files and are therefore not available in alternative formats. - // This is designated by an attachment type of OTHER instead of IMAGE. - "image/png": { - source: AssetSource.ATTACHMENT_MEDIA_PROXY, - static: ["png", "webp", "jpg"], - // APNG attachments are technically supported, but appear to get - // broken by Discord squashing them into static PNGs. APNG is kept - // here in case it is fixed some day. - animated: ["apng", "png", "webp", "jpg"] - }, - "image/webp": { - source: AssetSource.ATTACHMENT_MEDIA_PROXY, - static: ["webp", "png", "jpg"], - animated: ["awebp", "webp", "png", "jpg"] - }, - "image/avif": { - source: AssetSource.ATTACHMENT_MEDIA_PROXY, - static: ["avif", "png", "webp"], - animated: ["avif", "awebp", "png", "webp"] - }, - "image/jpeg": { - source: AssetSource.ATTACHMENT_MEDIA_PROXY, - static: ["jpg", "png", "webp"], - animated: [] - }, - "image/gif": { - source: AssetSource.ATTACHMENT_MEDIA_PROXY, - static: [], - animated: ["gif", "awebp", "png", "webp", "jpg"], - forceAnimated: true - }, - } -}; - -export function DownloadIcon({ width, height }: { width: number; height: number; }): JSX.Element { - return ( - - - - ); -} - -export interface ParsedFile { - path: string; - baseName: string; - extension: string | null; - twitterExtra: string | null; -} - -export interface ParsedURL { - url: string; - host: string; - path: string; - baseName: string; - extension: string | null; - params: { expiry: Record, other: Record; }; - twitterExtra: string | null; -} - -export interface InsertGroup { - id: { group?: string, child?: string; }; - type: "WITH_GROUP" | "BEFORE_GROUP" | "AFTER_GROUP"; - position?: "START" | "END"; -} - -export interface VoiceMessageDownloadButtonProps { - message: Message; - item: { - type: "AUDIO"; - originalItem: { - filename: string; - proxy_url: string; - }; - }; -} - -export interface HoverDownloadProps { - item: { - type: "AUDIO" | "IMAGE" | "VIDEO" | "OTHER"; - downloadUrl: string; - contentType: string; - srcIsAnimated: boolean; - originalItem: { - title?: string; - filename: string; - proxy_url: string; - url: string; - }; - }; -} - -export interface ExpandedModalDownloadProps { - item: { - type: "AUDIO" | "IMAGE" | "VIDEO" | "OTHER"; - url: string; - original: string; - proxyUrl?: string; - contentType?: string; - srcIsAnimated: boolean; - sourceMetadata?: { - identifier: { - type: "attachment" | "embed"; - title?: string; - }, - }; - }; -} - -export interface MessageContextMenuProps { - message: Message; - channel: Channel; - itemSrc: string; - itemSafeSrc?: string; - favoriteableType: "sticker" | "emoji" | null; - favoriteableName: string | null; - favoriteableId: string | null; - contextMenuAPIArguments: any[]; - mediaItem?: { - contentType: string; - proxyUrl: string; - url: string; - }; -} - -export interface GDMContextMenuProps { - channel: Channel & { - icon: string | null; - }; -} - -export interface GuildContextMenuProps { - guild: Guild; -} - -export interface UserContextMenuProps { - user: User; - guildId?: string; -} - -export interface CustomizedUser extends User { - avatarDecorationData: null | { - asset: string; - }; - collectibles: null | Record<"nameplate", { - asset: string; - label: string; - palette: string; - }>; -} - -export interface CustomizedMember extends Omit { - avatarDecoration: null | { - asset: string; - }; -} - -export const AttachmentFlags = { - IS_CLIP: 1 << 0, - IS_THUMBNAIL: 1 << 1, - IS_REMIX: 1 << 2, - IS_SPOILER: 1 << 3, - CONTAINS_EXPLICIT_MEDIA: 1 << 4, - IS_ANIMATED: 1 << 5, - CONTAINS_GORE_CONTENT: 1 << 6 -}; diff --git a/src/equicordplugins/downloadify.desktop/utils/handlers.tsx b/src/equicordplugins/downloadify.desktop/utils/handlers.tsx deleted file mode 100644 index 43f336578..000000000 --- a/src/equicordplugins/downloadify.desktop/utils/handlers.tsx +++ /dev/null @@ -1,999 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2025 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { ImageIcon } from "@components/Icons"; -import { StickerFormatType } from "@vencord/discord-types/enums"; -import { GuildMemberStore, GuildRoleStore, GuildStore, Menu, showToast, Toasts, UserProfileStore, UserStore } from "@webpack/common"; -import { JSX } from "react"; - -import { settings } from "../settings"; -import { ASSET_TYPE_EXTRACTOR, assetAvailability, AssetInfo, AssetSource, AttachmentFlags, CDN_BASE, CustomizedMember, CustomizedUser, d, defaultAssets, DownloadIcon, DownloadifyLogger, DownloadifyNative, ExpandedModalDownloadProps, GDMContextMenuProps, GuildContextMenuProps, HoverDownloadProps, IMAGE_EXT_1_DOMAIN_BASE, IMAGE_EXT_2_DOMAIN_BASE, MEDIA_PROXY_BASE, MessageContextMenuProps, ParsedURL, PRIMARY_DOMAIN_BASE, TENOR_BASE, TENOR_GIF_ID, TENOR_MP4_ID, unknownAttachmentMediaProxy, unknownCDN, unknownExternal, unknownExternalImageProxy, unknownPrimaryDomain, UserContextMenuProps, VoiceMessageDownloadButtonProps } from "./definitions"; -import { getFormattedNow, joinOrCreateContextMenuGroup, parseFile, parseURL, sanitizeFilename } from "./misc"; - -export function MessageContextMenu(children: Array, props: MessageContextMenuProps): void { - if (!children?.length || !props?.message?.id) { - return; - } - - const { allowUnicode } = settings.store; - let { favoriteableId, favoriteableName, favoriteableType } = props; - const { itemSrc, itemSafeSrc, message, channel, mediaItem, contextMenuAPIArguments } = props; - const target = contextMenuAPIArguments?.[0]?.target; - let targetURL = itemSafeSrc ?? itemSrc; - - const emojiTarget = target.dataset?.type === "emoji" - ? target - : target.children?.[1]?.firstChild?.dataset?.type === "emoji" - ? target.children[1].firstChild - : target.parentElement?.children?.[1]?.firstChild?.dataset?.type === "emoji" - ? target.parentElement.children[1].firstChild - : null; - - if (!!emojiTarget?.dataset?.id) { - favoriteableType = "emoji"; - favoriteableId = emojiTarget.dataset.id; - targetURL = emojiTarget.src; - } else if (!!emojiTarget?.dataset?.name) { - favoriteableType = "emoji"; - favoriteableName = emojiTarget.alt ?? emojiTarget.dataset.name; - favoriteableId = null; - targetURL = emojiTarget.src; - } - - const roleIconTarget = target.classList && (Array.from(target.classList) as string[]).some(cls => cls.includes("roleIcon")) - ? target - : target.firstChild?.classList && (Array.from(target.firstChild.classList) as string[]).some(cls => cls.includes("roleIcon")) - ? target.firstChild - : null; - - const clanBadgeTarget = target.classList && (Array.from(target.classList) as string[]).some(cls => cls.includes("badge")) - ? target - : target.parentElement?.firstChild?.classList && (Array.from(target.parentElement.firstChild.classList) as string[]).some(cls => cls.includes("badge")) - ? target.parentElement.firstChild - : target.firstChild?.firstChild?.classList && (Array.from(target.firstChild.firstChild.classList) as string[]).some(cls => cls.includes("badge")) - ? target.firstChild.firstChild - : null; - - if (target.tagName === "VIDEO" && !!target.src) { - targetURL = contextMenuAPIArguments[0].target.src; - } else if (target.parentElement?.parentElement?.firstChild?.tagName === "VIDEO" && !!target.parentElement?.parentElement?.firstChild?.src) { - targetURL = contextMenuAPIArguments[0].target.parentElement.parentElement.firstChild.src; - } else if (target.parentElement?.parentElement?.parentElement?.firstChild?.tagName === "VIDEO" && !!target.parentElement?.parentElement?.parentElement?.firstChild?.src) { - targetURL = contextMenuAPIArguments[0].target.parentElement.parentElement.parentElement.firstChild.src; - } else if (target.parentElement?.parentElement?.parentElement?.parentElement?.parentElement?.firstChild?.tagName === "VIDEO" && !!target.parentElement?.parentElement?.parentElement?.parentElement?.parentElement?.firstChild?.src) { - targetURL = contextMenuAPIArguments[0].target.parentElement.parentElement.parentElement.parentElement.parentElement.firstChild.src; - } - - const isRoleIcon = !!roleIconTarget; - const isCustomEmoji = favoriteableType === "emoji" && !!favoriteableId; - const isUnicodeEmoji = favoriteableType === "emoji" && !favoriteableId; - const isSticker = favoriteableType === "sticker"; - const isAttachment = !!mediaItem; - const emojiRegex = favoriteableId ? new RegExp(`<(a)?:(\\w+):(${favoriteableId})>`) : null; - let emojiName = isUnicodeEmoji ? (favoriteableName?.replaceAll(":", "") || null) : (favoriteableName ?? (emojiRegex?.exec(message.content || "")?.[2] || null)); - let emojiAnimated = isCustomEmoji ? emojiRegex?.exec(message.content || "")?.[1] === "a" : false; - const emojiURL = (isCustomEmoji || isUnicodeEmoji) ? parseURL(targetURL.replace(CDN_BASE, MEDIA_PROXY_BASE)) : null; - const sticker = isSticker ? message.stickerItems?.find(sticker => sticker.id === favoriteableId) : null; - const downloadifyItems: any[] = []; - - function findNestedComponent(components: any[], checkFor: string): any { - for (const innerComponent of components) { - if (innerComponent.emoji?.id === checkFor || innerComponent.emoji?.name === checkFor) { - return innerComponent; - } - - if (innerComponent.components?.length) { - const found = findNestedComponent(innerComponent.components, checkFor); - if (found) { - return found; - } - } - } - return null; - } - - if (isRoleIcon) { - const parsedURL = parseURL(roleIconTarget.src.replace(CDN_BASE, MEDIA_PROXY_BASE)); - const isUnicodeRoleIcon = itemSrc.startsWith(PRIMARY_DOMAIN_BASE); - const isCustomRoleIcon = !isUnicodeRoleIcon; - const roleID = GuildMemberStore.getMember(channel.guild_id, message.author.id)?.iconRoleId; - const role = roleID ? GuildRoleStore.getRole(channel.guild_id, roleID) : null; - const sanitizedRoleName = role ? sanitizeFilename(role.name, allowUnicode) : null; - - downloadifyItems.push( - ImageIcon({ width: 20, height: 20 })} - action={async () => { - await handleDownload( - parsedURL, - sanitizedRoleName ? `${sanitizedRoleName}-icon` : null, - isCustomRoleIcon ? assetAvailability["role-icon-custom"] : assetAvailability["role-icon-unicode-emoji"], - false - ); - }} - /> - ); - } else if (clanBadgeTarget && !!(message.author as any).primaryGuild) { - const { primaryGuild } = (message.author as any); - const sanitizedGuildTag = sanitizeFilename(primaryGuild.tag, allowUnicode); - - downloadifyItems.push( - ImageIcon({ width: 20, height: 20 })} - action={async () => { - await handleDownload( - parseURL(clanBadgeTarget.src.replace(CDN_BASE, MEDIA_PROXY_BASE)), - sanitizedGuildTag ? `${sanitizedGuildTag}-clan-badge` : null, - assetAvailability["clan-badge"], - false - ); - }} - /> - ); - } else if (isCustomEmoji) { - const reaction = message.reactions?.find(reaction => reaction.emoji.id === favoriteableId)?.emoji; - const component = message.components?.length ? findNestedComponent(message.components, favoriteableId as string) : null; - emojiAnimated = component?.emoji?.animated || reaction?.animated || emojiAnimated; - emojiName = component?.emoji?.name || reaction?.name || emojiName; - - downloadifyItems.push( - ImageIcon({ width: 20, height: 20 })} - action={async () => { - await handleDownload( - emojiURL as ParsedURL, - emojiName, - assetAvailability["custom-emoji"], - emojiAnimated - ); - }} - /> - ); - } else if (isUnicodeEmoji) { - const reaction = message.reactions?.find(reaction => reaction.emoji.name === favoriteableName)?.emoji; - const component = message.components?.length ? findNestedComponent(message.components, favoriteableName as string) : null; - emojiName = component?.emoji?.name || reaction?.name || emojiName; - emojiAnimated = false; - - downloadifyItems.push( - ImageIcon({ width: 20, height: 20 })} - action={async () => { - await handleDownload( - emojiURL as ParsedURL, - emojiName, - assetAvailability["unicode-emoji"], - emojiAnimated - ); - }} - /> - ); - } else if (isSticker) { - let stickerURL; - let stickerSource; - let stickerAnimated; - - if (sticker?.format_type === StickerFormatType.PNG) { - stickerURL = parseURL(`${MEDIA_PROXY_BASE}/stickers/${sticker.id}.png`); - stickerSource = assetAvailability.sticker.PNG; - stickerAnimated = false; - } else if (sticker?.format_type === StickerFormatType.GIF) { - stickerURL = parseURL(`${MEDIA_PROXY_BASE}/stickers/${sticker.id}.gif`); - stickerSource = assetAvailability.sticker.GIF; - stickerAnimated = true; - } else if (sticker?.format_type === StickerFormatType.APNG) { - stickerURL = parseURL(`${MEDIA_PROXY_BASE}/stickers/${sticker.id}.png`); - stickerSource = assetAvailability.sticker.APNG; - stickerAnimated = true; - } else if (sticker?.format_type === StickerFormatType.LOTTIE) { - stickerURL = parseURL(`${CDN_BASE}/stickers/${sticker.id}.json`); - stickerSource = assetAvailability.sticker.LOTTIE; - stickerAnimated = true; - } else { - return; - } - - downloadifyItems.push( - ImageIcon({ width: 20, height: 20 })} - action={async () => { - await handleDownload( - stickerURL, - sticker.name, - stickerSource, - stickerAnimated - ); - }} - /> - ); - } else if (isAttachment) { - const assetInfo = assetAvailability.attachment[mediaItem.contentType] ?? unknownCDN; - const attachment = message.attachments?.find(attachment => attachment.proxy_url === mediaItem.proxyUrl || attachment.url === mediaItem.url); - - if (!attachment) { - return; - } - - downloadifyItems.push( - ImageIcon({ width: 20, height: 20 })} - action={async () => { - await handleDownload( - parseURL(assetInfo.source === AssetSource.CDN ? mediaItem.url : mediaItem.proxyUrl), - (attachment as any)?.title || null, - assetInfo, - !!((attachment as any).flags & AttachmentFlags.IS_ANIMATED) - ); - }} - /> - ); - } else if (targetURL) { - let isTenor = false; - - const embedImage = message.embeds?.find(embed => { - const images = (embed as any).images ? (embed as any).images : embed.image ? [embed.image] : []; - return images.some(image => targetURL.startsWith(image.url) || targetURL.startsWith(image.proxyURL)); - }); - - const embedVideo = embedImage ? undefined : message.embeds?.find(embed => { - isTenor = embed.provider?.name === "Tenor"; - const videos = (embed as any).videos ? (embed as any).videos : embed.video ? [embed.video] : []; - return videos.some(video => targetURL.startsWith(video.url) || targetURL.startsWith(video.proxyURL)); - }); - - const embedThumbnail = message.embeds?.find(embed => { - return (embed.thumbnail?.url && targetURL.startsWith(embed.thumbnail.url)) || (embed.thumbnail?.proxyURL && targetURL.startsWith(embed.thumbnail.proxyURL)); - }); - - const embedAuthor = message.embeds?.find(embed => { - return (embed.author?.iconURL && targetURL.startsWith(embed.author.iconURL)) || (embed.author?.iconProxyURL && targetURL.startsWith(embed.author.iconProxyURL)); - }); - - const embedFooter = (message.embeds?.find(embed => { - return ((embed as any).footer?.iconURL && targetURL.startsWith((embed as any).footer.iconURL)) || ((embed as any).footer?.iconProxyURL && targetURL.startsWith((embed as any).footer.iconProxyURL)); - }) as any); - - const targetEmbed = (embedImage || embedVideo || embedThumbnail || embedAuthor || embedFooter); - const targetEmbedItem = (embedImage?.image || embedVideo?.video || embedThumbnail?.thumbnail || embedAuthor?.author || embedFooter?.footer); - const labelEmbedMedia = embedFooter?.footer - ? "Footer Icon" - : embedAuthor?.author - ? "Author Icon" - : embedThumbnail?.thumbnail - ? "Thumbnail Image" - : targetEmbed?.type === "rich" || !!targetEmbed?.provider - ? "Embed Media" - : "Media"; - - downloadifyItems.push( - ImageIcon({ width: 20, height: 20 })} - action={async () => { - let srcIsAnimated = !!(targetEmbedItem as any)?.srcIsAnimated; - let aliasBasename: string | null = null; - let assetInfo: any; - - const contentType = ((targetEmbedItem as any)?.contentType || await DownloadifyNative.queryURL(itemSrc)); - const isMediaProxy = targetURL?.startsWith(MEDIA_PROXY_BASE); - const isPrimaryDomain = targetURL?.startsWith(PRIMARY_DOMAIN_BASE); - const isCDN = targetURL?.startsWith(CDN_BASE); - const isImageExt1 = targetURL?.startsWith(IMAGE_EXT_1_DOMAIN_BASE); - const isImageExt2 = targetURL?.startsWith(IMAGE_EXT_2_DOMAIN_BASE); - const isImageExt = isImageExt1 || isImageExt2; - - if (isTenor && embedVideo) { - targetURL = embedVideo.url ?? targetURL; - } - - const parsedURL = parseURL(targetURL); - - if (isTenor) { - assetInfo = assetAvailability.tenor; - srcIsAnimated = true; - } else if (isImageExt) { - assetInfo = assetAvailability.attachment[contentType] - ?? assetAvailability.external[contentType] - ?? unknownExternalImageProxy; - } else if (isMediaProxy || isCDN) { - const guestimate = guesstimateAsset(parsedURL, contentType); - assetInfo = guestimate.assetInfo ?? (isCDN ? unknownCDN : (isImageExt ? unknownExternalImageProxy : unknownExternal)); - aliasBasename = guestimate.aliasBasename ?? aliasBasename; - srcIsAnimated = guestimate.srcIsAnimated ?? srcIsAnimated; - } else { - assetInfo = isPrimaryDomain ? unknownPrimaryDomain : unknownExternal; - } - - await handleDownload( - parsedURL, - aliasBasename, - assetInfo, - srcIsAnimated - ); - }} - /> - ); - } - - if (!downloadifyItems.length) { - return; - } - - joinOrCreateContextMenuGroup( - children, - downloadifyItems, - "message-content-group", - "downloadify-submenu", - "Download", - [{ - id: { child: "devmode-copy-id" }, - type: "WITH_GROUP", - position: "START" - }] - ); -} - -export function GDMContextMenu(children: Array, props: GDMContextMenuProps): void { - if (!children?.length || !props?.channel?.id) { - return; - } - - const { channel } = props; - const channelIconHash = channel.icon || null; - const channelIconURL = channelIconHash ? parseURL(`${MEDIA_PROXY_BASE}/channel-icons/${channel.id}/${channelIconHash}.png`) : null; - const channelTimestamp = Number(BigInt(channel.id) >> 22n); - const defaultChannelIconURL = channelTimestamp ? parseURL(`${PRIMARY_DOMAIN_BASE}${defaultAssets.DEFAULT_GROUP_DM_AVATARS[channelTimestamp % 8]}`) : null; - - const downloadifyItems = [ - channelIconURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => { - await handleDownload( - channelIconURL, - channel.name ? `${channel.name}-group-icon` : null, - assetAvailability["guild-icon"], - false - ); - }} - /> - ) : null, - defaultChannelIconURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => { - await handleDownload( - defaultChannelIconURL, - channel.name ? `${channel.name}-default-group-icon` : null, - assetAvailability["default-group-icon"], - false - ); - }} - /> - ) : null - ].filter(Boolean); - - joinOrCreateContextMenuGroup( - children, - downloadifyItems, - "channel-content-group", - "downloadify-submenu", - "Download", - [{ - id: { child: "devmode-copy-id" }, - type: "WITH_GROUP", - position: "START" - }] - ); -} - -export function GuildContextMenu(children: Array, props: GuildContextMenuProps): void { - if (!children?.length || !props?.guild?.id) { - return; - } - - const { guild } = props; - const guildIconHash = guild.icon || null; - const guildBannerHash = guild.banner || null; - const guildInviteHash = guild.splash !== guildBannerHash ? guild.splash || null : null; - const guildDiscoveryHash = guild.discoverySplash !== guildInviteHash && guild.discoverySplash !== guildBannerHash ? guild.discoverySplash || null : null; - const guildIconHashAnimated = guildIconHash?.startsWith("a_"); - const guildBannerHashAnimated = guildBannerHash?.startsWith("a_"); - const guildIconURL = guildIconHash ? parseURL(`${MEDIA_PROXY_BASE}/icons/${guild.id}/${guildIconHash}.${guildIconHashAnimated ? "gif" : "png"}`) : null; - const guildBannerURL = guildBannerHash ? parseURL(`${MEDIA_PROXY_BASE}/banners/${guild.id}/${guildBannerHash}.${guildBannerHashAnimated ? "gif" : "png"}`) : null; - const guildInviteURL = guildInviteHash ? parseURL(`${MEDIA_PROXY_BASE}/splashes/${guild.id}/${guildInviteHash}.png`) : null; - const guildDiscoveryURL = guildDiscoveryHash ? parseURL(`${MEDIA_PROXY_BASE}/discovery-splashes/${guild.id}/${guildDiscoveryHash}.png`) : null; - - if (!guildIconURL && !guildBannerURL && !guildInviteURL && !guildDiscoveryURL) { - return; - } - - const downloadifyItems = [guildIconURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - guildIconURL, - guild.name ? `${guild.name}-icon` : null, - assetAvailability["guild-icon"], - !!guildIconHash?.startsWith("a_") - )} - /> - ) : null, - guildBannerURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - guildBannerURL, - guild.name ? `${guild.name}-banner` : null, - assetAvailability["guild-banner"], - !!guildBannerHash?.startsWith("a_") - )} - /> - ) : null, - guildInviteURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - guildInviteURL, - guild.name ? `${guild.name}-invite-splash` : null, - assetAvailability["guild-invite-splash"], - !!guildInviteHash?.startsWith("a_") - )} - /> - ) : null, - guildDiscoveryURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - guildDiscoveryURL, - guild.name ? `${guild.name}-discovery-splash` : null, - assetAvailability["guild-discovery-splash"], - !!guildDiscoveryHash?.startsWith("a_") - )} - /> - ) : null].filter(Boolean); - - joinOrCreateContextMenuGroup( - children, - downloadifyItems, - "guild-content-group", - "downloadify-submenu", - "Download", - [{ - id: { child: "devmode-copy-id" }, - type: "WITH_GROUP", - position: "START" - }, - { - id: { child: "privacy" }, - type: "WITH_GROUP", - position: "END" - }] - ); -} - -export function UserContextMenu(children: Array, props: UserContextMenuProps): void { - if (!children?.length || !props.user?.id) { - return; - } - - const user = UserStore.getUser(props.user.id) as CustomizedUser; - const member = (props.guildId ? GuildMemberStore.getMember(props.guildId, props.user.id) : null) as CustomizedMember | null; - const guild = props.guildId ? GuildStore.getGuild(props.guildId) : null; - const userAvatarHash = user.avatar || null; - const memberAvatarHash = member?.avatar || null; - const userBannerHash = UserProfileStore.getUserProfile(user.id)?.banner || null; - const memberBannerHash = UserProfileStore.getGuildMemberProfile(user.id, props.guildId)?.banner || null; - const userAvatarDecorationHash = user.avatarDecorationData?.asset || null; - const memberAvatarDecorationHash = member?.avatarDecoration?.asset || null; - const userNameplatePath = user.collectibles?.nameplate?.asset || null; - const userAvatarURL = userAvatarHash ? parseURL(`${MEDIA_PROXY_BASE}/avatars/${user.id}/${userAvatarHash}.png`) : null; - const memberAvatarURL = memberAvatarHash ? parseURL(`${MEDIA_PROXY_BASE}/guilds/${props.guildId}/users/${user.id}/avatars/${memberAvatarHash}.png`) : null; - const userBannerURL = userBannerHash ? parseURL(`${MEDIA_PROXY_BASE}/banners/${user.id}/${userBannerHash}.png`) : null; - const memberBannerURL = memberBannerHash ? parseURL(`${MEDIA_PROXY_BASE}/guilds/${props.guildId}/users/${user.id}/banners/${memberBannerHash}.png`) : null; - const userAvatarDecorationURL = userAvatarDecorationHash ? parseURL(`${MEDIA_PROXY_BASE}/avatar-decoration-presets/${userAvatarDecorationHash}.png`) : null; - const memberAvatarDecorationURL = memberAvatarDecorationHash ? parseURL(`${MEDIA_PROXY_BASE}/avatar-decoration-presets/${memberAvatarDecorationHash}.png`) : null; - const userNameplateURL = userNameplatePath ? parseURL(`${CDN_BASE}/assets/collectibles/${userNameplatePath}static.png`) : null; - const defaultUserAvatarURL = parseURL(`${PRIMARY_DOMAIN_BASE}${defaultAssets.DEFAULT_AVATARS[user.discriminator === "0" ? (Math.floor(Number(BigInt(user.id) >> 22n)) % 6) : (Number(user.discriminator) % 5)]}`); - - const downloadifyItems = [userAvatarURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - userAvatarURL, - `${user.username}-avatar`, - assetAvailability["user-avatar"], - !!userAvatarHash?.startsWith("a_") - )} - /> - ) : null, - userBannerURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - userBannerURL, - `${user.username}-banner`, - assetAvailability["user-banner"], - !!userBannerHash?.startsWith("a_") - )} - /> - ) : null, - userAvatarDecorationURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - userAvatarDecorationURL, - `${user.username}-avatar-decoration`, - assetAvailability["avatar-decoration"], - true - )} - /> - ) : null, - memberAvatarURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - memberAvatarURL, - guild ? `${guild.name}-${user.username}-avatar` : `${user.username}-avatar`, - assetAvailability["user-avatar"], - !!memberAvatarHash?.startsWith("a_") - )} - /> - ) : null, - memberBannerURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - memberBannerURL, - guild ? `${guild.name}-${user.username}-banner` : `${user.username}-banner`, - assetAvailability["user-banner"], - !!memberBannerHash?.startsWith("a_") - )} - /> - ) : null, - memberAvatarDecorationURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - memberAvatarDecorationURL, - guild ? `${guild.name}-${user.username}-avatar-decoration` : `${user.username}-avatar-decoration`, - assetAvailability["avatar-decoration"], - true - )} - /> - ) : null, - defaultUserAvatarURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - defaultUserAvatarURL, - `${user.username}-default-avatar`, - assetAvailability["default-user-avatar"], - false - )} - /> - ) : null, - userNameplateURL ? ( - ImageIcon({ width: 20, height: 20 })} - action={async () => await handleDownload( - userNameplateURL, - `${user.username}-nameplate`, - assetAvailability.nameplate, - true - )} - /> - ) : null].filter(Boolean); - - joinOrCreateContextMenuGroup( - children, - downloadifyItems, - "user-content-group", - "downloadify-submenu", - "Download", - [{ - id: { child: "devmode-copy-id" }, - type: "WITH_GROUP", - position: "START" - }, - { - id: { child: "user-profile" }, - type: "WITH_GROUP", - position: "END" - }] - ); -} - -export async function handleHoverDownloadButtonClicked(props: HoverDownloadProps): Promise { - let { contentType } = props.item; - const { type, downloadUrl, srcIsAnimated, originalItem } = props.item; - const { displayStatus, statusDuration } = settings.store; - - if (!originalItem) { - // Fallback to default behavior. - DownloadifyLogger.info(`[${getFormattedNow()}] [UNRECOGNIZED HOVER DOWNLOAD ITEM]`, props.item); - displayStatus && showToast("Unknown Media Item. Falling Back to Default Behavior", Toasts.Type.MESSAGE, { duration: statusDuration * 1250 }); - window.open(downloadUrl, "_blank"); - } else { - contentType ??= (await DownloadifyNative.queryURL(downloadUrl) || ""); - const assetInfo = (type === "IMAGE" ? (assetAvailability.attachment[contentType]) : null) ?? unknownCDN; - - await handleDownload( - parseURL(assetInfo.source === AssetSource.CDN ? downloadUrl : originalItem.proxy_url), - originalItem.title || null, - assetInfo, - !!srcIsAnimated - ); - } -} - -export async function handleExpandedModalDownloadButtonClicked(props: ExpandedModalDownloadProps, fallback: () => Promise): Promise { - let { contentType, srcIsAnimated } = props.item; - const { url, original, proxyUrl, sourceMetadata } = props.item; - const { displayStatus, statusDuration } = settings.store; - - if (!url && !original) { - // Fallback to default behavior. - DownloadifyLogger.info(`[${getFormattedNow()}] [UNRECOGNIZED EXPANDED MODAL DOWNLOAD ITEM]`, props.item); - displayStatus && showToast("Unknown Media Item. Falling Back to Default Behavior", Toasts.Type.MESSAGE, { duration: statusDuration * 1250 }); - await fallback(); - } else { - contentType ??= (await DownloadifyNative.queryURL(url) || ""); - let aliasBasename = sourceMetadata?.identifier.title ?? null; - const isAttachment = sourceMetadata?.identifier.type === "attachment"; - const urlIsMediaProxy = url?.startsWith(MEDIA_PROXY_BASE); - const originalIsMediaProxy = original?.startsWith(MEDIA_PROXY_BASE); - const isMediaProxy = urlIsMediaProxy || originalIsMediaProxy; - const isPrimaryDomain = original?.startsWith(PRIMARY_DOMAIN_BASE); - const isCDN = original?.startsWith(CDN_BASE); - const isImageExt1 = url?.startsWith(IMAGE_EXT_1_DOMAIN_BASE); - const isImageExt2 = url?.startsWith(IMAGE_EXT_2_DOMAIN_BASE); - const isImageExt = isImageExt1 || isImageExt2; - let parsedURL: ParsedURL; - let assetInfo: any; - - if (isAttachment) { - parsedURL = parseURL(proxyUrl || url); - assetInfo = assetAvailability.attachment[contentType] ?? unknownAttachmentMediaProxy; - } else if (isImageExt) { - parsedURL = parseURL(url); - assetInfo = assetAvailability.attachment[contentType] ?? assetAvailability.external[contentType] ?? unknownExternalImageProxy; - } else if (isMediaProxy || isCDN) { - parsedURL = parseURL(urlIsMediaProxy ? url : original); - const guestimate = guesstimateAsset(parsedURL, contentType); - assetInfo = guestimate.assetInfo ?? (isCDN ? unknownCDN : unknownExternal); - aliasBasename = guestimate.aliasBasename ?? aliasBasename; - srcIsAnimated = guestimate.srcIsAnimated ?? srcIsAnimated; - } else { - parsedURL = parseURL(original ?? url); - assetInfo = isPrimaryDomain ? unknownPrimaryDomain : unknownExternal; - } - - await handleDownload( - parsedURL, - aliasBasename, - assetInfo, - !!srcIsAnimated, - ); - } -} - -function guesstimateAsset(parsedURL: ParsedURL, contentType: string): { - assetInfo?: AssetInfo; - aliasBasename?: string; - srcIsAnimated?: boolean; -} { - // Check if the URL is a known asset type. The only defined member - // of assetAvailability that we can't completely check for here are stickers - // due to their format information not being present in the URL. We can make - // accurate guesses based on certain extensions, but not for every format. - let assetInfo: AssetInfo | undefined; - let aliasBasename: string | undefined; - let srcIsAnimated: boolean | undefined; - const detected = parsedURL.url.match(ASSET_TYPE_EXTRACTOR); - const detectedPrimary = detected?.[1] ?? ""; - const detectedSecondary = detected?.[2] ?? ""; - const detectedTertiary = detected?.[3] ?? ""; - - if (detectedPrimary === "attachments") { - assetInfo = assetAvailability.attachment[contentType]; - } else if (detectedPrimary === "emojis") { - assetInfo = assetAvailability["custom-emoji"]; - } else if (detectedPrimary === "clan-badges") { - assetInfo = assetAvailability["clan-badge"]; - } else if (detectedPrimary === "role-icons") { - assetInfo = assetAvailability["role-icon-custom"]; - } else if (detectedPrimary === "discovery-splashes") { - assetInfo = assetAvailability["guild-discovery-splash"]; - } else if (detectedPrimary === "splashes") { - assetInfo = assetAvailability["guild-invite-splash"]; - } else if (detectedPrimary === "banners") { - assetInfo = assetAvailability["guild-banner"]; - } else if (detectedPrimary === "icons") { - assetInfo = assetAvailability["guild-icon"]; - } else if (detectedPrimary === "avatar-decoration-presets") { - assetInfo = assetAvailability["avatar-decoration"]; - srcIsAnimated = true; // Avatar decorations always have animated variants. - } else if (detectedPrimary === "avatars" || (detectedPrimary === "guilds" && detectedSecondary === "avatars")) { - assetInfo = assetAvailability["user-avatar"]; - } else if (detectedPrimary === "banners" || (detectedPrimary === "guilds" && detectedSecondary === "banners")) { - assetInfo = assetAvailability["user-banner"]; - } else if (detectedPrimary === "assets/collectibles/nameplates") { - assetInfo = assetAvailability.nameplate; - aliasBasename = detectedTertiary ?? "nameplate"; - srcIsAnimated = true; // Nameplates always have animated variants. - } else if (detectedPrimary === "stickers") { - if (parsedURL.extension === "gif") { - assetInfo = assetAvailability.sticker.GIF; - } else if (parsedURL.extension === "webp") { - if (srcIsAnimated) { - assetInfo = assetAvailability.sticker.GIF; - } else { - // There is no way to know if a static WEBP sticker - // link is sourced from an APNG, PNG, or GIF sticker. - } - } else if (parsedURL.extension === "png") { - if (srcIsAnimated) { - assetInfo = assetAvailability.sticker.PNG; - } else { - // There is no way to know if a static PNG sticker - // link is sourced from an APNG, PNG, or GIF sticker. - } - } else if (parsedURL.extension === "jpg") { - // There is no way to know if a JPG sticker link - // is sourced from an APNG, PNG, or GIF sticker. - } - } - - return { - assetInfo, - aliasBasename, - srcIsAnimated - }; -} - -export function VoiceMessageDownloadButton(props: VoiceMessageDownloadButtonProps): JSX.Element { - async function voiceMessageDownloadClicked(event: React.MouseEvent) { - event.preventDefault(); - const { item, message } = props; - const { proxy_url } = item.originalItem; - const baseName = `voice-message-${message.id}`; - const parsedURL = parseURL(proxy_url); - - await handleDownload( - parsedURL, - baseName, - { source: AssetSource.ATTACHMENT_MEDIA_PROXY, static: [], animated: [] }, - false, - ); - } - - const { voiceMessages } = settings.use(["voiceMessages"]); - - return ( - <> - {voiceMessages && - - {DownloadIcon({ width: 20, height: 20 })} - - } - > - ); -} - -/** - * Common download handler for all other handlers. - */ -async function handleDownload( - url: ParsedURL, - alias: string | null, - asset: AssetInfo, - animated: boolean, -) { - const extensions = animated || !!asset.forceAnimated ? asset.animated : asset.static; - extensions.length < 1 && url.extension && extensions.push(url.extension); - const { defaultDirectory, overwriteFiles, displayStatus, allowUnicode, statusDuration } = settings.store; - const baseName = alias - ? sanitizeFilename(alias, allowUnicode, "discord-download") - : url.baseName - ? sanitizeFilename(url.baseName, allowUnicode, "discord-download") - : "discord-download"; - - let chosenExtension = extensions[0] ?? ""; - let resolvedExtension = (chosenExtension).replace("apng", "png").replace("awebp", "webp"); - let resolvedPath: string | null = null; - let resolvedURL = url.url; - - if (defaultDirectory) { - const resolvedDirectory = defaultDirectory.trim().replace(/^["']|["']$/g, "").replace(/[/\\]+$/, "").replace(/\\/g, "/"); - resolvedPath = `${resolvedDirectory}/${baseName}${resolvedExtension ? `.${resolvedExtension}` : ""}`; - displayStatus && showToast("Download Started", Toasts.Type.MESSAGE, { duration: statusDuration * 1000 }); - - if (!overwriteFiles) { - for (let num = 1; await DownloadifyNative.fileExists(resolvedPath); num++) { - resolvedPath = `${resolvedDirectory}/${baseName}-${num}${resolvedExtension ? `.${resolvedExtension}` : ""}`; - } - } - } else { - try { - resolvedPath = await DownloadifyNative.getFilePath(baseName, extensions); - - if (!resolvedPath) { - DownloadifyLogger.info(`[${getFormattedNow()}] [SAVE DIALOGUE CLOSED / DOWNLOAD CANCELED]`); - displayStatus && showToast("Download Canceled", Toasts.Type.MESSAGE, { duration: statusDuration * 1000 }); - return; - } - - const chosenFile = parseFile(resolvedPath); - chosenExtension = chosenFile.extension || ""; - resolvedExtension = (chosenExtension || "").replace("apng", "png").replace("awebp", "webp"); - - if (url.extension) { - const extReplacer = new RegExp(`\\.${chosenExtension}(?!.*\\.${chosenExtension})`, "i"); - resolvedPath = resolvedPath.replace(extReplacer, `.${resolvedExtension}`); - } - } catch (error) { - DownloadifyLogger.error(`[${getFormattedNow()}] [ERROR OPENING SAVE DIALOGUE]:`, error); - displayStatus && showToast("Error Opening Save Dialogue", Toasts.Type.FAILURE, { duration: statusDuration * 1000 }); - return; - } - } - - try { - let success = false; - - if ([AssetSource.ASSET_MEDIA_PROXY, AssetSource.ATTACHMENT_MEDIA_PROXY, AssetSource.EXTERNAL_IMAGE_PROXY].includes(asset.source)) { - let baseURL; - - if (asset.source === AssetSource.ASSET_MEDIA_PROXY) { - baseURL = url.host + url.path + url.baseName + (resolvedExtension ? `.${resolvedExtension}` : ""); - } else { - // Attachments on the media proxy use the `format` query parameter (seen below) to retrieve alternate - // formats instead of changing the file extension directly like assets on the media proxy do. - baseURL = url.host + url.path + url.baseName + - (url.extension ? `.${url.extension}` : "") + - (url.twitterExtra ? encodeURIComponent(url.twitterExtra) : ""); - } - - const authParams = url.params.expiry; - const newURL = new URL(baseURL); - - Object.entries(authParams).forEach(([key, value]) => { - newURL.searchParams.append(key, value); - }); - - if ([AssetSource.ATTACHMENT_MEDIA_PROXY, AssetSource.EXTERNAL_IMAGE_PROXY].includes(asset.source)) { - if (url.extension !== resolvedExtension) { - newURL.searchParams.append("format", resolvedExtension.replace("jpg", "jpeg")); - } - } else { - // Attachments on the media proxy do not support - // resizing but assets on the media proxy do. - newURL.searchParams.append("size", "4096"); - } - - if (chosenExtension === "awebp" && animated) { - newURL.searchParams.append("animated", "true"); - } else if (chosenExtension === "png" && animated) { - newURL.searchParams.append("passthrough", "false"); - } - - if (resolvedExtension === "webp") { - newURL.searchParams.append("lossless", "true"); - } - - resolvedURL = newURL.href; - } else if (asset.source === AssetSource.CDN) { - if (asset.type === "nameplate") { - resolvedURL = url.host + url.path + ( - chosenExtension === "apng" - ? "img.png" - : chosenExtension === "webm" - ? "asset.webm" - : "static.png" - ); - } else { - // Currently only the LOTTIE sticker. - resolvedURL = url.url; - } - } else if (asset.source === AssetSource.TENOR) { - const tenorID = url.path.replaceAll("/", "").slice(0, -2); - - if (chosenExtension === "gif") { - resolvedURL = `${TENOR_BASE}/${tenorID}${TENOR_GIF_ID}/tenor.gif`; - } else if (chosenExtension === "mp4") { - resolvedURL = `${TENOR_BASE}/${tenorID}${TENOR_MP4_ID}/tenor.mp4`; - } else { - DownloadifyLogger.warn(`[${getFormattedNow()}] [UNSUPPORTED TENOR EXTENSION]\n${chosenExtension}\n${resolvedPath}\n${resolvedURL}`); - displayStatus && showToast("Unsupported Tenor Extension", Toasts.Type.FAILURE, { duration: statusDuration * 1000 }); - return; - } - } else if ([AssetSource.EXTERNAL, AssetSource.PRIMARY_DOMAIN].includes(asset.source)) { - resolvedURL = url.url; - } - - DownloadifyLogger.info(`[${getFormattedNow()}] [STARTING DOWNLOAD]\n${resolvedPath}\n${resolvedURL}`); - success = await DownloadifyNative.downloadURL(resolvedURL, resolvedPath); - - if (success) { - DownloadifyLogger.info(`[${getFormattedNow()}] [DOWNLOAD SUCCESS]\n${resolvedPath}\n${resolvedURL}`); - displayStatus && showToast("File Downloaded", Toasts.Type.SUCCESS, { duration: statusDuration * 1000 }); - } else { - DownloadifyLogger.error(`[${getFormattedNow()}] [DOWNLOAD FAILED]\n${resolvedPath}\n${resolvedURL}`); - displayStatus && showToast("File Download Failed", Toasts.Type.FAILURE, { duration: statusDuration * 1000 }); - } - } catch (error) { - DownloadifyLogger.error(`[${getFormattedNow()}] [DOWNLOAD ERRORED]\n${resolvedPath}\n${resolvedURL}`, error); - displayStatus && showToast("Error Downloading File", Toasts.Type.FAILURE, { duration: statusDuration * 1000 }); - } -} diff --git a/src/equicordplugins/downloadify.desktop/utils/misc.tsx b/src/equicordplugins/downloadify.desktop/utils/misc.tsx deleted file mode 100644 index ab1119fb4..000000000 --- a/src/equicordplugins/downloadify.desktop/utils/misc.tsx +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2025 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { findGroupChildrenByChildId } from "@api/ContextMenu"; -import { Menu } from "@webpack/common"; - -import { InsertGroup, ParsedFile, ParsedURL } from "./definitions"; - -/** - * Retrieve information about a file path by parsing its parts. - */ -export function parseFile(filePath: string): ParsedFile { - const parts = filePath.split(/[\\/]/); - let fileNameWithExtra = parts.pop() as string; - - while (fileNameWithExtra.includes("%25")) { - fileNameWithExtra = decodeURIComponent(fileNameWithExtra); - } - - fileNameWithExtra = decodeURIComponent(fileNameWithExtra); - - const posColon = fileNameWithExtra.indexOf(":"); - const fileNameWithoutExtra = posColon === -1 ? fileNameWithExtra : fileNameWithExtra.slice(0, posColon); - const twitterExtra = posColon === -1 ? null : fileNameWithExtra.slice(posColon); - - const path = parts.join("/") + "/"; - const posDot = fileNameWithoutExtra.lastIndexOf("."); - - if (fileNameWithoutExtra === "" || posDot < 1) { - return { - path, - baseName: fileNameWithoutExtra, - extension: null, - twitterExtra: null - }; - } - - return { - path, - baseName: fileNameWithoutExtra.slice(0, posDot), - extension: fileNameWithoutExtra.slice(posDot + 1).toLowerCase(), - twitterExtra: twitterExtra ?? null - }; -} - -/** - * Retrieve information about a URL by parsing its parts. - */ -export function parseURL(url: string): ParsedURL { - const parsed = new URL(url); - const { path, baseName, extension, twitterExtra } = parseFile(parsed.pathname); - const params = Array.from(parsed.searchParams.entries()).reduce( - (acc, [key, value]) => { - if (["ex", "is", "hm"].includes(key)) { - acc.expiry[key] = value; - } else { - acc.other[key] = value; - } - - return acc; - }, { expiry: {}, other: {} } as { expiry: Record, other: Record; } - ); - - return { - url: url, - host: parsed.origin.toLowerCase(), - path, - baseName, - extension, - params, - twitterExtra - }; -} - -/** - * Sanitize a file name by replacing invalid characters with underscores. - */ -export function sanitizeFilename(filename: string, allowUnicode: boolean, fallback?: string): string { - let sanitized = filename.replace(/[<>:"/\\|?*\x00-\x1F]/g, "_") // windows-reserved - .replace(/^\./, "_") // dot files - .replace(/(\.\.)+/g, "_") // relative paths - .replace(/[ \t\n\r]+/g, "_"); // spaces - - if (!allowUnicode) { - sanitized = sanitized.replace(/[^\x00-\x7F]/g, "_"); // non-ASCII characters - } else { - sanitized = sanitized.normalize("NFD"); // normalize unicode - } - - sanitized = sanitized.replace(/^_+|_+$/g, ""); // leading and trailing underscores - return sanitized || fallback || ""; -} - -/** - * Get the current date and time formatted as a string. - */ -export function getFormattedNow(): string { - return new Date().toLocaleString(undefined, { - year: "numeric", - month: "2-digit", - day: "2-digit", - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: true - }).replace(",", ""); -} - -/** - * Join or create a group in the guild context menu. - */ -export function joinOrCreateContextMenuGroup( - children: Array, - items: Array, - groupId: string, - submenuId?: string, - submenuLabel?: string, - insertOrder?: InsertGroup[] -) { - function joinOrCreateSubmenu() { - const existingItemShouldJoinSubmenu = existingGroup.props.children.find(child => child?.props?.submenuId === submenuId); - let existingSubmenu = existingGroup.props.children.find(child => child?.props?.id === submenuId); - let numitems = items.length; - - if (existingItemShouldJoinSubmenu) { - existingSubmenu = null; - items.unshift(existingItemShouldJoinSubmenu); - numitems++; - const indexOfExistingItemShouldJoinSubmenu = existingGroup.props.children.findIndex(child => child === existingItemShouldJoinSubmenu); - existingGroup.props.children.splice(indexOfExistingItemShouldJoinSubmenu, 1); - } - - if (existingSubmenu) { - if (!Array.isArray(existingSubmenu.props.children)) { - existingSubmenu.props.children = [existingSubmenu.props.children]; - } - - items.forEach(member => member.props.label = member.props.submenuItemLabel ?? member.props.label); - existingSubmenu.props.children.push(...items); - } else if (numitems === 1) { - if (!chosenInsertOption?.position || chosenInsertOption.position === "END") { - existingGroup.props.children.push(items[0]); - } else { - existingGroup.props.children.unshift(items[0]); - } - } else { - existingSubmenu = ( - - {[...items]} - - ); - - items.forEach(member => member.props.label = member.props.submenuItemLabel ?? member.props.label); - - if (!chosenInsertOption?.position || chosenInsertOption.position === "END") { - existingGroup.props.children.push(existingSubmenu); - } else { - existingGroup.props.children.unshift(existingSubmenu); - } - } - } - - items.forEach(member => { - member.props.groupId = groupId; - member.props.submenuId = submenuId; - member.props.submenuLabel = submenuLabel; - }); - - const insertWithOptions = insertOrder?.filter(item => item.type === "WITH_GROUP"); - let existingGroup = children.find(child => child?.props?.id === groupId); - let chosenInsertOption; - - if (!existingGroup && insertWithOptions) { - let targetItem; - - for (const item of insertWithOptions) { - if (item.id.group) { - targetItem = children.find(child => child?.props?.id === item.id.group); - } - - if (!targetItem && item.id.child) { - targetItem = findGroupChildrenByChildId(item.id.child, children, true); - } - - if (targetItem) { - existingGroup = children.find(child => child?.props?.children === targetItem); - chosenInsertOption = item; - break; - } - } - } - - if (existingGroup) { - if (!Array.isArray(existingGroup.props.children)) { - existingGroup.props.children = [existingGroup.props.children]; - } - - if (!submenuId) { - if (!chosenInsertOption?.position || chosenInsertOption.position === "END") { - existingGroup.props.children.push(...items); - } else { - existingGroup.props.children.unshift(...items); - } - - return; - } else { - joinOrCreateSubmenu(); - } - } else { - if (!submenuId) { - existingGroup = ( - - {[...items]} - - ); - } else { - existingGroup = ({[]}); - joinOrCreateSubmenu(); - } - - const insertAfterBeforeOptions = insertOrder?.filter(item => item.type !== "WITH_GROUP") || []; - let targetIndex = children.length; - let targetItem; - - for (const item of insertAfterBeforeOptions) { - if (item.id.group) { - targetItem = children.find(child => child?.props?.id === item.id.group); - } - - if (!targetItem && item.id.child) { - targetItem = findGroupChildrenByChildId(item.id.child, children, true); - } - - if (targetItem) { - targetIndex = children.findIndex(child => child?.props?.children === targetItem); - - if (item.type === "AFTER_GROUP") { - targetIndex++; - } - - break; - } - } - - children.splice(targetIndex, 0, existingGroup); - } -} diff --git a/src/equicordplugins/equicordHelper/utils.tsx b/src/equicordplugins/equicordHelper/utils.tsx index 5d67bd929..57d1f072a 100644 --- a/src/equicordplugins/equicordHelper/utils.tsx +++ b/src/equicordplugins/equicordHelper/utils.tsx @@ -100,3 +100,31 @@ export async function toggleEnabled(name: string) { settings.enabled = !wasEnabled; return await beforeReturn(settings, wasEnabled); } + +export function getWindowsName(release: string) { + const build = parseInt(release.split(".")[2]); + if (build >= 22000) return "Windows 11"; + if (build >= 10240) return "Windows 10"; + if (build >= 9200) return "Windows 8.1"; + if (build >= 7600) return "Windows 7"; + return `Windows (${release})`; +} + +export function getMacOSName(release: string) { + const major = parseInt(release.split(".")[0]); + if (major === 24) return "MacOS 15 (Sequoia)"; + if (major === 23) return "MacOS 14 (Sonoma)"; + if (major === 22) return "MacOS 13 (Ventura)"; + if (major === 21) return "MacOS 12 (Monterey)"; + if (major === 20) return "MacOS 11 (Big Sur)"; + if (major === 19) return "MacOS 10.15 (Catalina)"; + return `MacOS (${release})`; +} + +export function platformName() { + if (typeof DiscordNative === "undefined") return navigator.platform; + if (DiscordNative.process.platform === "win32") return `${getWindowsName(DiscordNative.os.release)}`; + if (DiscordNative.process.platform === "darwin") return `${getMacOSName(DiscordNative.os.release)} (${DiscordNative.process.arch === "arm64" ? "Apple Silicon" : "Intel Silicon"})`; + if (DiscordNative.process.platform === "linux") return `Linux (${DiscordNative.os.release})`; + return DiscordNative.process.platform; +} diff --git a/src/equicordplugins/forwardAnywhere/index.ts b/src/equicordplugins/forwardAnywhere/index.ts index 0f38b467b..528548223 100644 --- a/src/equicordplugins/forwardAnywhere/index.ts +++ b/src/equicordplugins/forwardAnywhere/index.ts @@ -8,7 +8,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { sendMessage } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; -import { Message } from "@vencord/discord-types"; +import { Channel, Message } from "@vencord/discord-types"; // Taken From Signature :) const settings = definePluginSettings({ @@ -43,11 +43,21 @@ export default definePlugin({ } }, ], - sendForward(channels: any, message: Message) { - for (const c of channels) { - sendMessage(c.id, { - content: `${message.content}\n${settings.store.forwardPreface} Forwarded from <#${message.channel_id}>` - }); - } + + sendForward(channels: Channel[], message: Message) { + const chunkSize = 5; + channels.forEach(c => { + if (message.attachments.length) { + for (let i = 0; i < message.attachments.length; i += chunkSize) { + const group = message.attachments.slice(i, i + chunkSize); + const text = `${message.content}\nAttachments:\n${group.map(a => a.url).join("\n")}\n${settings.store.forwardPreface} Forwarded from <#${message.channel_id}>`; + sendMessage(c.id, { content: text }); + } + } else { + sendMessage(c.id, { + content: `${message.content}\n${settings.store.forwardPreface} Forwarded from <#${message.channel_id}>` + }); + } + }); } }); diff --git a/src/equicordplugins/friendCodes/FriendCodesPanel.tsx b/src/equicordplugins/friendCodes/FriendCodesPanel.tsx index 482203969..40c875d7b 100644 --- a/src/equicordplugins/friendCodes/FriendCodesPanel.tsx +++ b/src/equicordplugins/friendCodes/FriendCodesPanel.tsx @@ -99,7 +99,7 @@ export default function FriendCodesPanel() { revokeFriendInvites().then(setInvites([]))} > diff --git a/src/equicordplugins/friendshipRanks/icons.tsx b/src/equicordplugins/friendshipRanks/icons.tsx index 8740270a5..46ade68ed 100644 --- a/src/equicordplugins/friendshipRanks/icons.tsx +++ b/src/equicordplugins/friendshipRanks/icons.tsx @@ -4,9 +4,9 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -export function sproutIcon(props) { +export function sproutIcon() { return ( - + @@ -52,9 +52,9 @@ export function sproutIcon(props) { ); } -export function bloomingIcon(props) { +export function bloomingIcon() { return ( - + @@ -99,9 +99,9 @@ export function bloomingIcon(props) { ); } -export function burningIcon(props) { +export function burningIcon() { return ( - + @@ -146,9 +146,9 @@ export function burningIcon(props) { ); } -export function fighterIcon(props) { +export function fighterIcon() { return ( - + @@ -192,9 +192,9 @@ export function fighterIcon(props) { ); } -export function starIcon(props) { +export function starIcon() { return ( - + @@ -239,9 +239,9 @@ export function starIcon(props) { ); } -export function royalIcon(props) { +export function royalIcon() { return ( - + @@ -286,9 +286,9 @@ export function royalIcon(props) { ); } -export function bestiesIcon(props) { +export function bestiesIcon() { return ( - + diff --git a/src/equicordplugins/friendshipRanks/index.tsx b/src/equicordplugins/friendshipRanks/index.tsx index 015031115..1cad7290a 100644 --- a/src/equicordplugins/friendshipRanks/index.tsx +++ b/src/equicordplugins/friendshipRanks/index.tsx @@ -111,12 +111,12 @@ function openRankModal(rank: rankInfo) { )); } -function getBadgeComponent(rank,) { +function getBadgeComponent(rank) { // there may be a better button component to do this with return ( - - openRankModal(rank)} width={"21.69px"} height={"21.69px"} size={Button.Sizes.NONE} look={Button.Looks.LINK}> - + + openRankModal(rank)} width={"22px"} height={"22px"} size={Button.Sizes.NONE} look={Button.Looks.LINK}> + ); diff --git a/src/equicordplugins/githubRepos/components/ReposModal.tsx b/src/equicordplugins/githubRepos/components/ReposModal.tsx index 67d13dbb1..eeac8e39d 100644 --- a/src/equicordplugins/githubRepos/components/ReposModal.tsx +++ b/src/equicordplugins/githubRepos/components/ReposModal.tsx @@ -91,7 +91,7 @@ export function ReposModal({ repos, username, rootProps }: ReposModalProps) { Close diff --git a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx index 19b17852e..b504914a2 100644 --- a/src/equicordplugins/hideServers/components/HiddenServersButton.tsx +++ b/src/equicordplugins/hideServers/components/HiddenServersButton.tsx @@ -23,7 +23,7 @@ function HiddenServersButton() { {actuallyHidden > 0 ? ( openHiddenServersModal()} > diff --git a/src/equicordplugins/holyNotes/components/modals/NoteBookTab.tsx b/src/equicordplugins/holyNotes/components/modals/NoteBookTab.tsx index 031355779..3ce4fe6ea 100644 --- a/src/equicordplugins/holyNotes/components/modals/NoteBookTab.tsx +++ b/src/equicordplugins/holyNotes/components/modals/NoteBookTab.tsx @@ -147,7 +147,7 @@ export function NoteBookTabs({ tabs, selectedTabId, onSelectTab }: { tabs: strin {...props} className={"vc-notebook-overflow-chevron"} size={Button.Sizes.SMALL} - look={Button.Looks.LINK} + look={Button.Looks.FILLED} onClick={() => setShow(v => !v)} > diff --git a/src/equicordplugins/ignoreCalls/index.tsx b/src/equicordplugins/ignoreCalls/index.tsx index 9cd518233..469606e28 100644 --- a/src/equicordplugins/ignoreCalls/index.tsx +++ b/src/equicordplugins/ignoreCalls/index.tsx @@ -130,7 +130,6 @@ export default definePlugin({ onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} onClick={() => { - ignoredChannelIds.add(channel.id); FluxDispatcher.dispatch({ type: "CALL_UPDATE", channelId: channel.id, diff --git a/src/equicordplugins/instantScreenshare/index.tsx b/src/equicordplugins/instantScreenshare/index.tsx index 980973b9c..8a148c4ac 100644 --- a/src/equicordplugins/instantScreenshare/index.tsx +++ b/src/equicordplugins/instantScreenshare/index.tsx @@ -4,45 +4,60 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { getUserSettingLazy } from "@api/UserSettings"; import { HeadingSecondary } from "@components/Heading"; import { Paragraph } from "@components/Paragraph"; -import { Devs } from "@utils/constants"; +import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; import { VoiceState } from "@vencord/discord-types"; -import { findByCodeLazy } from "@webpack"; -import { ChannelStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common"; +import { findByCodeLazy, findStoreLazy } from "@webpack"; +import { ChannelStore, MediaEngineStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore, VoiceActions } from "@webpack/common"; import { getCurrentMedia, settings } from "./utils"; -const startStream = findByCodeLazy('type:"STREAM_START"'); - let hasStreamed; +const startStream = findByCodeLazy('type:"STREAM_START"'); +const StreamPreviewSettings = getUserSettingLazy("voiceAndVideo", "disableStreamPreviews")!; +const ApplicationStreamingSettingsStore = findStoreLazy("ApplicationStreamingSettingsStore"); async function autoStartStream() { const selected = SelectedChannelStore.getVoiceChannelId(); if (!selected) return; - const channel = ChannelStore.getChannel(selected); - if (channel.type === 13 || !PermissionStore.can(PermissionsBits.STREAM, channel)) return; + const channel = ChannelStore.getChannel(selected); + const isGuildChannel = !channel.isDM() && !channel.isGroupDM(); + + if (channel.type === 13 || isGuildChannel && !PermissionStore.can(PermissionsBits.STREAM, channel)) return; + + if (settings.store.autoDeafen && !MediaEngineStore.isSelfDeaf()) { + VoiceActions.toggleSelfDeaf(); + } else if (settings.store.autoMute && !MediaEngineStore.isSelfMute()) { + VoiceActions.toggleSelfMute(); + } const streamMedia = await getCurrentMedia(); + const preview = StreamPreviewSettings.getSetting(); + const { soundshareEnabled } = ApplicationStreamingSettingsStore.getState(); + let sourceId = streamMedia.id; + if (streamMedia.type === "video_device") sourceId = `camera:${streamMedia.id}`; - startStream(channel.guild_id, selected, { + startStream(channel.guild_id ?? null, selected, { "pid": null, - "sourceId": streamMedia.id, + "sourceId": sourceId, "sourceName": streamMedia.name, - "audioSourceId": null, - "sound": true, - "previewDisabled": false + "audioSourceId": streamMedia.name, + "sound": soundshareEnabled, + "previewDisabled": preview }); } export default definePlugin({ name: "InstantScreenshare", - description: "Instantly screenshare when joining a voice channel", - authors: [Devs.HAHALOSAH, Devs.thororen], + description: "Instantly screenshare when joining a voice channel with support for desktop sources, windows, and video input devices (cameras, capture cards)", + authors: [Devs.HAHALOSAH, Devs.thororen, EquicordDevs.mart], getCurrentMedia, settings, + settingsAboutComponent: () => ( <> For Linux @@ -51,8 +66,19 @@ export default definePlugin({ For X11 it may or may not work :shrug: + + Video Devices + + Supports cameras and capture cards (like Elgato HD60X) when enabled in settings + + + Regarding Sound & Preview Settings + + We use the settings set and used by discord to decide if stream preview and sound should be enabled or not + > ), + flux: { async VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) { const myId = UserStore.getCurrentUser().id; diff --git a/src/equicordplugins/instantScreenshare/utils.tsx b/src/equicordplugins/instantScreenshare/utils.tsx index 853b1c14d..3b0dc6a63 100644 --- a/src/equicordplugins/instantScreenshare/utils.tsx +++ b/src/equicordplugins/instantScreenshare/utils.tsx @@ -9,7 +9,7 @@ import { Heading } from "@components/Heading"; import { Paragraph } from "@components/Paragraph"; import { Logger } from "@utils/Logger"; import { OptionType } from "@utils/types"; -import { findByCodeLazy } from "@webpack"; +import { findByCodeLazy, findByPropsLazy } from "@webpack"; import { MediaEngineStore, SearchableSelect, useEffect, useState } from "@webpack/common"; interface PickerProps { @@ -18,26 +18,53 @@ interface PickerProps { } const getDesktopSources = findByCodeLazy("desktop sources"); +const configModule = findByPropsLazy("getOutputVolume"); +const log = new Logger("InstantScreenShare"); export const settings = definePluginSettings({ streamMedia: { type: OptionType.COMPONENT, component: SettingSection, }, + includeVideoDevices: { + type: OptionType.BOOLEAN, + description: "Include video input devices (cameras, capture cards) in the source list", + default: false, + }, + autoMute: { + type: OptionType.BOOLEAN, + description: "Automatically mute your microphone when joining a voice channel", + default: false, + }, + autoDeafen: { + type: OptionType.BOOLEAN, + description: "Automatically deafen when joining a voice channel (also mutes you)", + default: false, + }, }); export async function getCurrentMedia() { const media = MediaEngineStore.getMediaEngine(); - const sources = [ - ...(await getDesktopSources(media, ["screen"], null) ?? []), - ...(await getDesktopSources(media, ["window", "application"], null) ?? []) - ]; - const streamMedia = sources.find(screen => screen.id === settings.store.streamMedia); - console.log(sources); + const sources = await getDesktopSources(media, ["screen", "window"], null) ?? []; + if (settings.store.includeVideoDevices) { + try { + const videoDevices = Object.values(configModule.getVideoDevices() || {}); + const videoSources = videoDevices.map((device: any) => ({ + id: device.id, + name: device.name, + type: "video_device" + })); + sources.push(...videoSources); + } catch (e) { + new log.warn("Failed to get video devices:", e); + } + } + + const streamMedia = sources.find(screen => screen.id === settings.store.streamMedia); if (streamMedia) return streamMedia; - new Logger("InstantScreenShare").error(`Stream Media "${settings.store.streamMedia}" not found. Resetting to default.`); + log.error(`Stream Media "${settings.store.streamMedia}" not found. Resetting to default.`); settings.store.streamMedia = sources[0]; return sources[0]; @@ -63,7 +90,7 @@ function StreamSimplePicker({ streamMediaSelection, streamMedia }: PickerProps) } function ScreenSetting() { - const { streamMedia } = settings.use(["streamMedia"]); + const { streamMedia, includeVideoDevices } = settings.use(["streamMedia", "includeVideoDevices"]); const media = MediaEngineStore.getMediaEngine(); const [streamMediaSelection, setStreamMediaSelection] = useState([]); const [loading, setLoading] = useState(true); @@ -72,10 +99,21 @@ function ScreenSetting() { let active = true; async function fetchMedia() { setLoading(true); - const sources = [ - ...(await getDesktopSources(media, ["screen"], null) ?? []), - ...(await getDesktopSources(media, ["window", "application"], null) ?? []) - ]; + const sources = await getDesktopSources(media, ["screen", "window"], null) ?? []; + + if (includeVideoDevices) { + try { + const videoDevices = Object.values(configModule.getVideoDevices() || {}); + const videoSources = videoDevices.map((device: any) => ({ + id: device.id, + name: device.name, + type: "video_device" + })); + sources.push(...videoSources); + } catch (e) { + log.warn("Failed to get video devices:", e); + } + } if (active) { setStreamMediaSelection(sources); @@ -84,7 +122,7 @@ function ScreenSetting() { } fetchMedia(); return () => { active = false; }; - }, []); + }, [includeVideoDevices]); if (loading) return Loading media sources...; if (!streamMediaSelection.length) return No Media found.; diff --git a/src/equicordplugins/keyboardNavigation/commands.tsx b/src/equicordplugins/keyboardNavigation/commands.tsx index 81f17e91c..72e065042 100644 --- a/src/equicordplugins/keyboardNavigation/commands.tsx +++ b/src/equicordplugins/keyboardNavigation/commands.tsx @@ -6,9 +6,10 @@ import { showNotification } from "@api/Notifications"; import { Settings } from "@api/Settings"; +import { gitHashShort } from "@shared/vencordUserAgent"; import { copyToClipboard } from "@utils/clipboard"; import { relaunch, showItemInFolder } from "@utils/native"; -import { checkForUpdates, getRepo, shortGitHash } from "@utils/updater"; +import { checkForUpdates, getRepo } from "@utils/updater"; import { GuildStore, NavigationRouter, SettingsRouter, Toasts } from "@webpack/common"; import gitRemote from "~git-remote"; @@ -115,7 +116,7 @@ export const actions: ButtonAction[] = [ { id: "copyGitInfo", label: "Copy Git Info", callback: async () => { - copyToClipboard(`gitHash: ${shortGitHash()}\ngitRemote: ${gitRemote}`); + copyToClipboard(`gitHash: ${gitHashShort}\ngitRemote: ${gitRemote}`); Toasts.show({ message: "Copied git info to clipboard!", diff --git a/src/equicordplugins/keywordNotify/index.tsx b/src/equicordplugins/keywordNotify/index.tsx index d4dbffbbe..055ecc0e1 100644 --- a/src/equicordplugins/keywordNotify/index.tsx +++ b/src/equicordplugins/keywordNotify/index.tsx @@ -100,7 +100,7 @@ function Collapsible({ title, children }) { setIsOpen(!isOpen)} - look={Button.Looks.LINK} + look={Button.Looks.FILLED} size={Button.Sizes.SMALL} className={cl("collapsible")}> @@ -145,7 +145,7 @@ function ListedIds({ listIds, setListIds }) { setListIds(values); update(); }} - look={Button.Looks.LINK} + look={Button.Looks.FILLED} size={Button.Sizes.SMALL} className={cl("delete")}> @@ -221,7 +221,7 @@ function KeywordEntries() { removeKeywordEntry(i, update)} - look={Button.Looks.LINK} + look={Button.Looks.FILLED} size={Button.Sizes.SMALL} className={cl("delete")}> diff --git a/src/equicordplugins/loginWithQR/ui/modals/VerifyModal.tsx b/src/equicordplugins/loginWithQR/ui/modals/VerifyModal.tsx index 867d84c0a..93a80c482 100644 --- a/src/equicordplugins/loginWithQR/ui/modals/VerifyModal.tsx +++ b/src/equicordplugins/loginWithQR/ui/modals/VerifyModal.tsx @@ -214,7 +214,7 @@ function VerifyModal({ ) : ( {state === VerifyState.NotFound diff --git a/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx b/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx index ccea5f59f..281820510 100644 --- a/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx +++ b/src/equicordplugins/messageLoggerEnhanced/components/LogsModal.tsx @@ -14,7 +14,7 @@ import { closeAllModals, ModalContent, ModalFooter, ModalHeader, ModalProps, Mod import { LazyComponent } from "@utils/react"; import { User } from "@vencord/discord-types"; import { find, findByCode, findByCodeLazy } from "@webpack"; -import { Alerts, Button, ChannelStore, ContextMenuApi, FluxDispatcher, Menu, NavigationRouter, React, TabBar, TextInput, Tooltip, useMemo, useRef, useState } from "@webpack/common"; +import { Alerts, Button, ChannelStore, ContextMenuApi, FluxDispatcher, GuildStore, Menu, NavigationRouter, React, TabBar, TextInput, Tooltip, useMemo, useRef, useState } from "@webpack/common"; import { clearMessagesIDB, DBMessageRecord, deleteMessageIDB, deleteMessagesBulkIDB } from "../db"; import { settings } from "../index"; @@ -159,7 +159,7 @@ export function LogsModal({ modalProps, initalQuery }: Props) { Alerts.show({ title: "Clear Logs", @@ -176,7 +176,7 @@ export function LogsModal({ modalProps, initalQuery }: Props) { Clear Visible Logs { setSortNewest(e => { @@ -308,6 +308,9 @@ function LMessage({ log, isGroupStart, reset, }: LMessageProps) { if (!message) return null; + const channel = ChannelStore.getChannel(message?.channel_id); + const guild = GuildStore.getGuild(channel?.guild_id); + return ( { @@ -416,8 +419,16 @@ function LMessage({ log, isGroupStart, reset, }: LMessageProps) { isAutomodBlockedMessage={false} /> } - /> + {settings.store.ShowWhereMessageIsFrom && channel?.isDM() && message?.author && ( + From {message.author.username}'s DMs + )} + {settings.store.ShowWhereMessageIsFrom && channel?.isGroupDM() && channel?.name && ( + From {channel.name} Group DM + )} + {settings.store.ShowWhereMessageIsFrom && !channel?.isDM() && !channel?.isGroupDM() && channel?.name && guild?.name && ( + From {channel.name} in {guild.name} + )} ); } diff --git a/src/equicordplugins/messageLoggerEnhanced/settings.tsx b/src/equicordplugins/messageLoggerEnhanced/settings.tsx index bc49c91c2..695a999da 100644 --- a/src/equicordplugins/messageLoggerEnhanced/settings.tsx +++ b/src/equicordplugins/messageLoggerEnhanced/settings.tsx @@ -111,6 +111,12 @@ export const settings = definePluginSettings({ restartNeeded: true, }, + ShowWhereMessageIsFrom: { + default: false, + type: OptionType.BOOLEAN, + description: "Show message channel/author name and server name", + }, + messagesToDisplayAtOnceInLogs: { default: 100, type: OptionType.NUMBER, diff --git a/src/equicordplugins/messageLoggerEnhanced/styles.css b/src/equicordplugins/messageLoggerEnhanced/styles.css index 97d1cadad..748e40188 100644 --- a/src/equicordplugins/messageLoggerEnhanced/styles.css +++ b/src/equicordplugins/messageLoggerEnhanced/styles.css @@ -58,6 +58,19 @@ justify-content: space-around; } +.msg-logger-modal-from { + font-size: 14px; + margin-left: 12px; +} + +.msg-logger-modal-from-deleted { + color: var(--status-danger); +} + +.msg-logger-modal-from-edited { + color: var(--white-600); +} + .vc-log-toolbox-btn svg { color: var(--interactive-normal); } @@ -82,4 +95,4 @@ .folder-upload-button { margin: 6px; padding: 4px 8px; -} +} \ No newline at end of file diff --git a/src/equicordplugins/questify/index.tsx b/src/equicordplugins/questify/index.tsx index f3ae46e27..11761ebcc 100644 --- a/src/equicordplugins/questify/index.tsx +++ b/src/equicordplugins/questify/index.tsx @@ -6,6 +6,7 @@ import "./styles.css"; +import { showNotification } from "@api/Notifications"; import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList"; import { ErrorBoundary, openPluginModal } from "@components/index"; import { EquicordDevs } from "@utils/constants"; @@ -558,6 +559,15 @@ async function startVideoProgressTracking(quest: Quest, questDuration: number): if (success) { QuestifyLogger.info(`[${getFormattedNow()}] Quest ${questName} completed.`); + + if (settings.store.notifyOnQuestComplete) { + showNotification({ + title: "Quest Completed!", + body: `The ${questName} Quest has completed.`, + dismissOnClick: true, + onClick: () => NavigationRouter.transitionTo(`${questPath}#${quest.id}`) + }); + } } else { QuestifyLogger.error(`[${getFormattedNow()}] Failed to complete Quest ${questName}.`); } @@ -642,6 +652,15 @@ async function startPlayGameProgressTracking(quest: Quest, questDuration: number if (success) { QuestifyLogger.info(`[${getFormattedNow()}] Quest ${questName} completed.`); + + if (settings.store.notifyOnQuestComplete) { + showNotification({ + title: "Quest Completed!", + body: `The ${questName} Quest has completed.`, + dismissOnClick: true, + onClick: () => NavigationRouter.transitionTo(`${questPath}#${quest.id}`), + }); + } } else { QuestifyLogger.error(`[${getFormattedNow()}] Failed to complete Quest ${questName}.`); } @@ -655,6 +674,15 @@ async function startPlayGameProgressTracking(quest: Quest, questDuration: number if (success) { QuestifyLogger.info(`[${getFormattedNow()}] Quest ${questName} completed.`); + + if (settings.store.notifyOnQuestComplete) { + showNotification({ + title: "Quest Completed!", + body: `The ${questName} Quest has completed.`, + dismissOnClick: true, + onClick: () => NavigationRouter.transitionTo(`${questPath}#${quest.id}`), + }); + } } else { QuestifyLogger.error(`[${getFormattedNow()}] Failed to complete Quest ${questName}.`); } @@ -1115,14 +1143,6 @@ export default definePlugin({ } ] }, - { - // Prevent scrolling to a sponsored Quest. - find: "Id(\"quest-tile-\".concat", - replacement: { - match: /(?=document.getElementById)/, - replace: "null&&" - } - }, { // Whether preloading assets is enabled or not, the placeholders loading // before the assets causes a lot of element shifting, whereas if diff --git a/src/equicordplugins/questify/settings.tsx b/src/equicordplugins/questify/settings.tsx index 5aa71d47d..d6e272a04 100644 --- a/src/equicordplugins/questify/settings.tsx +++ b/src/equicordplugins/questify/settings.tsx @@ -674,7 +674,8 @@ function DisableQuestsSetting(): JSX.Element { disableFriendsListActiveNowPromotion, makeMobileQuestsDesktopCompatible, completeVideoQuestsInBackground, - completeGameQuestsInBackground + completeGameQuestsInBackground, + notifyOnQuestComplete } = settings.use([ "disableQuestsEverything", "disableQuestsDiscoveryTab", @@ -685,7 +686,8 @@ function DisableQuestsSetting(): JSX.Element { "disableFriendsListActiveNowPromotion", "makeMobileQuestsDesktopCompatible", "completeVideoQuestsInBackground", - "completeGameQuestsInBackground" + "completeGameQuestsInBackground", + "notifyOnQuestComplete" ]); const options: DynamicDropdownSettingOption[] = [ @@ -696,9 +698,10 @@ function DisableQuestsSetting(): JSX.Element { { label: "Disable Popup Above User Panel", value: "popup", selected: disableQuestsPopupAboveAccountPanel, type: "disable" }, { label: "Disable Gift Inventory Relocation Notice", value: "inventory", selected: disableQuestsGiftInventoryRelocationNotice, type: "disable" }, { label: "Disable Friends List Active Now Promotion", value: "friends-list", selected: disableFriendsListActiveNowPromotion, type: "disable" }, + { label: "Make Mobile Quests Desktop Compatible", value: "mobile-desktop-compatible", selected: makeMobileQuestsDesktopCompatible, type: "modification" }, { label: "Complete Game Quests in Background", value: "game-quests-background", selected: completeGameQuestsInBackground, type: "modification" }, { label: "Complete Video Quests in Background", value: "video-quests-background", selected: completeVideoQuestsInBackground, type: "modification" }, - { label: "Make Mobile Quests Desktop Compatible", value: "mobile-desktop-compatible", selected: makeMobileQuestsDesktopCompatible, type: "modification" } + { label: "Notify on Auto-Complete", value: "notify-on-complete", selected: notifyOnQuestComplete, type: "modification" }, ]; const disableOptions = options.filter(option => option.type === "disable"); @@ -725,9 +728,10 @@ function DisableQuestsSetting(): JSX.Element { settings.store.disableQuestsBadgeOnUserProfiles = enabledValues.includes("badge"); settings.store.disableQuestsGiftInventoryRelocationNotice = enabledValues.includes("inventory"); settings.store.disableFriendsListActiveNowPromotion = enabledValues.includes("friends-list"); + settings.store.makeMobileQuestsDesktopCompatible = enabledValues.includes("mobile-desktop-compatible"); settings.store.completeGameQuestsInBackground = enabledValues.includes("game-quests-background"); settings.store.completeVideoQuestsInBackground = enabledValues.includes("video-quests-background"); - settings.store.makeMobileQuestsDesktopCompatible = enabledValues.includes("mobile-desktop-compatible"); + settings.store.notifyOnQuestComplete = enabledValues.includes("notify-on-complete"); redoAutoFetch ? checkAutoFetchInterval(settings.store.fetchingQuestsInterval) : null; setCurrentValue(enabled); @@ -1658,6 +1662,12 @@ export const settings = definePluginSettings({ } }, }, + notifyOnQuestComplete: { + type: OptionType.BOOLEAN, + description: "Show a notification when a Quest is auto-completed.", + default: true, + hidden: true + }, questButton: { type: OptionType.COMPONENT, component: QuestButtonSettings, diff --git a/src/equicordplugins/songLink.desktop/Providers.ts b/src/equicordplugins/songLink.desktop/Providers.ts new file mode 100644 index 000000000..095fb27d2 --- /dev/null +++ b/src/equicordplugins/songLink.desktop/Providers.ts @@ -0,0 +1,97 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 nin0 + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +const Providers = { + amazonMusic: { + name: "Amazon Music", + logo: "data:image/png;base64,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" + }, + spotify: { + name: "Spotify", + logo: "data:image/png;base64,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", + native: true + }, + itunes: { + name: "iTunes", + logo: "data:image/png;base64,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", + native: true + }, + appleMusic: { + name: "Apple Music", + logo: "data:image/png;base64,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", + native: true + }, + youtube: { + name: "YouTube", + logo: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAAGWpJREFUeJztnXuQJddd3z+/030f857d2V1pd/VAkrU2K0u2JGzAxmVwMCBDEmIikgoOgSoSBSfCKScxxC6qFBIqkCIOEDmAQlkSLuQKAmnLGGPJsiTLyFi2bBzLWlmP0kqytCvt7MzuzJ07997uPueXP0533/fsjLSPe9f9neqavt2nu0+f3+/8zu91TkOBAgUKFChQoECBAgUKFChQoECBAgUKFChQoECBAgUKFChQoECBAgUKFDhHIGe7Ahn0n/+HKSamP4yasLVWZ+3EcQwQL9WgFYOD9fV1AJy1tNab/kLnQBUSR5g4AucPG2MwxuT3Nx2vGhoLuPY5Z3BAM4nah43BmDIm9NeZ0NBsxsRJgjNQmaqACfOyhAY1BjH+9/TsDCYUJAyZ23M+zoCIPlG+86O3nfLGew04ZQyg1/6r0pEZ5mpPHYEazFCDWo2Ff/jud6yVzB+vLC9BZFlZWsasLjEdN3HWoc63eFmRGVOZDxCx1hJHEUaESgIlJP/zD/Ntrgqq6pnAKUYdBgUxYKrdb6iAayFkxNd0ywoZVCXdD1EREIOg/kE4VB0W568S0I7mEzE4EcQIxgQYI6iCNUocCAmKJjayUbKGCWlUApqlADUBwcQkKoY4NNi5CSZ3LVDePocsHv+J8PNPHwKoAczAvmv3IQ/ecuxU0e1VM8DRf3fTm5cPHf7HK994isnjTSZL5Uuq83M/I2tNyg1H2IoJE0XUGRVTQsCJb/qAJiWNEBFUHSAYBKOACKjDpYxhUrKrCCLSrnW6rwqaEkn8D19O0itzBvBlVBMQi7heBsg2g4o/pdnzMKhk16uvt8kYUlGnHdVqP1tUPZuJZ1TVtB4IzdAQB4ZQA0InGGdQEZLQ4IwBEdRpJCoq6tsuKhlm9u7UlReO3LwcaIu9u5l64yWUFyZv2XPLTS+8GjpuiQEOXvu+3bumd/6jxuHnbtTVpVln3R5dqTMjZSoYbJIQqvhNPFHTt+5pbJc+WNrH88ZOGSCVxe1+nxEo+ydpLwRQnCqK4iS9RgQjHa+XNr5qApkU0E4BkRJOwGl+Y5wYT3BoM5mktRLJGQvAkA017aGnuwIWX8vO1kiZSAxGs7f111sjOBECB6LgDLQ0QUohkRHiMCCerrDcXH1+7sK9jfmF+V//Zjl56O33fuLopgjKJhlg6edunF1/7vgN9vDaL1dfXr1oNm4FoYs9KZ0lkAAxnuPz1813uxuo//FZQeMbxtDurek17UoOalgPh6KiuXiWrlE/e4wX5V5muDbvAYrJeApU2uJdyCVCfh/pYseu+/Qxa0cNfVHXfRGA+KNGTdf11mSPM146osQaIxiUBIDAlIhFkTDk+IRJvnPe1BPbd593y+sevOPmoY3VgZMywBMzb1iQPed9YueS+/Hp5ciEzldEOzujguCQDsUqQ5uo7WNG+4oBaRN10liHl93s9aL9L5n3wM2W7aWnG0xi8KK6F/l9O891vpe0+bOriHQVGXBj03WrWCwrQURzx/ZoZXbmT923X7zxTXyzPujSDOFGJx/nivICU7fJ08eum3VVQoK+um9YwUEFtkDQraKzo/Y+9mTlTkXZgSd1gxv3/hx0bBPIypUcLKghWo7LLC3/4onJuTrr3LjZqnbh8dkrts8y9cmda8mPlpyatjrmL9Oe8dUbUhtIgM6yp0kCnK5nuQESYNhg1FdWh0uL1wzproWqQ7EIZRIVToRBwq7ZP3H2yK+c/8pgSRAMOvhHXFu6aJZP7llpvafsApF0fHYiOOMVJul5JelSbXoruuHP9gtsoexQnIZn9Q0BA4aKoWU5nQzQfVcVSAJPrVAdVbWGRvPq5enzJ9+xdvV9d3Kwj0B9Q4Bef325nuz4bP3+L/1wWdLBToUArztbAaOK9N1qeFeVfsHQh0EEyY5vpfE286z8UZstuwFvb6rsaaF+PzSVzCrebDDiqGiCW17+wLXvvLTCF3h/X317Dxy86r3XzL5w5Ku7a5Ex1tvouajJ/jtL71vmylIPBonVYWUHNtQWhoDNPmuQsnc6y/qTw068BvQOAbQ7TKaQO6BpSry8beLpyqXb33XBI3e92HlN1x1uAiOLJ9432xIjLrNHhX4htgFVBhXfTNlh5XvLDNu28qzXWqet3vMMoZNimVfCYJhwMFVrXL7y/NF39/Jo149vX3X96yeO1b6989gKlSTCaGb09ryhG6LsDeCTDXvlqWy4LTwrG9kGlu3R7jZddogZ2b7R0Jq/eshwv0gnrDooTbK67wLs3OTenQ9//HB2rksHaD15mBknBLnTpMC5ACNlSBz22VdoEHWfy3YevfbvT0aX7LgvQP0Y7xxothXMMK5wQGSURJRyYlm7Zt9tnedzCVD62vMyPz81WXURRl0usTIHpneWbiByhqrKQ+yi7LanG4OetZF3Z5AG/1rLni7o5swYowZwhDSpPP98tetctrPvPT/4a9tazfmSttKxnzRs0f7f9gOZns37BQx0b0MUNuktd4q2zT5LznDZs60kGiXt1BGl+tqC/tMPXpify3aiw0vTYaImUPGBlTxq1cPSA19mRNTgAgPhQ1+KOIdT9r9ybOXd2bmcAWovHaWUOIwqDkcsSiSaBzdUFacOp66fKQqMNDJqhQ4mrbD49Hfyc+1BfW0dUSVIvUmxQLI5K4OugU97tgJnHRkZAoWg5SBLpyNlgMV/8KEZpqp7fWHJfd1BZwy8i5iDqNz2QY3M4FcAT8wEJEYUwthSmZnad+gXbqpCygDHysevjHZOX29TB6JBKDlD6Ezq809VHjUd2RG2Z8vUoqBjK5jg7EMRIkS8/V9WQ3nXtl8Na8sXQmoGNl86Slhv5Bk4MIxsMnC3LR16r/LmR4GzC4PmNApE4MQqNW0BKQM0Dh9naq05hOiZgdOZnHWmDd7NwapFMDgsQUekW3pd2d9FMIA64y0BEUQMHF+DqIMBJmqrlFsbNVImyke7R0tQAms3dlh9F0LSsKWKYByU6hE0GkDKANviCSZczKCcPvJEymy/f7eNNHx8FmABNzNBHIYkK6tUEj9HIEuzNCjS5TkbEOTKjp+TEJxAoJZyLFTwEsAAVGJTqbYcISFBvgUE+EQQ35AWIUFIGKzxZzg7dmAjFNbe9UNU/+i3OX7dW3hlvswxGqwRo5jUdxEBDaBJ9+SQc91m9aa9E+/QrySwq7StDGD0+pvKC2+/6taS+h4hXZtBRNsb2eZvOkrmXmwMJyYC5NrLuOj3PsLC//wg/Nj3U5uZoaGCqrdQrIHEKC6L3Y7OK5w+dLjwvRFnCd7y+t8HCDn4OPVKUJowBtzmcvpGtbM04xaUDXZ+hsmfvY6Ja95K/ZP38PIdn2L2lWXmIiAAJ5ZKci5TvBNZt81/giqt+noI6RDQbKz7OXZjDAFCESiVCEwZCSYw33MB0x/6BS478FHMDe/lyK4Z1kwZl5Q4t7t8J/pFnAjEsfcGmoOPH2RtbY0Nu/U4DZUiOAmBAEohTJXhyktZ+NX3cf4ffoTkJ36Axs55osBgIZ+5lgW+FE3n8el4vO8mkCWLZilMxsBafQ3I8gHqLUId9raDlLzRhHEBWG//O9NOXI9F4fwFqj/1w1T3v571v/oix+++B/PNQ2yvRZSsJdaW7xpiCDSb4zco4D9+kiPT2gL1A0LZCUEj9QPsB15OhEC9oteNXgeQv90oMoFxQmhDsN59nYgjNIAogQQgijMKl+5m8l++l8oPfR/Nux/gxF/cy8TzLxNGMUE6Y7htzHomEE0TALacpH724cBbvAqBemsoEJCadw17CaB+qkffjIeBdB5NL2Avsgm+7T6b2v0B6EQZufp1TL5uD5UfvYaVWw9Quu9R3MvHCBUSYL0kTMSGUpodkefEjBf928jJKzlDAIQHgblh2v844yR8KhZkaoLw+69g4aLzif/e16n/+QOEX32K6OgSJVtKh4EYJW7frOOe4+JidjnBU9mmkmd2hzAg62dsIen7Za7P9uEuZPPwBahWkYsXKO9+J+W3XUvzk/eyePf9lJ86Qmk9XXGkJ5P2XIIfAjolQCcvbJQePorMb0gVuc7Jq/31VxE0kHR4MGDKUBG4uET13/w0e657O40//TwrB75A+NIR5pIQky9qkXkQTZdQcDJePqUsWhIC7YUdemdF5BhfCdE5pzlHJ5VEyHIXVBTmy1SunKb6GzfAu9/GsdsOkDzwd3BimYpTRBv4yAPQMV3epk1XGt1YWReySTSpEngWa3IKIUguBTbshQM5wq9i4YzDVAxaNsg7r2DhyktIHnmMxd//BNW/e4q5RtIV8/Iz6PxkGhlDZ1rHzKDx0O43RMeYr5k+gG5aJOfJbWncw02FMDFD+NM/yO43Xc7a7Xdz7LMPwTMvMN1oUdZ0aRdpp9HJmLWhmfvX/+xGqZa2j8XAtWVsnhgO7zxCBHHg1BGJJQ7ABiHxZTuZ+vDPs3Dzh5n4ufewuL1KLVACMQTOJ9AmRtDeORMj3rChBuwnpHryoiOO3FGT5S/0TgU6OSxgHATO/68EEBmhZbyyGFTLhFdfxswlv0TlumtYvO0u6l94jB1rProYWgV1KDIGJqJvn9DGEVUjW5v/N5LvlhJefeJHQLbg2rDk1H5tzWvEQOZGFiXsvNIYtFJCdm2j8mM/wt4r3kT9kW+x/LE7qHzjELOuhVW/eppRM9JMILMz+4/87H++LlTrutfT60HmSuy6uP/QSEB6xnsZsDcM+UJm2ZTrAMBhxPUkmPncApEQLtjN1K5dVPftw37ir0jueYjKk0/htPea0YMphzus6mUbrhKWY0zyAXqxVSaVvp1BnvGU/dUnV0glJNx/IeEHfxGCAH3mEIlr5Xl4I4tU4ofpcjLnLoYFOOkfBPol2xAiGtDAeSIvrxN97WnM7Z+mfv8XmbGWUhp0GeUhwBhDWKkQau7QGr9IVy+yzJcsEOTXJd7qPciZptOboNLu/+ocNGP06Rep3fk5ogOfo/zsYSYSm/oCxqAdBQIDoXO8iNMYtDS05DhIiGwdGIH+OQw9RQddjnQ6eTGZeEitC4ufXRs0LbyySuOBv2X543/GxGPPsq2Z0IzXSDCUpDrwmaMGZ3VNDYvhpbfe9ZvHpmd+CdjTt8iNZivunlyDHg101nNjNUwHnHfa8bmAzNsnaSg4SXCrdZL7H6Vx5+eIH/gK29ealK0SKEwGU3lmka9JGn7uWEx6lGCXjn99/ub/9H9DAOtS3/boM+4m0E6C3HyzpwoRPkyMA8nVYweJha8+ReO2AzQ/+zDhK0eZd4ZSvkiTjy6OY1Q1PAhsH796nxTat9MDyXJ/IOv3ouoTJl1ESBlpWuJnjlA/cB8rd3+eqYPPsWAtzjkC099b+iIQo5k81QUvAUZQRJ15OC+ybYNQDNQi9N6v8Nzv3Mr0Nw5xfmwpY1EaEDhUg3NBb04TQsSLTf8xhLNdpdeItNf12u/d6WF5n2+niznB2TrBekTrgf/H2m33YP/2MXasNJhNHIJF8dPnQtfrah4/ZLUPARJjxp+Zpa3Hm1TFE3yAJk6/8xMiBOqJHqk3EUPAOIfUa7hvH6J++10sfvpL7HhpjSknqNrUpEzNQPHGkskMzF4+GBO+SJOn0yHAjFsQcwhUc1VMcBiyBS980quP2ft+X0rNOxMl6NMvUb/7Xk785T2Yp19goZ4wadvBXe8iBtKviuQY4wh6pv2EAMF49/0OpK+VDQHqjcFyuq/O+fmOCqXI4U6s0Xj0ceofu4Pql77F5PoyU+UqWMkdSX1W8Ebevb50us7rXtubnXr4CnljZ4Rdlq8VWSd1AolaSs4gazHNL3+Txu1/iX3wK0ydqFGJI4yWoWlx4jOLjBsLRX5TGBaa8BHQ1KQZez1goNfPe/FQQ3k1xj5xmOaffZ7jn7mP6ZcWmW01CTTCzwbI4okdGT7aExE9B7ih0+jzVsB0FbvWwIzx/AAnEIeChj65UxOLCwIER5A0kRdqnPjUl2ne9mnmn1tiV71GSa3PjrQuFxX5eN9hSQwOlg3x8PV6oEeoSR1gTEAiCW6mCisQ7r9iPy9WLPHiibHJaB0EJxClc0K9RWAgEWR5heThL7P48c9QfegJttct/pN3HZ+PFZ86khFfOu4pqR6xWUL2NaGMVv6EGmgZqO5egBdTCWBKIXHiReC4QlQxGiM2QhJFWw736HOcuP0A9U/dy9x6wnSULRjTYcdn2mLnV0k7/7+aHtyfRDAy0KSJVMtUJicBCNl/BTPL34EggGRMRUBq21cDRes19DsrtP78QY7deS9Tzy+yOxashURSH0En7Xv2znX46e+Oqdm5VYCQZw9rZXf1G5Gw52xX7tVDKFuYP96k9Rf3s3LXZ5h+/GV2rbQwNsKmBLY4EuMIVAmdkHs/zmErqAsCkk7+ad775Q+mh+C5iTe/bcbKw9uSnhx69WPj6IeD/YjO7BQttbj1JhNWMBLgXEJCFhxWkgDUKNU4HfWFPA9QdYC5lDuPNocz+t3AzdQHcOI/qxs4cBJQrwZEYXXfrtWHng4BauEEAZb5JBpTYZhG92t1n9+ef0ncz+crpWUECK3gHGzF97lBVtnIw5uyCmKxgUOssl6eplEqA5kjaPsMWmtAa3xnwea6XPo7i80LrodY0l4EezNUbLsF2hjyIalRRCZ9VL1JowI6OwNTFVhMJWO4aztMV85uTc8UhLMrk88w/BAgqHiXhxPQ2UnYuQNIGWD2sj0E89OjZrG8Ooinbud6h3Ru34Vw4nUbUZ/7ITvmmLn0AiBlgPWV+GvupaMfP6u1LHDa4MTHRlFDhKP11KEP7XzOHoKUAS7/6//VSuJoFXVYVf8xqnwl1SwuNmqa/yB4xW/L3pfOiI+efDupIOksPxJoVzgIDCaOluTBmxLoSJ2d2L4dTM8HIs6JgXJT3xsD2m99sk2ywgO2oeXPIvxXw9LZz6UykzsX2ueynelLd5NUS2TLq4x/0lMBSJnagTiDE6FVKSnnz+ekzRkgvOC8w8tECcZ5Bhj12Y0FNgXBrw8YKiQCq0njUdzandn5nMxP3vPZ/+2mK6tg2kuLy/BEggJjgtw3IlhjSGYmmrsevHMtO93u5+edj+7eTSIlVAVRGf412AJjBU9GQ8uU2HX1lYud53IGuOoHfiaa2fe9t9arszgCTBoHkAHKUoHxguJIxLBameTQI4/f2Hkup6rcckNsDtdusXOzSRQYsuX0CwEw/hACbBBgLt0bX3jtW7pI2tWtZ//mY08tVuxHlzUCDA2J8mnRBUYLG7kqesvZUsDhasLR1vK/2HnP7x7pPN8n12ut+n8/UcIJQjWcOLcXjxhjKG0lPd8GlRNoBZaVGfP1pZbc1Xu+jwHeerld2fvWq353baKkYv0noxxg8R+V9mtpFVwxmnDeE6oun9dgRXilKrrr6jf8yXue+etW7xUDjTwF89jMj/zO96ybD1TcuokkwpSVMIkoJxalhMrmlhcqcGrR7noud0nn0W11GE2AACshDYREytGzU3rjNbVH/s8gITGQigLugZp+aO68OZlrmBtLcSNIoholFMT5z6kU/oEzjg3lbj5Nzc9tsGqoTVRaTWc+ck3tb/54yAixMRmV64PFy9z/aC0tvX+u3ihN2AiVBFHTnhxZ4IyhPUeRNOiVHfe0NQjilFZQYrlSaS5r8mtXNh75vY3uedJ+rCCPv/mf3Dz18vEbZpdXg7JxhHFCuVADzjiGMYBVB0agVGE9Smict9A4vm3qv1xx8MBvcRLBcVLvjoBOvfl7/z0XnfeTtTfs+cLqVHXZViokAlbageIuM+Rk9skmQq6Dqt1/Sk52yfAbvIrnn1JstS7afZlL2z8xkASG2ECtpEdX9+/9dLR7x09dcfDAf9vMW2x5JF/7yX/740tPPvPLWm9dKCa8plRrMRFZSnFM6GKM+k+U+9x7PznbpbPrxQQ455O0g3wJ1/6WybL5co7PvgDS+UbZlHZNp4PrJkKvOiAbLNeg+opuKQ6S1Vo1q1N322efp8hCxt2ZxgE+ad3lH752addq//YEj0OIcKwHIcnEJDI3hY0aXyxPVJb3XHzxb8j9t3x987V+DarcsZ//r3sXn33+6uDQy0yutpieCd9opsMPtFZrmNghsYM4JhBTjkW2W2cplyu0mk1KScJEmp8ehD5nV1RxceJf1wiIX2tXrV+gIQto5MF46f0aJoSu/SGE04YhjKKQpqBbFCUk9Nq0gjV+0XpBCMMSacIyOOfjLkGIuoTEWFwQpiuPOwhNFFq3nLQiSpNVZG4Ktk1R3bGNZ7/6rffbbefF2954OcyXH774jt86fgpe59SjfsNvf19jrfGbxw6/QnNllWS9SXl1lWpzHdThrJcPJSflbQs73mFEApdYXBIjYpiIHEHkuoWZc1jr0tRvbTPIFojf6eDKFej0x0m1bXryozpaUQL/IwxCRIQ4FBpTJWwgGFMiLIVobJdOHFn8WiNdVLJSmaQchrj5KbhwB/Uy6OwEU7sWnrz0D379Vzb/VlvHyBhzev31ZWaveheCaa2vs7687E8cXyc5vgoJWOuXs0tswnp9PbdhEyC0W1sVNEiGn7MbuDiyj8UkQXs/Q2WqQjnw2dWz22a9kR0EsHMbVELCyiST27YBbkk+9h8f2UJ1CxQoUKBAgQIFChQoUKBAgQIFChQoUKBAgQIFChQoUKBAgQIFChQoUKBAgQIFNsT/B+a8C0LCHjG4AAAAAElFTkSuQmCC" + }, + youtubeMusic: { + name: "YouTube Music", + logo: "data:image/png;base64,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" + }, + google: { + name: "Google", + logo: "data:image/png;base64,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" + }, + googleMusic: { + name: "Google Music", + logo: "data:image/png;base64,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" + }, + pandora: { + name: "Pandora", + logo: "data:image/png;base64,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" + }, + deezer: { + name: "Deezer", + logo: "data:image/png;base64,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" + }, + tidal: { + name: "Tidal", + logo: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABHNCSVQICAgIfAhkiAAACl5JREFUeJztnUuMFNcZhc/f0z0DJIqlgaFnTGQ7jhXkECKNeSYgeel9so8UsWUFA0iIxwKQALHAjuI4ThDC4OAFSAjCY9g5C9sRiSMF2eBsjDQxzANYOIEJM9N1sugqaHqqp29V3Ud19f2kXjB01f/XPafvq+reEhQUkkMA3gLwJoCfAViZ8BRfAfgEwF8AjIrIPb0ZerRBcg3J0yQDmicIY61xfd1dC8mlJD+yILYqZ0kOuC6XNIjrBFQhWQXwZwBrXefShhsAfiEi/3adiAol1wm0g+TvSBLAOPIvPgCsAzBGkrVa7T3XybQjlzVAWJ1+DuD7rnPRxASA1SIy5TqRZnJlAJKvAbgFoOw6F0PMAVglIv9ynUhELgzA+pDtDoBex6nYYhbAy3kYWjo1AEkB8A2AIZd5OOSOiPzAZQLOOoEk3wYQoHvFB4BXwmHkO64SsF4DkFwKYMpF7JxDAEMiMmEzqNUagOQfAdyHFz8OATBO8pTtoMYh2QNgGkDFRrwCMAdgkYjUTAcyXgOQXIv6BXnx1SkDmCO53nQgowYIZ8JumIxRcP4aNpvGMNYEkPwSwOumzt9l3BYRI2VpxABBEDwSkSUmzt3FTJsoU+0GIFlDB9xk6lACEenReUKtBgjv2nkMIyLadNN2Ii++XXSZQEtVTTLQcR6POmFTm5nMBiD5CH5mzwUlkv/NfJIsB4dDPd/bd8d3SH6R5QSpDUDyffhxfh74McnUj56lqrpZfyT6b2mDeoywTkQSa5LYAOGNnbmkx3msUE56AylNEzCd4hiPHf6X9IBEBiB5Ev6uXp4pkzyR5ADlJiB8VHsycUoeF1RFREmrJAYIknzf4xbVmUKlJiB8aNGL30GQ/I3K99qKGj667ad6O5OSiCx4j0alBvhGUzIe+4y1+8KCBiD5Irr7uf1OZ0W46qolCzYBJJ+ge5ZrFZUZEelr9Z8tawCSP4IXvwj0hotuY2lZA5CcRXFX6XYbsyIS+2OOrQHCSR8vfnGokHwh7j9aNQH/NJiMxw034/4Y2wT45/uKSdzs4LwaIMvDBZ58Q/Ld5r/Nc4T/9Reb5lrguRqA5Aq76XhsQ3Kw8d/NTcAFi7l43PCcxs9VB7767w4am4GnNQDJZW7Smc/Zs2chIqjVzO6PsGXLFvT1tZwl1cK5c+cgIgiC/NxQJdkf98dc7L17/vx5or5fDgGwVqsZibN169anMSqVipEYV65csXItKfgwzgDOuXjx4nMFZqrgtm/fPi9Gb2+v1hjXrl2zci1pyZ0BLl++HFtgugtuZGSkZQxdNUHzLz+PJmgWf43LZFr9WnQX3I4dO9rGyGoCW9eigeFGA5xxlYVqgWUtOBXxs5rg6tWrVq5FEx80GsBJJknFT1twC1X7ukyQVPwcmODZEMtF9LTiJy24NOInNUFa8V2bwJkBsoqvWnDbtm3LHKOdCdp1+PJsgkj8IZtBR0dHtRRYu4LL8stv/pTL5dgYly5dsnItBqmC5K9tRbt+/brWAmtVcPv27dMeo1QqPRej1ZxFh5ngVyB50kYk3b/8VgV39OhRYzEiE1y4cMHKtVjgBEjeNh1lbGzMaIFFn9OnTxuPEa60Mf6xxC3QzssWuWfPHqMFNjk5SZJcvXq18V+nzv5F3GdsbMyGJCQZWB0B7N2716j4EcPDw8bEjzBlgrt379qUhNaHgLprgmbxI9auXWtM/Ii4m0pZPuPj46aLfx5OJoF01QStxI/YsGGDMfEjdNUEExMTuotZCWd3AbMO1dqJH5HVBCo98qwmcPHLj3B6GzitCVTFj0hrgiTDsbQmcCk+6dgAJLl//36j4kesX78+UZwgSD44StoncC0+mQMDkOSBAweMih+hWhOkET9CtSa4d+9epmvRRS4MQLY3QVbxIzZu3GhM/Ih2JsiL+GSODECSBw8eNCp+xKZNm4yJH9HKBHkSn8yZAcj5JtAtfsTmzZuNiR/RbIKpqSntMbJibSo4CYcOHTIqfkRkAhPiR0QmePjwobEYGQiE5G0AK5EzHj9+jCVLzL+KoFaroadH63uY5jE9PY3FixcbjZGSWyUAn7rOIg4b4lerVZTLZeMrkHIqPgB8WgLwsessXFCtVjE5Wd9O14YJcsrHwvo+cnddZ2KTRvEbmZubM94c5IxBAdBVq4JbiR9Rq9VQKnXHey9FRLrjSkPaiQ8APT09uVrJa5rIAIW/YhXxI7rEBAHwzAB/cpiIcQYHB5XFj+gCE5wBwh1CWOC3gA0ODmJiYiL18QXuGL4hIv94ulVIETuCWcWPKKIJom1iCtsJrFarWsQH6vMEc3PFfFNeowE+cpaFZpJ0+FSpVCpFMsHTLWIam4ClAO47SUcjJsRvZGZmBpVKx785b6mIPAQaagAReeAuHz0cO3bMqPgAMDTU+S9QicQH5vcBbljORSsjIyMYGRkxdv7+/n7cv9/xleRnLf+H5IvO7kxrJMl2MKqf/v5+15eli0EshOvsdLFz504vfgzNescNAwuxXfyRI0ewa9euzOfp7+/Hgwcd3z2KmLddfCyuXaqT3bt3p/7lL1u2zHX6WlG2CUm7S1QNk2YtYtHEJ6n+AlCSA66z1U2SVckDA4W7fLI+z6MOyRnXGetGxQQFFX8mkfihAV5znbUJFmoOCio+Sf4wsQFCEzxxnbkJ4mqC5cuXu07LFE9SiR8awOoegjZpHB0UsMPXyIITPwu+PDo0wdcAXkntohxz+PBhHD9+HOPj465TMcXXIvLqQl9oawCgmA+LdANxL4psRvWBkHcy5uKxz3GVLynVAABAMkjyfY9TqPrIf5JHwqopk/HYZ7nqF5UNICJTQRCcTJePxyInRET5oYXEVTrrs0od/0xUQZkVkd4kB6R5Kji3a509WJT0gMQGEJEagDVJj/MYZ52IJF7KlGpdgIh8DuD9NMd6jPCeiKRa2ZVpWEfyJoCfZDmHJzNfisiqtAdnHteT/A+A72Y9jycVj0QkU9lrmdhh/R10hV1mllNqIlLOehJtM3v+foFVlGf62qF1atdPF9tB5SaPKlqr7dCVXbndliUCneIDBtrtsF16pPu8HjwSEe2bFBjpuIU905smzt2lfJG1t98KYz13EfkpgN+aOn8X8XsRMTbXYrzDRvINAH83HaegrAlnXY1hpcdOsgfANPxdRFXmAPSlmdtPipXJGxGphbcp/2AjXodzSkQqNsQHHIzZSQ4AMLuNR2dCAFURmbIZ1Pr0rYhMhWNZ/6DpM94WkZJt8QHHs3YkBcAYgBUu83BI2+f2TZOLadtw9codAH2OU7ECyVkReUlEnK9IycUdPBEZF5FFAF4FMOs6H4PMAlhZKpV68yA+kJMaoBmS3yN5U0Recp2LJiYArMrjVny5qAGaEZFvS6XSy2FnUW1fm3zyrtQZzKP4QE5rgDjC4eMlABtc59KGzwD8UkS66jU8ViH5AskPba6xbsMZkstcl0vXQnKY5Ack1d/3np4ayVMkh11ftw46pglICskqgLcAvAng56i/HFP1egngKwCfoP5avVER0bP3fM74P9gRehaJC10dAAAAAElFTkSuQmCC" + }, + amazonStore: { + name: "Amazon Store", + logo: "data:image/png;base64,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" + }, + soundcloud: { + name: "SoundCloud", + logo: "data:image/png;base64,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" + }, + napster: { + name: "Napster", + logo: "data:image/png;base64,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" + }, + yandex: { + name: "Yandex", + logo: "data:image/png;base64,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" + }, + spinrilla: { + name: "Spinrilla", + logo: "data:image/png;base64,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" + }, + audius: { + name: "Audius", + logo: "data:image/png;base64,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" + }, + audiomack: { + name: "Audiomack", + logo: "data:image/png;base64,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" + }, + anghami: { + name: "Anghami", + logo: "data:image/png;base64,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" + }, + boomplay: { + name: "Boomplay", + logo: "data:image/png;base64,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" + }, + bandcamp: { + name: "Bandcamp", + logo: "data:image/png;base64,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" + } +}; + +export default Providers; diff --git a/src/equicordplugins/songLink.desktop/Settings.tsx b/src/equicordplugins/songLink.desktop/Settings.tsx new file mode 100644 index 000000000..78bfd316a --- /dev/null +++ b/src/equicordplugins/songLink.desktop/Settings.tsx @@ -0,0 +1,62 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 nin0 + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { FormSwitch } from "@components/FormSwitch"; +import { HeadingTertiary } from "@components/Heading"; + +import { settings } from "."; +import Providers from "./Providers"; + +export function Settings() { + const blazinglyFastSettings = settings.use(["servicesSettings"]); + + return + { + Object.entries(Providers).map(provider => + + + + {provider[1].name} + + + + settings.store.servicesSettings[provider[0]].enabled = v} + /> + { + /* @ts-ignore */ + provider[1].native && settings.store.servicesSettings[provider[0]].openInNative = v} + /> + } + + ) + } + ; +} diff --git a/src/equicordplugins/songLink.desktop/SongLinker.tsx b/src/equicordplugins/songLink.desktop/SongLinker.tsx new file mode 100644 index 000000000..01d6ca45f --- /dev/null +++ b/src/equicordplugins/songLink.desktop/SongLinker.tsx @@ -0,0 +1,61 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 nin0 + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { BaseText } from "@components/BaseText"; +import { findComponentByCodeLazy } from "@webpack"; +import { Button, Card, useEffect, useState } from "@webpack/common"; + +import pl, { Native, settings, SongLinkResult } from "."; +import Providers from "./Providers"; + +const HeadphonesIcon = findComponentByCodeLazy("4.1-2.13h1.86A9"); + +export default function SongLinker({ url }: { url: string; }) { + const [songData, setSongData] = useState(); + + useEffect(() => { + async function doStuff() { + if (pl.cache[url]) return void setSongData(pl.cache[url]); + + const sd = await Native.getTrackData(url); + setSongData(sd); + pl.addToCache(url, sd); + } + doStuff(); + }); + + return + { + songData ? + + + {songData.info?.title} - {songData.info?.artist} + + + { + Object.keys(songData.links).map(service => settings.store.servicesSettings[service].enabled && { + // FIXME: fix type error + // @ts-expect-error ??? + VencordNative.native.openExternal(settings.store.servicesSettings[service].openInNative && Providers[service].native ? songData.links[service].nativeUri : songData.links[service].url); + }}> + + ) + } + + + : Loading song link... + } + ; +} diff --git a/src/equicordplugins/songLink.desktop/index.tsx b/src/equicordplugins/songLink.desktop/index.tsx new file mode 100644 index 000000000..2961f1bd7 --- /dev/null +++ b/src/equicordplugins/songLink.desktop/index.tsx @@ -0,0 +1,87 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 nin0 + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType, PluginNative } from "@utils/types"; + +import Providers from "./Providers"; +import { Settings } from "./Settings"; +import SongLinker from "./SongLinker"; + +export const settings = definePluginSettings({ + servicesSettings: { + type: OptionType.CUSTOM, + description: "settings for services", + default: Object.fromEntries(Object.entries(Providers).map(([name, data]) => [name, { + enabled: true, + // @ts-ignore + openInNative: data.native || false + }])) + }, + userCountry: { + type: OptionType.STRING, + description: "Country used for lookup (Two letter country code)", + default: "US" + }, + servicesComponent: { + type: OptionType.COMPONENT, + component: () => + } +}); + +export type SongLinkResult = { + info?: { + title: string; + artist: string; + }; + links: { + [platform: string]: { + url: string; + nativeUri?: string; + }; + }; +}; + +export const Native = VencordNative.pluginHelpers.SongLink as PluginNative; + +export default definePlugin({ + name: "SongLink", + description: "Adds streaming service buttons below song links", + authors: [Devs.nin0dev], + settings, + Providers, + cache: ({} as Record), + addToCache(link, data: SongLinkResult) { + this.cache[link] = data; + }, + renderMessageAccessory(props: Record) { + const { content }: { + content: string; + } = props.message; + if (!content) return; + + const regexes = [ + /https:\/\/(?:open|play)\.spotify\.com\/track\/[a-zA-Z0-9]+/, // spotify + /https:\/\/(music|itunes)\.apple\.com\/[a-z]{2}\/album\/\S+/, // apple music/itunes + /https:\/\/music\.youtube\.com\/watch\?v=[0-9A-Za-z_-]+/, // yt music + /https:\/\/tidal\.com\/track\/[0-9]+\/u/ // tidal + ]; + const musicLinks = content.match(new RegExp(regexes.map(r => r.source).join("|"), "g")); + if (!musicLinks?.length) return; + + return + { + musicLinks.map(item => ) + } + ; + } +}); diff --git a/src/equicordplugins/songLink.desktop/native.ts b/src/equicordplugins/songLink.desktop/native.ts new file mode 100644 index 000000000..f4a501f08 --- /dev/null +++ b/src/equicordplugins/songLink.desktop/native.ts @@ -0,0 +1,40 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 nin0 + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { RendererSettings } from "@main/settings"; + +type SongLinkResult = { + info?: { + title: string; + artist: string; + }; + links: { + [platform: string]: { + url: string; + nativeUri?: string; + }; + }; +}; + +export async function getTrackData(_, trackURL: string): Promise { + const url = new URL("https://api.song.link/v1-alpha.1/links"); + url.searchParams.set("url", trackURL); + url.searchParams.set("userCountry", RendererSettings.store.plugins?.SongLink.userCountry || "US"); + const raw = await fetch(url.toString()).then(u => u.json()); + const [, entry]: any = Object.entries(raw.entitiesByUniqueId) + .find(([key]) => !key.includes("YOUTUBE")) || []; + const possibleTrackInfo = entry + ? { title: (entry.title as string), artist: (entry.artistName as string) } + : null; + return { + // @ts-ignore + info: possibleTrackInfo, + links: Object.fromEntries(Object.entries(raw.linksByPlatform).map(([name, data]) => [name, { + url: data.url, + nativeUri: data.nativeAppUriDesktop + }])) + }; +} diff --git a/src/equicordplugins/soundBoardLogger/components/Icons.tsx b/src/equicordplugins/soundBoardLogger/components/Icons.tsx index b1b2ae713..b4314d169 100644 --- a/src/equicordplugins/soundBoardLogger/components/Icons.tsx +++ b/src/equicordplugins/soundBoardLogger/components/Icons.tsx @@ -42,7 +42,7 @@ export function IconWithTooltip({ text, icon, onClick }) { aria-haspopup="dialog" aria-label={text} size="" - look={Button.Looks.LINK} + look={Button.Looks.FILLED} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} className={ButtonWrapperClasses.button} diff --git a/src/equicordplugins/themeLibrary/components/LikesComponent.tsx b/src/equicordplugins/themeLibrary/components/LikesComponent.tsx index 237fc9f68..dc6623c59 100644 --- a/src/equicordplugins/themeLibrary/components/LikesComponent.tsx +++ b/src/equicordplugins/themeLibrary/components/LikesComponent.tsx @@ -82,7 +82,7 @@ export const LikesComponent = ({ themeId, likedThemes: initialLikedThemes }: { t handleLikeClick(themeId)} size={Button.Sizes.MEDIUM} color={Button.Colors.PRIMARY} - look={Button.Looks.LINK} + look={Button.Looks.FILLED} disabled={themeId === "preview"} style={{ marginLeft: "8px" }} > diff --git a/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx b/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx index e864052fb..c16f2a0ed 100644 --- a/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx +++ b/src/equicordplugins/themeLibrary/components/ThemeInfoModal.tsx @@ -141,7 +141,7 @@ export const ThemeInfoModal: React.FC = ({ author, theme, . modalProps.onClose()} > Close @@ -186,14 +186,14 @@ export const ThemeInfoModal: React.FC = ({ author, theme, . props.onClose()} > Close { diff --git a/src/equicordplugins/voiceButtons/utils.tsx b/src/equicordplugins/voiceButtons/utils.tsx index 7c8dd7532..d6982df49 100644 --- a/src/equicordplugins/voiceButtons/utils.tsx +++ b/src/equicordplugins/voiceButtons/utils.tsx @@ -31,7 +31,7 @@ function VoiceUserButton({ user, tooltip, icon, onClick }: { user: User; tooltip { e.preventDefault(); diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 434518cff..39c84cb8e 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -15,10 +15,10 @@ import { VencordTab, } from "@components/settings"; import ThemesTab from "@components/ThemeSettings/ThemesTab"; +import { gitHashShort } from "@shared/vencordUserAgent"; import { Devs } from "@utils/constants"; import { getIntlMessage } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; -import { shortGitHash } from "@utils/updater"; import { React } from "@webpack/common"; type SectionType = "HEADER" | "DIVIDER" | "CUSTOM"; @@ -285,7 +285,7 @@ export default definePlugin({ getInfoRows() { const { electronVersion, chromiumVersion, getVersionInfo } = this; - const rows = [`Equicord ${shortGitHash()}${getVersionInfo()}`]; + const rows = [`Equicord ${gitHashShort}${getVersionInfo()}`]; if (electronVersion) rows.push(`Electron ${electronVersion}`); if (chromiumVersion) rows.push(`Chromium ${chromiumVersion}`); diff --git a/src/plugins/_core/supportHelper.tsx b/src/plugins/_core/supportHelper.tsx index 030645ae0..0a30f49fe 100644 --- a/src/plugins/_core/supportHelper.tsx +++ b/src/plugins/_core/supportHelper.tsx @@ -25,6 +25,8 @@ import { Flex } from "@components/Flex"; import { Link } from "@components/Link"; import { Paragraph } from "@components/Paragraph"; import { openUpdaterModal } from "@components/settings/tabs/updater"; +import { platformName } from "@equicordplugins/equicordHelper/utils"; +import { gitHashShort } from "@shared/vencordUserAgent"; import { CONTRIB_ROLE_ID, Devs, DONOR_ROLE_ID, EQUIBOP_CONTRIB_ROLE_ID, EQUICORD_TEAM, GUILD_ID, SUPPORT_CHANNEL_ID, SUPPORT_CHANNEL_IDS, VC_CONTRIB_ROLE_ID, VC_DONOR_ROLE_ID, VC_GUILD_ID, VC_REGULAR_ROLE_ID, VC_SUPPORT_CHANNEL_IDS, VENCORD_CONTRIB_ROLE_ID } from "@utils/constants"; import { sendMessage } from "@utils/discord"; import { Logger } from "@utils/Logger"; @@ -34,7 +36,7 @@ import { relaunch } from "@utils/native"; import { onlyOnce } from "@utils/onlyOnce"; import { makeCodeblock } from "@utils/text"; import definePlugin from "@utils/types"; -import { checkForUpdates, isOutdated, shortGitHash, update } from "@utils/updater"; +import { checkForUpdates, isOutdated, update } from "@utils/updater"; import { Alerts, Button, Card, ChannelStore, GuildMemberStore, Parser, PermissionsBits, PermissionStore, RelationshipStore, SelectedChannelStore, showToast, Toasts, UserStore } from "@webpack/common"; import { JSX } from "react"; @@ -71,41 +73,20 @@ async function forceUpdate() { return outdated; } -function getWindowsName(release: string) { - const build = parseInt(release.split(".")[2]); - if (build >= 22000) return "Windows 11"; - if (build >= 10240) return "Windows 10"; - if (build >= 9200) return "Windows 8.1"; - if (build >= 7600) return "Windows 7"; - return `Windows (${release})`; -} - -function getMacOSName(release: string) { - const major = parseInt(release.split(".")[0]); - if (major === 24) return "MacOS 15 (Sequoia)"; - if (major === 23) return "MacOS 14 (Sonoma)"; - if (major === 22) return "MacOS 13 (Ventura)"; - if (major === 21) return "MacOS 12 (Monterey)"; - if (major === 20) return "MacOS 11 (Big Sur)"; - if (major === 19) return "MacOS 10.15 (Catalina)"; - return `MacOS (${release})`; -} - -function platformName() { - if (typeof DiscordNative === "undefined") return navigator.platform; - if (DiscordNative.process.platform === "win32") return `${getWindowsName(DiscordNative.os.release)}`; - if (DiscordNative.process.platform === "darwin") return `${getMacOSName(DiscordNative.os.release)} (${DiscordNative.process.arch === "arm64" ? "Apple Silicon" : "Intel Silicon"})`; - if (DiscordNative.process.platform === "linux") return `Linux (${DiscordNative.os.release})`; - return DiscordNative.process.platform; -} - async function generateDebugInfoMessage() { const { RELEASE_CHANNEL } = window.GLOBAL_ENV; const client = (() => { if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`; if (IS_VESKTOP) return `Vesktop v${VesktopNative.app.getVersion()}`; - if (IS_EQUIBOP) return `Equibop v${VesktopNative.app.getVersion()}`; + if (IS_EQUIBOP) { + const equibopGitHash = tryOrElse(() => VesktopNative.app.getGitHash?.(), null); + if (equibopGitHash) { + const shortHash = equibopGitHash.slice(0, 7); + return `Equibop v${VesktopNative.app.getVersion()} • [${shortHash}]()`; + } + return `Equibop v${VesktopNative.app.getVersion()}`; + } if ("legcord" in window) return `LegCord v${window.legcord.version}`; // @ts-expect-error @@ -115,8 +96,8 @@ async function generateDebugInfoMessage() { const info = { Equicord: - `v${VERSION} • [${shortGitHash()}]()` + - `${SettingsPlugin.getVersionInfo()} - ${Intl.DateTimeFormat("en-GB", { dateStyle: "medium" }).format(BUILD_TIMESTAMP)}`, + `v${VERSION} • [${gitHashShort}]()` + + `${IS_EQUIBOP ? "" : SettingsPlugin.getVersionInfo()} - ${Intl.DateTimeFormat("en-US", { dateStyle: "medium" }).format(BUILD_TIMESTAMP)}`, Client: `${RELEASE_CHANNEL} ~ ${client}`, Platform: platformName() }; @@ -138,6 +119,7 @@ async function generateDebugInfoMessage() { "Activity Sharing Disabled": tryOrElse(() => !ShowCurrentGame.getSetting(), false), "Link Embeds Disabled": tryOrElse(() => !ShowEmbeds.getSetting(), false), "Equicord DevBuild": !IS_STANDALONE, + "Equibop DevBuild": IS_EQUIBOP && tryOrElse(() => VesktopNative.app.isDevBuild?.(), false), "Has UserPlugins": Object.values(PluginMeta).some(m => m.userPlugin), ">2 Weeks Outdated": BUILD_TIMESTAMP < Date.now() - 12096e5, [`Potentially Problematic Plugins: ${potentiallyProblematicPlugins.join(", ")}`]: potentiallyProblematicPlugins.length @@ -360,7 +342,7 @@ export default definePlugin({ ); } - if (isSupportChannel(props.channel.id) && PermissionStore.can(PermissionsBits.SEND_MESSAGES, props.channel)) { + if (isSupportChannel(props.channel.id) && PermissionStore.can(PermissionsBits.SEND_MESSAGES, props.channel) && equicordSupport) { if (props.message.content.includes("/equicord-debug") || props.message.content.includes("/equicord-plugins")) { buttons.push( { return socket.application; } -let hideSetting = false; let ws: WebSocket; -if (IS_VESKTOP || IS_EQUIBOP || "legcord" in window) { - hideSetting = true; -} else if ("goofcord" in window) { - hideSetting = false; -} - migratePluginSettings("WebRichPresence", "WebRichPresence (arRPC)"); export default definePlugin({ name: "WebRichPresence", description: "Client plugin for arRPC to enable RPC on Discord Web (experimental)", authors: [Devs.Ducko], reporterTestable: ReporterTestable.None, - hidden: hideSetting, + hidden: !IS_EQUIBOP && !IS_VESKTOP && !("legcord" in window), settingsAboutComponent: () => ( <> diff --git a/src/plugins/expressionCloner/index.tsx b/src/plugins/expressionCloner/index.tsx index 7f571c530..52e049fa1 100644 --- a/src/plugins/expressionCloner/index.tsx +++ b/src/plugins/expressionCloner/index.tsx @@ -28,6 +28,7 @@ import { Margins } from "@utils/margins"; import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal"; import definePlugin from "@utils/types"; import { Guild, GuildSticker } from "@vencord/discord-types"; +import { StickerFormatType } from "@vencord/discord-types/enums"; import { findByCodeLazy } from "@webpack"; import { Constants, EmojiStore, FluxDispatcher, GuildStore, IconUtils, Menu, PermissionsBits, PermissionStore, React, RestAPI, StickersStore, Toasts, Tooltip, UserStore } from "@webpack/common"; import { Promisable } from "type-fest"; @@ -49,13 +50,32 @@ interface Emoji { type Data = Emoji | Sticker; -const StickerExt = [, "png", "png", "json", "gif"] as const; +const StickerExtMap = { + [StickerFormatType.PNG]: "png", + [StickerFormatType.APNG]: "png", + [StickerFormatType.LOTTIE]: "json", + [StickerFormatType.GIF]: "gif" +} as const; + +const PremiumTierStickerLimitMap = { + 0: 5, + 1: 15, + 2: 30, + 3: 60 +} as const; + +function getGuildMaxStickerSlots(guild: Guild) { + if (guild.features.has("MORE_STICKERS") && guild.premiumTier === 3) + return 120; + + return PremiumTierStickerLimitMap[guild.premiumTier] ?? PremiumTierStickerLimitMap[0]; +} function getUrl(data: Data) { if (data.t === "Emoji") return `${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${data.id}.${data.isAnimated ? "gif" : "png"}?size=4096&lossless=true`; - return `${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${data.id}.${StickerExt[data.format_type]}?size=4096&lossless=true`; + return `${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${data.id}.${StickerExtMap[data.format_type]}?size=4096&lossless=true`; } async function fetchSticker(id: string) { @@ -120,17 +140,25 @@ function getGuildCandidates(data: Data) { (PermissionStore.getGuildPermissions({ id: g.id }) & PermissionsBits.CREATE_GUILD_EXPRESSIONS) === PermissionsBits.CREATE_GUILD_EXPRESSIONS; if (!canCreate) return false; - if (data.t === "Sticker") return true; + if (data.t === "Sticker") { + const stickerSlots = getGuildMaxStickerSlots(g); + const stickers = StickersStore.getStickersByGuildId(g.id); + + return !stickers || stickers.length < stickerSlots; + } const { isAnimated } = data as Emoji; const emojiSlots = getGuildMaxEmojiSlots(g); - const { emojis } = EmojiStore.getGuilds()[g.id]; + const emojis = EmojiStore.getGuildEmoji(g.id); let count = 0; - for (const emoji of emojis) - if (emoji.animated === isAnimated && !emoji.managed) + for (const emoji of emojis) { + if (emoji.animated === isAnimated && !emoji.managed) { count++; + } + } + return count < emojiSlots; }).sort((a, b) => a.name.localeCompare(b.name)); } diff --git a/src/plugins/replaceGoogleSearch/index.tsx b/src/plugins/replaceGoogleSearch/index.tsx index f2b72e9ca..6bce7d6d9 100644 --- a/src/plugins/replaceGoogleSearch/index.tsx +++ b/src/plugins/replaceGoogleSearch/index.tsx @@ -22,6 +22,11 @@ const DefaultEngines = { Wikipedia: "https://wikipedia.org/w/index.php?search=", } as const; +const enum ReplacementEngineValue { + OFF = "off", + CUSTOM = "custom", +} + const settings = definePluginSettings({ customEngineName: { description: "Name of the custom search engine", @@ -32,6 +37,15 @@ const settings = definePluginSettings({ description: "The URL of your Engine", type: OptionType.STRING, placeholder: "https://google.com/search?q=" + }, + replacementEngine: { + description: "Replace with a specific search engine instead of adding a menu", + type: OptionType.SELECT, + options: [ + { label: "Off", value: ReplacementEngineValue.OFF, default: true }, + { label: "Custom Engine", value: ReplacementEngineValue.CUSTOM }, + ...Object.keys(DefaultEngines).map(engine => ({ label: engine, value: engine })) + ] } }); @@ -40,13 +54,31 @@ function search(src: string, engine: string) { } function makeSearchItem(src: string) { - let Engines = {}; + const { customEngineName, customEngineURL, replacementEngine } = settings.store; - if (settings.store.customEngineName && settings.store.customEngineURL) { - Engines[settings.store.customEngineName] = settings.store.customEngineURL; + const hasCustomEngine = Boolean(customEngineName && customEngineURL); + const hasValidReplacementEngine = replacementEngine !== ReplacementEngineValue.OFF && !(replacementEngine === ReplacementEngineValue.CUSTOM && !hasCustomEngine); + + const Engines = { ...DefaultEngines }; + + if (hasCustomEngine) { + Engines[customEngineName!] = customEngineURL; } - Engines = { ...Engines, ...DefaultEngines }; + if (hasValidReplacementEngine) { + const name = replacementEngine === ReplacementEngineValue.CUSTOM && hasCustomEngine + ? customEngineName + : replacementEngine; + + return ( + search(src, Engines[name!])} + /> + ); + } return ( ); // iife so #__PURE__ works correctly diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 377282e33..ea0c8bce3 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -119,18 +119,24 @@ export function tryOrElse(func: () => T, fallback: T): T { } } -export function isEquicordGuild(id: string, isGuildId: boolean = false): boolean { +export function isEquicordGuild(id: string | null | undefined, isGuildId: boolean = false): boolean { + if (!id) return false; + if (isGuildId) return id === GUILD_ID; const channel = ChannelStore.getChannel(id); return channel.guild_id === GUILD_ID; } -export function isSupportChannel(channelId: string): boolean { +export function isSupportChannel(channelId: string | null | undefined): boolean { + if (!channelId) return false; + return channelId === SUPPORT_CHANNEL_ID; } -export function isEquicordSupport(userId: string): boolean { +export function isEquicordSupport(userId: string | null | undefined): boolean { + if (!userId) return false; + const member = GuildMemberStore.getMember(GUILD_ID, userId); return member?.roles?.includes(EQUICORD_HELPERS) || false; } diff --git a/src/utils/updater.ts b/src/utils/updater.ts index f8c800012..25fe6ee8c 100644 --- a/src/utils/updater.ts +++ b/src/utils/updater.ts @@ -28,11 +28,6 @@ export let isNewer = false; export let updateError: any; export let changes: Record<"hash" | "author" | "message", string>[]; - -export function shortGitHash(length = 7) { - return gitHash.slice(0, length); -} - async function Unwrap(p: Promise>) { const res = await p;