diff --git a/README.md b/README.md index 47f3723c..a04423c1 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,6 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - DisableAnimations by S€th - DisableCameras by Joona - DontFilterMe by Samwich -- EmojiDumper by Cortex, Samwich, Woosh - Encryptcord by Inbestigator - EquicordHelper by thororen & nyx - Equissant by SomeAspy & thororen @@ -77,6 +76,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - Glide by Samwich - GlobalBadges by HypedDomi & Hosted by Wolfie - GoogleThat by Samwich +- GuildPickerDumper by Cortex, Samwich, Woosh, thororen - HideChatButtons by iamme - HideServers by bepvte - HolyNotes by Wolfie diff --git a/src/equicordplugins/emojiDumper/index.tsx b/src/equicordplugins/emojiDumper/index.tsx deleted file mode 100644 index 9bc3f1c7..00000000 --- a/src/equicordplugins/emojiDumper/index.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; -import { Devs, EquicordDevs } from "@utils/constants"; -import definePlugin from "@utils/types"; -import { Menu } from "@webpack/common"; -import type { Guild } from "discord-types/general"; -import { zipSync } from "fflate"; - -const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => { - // Assuming "privacy" is the correct ID for the group you want to modify. - const group = findGroupChildrenByChildId("privacy", children); - - if (group) { - group.push( - zipServerEmojis(guild)}> - ); - } -}; - -export default definePlugin({ - name: "EmojiDumper", - description: "Context menu to dump and download a server's emojis.", - authors: [EquicordDevs.Cortex, Devs.Samwich, EquicordDevs.Woosh], - contextMenus: { - "guild-context": Patch, - "guild-header-popout": Patch - } -}); - -async function zipServerEmojis(guild: Guild) { - - const emojis = Vencord.Webpack.Common.EmojiStore.getGuilds()[guild.id]?.emojis; - if (!emojis) { - return console.log("Server not found!"); - } - - const fetchEmojis = async e => { - const filename = e.id + (e.animated ? ".gif" : ".png"); - const emoji = await fetch("https://cdn.discordapp.com/emojis/" + filename + "?size=512&quality=lossless").then(res => res.blob()); - return { file: new Uint8Array(await emoji.arrayBuffer()), filename }; - }; - const emojiPromises = emojis.map(e => fetchEmojis(e)); - - Promise.all(emojiPromises) - .then(results => { - const emojis = zipSync(Object.fromEntries(results.map(({ file, filename }) => [filename, file]))); - const blob = new Blob([emojis], { type: "application/zip" }); - const link = document.createElement("a"); - link.href = URL.createObjectURL(blob); - link.download = `${guild.name}-emojis.zip`; - link.click(); - link.remove(); - }) - .catch(error => { - console.error(error); - }); -} diff --git a/src/equicordplugins/guildPickerDumper/index.tsx b/src/equicordplugins/guildPickerDumper/index.tsx new file mode 100644 index 00000000..1e9ad32a --- /dev/null +++ b/src/equicordplugins/guildPickerDumper/index.tsx @@ -0,0 +1,75 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { migratePluginSettings } from "@api/Settings"; +import { Devs, EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { EmojiStore, Menu, StickersStore } from "@webpack/common"; +import type { Guild } from "discord-types/general"; +import { zipSync } from "fflate"; + +const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => { + // Assuming "privacy" is the correct ID for the group you want to modify. + const group = findGroupChildrenByChildId("privacy", children); + + if (group) { + group.push( + <> + zipGuildAssets(guild, "emojis")}> + zipGuildAssets(guild, "stickers")}> + + ); + } +}; + +async function zipGuildAssets(guild: Guild, type: "emojis" | "stickers") { + const isEmojis = type === "emojis"; + const items = isEmojis + ? EmojiStore.getGuilds()[guild.id]?.emojis + : StickersStore.getStickersByGuildId(guild.id); + + if (!items) { + return console.log("Server not found!"); + } + + const fetchAsset = async e => { + const ext = e.animated ? ".gif" : ".png"; + const filename = e.id + ext; + const url = isEmojis + ? `https://${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/emojis/${filename}?size=512&quality=lossless` + : `https://${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${filename}?size=4096&lossless=true`; + + const response = await fetch(url); + const blob = await response.blob(); + return { file: new Uint8Array(await blob.arrayBuffer()), filename }; + }; + + const assetPromises = items.map(e => fetchAsset(e)); + + Promise.all(assetPromises) + .then(results => { + const zipped = zipSync(Object.fromEntries(results.map(({ file, filename }) => [filename, file]))); + const blob = new Blob([zipped], { type: "application/zip" }); + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + link.download = `${guild.name}-${type}.zip`; + link.click(); + link.remove(); + }) + .catch(console.error); +} + +migratePluginSettings("GuildPickerDumper", "EmojiDumper"); +export default definePlugin({ + name: "GuildPickerDumper", + description: "Context menu to dump and download a server's emojis and stickers.", + authors: [EquicordDevs.Cortex, Devs.Samwich, EquicordDevs.Woosh, EquicordDevs.thororen], + contextMenus: { + "guild-context": Patch, + "guild-header-popout": Patch + } +}); diff --git a/src/equicordplugins/imagePreview/index.ts b/src/equicordplugins/imagePreview/index.ts index 486eb737..2d177f28 100644 --- a/src/equicordplugins/imagePreview/index.ts +++ b/src/equicordplugins/imagePreview/index.ts @@ -9,14 +9,11 @@ import "./styles.css"; import { EquicordDevs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin from "@utils/types"; -import { findStoreLazy } from "@webpack"; +import { StickersStore } from "@webpack/common"; import { getMimeType, isLinkAnImage, settings, stripDiscordParams } from "./settings"; const logger = new Logger("ImagePreview", "#FFFFFF"); -const StickerStore = findStoreLazy("StickersStore") as { - getStickerById(id: string): any; -}; let currentPreview: HTMLDivElement | null = null; let currentPreviewFile: HTMLImageElement | HTMLVideoElement | null = null; @@ -124,7 +121,7 @@ function loadImagePreview(url: string, sticker: boolean) { if (sticker) { const stickerId = url.split("/").pop()?.split(".")[0] ?? null; - const stickerData = stickerId ? StickerStore.getStickerById(stickerId) : null; + const stickerData = stickerId ? StickersStore.getStickerById(stickerId) : null; if (stickerData) { switch (stickerData.type) { diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 6adcb032..f560211d 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -24,7 +24,7 @@ import { getCurrentGuild, getEmojiURL } from "@utils/discord"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType, Patch } from "@utils/types"; import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; -import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; +import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, lodash, Parser, PermissionsBits, PermissionStore, StickersStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import type { Emoji } from "@webpack/types"; import type { Message } from "discord-types/general"; import { applyPalette, GIFEncoder, quantize } from "gifenc"; @@ -33,12 +33,6 @@ import type { ReactElement, ReactNode } from "react"; // @ts-ignore const premiumType = UserStore?.getCurrentUser()?._realPremiumType ?? UserStore?.getCurrentUser()?.premiumType ?? 0; -const StickerStore = findStoreLazy("StickersStore") as { - getPremiumPacks(): StickerPack[]; - getAllGuildStickers(): Map; - getStickerById(id: string): Sticker | undefined; -}; - const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore"); const BINARY_READ_OPTIONS = findByPropsLazy("readerFactory"); @@ -558,8 +552,8 @@ export default definePlugin({ const gifMatch = child.props.href.match(fakeNitroGifStickerRegex); if (gifMatch) { - // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker - if (StickerStore.getStickerById(gifMatch[1])) return null; + // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker + if (StickersStore.getStickerById(gifMatch[1])) return null; } } @@ -647,7 +641,7 @@ export default definePlugin({ url = new URL(item); } catch { } - const stickerName = StickerStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker"; + const stickerName = StickersStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker"; stickers.push({ format_type: 1, id: imgMatch[1], @@ -660,9 +654,9 @@ export default definePlugin({ const gifMatch = item.match(fakeNitroGifStickerRegex); if (gifMatch) { - if (!StickerStore.getStickerById(gifMatch[1])) continue; + if (!StickersStore.getStickerById(gifMatch[1])) continue; - const stickerName = StickerStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker"; + const stickerName = StickersStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker"; stickers.push({ format_type: 2, id: gifMatch[1], @@ -695,8 +689,8 @@ export default definePlugin({ const gifMatch = embed.url!.match(fakeNitroGifStickerRegex); if (gifMatch) { - // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker - if (StickerStore.getStickerById(gifMatch[1])) return true; + // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker + if (StickersStore.getStickerById(gifMatch[1])) return true; } } @@ -713,8 +707,8 @@ export default definePlugin({ const match = attachment.url.match(fakeNitroGifStickerRegex); if (match) { - // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker - if (StickerStore.getStickerById(match[1])) return false; + // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker + if (StickersStore.getStickerById(match[1])) return false; } return true; @@ -870,7 +864,7 @@ export default definePlugin({ if (!s.enableStickerBypass) break stickerBypass; - const sticker = StickerStore.getStickerById(extra.stickers?.[0]!); + const sticker = StickersStore.getStickerById(extra.stickers?.[0]!); if (!sticker) break stickerBypass; diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index 76804488..c238350e 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -54,6 +54,7 @@ export let RelationshipStore: Stores.RelationshipStore & t.FluxStore & { }; export let EmojiStore: t.EmojiStore; +export let StickersStore: t.StickersStore; export let ThemeStore: t.ThemeStore; export let WindowStore: t.WindowStore; export let DraftStore: t.DraftStore; @@ -86,5 +87,6 @@ waitForStore("GuildChannelStore", m => GuildChannelStore = m); waitForStore("MessageStore", m => MessageStore = m); waitForStore("WindowStore", m => WindowStore = m); waitForStore("EmojiStore", m => EmojiStore = m); +waitForStore("StickersStore", m => StickersStore = m); waitForStore("TypingStore", m => TypingStore = m); waitForStore("ThemeStore", m => ThemeStore = m); diff --git a/src/webpack/common/types/stores.d.ts b/src/webpack/common/types/stores.d.ts index 67148303..c54811b0 100644 --- a/src/webpack/common/types/stores.d.ts +++ b/src/webpack/common/types/stores.d.ts @@ -177,6 +177,17 @@ export class EmojiStore extends FluxStore { }; } +export class StickersStore extends FluxStore { + getStickerById(id: string): Sticker | undefined; + getStickerPack(id: string): StickerPack | undefined; + getPremiumPacks(): StickerPack[]; + isPremiumPack(id: string): boolean; + getRawStickersByGuild(): Map; + getAllStickersIterator(): IterableIterator; + getAllGuildStickers(): Map; + getStickersByGuildId(id: string): Sticker[] | undefined; +} + export interface DraftObject { channelId: string; timestamp: number;