diff --git a/src/plugins/betterNotes/index.tsx b/src/plugins/betterNotes/index.tsx index 5ebca1f8..cacdba5f 100644 --- a/src/plugins/betterNotes/index.tsx +++ b/src/plugins/betterNotes/index.tsx @@ -61,7 +61,7 @@ export default definePlugin({ find: ".popularApplicationCommandIds,", replacement: { match: /lastSection:(!?\i)}\),/, - replace: "$&$self.patchPadding($1)," + replace: "$&$self.patchPadding({lastSection:$1})," } } ], @@ -81,12 +81,10 @@ export default definePlugin({ } }, - patchPadding(lastSection: any) { - if (!lastSection) return; + patchPadding: ErrorBoundary.wrap(({ lastSection }) => { + if (!lastSection) return null; return ( - -
-
+
); - } + }) }); diff --git a/src/plugins/betterSessions/index.tsx b/src/plugins/betterSessions/index.tsx index 539508f8..9c93289c 100644 --- a/src/plugins/betterSessions/index.tsx +++ b/src/plugins/betterSessions/index.tsx @@ -22,7 +22,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy, findExportedComponentLazy, findStoreLazy } from "@webpack"; -import { React, RestAPI, Tooltip } from "@webpack/common"; +import { Constants, React, RestAPI, Tooltip } from "@webpack/common"; import { RenameButton } from "./components/RenameButton"; import { Session, SessionInfo } from "./types"; @@ -168,7 +168,7 @@ export default definePlugin({ async checkNewSessions() { const data = await RestAPI.get({ - url: "/auth/sessions" + url: Constants.Endpoints.AUTH_SESSIONS }); for (const session of data.body.user_sessions) { diff --git a/src/plugins/emoteCloner/index.tsx b/src/plugins/emoteCloner/index.tsx index cd9890a8..b456c351 100644 --- a/src/plugins/emoteCloner/index.tsx +++ b/src/plugins/emoteCloner/index.tsx @@ -24,7 +24,7 @@ import { Margins } from "@utils/margins"; import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal"; import definePlugin from "@utils/types"; import { findByPropsLazy, findStoreLazy } from "@webpack"; -import { EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common"; +import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common"; import { Promisable } from "type-fest"; const StickersStore = findStoreLazy("StickersStore"); @@ -64,7 +64,7 @@ async function fetchSticker(id: string) { if (cached) return cached; const { body } = await RestAPI.get({ - url: `/stickers/${id}` + url: Constants.Endpoints.STICKER(id) }); FluxDispatcher.dispatch({ @@ -83,7 +83,7 @@ async function cloneSticker(guildId: string, sticker: Sticker) { data.append("file", await fetchBlob(getUrl(sticker))); const { body } = await RestAPI.post({ - url: `/guilds/${guildId}/stickers`, + url: Constants.Endpoints.GUILD_STICKER_PACKS(guildId), body: data, }); @@ -322,8 +322,9 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) = switch (favoriteableType) { case "emoji": const match = props.message.content.match(RegExp(`|https://cdn\\.discordapp\\.com/emojis/${favoriteableId}\\.`)); - if (!match) return; - const name = match[1] ?? "FakeNitroEmoji"; + const reaction = props.message.reactions.find(reaction => reaction.emoji.id === favoriteableId); + if (!match && !reaction) return; + const name = (match && match[1]) ?? reaction?.emoji.name ?? "FakeNitroEmoji"; return buildMenuItem("Emoji", () => ({ id: favoriteableId, diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 91212b0f..498dca21 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -39,6 +39,7 @@ const StickerStore = findStoreLazy("StickersStore") as { const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore"); const ProtoUtils = findByPropsLazy("BINARY_READ_OPTIONS"); +const RoleSubscriptionEmojiUtils = findByPropsLazy("isUnusableRoleSubscriptionEmoji"); function searchProtoClassField(localName: string, protoClass: any) { const field = protoClass?.fields?.find((field: any) => field.localName === localName); @@ -408,6 +409,15 @@ export default definePlugin({ match: /canUseCustomNotificationSounds:function\(\i\){/, replace: "$&return true;" } + }, + // Allows the usage of subscription-locked emojis + { + find: "isUnusableRoleSubscriptionEmoji:function", + replacement: { + match: /isUnusableRoleSubscriptionEmoji:function/, + // replace the original export with a func that always returns false and alias the original + replace: "isUnusableRoleSubscriptionEmoji:()=>()=>false,isUnusableRoleSubscriptionEmojiOriginal:function" + } } ], @@ -807,6 +817,9 @@ export default definePlugin({ if (e.require_colons === false) return true; if (e.available === false) return false; + const isUnusableRoleSubEmoji = RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmojiOriginal ?? RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmoji; + if (isUnusableRoleSubEmoji(e, this.guildId)) return false; + if (this.canUseEmotes) return e.guildId === this.guildId || hasExternalEmojiPerms(channelId); else diff --git a/src/plugins/friendInvites/index.ts b/src/plugins/friendInvites/index.ts index e5ff447e..47e312c3 100644 --- a/src/plugins/friendInvites/index.ts +++ b/src/plugins/friendInvites/index.ts @@ -20,7 +20,7 @@ import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { RestAPI, UserStore } from "@webpack/common"; +import { Constants, RestAPI, UserStore } from "@webpack/common"; const FriendInvites = findByPropsLazy("createFriendInvite"); const { uuid4 } = findByPropsLazy("uuid4"); @@ -58,7 +58,7 @@ export default definePlugin({ if (uses === 1) { const random = uuid4(); const { body: { invite_suggestions } } = await RestAPI.post({ - url: "/friend-finder/find-friends", + url: Constants.Endpoints.FRIEND_FINDER, body: { modified_contacts: { [random]: [1, "", ""] diff --git a/src/plugins/invisibleChat.desktop/index.tsx b/src/plugins/invisibleChat.desktop/index.tsx index 78121f84..7575cf7e 100644 --- a/src/plugins/invisibleChat.desktop/index.tsx +++ b/src/plugins/invisibleChat.desktop/index.tsx @@ -23,7 +23,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { getStegCloak } from "@utils/dependencies"; import definePlugin, { OptionType } from "@utils/types"; -import { ChannelStore, FluxDispatcher, RestAPI, Tooltip } from "@webpack/common"; +import { ChannelStore, Constants, FluxDispatcher, RestAPI, Tooltip } from "@webpack/common"; import { Message } from "discord-types/general"; import { buildDecModal } from "./components/DecryptionModal"; @@ -149,7 +149,7 @@ export default definePlugin({ // Gets the Embed of a Link async getEmbed(url: URL): Promise { const { body } = await RestAPI.post({ - url: "/unfurler/embed-urls", + url: Constants.Endpoints.UNFURL_EMBED_URLS, body: { urls: [url] } diff --git a/src/plugins/messageLatency/index.tsx b/src/plugins/messageLatency/index.tsx index 48b57863..301e605f 100644 --- a/src/plugins/messageLatency/index.tsx +++ b/src/plugins/messageLatency/index.tsx @@ -24,19 +24,27 @@ interface Diff { seconds: number; } +const DISCORD_KT_DELAY = 1471228.928; const HiddenVisually = findExportedComponentLazy("HiddenVisually"); export default definePlugin({ name: "MessageLatency", description: "Displays an indicator for messages that took ≥n seconds to send", authors: [Devs.arHSM], + settings: definePluginSettings({ latency: { type: OptionType.NUMBER, description: "Threshold in seconds for latency indicator", default: 2 + }, + detectDiscordKotlin: { + type: OptionType.BOOLEAN, + description: "Detect old Discord Android clients", + default: true } }), + patches: [ { find: "showCommunicationDisabledStyles", @@ -46,6 +54,7 @@ export default definePlugin({ } } ], + stringDelta(delta: number) { const diff: Diff = { days: Math.round(delta / (60 * 60 * 24)), @@ -71,15 +80,25 @@ export default definePlugin({ ); }, ""); - return [ts || "0 seconds", diff.days === 17 && diff.hours === 1] as const; + return ts || "0 seconds"; }, + latencyTooltipData(message: Message) { + const { latency, detectDiscordKotlin } = this.settings.store; const { id, nonce } = message; // Message wasn't received through gateway if (!isNonNullish(nonce)) return null; - const delta = Math.round((SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce)) / 1000); + let isDiscordKotlin = false; + let delta = Math.round((SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce)) / 1000); + + // Old Discord Android clients have a delay of around 17 days + // This is a workaround for that + if (-delta >= DISCORD_KT_DELAY - 86400) { // One day of padding for good measure + isDiscordKotlin = detectDiscordKotlin; + delta += DISCORD_KT_DELAY; + } // Thanks dziurwa (I hate you) // This is when the user's clock is ahead @@ -87,15 +106,13 @@ export default definePlugin({ const abs = Math.abs(delta); const ahead = abs !== delta; - const [stringDelta, isSuspectedKotlinDiscord] = this.stringDelta(abs); - const isKotlinDiscord = ahead && isSuspectedKotlinDiscord; + const stringDelta = abs >= latency ? this.stringDelta(abs) : null; // Also thanks dziurwa // 2 minutes const TROLL_LIMIT = 2 * 60; - const { latency } = this.settings.store; - const fill: Fill = isKotlinDiscord + const fill: Fill = isDiscordKotlin ? ["status-positive", "status-positive", "text-muted"] : delta >= TROLL_LIMIT || ahead ? ["text-muted", "text-muted", "text-muted"] @@ -103,17 +120,24 @@ export default definePlugin({ ? ["status-danger", "text-muted", "text-muted"] : ["status-warning", "status-warning", "text-muted"]; - return abs >= latency ? { delta: stringDelta, ahead, fill, isKotlinDiscord } : null; + return (abs >= latency || isDiscordKotlin) ? { delta: stringDelta, ahead, fill, isDiscordKotlin } : null; }, + Tooltip() { return ErrorBoundary.wrap(({ message }: { message: Message; }) => { - const d = this.latencyTooltipData(message); if (!isNonNullish(d)) return null; + let text: string; + if (!d.delta) { + text = "User is suspected to be on an old Discord Android client"; + } else { + text = (d.ahead ? `This user's clock is ${d.delta} ahead.` : `This message was sent with a delay of ${d.delta}.`) + (d.isDiscordKotlin ? " User is suspected to be on an old Discord Android client." : ""); + } + return { @@ -126,8 +150,9 @@ export default definePlugin({ ; }); }, + Icon({ delta, fill, props }: { - delta: string; + delta: string | null; fill: Fill, props: { onClick(): void; @@ -147,7 +172,7 @@ export default definePlugin({ role="img" fill="none" style={{ marginRight: "8px", verticalAlign: -1 }} - aria-label={delta} + aria-label={delta ?? "Old Discord Android client"} aria-hidden="false" {...props} > diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index cf1feb97..287d58bb 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -27,6 +27,7 @@ import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { Button, ChannelStore, + Constants, FluxDispatcher, GuildStore, IconUtils, @@ -132,7 +133,7 @@ async function fetchMessage(channelID: string, messageID: string) { messageCache.set(messageID, { fetched: false }); const res = await RestAPI.get({ - url: `/channels/${channelID}/messages`, + url: Constants.Endpoints.MESSAGES(channelID), query: { limit: 1, around: messageID diff --git a/src/plugins/messageLogger/deleteStyleOverlay.css b/src/plugins/messageLogger/deleteStyleOverlay.css index 3778e80b..59e4ac66 100644 --- a/src/plugins/messageLogger/deleteStyleOverlay.css +++ b/src/plugins/messageLogger/deleteStyleOverlay.css @@ -1,3 +1,3 @@ .messagelogger-deleted { - background-color: rgba(240 71 71 / 15%) !important; + background-color: hsla(var(--red-430-hsl, 0 85% 61%) / 15%) !important; } diff --git a/src/plugins/messageLogger/deleteStyleText.css b/src/plugins/messageLogger/deleteStyleText.css index 8fb8bf12..3477ef22 100644 --- a/src/plugins/messageLogger/deleteStyleText.css +++ b/src/plugins/messageLogger/deleteStyleText.css @@ -1,19 +1,19 @@ /* Message content highlighting */ .messagelogger-deleted [class*="contents"] > :is(div, h1, h2, h3, p) { - color: #f04747 !important; + color: var(--status-danger, #f04747) !important; } /* Bot "thinking" text highlighting */ .messagelogger-deleted [class*="colorStandard"] { - color: #f04747 !important; + color: var(--status-danger, #f04747) !important; } /* Embed highlighting */ .messagelogger-deleted article :is(div, span, h1, h2, h3, p) { - color: #f04747 !important; + color: var(--status-danger, #f04747) !important; } .messagelogger-deleted a { - color: #be3535 !important; + color: var(--red-460, #be3535) !important; text-decoration: underline; } diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index d218c42c..370718d2 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -56,12 +56,12 @@ export default definePlugin({ find: ".UserProfileSections.USER_INFO_CONNECTIONS:", replacement: { match: /(?<={user:(\i),onClose:(\i)}\);)(?=case \i\.\i\.MUTUAL_FRIENDS)/, - replace: "case \"MUTUAL_GDMS\":return $self.renderMutualGDMs($1,$2);" + replace: "case \"MUTUAL_GDMS\":return $self.renderMutualGDMs({user: $1, onClose: $2});" } } ], - renderMutualGDMs: ErrorBoundary.wrap((user: User, onClose: () => void) => { + renderMutualGDMs: ErrorBoundary.wrap(({ user, onClose }: { user: User, onClose: () => void; }) => { const entries = ChannelStore.getSortedPrivateChannels().filter(c => c.isGroupDM() && c.recipients.includes(user.id)).map(c => ( { return ( - -
- {i18n.Messages.GUILD_INVITE_DISABLE_ACTION_SHEET_DESCRIPTION} - {this.showDisableInvites(guildId) && { - setChecked(true); - this.disableInvites(guildId); - }}> Pause Indefinitely.} -
-
+
+ {i18n.Messages.GUILD_INVITE_DISABLE_ACTION_SHEET_DESCRIPTION} + {showDisableInvites(guildId) && { + setChecked(true); + disableInvites(guildId); + }}> Pause Indefinitely.} +
); - } + }) }); diff --git a/src/plugins/pinDms/index.tsx b/src/plugins/pinDms/index.tsx index 010b5506..60484561 100644 --- a/src/plugins/pinDms/index.tsx +++ b/src/plugins/pinDms/index.tsx @@ -83,7 +83,7 @@ export default definePlugin({ // Rendering { match: /"renderRow",(\i)=>{(?<="renderDM",.+?(\i\.default),\{channel:.+?)/, - replace: "$&if($self.isChannelIndex($1.section, $1.row))return $self.renderChannel($1.section,$1.row,$2);" + replace: "$&if($self.isChannelIndex($1.section, $1.row))return $self.renderChannel($1.section,$1.row,$2)();" }, { match: /"renderSection",(\i)=>{/, @@ -320,25 +320,26 @@ export default definePlugin({ ); - }), + }, { noop: true }), renderChannel(sectionIndex: number, index: number, ChannelComponent: React.ComponentType) { - const { channel, category } = this.getChannel(sectionIndex, index, this.instance.props.channels); + return ErrorBoundary.wrap(() => { + const { channel, category } = this.getChannel(sectionIndex, index, this.instance.props.channels); - if (!channel || !category) return null; - if (this.isChannelHidden(sectionIndex, index)) return null; + if (!channel || !category) return null; + if (this.isChannelHidden(sectionIndex, index)) return null; - return ( - - {channel.id} - - ); + return ( + + {channel.id} + + ); + }, { noop: true }); }, - getChannel(sectionIndex: number, index: number, channels: Record) { const category = categories[sectionIndex - 1]; if (!category) return { channel: null, category: null }; diff --git a/src/plugins/showHiddenThings/index.ts b/src/plugins/showHiddenThings/index.ts index 1858582a..8de70aca 100644 --- a/src/plugins/showHiddenThings/index.ts +++ b/src/plugins/showHiddenThings/index.ts @@ -41,13 +41,18 @@ const settings = definePluginSettings({ description: "Disable filters in Server Discovery search that hide servers that don't meet discovery criteria.", default: true, }, + disableDisallowedDiscoveryFilters: { + type: OptionType.BOOLEAN, + description: "Disable filters in Server Discovery search that hide NSFW & disallowed servers.", + default: true, + }, }); migratePluginSettings("ShowHiddenThings", "ShowTimeouts"); export default definePlugin({ name: "ShowHiddenThings", tags: ["ShowTimeouts", "ShowInvitesPaused", "ShowModView", "DisableDiscoveryFilters"], - description: "Displays various moderator-only elements regardless of permissions.", + description: "Displays various hidden & moderator-only things regardless of permissions.", authors: [Devs.Dolfies], patches: [ { @@ -81,6 +86,23 @@ export default definePlugin({ match: /filters:\i\.join\(" AND "\),facets:\[/, replace: "facets:[" } + }, + { + find: "DiscoveryBannedSearchWords.includes", + predicate: () => settings.store.disableDisallowedDiscoveryFilters, + replacement: { + match: /(?<=function\(\){)(?=.{0,130}DiscoveryBannedSearchWords\.includes)/, + replace: "return false;" + } + }, + { + find: "Endpoints.GUILD_DISCOVERY_VALID_TERM", + predicate: () => settings.store.disableDisallowedDiscoveryFilters, + all: true, + replacement: { + match: /\i\.HTTP\.get\(\{url:\i\.Endpoints\.GUILD_DISCOVERY_VALID_TERM,query:\{term:\i\},oldFormErrors:!0\}\);/g, + replace: "Promise.resolve({ body: { valid: true } });" + } } ], settings, diff --git a/src/plugins/unsuppressEmbeds/index.tsx b/src/plugins/unsuppressEmbeds/index.tsx index 0e87201c..16debf71 100644 --- a/src/plugins/unsuppressEmbeds/index.tsx +++ b/src/plugins/unsuppressEmbeds/index.tsx @@ -20,7 +20,7 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/Co import { ImageInvisible, ImageVisible } from "@components/Icons"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { Menu, PermissionsBits, PermissionStore, RestAPI, UserStore } from "@webpack/common"; +import { Constants, Menu, PermissionsBits, PermissionStore, RestAPI, UserStore } from "@webpack/common"; const EMBED_SUPPRESSED = 1 << 2; @@ -44,7 +44,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { channe icon={isEmbedSuppressed ? ImageVisible : ImageInvisible} action={() => RestAPI.patch({ - url: `/channels/${channel.id}/messages/${messageId}`, + url: Constants.Endpoints.MESSAGE(channel.id, messageId), body: { flags: isEmbedSuppressed ? flags & ~EMBED_SUPPRESSED : flags | EMBED_SUPPRESSED } }) } diff --git a/src/plugins/validUser/index.tsx b/src/plugins/validUser/index.tsx index 7a21ac86..4825cdaa 100644 --- a/src/plugins/validUser/index.tsx +++ b/src/plugins/validUser/index.tsx @@ -18,28 +18,30 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; +import { isNonNullish } from "@utils/guards"; import { sleep } from "@utils/misc"; import { Queue } from "@utils/Queue"; import definePlugin from "@utils/types"; import { Constants, FluxDispatcher, RestAPI, UserProfileStore, UserStore, useState } from "@webpack/common"; -import type { ComponentType, ReactNode } from "react"; +import { type ComponentType, type ReactNode } from "react"; // LYING to the type checker here const UserFlags = Constants.UserFlags as Record; const badges: Record = { - "active_developer": { id: "active_developer", description: "Active Developer", icon: "6bdc42827a38498929a4920da12695d9", link: "https://support-dev.discord.com/hc/en-us/articles/10113997751447" }, - "bug_hunter_level_1": { id: "bug_hunter_level_1", description: "Discord Bug Hunter", icon: "2717692c7dca7289b35297368a940dd0", link: "https://support.discord.com/hc/en-us/articles/360046057772-Discord-Bugs" }, - "bug_hunter_level_2": { id: "bug_hunter_level_2", description: "Discord Bug Hunter", icon: "848f79194d4be5ff5f81505cbd0ce1e6", link: "https://support.discord.com/hc/en-us/articles/360046057772-Discord-Bugs" }, - "certified_moderator": { id: "certified_moderator", description: "Moderator Programs Alumni", icon: "fee1624003e2fee35cb398e125dc479b", link: "https://discord.com/safety" }, - "discord_employee": { id: "staff", description: "Discord Staff", icon: "5e74e9b61934fc1f67c65515d1f7e60d", link: "https://discord.com/company" }, - "hypesquad": { id: "hypesquad", description: "HypeSquad Events", icon: "bf01d1073931f921909045f3a39fd264", link: "https://discord.com/hypesquad" }, - "hypesquad_online_house_1": { id: "hypesquad_house_1", description: "HypeSquad Bravery", icon: "8a88d63823d8a71cd5e390baa45efa02", link: "https://discord.com/settings/hypesquad-online" }, - "hypesquad_online_house_2": { id: "hypesquad_house_2", description: "HypeSquad Brilliance", icon: "011940fd013da3f7fb926e4a1cd2e618", link: "https://discord.com/settings/hypesquad-online" }, - "hypesquad_online_house_3": { id: "hypesquad_house_3", description: "HypeSquad Balance", icon: "3aa41de486fa12454c3761e8e223442e", link: "https://discord.com/settings/hypesquad-online" }, - "partner": { id: "partner", description: "Partnered Server Owner", icon: "3f9748e53446a137a052f3454e2de41e", link: "https://discord.com/partners" }, - "premium": { id: "premium", description: "Subscriber", icon: "2ba85e8026a8614b640c2837bcdfe21b", link: "https://discord.com/settings/premium" }, - "premium_early_supporter": { id: "early_supporter", description: "Early Supporter", icon: "7060786766c9c840eb3019e725d2b358", link: "https://discord.com/settings/premium" }, - "verified_developer": { id: "verified_developer", description: "Early Verified Bot Developer", icon: "6df5892e0f35b051f8b61eace34f4967" }, + active_developer: { id: "active_developer", description: "Active Developer", icon: "6bdc42827a38498929a4920da12695d9", link: "https://support-dev.discord.com/hc/en-us/articles/10113997751447" }, + bug_hunter_level_1: { id: "bug_hunter_level_1", description: "Discord Bug Hunter", icon: "2717692c7dca7289b35297368a940dd0", link: "https://support.discord.com/hc/en-us/articles/360046057772-Discord-Bugs" }, + bug_hunter_level_2: { id: "bug_hunter_level_2", description: "Discord Bug Hunter", icon: "848f79194d4be5ff5f81505cbd0ce1e6", link: "https://support.discord.com/hc/en-us/articles/360046057772-Discord-Bugs" }, + certified_moderator: { id: "certified_moderator", description: "Moderator Programs Alumni", icon: "fee1624003e2fee35cb398e125dc479b", link: "https://discord.com/safety" }, + discord_employee: { id: "staff", description: "Discord Staff", icon: "5e74e9b61934fc1f67c65515d1f7e60d", link: "https://discord.com/company" }, + get staff() { return this.discord_employee; }, + hypesquad: { id: "hypesquad", description: "HypeSquad Events", icon: "bf01d1073931f921909045f3a39fd264", link: "https://discord.com/hypesquad" }, + hypesquad_online_house_1: { id: "hypesquad_house_1", description: "HypeSquad Bravery", icon: "8a88d63823d8a71cd5e390baa45efa02", link: "https://discord.com/settings/hypesquad-online" }, + hypesquad_online_house_2: { id: "hypesquad_house_2", description: "HypeSquad Brilliance", icon: "011940fd013da3f7fb926e4a1cd2e618", link: "https://discord.com/settings/hypesquad-online" }, + hypesquad_online_house_3: { id: "hypesquad_house_3", description: "HypeSquad Balance", icon: "3aa41de486fa12454c3761e8e223442e", link: "https://discord.com/settings/hypesquad-online" }, + partner: { id: "partner", description: "Partnered Server Owner", icon: "3f9748e53446a137a052f3454e2de41e", link: "https://discord.com/partners" }, + premium: { id: "premium", description: "Subscriber", icon: "2ba85e8026a8614b640c2837bcdfe21b", link: "https://discord.com/settings/premium" }, + premium_early_supporter: { id: "early_supporter", description: "Early Supporter", icon: "7060786766c9c840eb3019e725d2b358", link: "https://discord.com/settings/premium" }, + verified_developer: { id: "verified_developer", description: "Early Verified Bot Developer", icon: "6df5892e0f35b051f8b61eace34f4967" }, }; const fetching = new Set(); @@ -73,7 +75,7 @@ async function getUser(id: string) { if (userObj) return userObj; - const user: any = await RestAPI.get({ url: `/users/${id}` }).then(response => { + const user: any = await RestAPI.get({ url: Constants.Endpoints.USER(id) }).then(response => { FluxDispatcher.dispatch({ type: "USER_UPDATE", user: response.body, @@ -93,7 +95,8 @@ async function getUser(id: string) { userObj = UserStore.getUser(id); const fakeBadges: ProfileBadge[] = Object.entries(UserFlags) .filter(([_, flag]) => !isNaN(flag) && userObj.hasFlag(flag)) - .map(([key]) => badges[key.toLowerCase()]); + .map(([key]) => badges[key.toLowerCase()]) + .filter(isNonNullish); if (user.premium_type || !user.bot && (user.banner || user.avatar?.startsWith?.("a_"))) fakeBadges.push(badges.premium); @@ -202,6 +205,7 @@ export default definePlugin({ return ( { RestAPI.post({ - url: `/channels/${channelId}/messages`, + url: Constants.Endpoints.MESSAGES(channelId), body: { flags: 1 << 13, channel_id: channelId, diff --git a/src/plugins/whoReacted/index.tsx b/src/plugins/whoReacted/index.tsx index b3728c21..5721dc91 100644 --- a/src/plugins/whoReacted/index.tsx +++ b/src/plugins/whoReacted/index.tsx @@ -23,7 +23,7 @@ import { Queue } from "@utils/Queue"; import { useForceUpdater } from "@utils/react"; import definePlugin from "@utils/types"; import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; -import { ChannelStore, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common"; +import { ChannelStore, Constants, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common"; import { CustomEmoji } from "@webpack/types"; import { Message, ReactionEmoji, User } from "discord-types/general"; @@ -36,7 +36,7 @@ let reactions: Record; function fetchReactions(msg: Message, emoji: ReactionEmoji, type: number) { const key = emoji.name + (emoji.id ? `:${emoji.id}` : ""); return RestAPI.get({ - url: `/channels/${msg.channel_id}/messages/${msg.id}/reactions/${key}`, + url: Constants.Endpoints.REACTIONS(msg.channel_id, msg.id, key), query: { limit: 100, type diff --git a/src/plugins/xsOverlay.desktop/index.ts b/src/plugins/xsOverlay.desktop/index.ts index b666d116..5251959f 100644 --- a/src/plugins/xsOverlay.desktop/index.ts +++ b/src/plugins/xsOverlay.desktop/index.ts @@ -68,7 +68,6 @@ interface Call { ringing: string[]; } -const MuteStore = findByPropsLazy("isSuppressEveryoneEnabled"); const Notifs = findByPropsLazy("makeTextChatNotification"); const XSLog = new Logger("XSOverlay"); @@ -115,13 +114,13 @@ const settings = definePluginSettings({ }, timeout: { type: OptionType.NUMBER, - description: "Notif duration (secs)", - default: 1.0, + description: "Notification duration (secs)", + default: 3, }, - timeoutPerCharacter: { - type: OptionType.NUMBER, - description: "Duration multiplier per character", - default: 0.5 + lengthBasedTimeout: { + type: OptionType.BOOLEAN, + description: "Extend duration with message length", + default: true }, opacity: { type: OptionType.SLIDER, @@ -262,12 +261,11 @@ function shouldIgnoreForChannelType(channel: Channel) { } function sendMsgNotif(titleString: string, content: string, message: Message) { - const timeout = Math.max(settings.store.timeout, content.length * settings.store.timeoutPerCharacter); fetch(`https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.png?size=128`).then(response => response.arrayBuffer()).then(result => { const msgData = { messageType: 1, index: 0, - timeout, + timeout: settings.store.lengthBasedTimeout ? calculateTimeout(content) : settings.store.timeout, height: calculateHeight(content), opacity: settings.store.opacity, volume: settings.store.volume, @@ -286,7 +284,7 @@ function sendOtherNotif(content: string, titleString: string) { const msgData = { messageType: 1, index: 0, - timeout: settings.store.timeout, + timeout: settings.store.lengthBasedTimeout ? calculateTimeout(content) : settings.store.timeout, height: calculateHeight(content), opacity: settings.store.opacity, volume: settings.store.volume, @@ -313,3 +311,10 @@ function calculateHeight(content: string) { if (content.length <= 300) return 200; return 250; } + +function calculateTimeout(content: string) { + if (content.length <= 100) return 3; + if (content.length <= 200) return 4; + if (content.length <= 300) return 5; + return 6; +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 55620898..567d1167 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -475,6 +475,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ ImBanana: { name: "Im_Banana", id: 635250116688871425n + }, + xocherry: { + name: "xocherry", + id: 221288171013406720n } } satisfies Record); diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index 74e1aefe..57202ba3 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -17,7 +17,7 @@ */ import { MessageObject } from "@api/MessageEvents"; -import { ChannelStore, ComponentDispatch, FluxDispatcher, GuildStore, InviteActions, MaskedLink, MessageActions, ModalImageClasses, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; +import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, InviteActions, MaskedLink, MessageActions, ModalImageClasses, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; import { Guild, Message, User } from "discord-types/general"; import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal"; @@ -162,7 +162,7 @@ export async function fetchUserProfile(id: string, options?: FetchUserProfileOpt FluxDispatcher.dispatch({ type: "USER_PROFILE_FETCH_START", userId: id }); const { body } = await RestAPI.get({ - url: `/users/${id}/profile`, + url: Constants.Endpoints.USER_PROFILE(id), query: { with_mutual_guilds: false, with_mutual_friends_count: false,