Compare commits

..

23 commits

Author SHA1 Message Date
76b4e72917
owo 2023-10-25 17:18:05 +09:00
Vendicated
97c0face2f
PinDMs: Fix canary crash 2023-10-25 00:38:02 +02:00
V
1a1d9b07e8
Fix canary crashing (#1833) 2023-10-25 00:17:11 +02:00
Nuckyz
4a2def03e7
Fix MessageDecorationsAPI patch 2023-10-23 22:42:23 -03:00
Vendicated
544edce9f9
bump to v1.5.8 2023-10-21 19:26:29 +02:00
Dea
e4485165d0
onePingPerDM: add settings (#1802)
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: Dea <dea-banana@riseup.net>
2023-10-21 18:42:37 +02:00
Macintosh II
fada76ec81
PlatformIndicators: make size same as other memberlist icons (#1789)
Co-authored-by: V <vendicated@riseup.net>
2023-10-21 18:41:56 +02:00
zImPatrick
f659c46031
FakeNitro: Add app icon customization (#1822)
Co-authored-by: V <vendicated@riseup.net>
2023-10-21 17:53:00 +02:00
Nuckyz
ae1dc4eab0
Make reporter ignore useless Discord errors (#1829) 2023-10-21 17:51:07 +02:00
Nuckyz
fe60a72b80
Fix NoPendingCount patch 2023-10-21 12:26:04 -03:00
Nuckyz
5a0b2ee3f5
Fix FakeNitro patch 2023-10-21 12:26:04 -03:00
Nuckyz
6c1b8b0d8a
Fix MessageDecorationsAPI 2023-10-21 12:25:57 -03:00
Nuckyz
b2a1410a96
Remove useless Experiments patch 2023-10-21 12:03:54 -03:00
Nuckyz
c25c95eecd
Fix IgnoreActivities making reporter angry 2023-10-21 12:00:09 -03:00
ioj4
d94418f42f
fix: windows host update patching (#1820) 2023-10-19 11:14:40 +02:00
Vendicated
da1a8cdd67
web: Fix themes tab 2023-10-19 10:13:05 +02:00
Vendicated
5d7ede34d8
bump to v1.5.7 2023-10-19 01:23:59 +02:00
Vendicated
cd61354998
ContributorModal: Fix vertical overflow on multi word names 2023-10-19 00:22:49 +02:00
AutumnVN
a452945ac8
feat(plugin): NoTypingAnimation (#1680) 2023-10-19 00:14:14 +02:00
Marocco2
b577660800
feat(VcNarrator): add {{NICKNAME}} as placeholder (#1792) 2023-10-19 00:05:47 +02:00
AutumnVN
4f57c7eded
betterNotes, userVoiceShow: fix padding issue (#1804) 2023-10-18 23:54:35 +02:00
Ryan Cao
e3e5da10a9
fix: Content Security Policy patching (#1814)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-10-18 23:44:29 +02:00
Vendicated
998ce72f3b
ForceOwnerCrown: Remove log spam 2023-10-14 02:46:57 +02:00
30 changed files with 253 additions and 107 deletions

View file

@ -1,7 +1,7 @@
{
"name": "vencord",
"private": "true",
"version": "1.5.6",
"version": "1.5.8",
"description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": {

View file

@ -43,7 +43,7 @@ const nodeCommonOpts = {
format: "cjs",
platform: "node",
target: ["esnext"],
external: ["electron", ...commonOpts.external],
external: ["electron", "original-fs", ...commonOpts.external],
define: defines,
};

View file

@ -61,6 +61,13 @@ const report = {
otherErrors: [] as string[]
};
const IGNORED_DISCORD_ERRORS = [
"KeybindStore: Looking for callback action",
"Unable to process domain list delta: Client revision number is null",
"Downloading the full bad domains file",
/\[GatewaySocket\].{0,110}Cannot access '/
] as Array<string | RegExp>;
function toCodeBlock(s: string) {
s = s.replace(/```/g, "`\u200B`\u200B`");
return "```" + s + " ```";
@ -86,6 +93,8 @@ async function printReport() {
console.log(` - Error: ${toCodeBlock(p.error)}`);
});
report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex)));
console.log("## Discord Errors");
report.otherErrors.forEach(e => {
console.log(`- ${toCodeBlock(e)}`);

View file

@ -17,6 +17,7 @@
font-size: 20px;
height: 20px;
position: relative;
text-wrap: nowrap;
}
.vc-author-modal-name::before {

View file

@ -25,7 +25,7 @@ import { Margins } from "@utils/margins";
import { classes } from "@utils/misc";
import { showItemInFolder } from "@utils/native";
import { useAwaiter } from "@utils/react";
import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack";
import { findByPropsLazy, findLazy } from "@webpack";
import { Button, Card, FluxDispatcher, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
import { UserThemeHeader } from "main/themes";
import type { ComponentType, Ref, SyntheticEvent } from "react";
@ -41,7 +41,7 @@ type FileInput = ComponentType<{
}>;
const InviteActions = findByPropsLazy("resolveInvite");
const FileInput: FileInput = findByCodeLazy("activateUploadDialogue=");
const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef);
const TextAreaProps = findLazy(m => typeof m.textarea === "string");
const cl = classNameFactory("vc-settings-theme-");

View file

@ -62,6 +62,10 @@ if (IS_VESKTOP || !IS_VANILLA) {
} catch { }
const findHeader = (headers: Record<string, string[]>, headerName: Lowercase<string>) => {
return Object.keys(headers).find(h => h.toLowerCase() === headerName);
};
// Remove CSP
type PolicyResult = Record<string, string[]>;
@ -73,6 +77,7 @@ if (IS_VESKTOP || !IS_VANILLA) {
result[directiveKey] = directiveValue;
}
});
return result;
};
const stringifyPolicy = (policy: PolicyResult): string =>
@ -81,31 +86,39 @@ if (IS_VESKTOP || !IS_VANILLA) {
.map(directive => directive.flat().join(" "))
.join("; ");
function patchCsp(headers: Record<string, string[]>, header: string) {
if (header in headers) {
const patchCsp = (headers: Record<string, string[]>) => {
const header = findHeader(headers, "content-security-policy");
if (header) {
const csp = parsePolicy(headers[header][0]);
for (const directive of ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"]) {
csp[directive] = ["*", "blob:", "data:", "vencord:", "'unsafe-inline'"];
csp[directive] ??= [];
csp[directive].push("*", "blob:", "data:", "vencord:", "'unsafe-inline'");
}
// TODO: Restrict this to only imported packages with fixed version.
// Perhaps auto generate with esbuild
csp["script-src"] ??= [];
csp["script-src"].push("'unsafe-eval'", "https://unpkg.com", "https://cdnjs.cloudflare.com");
headers[header] = [stringifyPolicy(csp)];
}
}
};
session.defaultSession.webRequest.onHeadersReceived(({ responseHeaders, resourceType }, cb) => {
if (responseHeaders) {
if (resourceType === "mainFrame")
patchCsp(responseHeaders, "content-security-policy");
patchCsp(responseHeaders);
// Fix hosts that don't properly set the css content type, such as
// raw.githubusercontent.com
if (resourceType === "stylesheet")
responseHeaders["content-type"] = ["text/css"];
if (resourceType === "stylesheet") {
const header = findHeader(responseHeaders, "content-type");
if (header)
responseHeaders[header] = ["text/css"];
}
}
cb({ cancel: false, responseHeaders });
});

View file

@ -17,7 +17,7 @@
*/
import { app } from "electron";
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "fs";
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "original-fs";
import { basename, dirname, join } from "path";
function isNewer($new: string, old: string) {

View file

@ -27,8 +27,8 @@ export default definePlugin({
{
find: ".withMentionPrefix",
replacement: {
match: /(.roleDot.{10,50}{children:.{1,2})}\)/,
replace: "$1.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))})"
match: /currentUserIsPremium:.{0,70}{children:\i(?=}\))/,
replace: "$&.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))"
}
}
],

View file

@ -63,26 +63,26 @@ export default definePlugin({
replacement: {
get match() {
switch (Settings.plugins.Settings.settingsLocation) {
case "top": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/;
case "aboveNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/;
case "belowNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/;
case "belowActivity": return /(?<=\{section:(\i)\.ID\.DIVIDER},)\{section:"changelog"/;
case "bottom": return /\{section:(\i)\.ID\.CUSTOM,\s*element:.+?}/;
case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/;
case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/;
case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/;
case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/;
case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/;
case "aboveActivity":
default:
return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/;
return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/;
}
},
replace: "...$self.makeSettingsCategories($1),$&"
}
}],
customSections: [] as ((ID: Record<string, unknown>) => any)[],
customSections: [] as ((SectionTypes: Record<string, unknown>) => any)[],
makeSettingsCategories({ ID }: { ID: Record<string, unknown>; }) {
makeSettingsCategories(SectionTypes: Record<string, unknown>) {
return [
{
section: ID.HEADER,
section: SectionTypes.HEADER,
label: "Vencord",
className: "vc-settings-header"
},
@ -128,9 +128,9 @@ export default definePlugin({
element: require("@components/VencordSettings/PatchHelperTab").default,
className: "vc-patch-helper"
},
...this.customSections.map(func => func(ID)),
...this.customSections.map(func => func(SectionTypes)),
{
section: ID.DIVIDER
section: SectionTypes.DIVIDER
}
].filter(Boolean);
},

View file

@ -19,6 +19,9 @@
import { Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
const UserPopoutSectionCssClasses = findByPropsLazy("section", "lastSection");
export default definePlugin({
name: "BetterNotesBox",
@ -34,12 +37,20 @@ export default definePlugin({
match: /hideNote:.+?(?=[,}])/g,
replace: "hideNote:true",
}
}, {
},
{
find: "Messages.NOTE_PLACEHOLDER",
replacement: {
match: /\.NOTE_PLACEHOLDER,/,
replace: "$&spellCheck:!Vencord.Settings.plugins.BetterNotesBox.noSpellCheck,"
}
},
{
find: ".Messages.NOTE}",
replacement: {
match: /(\i)\.hideNote\?null/,
replace: "$1.hideNote?$self.patchPadding($1)"
}
}
],
@ -56,5 +67,12 @@ export default definePlugin({
disabled: () => Settings.plugins.BetterNotesBox.hide,
default: false
}
},
patchPadding(e: any) {
if (!e.lastSection) return;
return (
<div className={UserPopoutSectionCssClasses.lastSection}></div>
);
}
});

View file

@ -77,15 +77,6 @@ export default definePlugin({
}
]
},
// Fix search history being disabled / broken with isStaff
{
find: 'get("disable_new_search")',
predicate: () => settings.store.enableIsStaff,
replacement: {
match: /(?<=showNewSearch"\);return)\s?!/,
replace: "!1&&!"
}
},
{
find: 'H1,title:"Experiments"',
replacement: {

View file

@ -295,7 +295,7 @@ export default definePlugin({
},
{
predicate: () => settings.store.transformStickers,
match: /renderAttachments=function\(\i\){var (\i)=\i.attachments.+?;/,
match: /renderAttachments=function\(\i\){var \i=this,(\i)=\i.attachments.+?;/,
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
}
]
@ -329,6 +329,20 @@ export default definePlugin({
match: /(?<=\.Messages\.EMOJI_POPOUT_ADDED_PACK_DESCRIPTION.+?return ).{0,1200}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?(?=}\()/,
replace: reactNode => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!arguments[0]?.fakeNitroNode?.fake)`
}
},
{
find: "canUsePremiumAppIcons:function",
replacement: {
match: /canUsePremiumAppIcons:function\(\i\){/,
replace: "$&return true;"
}
},
{
find: "location:\"AppIconHome\"",
replacement: {
match: /\i\.\i\.isPremium\(\i\.\i\.getCurrentUser\(\)\)/,
replace: "true"
}
}
],

View file

@ -19,6 +19,7 @@
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import { GuildStore } from "@webpack/common";
import { Channel, User } from "discord-types/general";
export default definePlugin({
name: "ForceOwnerCrown",
@ -34,25 +35,15 @@ export default definePlugin({
}
}
],
isGuildOwner(props) {
// Check if channel is a Group DM, if so return false
if (props?.channel?.type === 3) {
isGuildOwner(props: { user: User, channel: Channel, guildId?: string; }) {
if (!props?.user?.id) return false;
if (props.channel?.type === 3 /* GROUP_DM */)
return false;
}
// guild id is in props twice, fallback if the first is undefined
const guildId = props?.guildId ?? props?.channel?.guild_id;
const userId = props?.user?.id;
const guildId = props.guildId ?? props.channel?.guild_id;
const userId = props.user.id;
if (guildId && userId) {
const guild = GuildStore.getGuild(guildId);
if (guild) {
return guild.ownerId === userId;
}
console.error("[ForceOwnerCrown] failed to get guild", { guildId, guild, props });
} else {
console.error("[ForceOwnerCrown] no guildId or userId", { guildId, userId, props });
}
return false;
return GuildStore.getGuild(guildId)?.ownerId === userId;
},
});

View file

@ -106,7 +106,7 @@ export default definePlugin({
}
},
{
find: ".Messages.EMBEDDED_ACTIVITIES_HAVE_PLAYED_ONE_KNOWN",
find: ".Messages.EMBEDDED_ACTIVITIES_DEVELOPER_SHELF_SUBTITLE",
replacement: [
{
match: /(?<=\(\)\.activityTitleText.+?children:(\i)\.name.*?}\),)/,

View file

@ -360,7 +360,7 @@ export default definePlugin({
{
// Render editHistory in the deepest div for message content
match: /(\)\("div",\{id:.+?children:\[)/,
replace: "$1 (arguments[0].message.editHistory.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), "
replace: "$1 (arguments[0].message.editHistory?.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), "
}
]
},

View file

@ -84,8 +84,8 @@ export default definePlugin({
find: "showProgressBadge:",
predicate: () => settings.store.hidePremiumOffersCount,
replacement: {
match: /\(function\(\){return \i\.\i\.getUnacknowledgedOffers\(\i\)\.length}\)/,
replace: "(function(){return 0})"
match: /=\i\.unviewedTrialCount\+\i\.unviewedDiscountCount/,
replace: "=0"
}
}
],

View file

@ -0,0 +1,21 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
export default definePlugin({
name: "NoTypingAnimation",
authors: [Devs.AutumnVN],
description: "Disables the CPU-intensive typing dots animation",
patches: [{
find: "dotCycle",
replacement: {
match: /document.hasFocus\(\)/,
replace: "false"
}
}]
});

View file

@ -4,20 +4,44 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import { ChannelStore, ReadStateStore } from "@webpack/common";
import { Message } from "discord-types/general";
import definePlugin, { OptionType } from "@utils/types";
import { ChannelStore, ReadStateStore, UserStore } from "@webpack/common";
import { MessageJSON } from "discord-types/general";
const enum ChannelType {
DM = 1,
GROUP_DM = 3
}
const settings = definePluginSettings({
channelToAffect: {
type: OptionType.SELECT,
description: "Select the type of DM for the plugin to affect",
options: [
{ label: "Both", value: "both_dms", default: true },
{ label: "User DMs", value: "user_dm" },
{ label: "Group DMs", value: "group_dm" },
]
},
allowMentions: {
type: OptionType.BOOLEAN,
description: "Receive audio pings for @mentions",
default: false,
},
allowEveryone: {
type: OptionType.BOOLEAN,
description: "Receive audio pings for @everyone and @here in group DMs",
default: false,
},
});
export default definePlugin({
name: "OnePingPerDM",
description: "If unread messages are sent by a user in DMs multiple times, you'll only receive one audio ping. Read the messages to reset the limit",
authors: [Devs.ProffDea],
settings,
patches: [{
find: ".getDesktopType()===",
replacement: [{
@ -29,11 +53,19 @@ export default definePlugin({
replace: "sound:!$self.isPrivateChannelRead(arguments[0]?.message)?undefined:$1"
}]
}],
isPrivateChannelRead(message: Message) {
isPrivateChannelRead(message: MessageJSON) {
const channelType = ChannelStore.getChannel(message.channel_id)?.type;
if (channelType !== ChannelType.DM && channelType !== ChannelType.GROUP_DM) {
return false;
}
if (
(channelType === ChannelType.DM && settings.store.channelToAffect === "group_dm") ||
(channelType === ChannelType.GROUP_DM && settings.store.channelToAffect === "user_dm") ||
(settings.store.allowMentions && message.mentions.some(m => m.id === UserStore.getCurrentUser().id)) ||
(settings.store.allowEveryone && message.mention_everyone)
) {
return true;
}
return ReadStateStore.getOldestUnreadMessageId(message.channel_id) === message.id;
},
});

View file

@ -81,7 +81,7 @@ export default definePlugin({
// array with the count, or an empty array. Due to spreading, only in the former
// case will an element be added to the outer array
// Thanks for the fix, Strencher!
replace: "$&...$1.props.pinCount,"
replace: "$&...($1.props.pinCount ?? []),"
},
{
// Patch renderSection (renders the header) to set the text to "Pinned DMs" instead of "Direct Messages"

View file

@ -30,13 +30,13 @@ import { User } from "discord-types/general";
const SessionsStore = findStoreLazy("SessionsStore");
function Icon(path: string, opts?: { viewBox?: string; width?: number; height?: number; }) {
return ({ color, tooltip }: { color: string; tooltip: string; }) => (
return ({ color, tooltip, small }: { color: string; tooltip: string; small: boolean; }) => (
<Tooltip text={tooltip} >
{(tooltipProps: any) => (
<svg
{...tooltipProps}
height={opts?.height ?? 20}
width={opts?.width ?? 20}
height={(opts?.height ?? 20) - (small ? 3 : 0)}
width={(opts?.width ?? 20) - (small ? 3 : 0)}
viewBox={opts?.viewBox ?? "0 0 24 24"}
fill={color}
>
@ -57,16 +57,16 @@ type Platform = keyof typeof Icons;
const getStatusColor = findByCodeLazy(".TWITCH", ".STREAMING", ".INVISIBLE");
const PlatformIcon = ({ platform, status }: { platform: Platform, status: string; }) => {
const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => {
const tooltip = platform[0].toUpperCase() + platform.slice(1);
const Icon = Icons[platform] ?? Icons.desktop;
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} />;
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} small={small} />;
};
const getStatus = (id: string): Record<Platform, string> => PresenceStore.getState()?.clientStatuses?.[id];
const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false }: { user: User; wantMargin?: boolean; wantTopMargin?: boolean; }) => {
const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false, small = false }: { user: User; wantMargin?: boolean; wantTopMargin?: boolean; small?: boolean; }) => {
if (!user || user.bot) return null;
if (user.id === UserStore.getCurrentUser().id) {
@ -99,6 +99,7 @@ const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false }: {
key={platform}
platform={platform as Platform}
status={status}
small={small}
/>
));
@ -137,7 +138,7 @@ const indicatorLocations = {
description: "In the member list",
onEnable: () => addDecorator("platform-indicator", props =>
<ErrorBoundary noop>
<PlatformIndicator user={props.user} />
<PlatformIndicator user={props.user} small={true} />
</ErrorBoundary>
),
onDisable: () => removeDecorator("platform-indicator")

View file

@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import ErrorBoundary from "@components/ErrorBoundary";
import { classes } from "@utils/misc";
import { findByPropsLazy } from "@webpack";
import { UserStore } from "@webpack/common";
@ -39,17 +40,17 @@ function shouldShow(message: Message): boolean {
return true;
}
export function PronounsChatComponentWrapper({ message }: { message: Message; }) {
export const PronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
return shouldShow(message)
? <PronounsChatComponent message={message} />
: null;
}
}, { noop: true });
export function CompactPronounsChatComponentWrapper({ message }: { message: Message; }) {
export const CompactPronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
return shouldShow(message)
? <CompactPronounsChatComponent message={message} />
: null;
}
}, { noop: true });
function PronounsChatComponent({ message }: { message: Message; }) {
const [result] = useFormattedPronouns(message.author.id);
@ -63,7 +64,7 @@ function PronounsChatComponent({ message }: { message: Message; }) {
: null;
}
export function CompactPronounsChatComponent({ message }: { message: Message; }) {
export const CompactPronounsChatComponent = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
const [result] = useFormattedPronouns(message.author.id);
return result
@ -73,4 +74,4 @@ export function CompactPronounsChatComponent({ message }: { message: Message; })
> {result}</span>
)
: null;
}
}, { noop: true });

View file

@ -41,7 +41,7 @@ export default definePlugin({
find: "showCommunicationDisabledStyles",
replacement: {
match: /("span",{id:\i,className:\i,children:\i}\))/,
replace: "$1, $self.CompactPronounsChatComponentWrapper(e)"
replace: "$1, $self.CompactPronounsChatComponentWrapper(arguments[0])"
}
},
// Patch the chat timestamp element (normal mode)
@ -49,7 +49,7 @@ export default definePlugin({
find: "showCommunicationDisabledStyles",
replacement: {
match: /(?<=return\s*\(0,\i\.jsxs?\)\(.+!\i&&)(\(0,\i.jsxs?\)\(.+?\{.+?\}\))/,
replace: "[$1, $self.PronounsChatComponentWrapper(e)]"
replace: "[$1, $self.PronounsChatComponentWrapper(arguments[0])]"
}
},
// Patch the profile popout username header to use our pronoun hook instead of Discord's pronouns

View file

@ -1,4 +1,4 @@
.vc-uvs-button > div {
.vc-uvs-button>div {
white-space: normal !important;
}
@ -21,6 +21,7 @@
margin-bottom: 0 !important;
}
.vc-uvs-popout-margin > [class^="section"] {
margin-top: -12px;
.vc-uvs-popout-margin-self>[class^="section"] {
padding-top: 0;
padding-bottom: 12px;
}

View file

@ -20,14 +20,13 @@ import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findStoreLazy } from "@webpack";
import { findStoreLazy } from "@webpack";
import { ChannelStore, GuildStore, UserStore } from "@webpack/common";
import { User } from "discord-types/general";
import { VoiceChannelSection } from "./components/VoiceChannelSection";
const VoiceStateStore = findStoreLazy("VoiceStateStore");
const UserPopoutSectionCssClasses = findByPropsLazy("section", "lastSection");
const settings = definePluginSettings({
showInUserProfileModal: {
@ -88,7 +87,7 @@ export default definePlugin({
patchPopout: ({ user }: UserProps) => {
const isSelfUser = user.id === UserStore.getCurrentUser().id;
return (
<div className={isSelfUser ? `vc-uvs-popout-margin ${UserPopoutSectionCssClasses.lastSection}` : ""}>
<div className={isSelfUser ? "vc-uvs-popout-margin-self" : ""}>
<VoiceChannelField user={user} />
</div>
);

View file

@ -24,7 +24,7 @@ import { Margins } from "@utils/margins";
import { wordsToTitle } from "@utils/text";
import definePlugin, { OptionType, PluginOptionsItem } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { Button, ChannelStore, Forms, SelectedChannelStore, useMemo, UserStore } from "@webpack/common";
import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common";
interface VoiceState {
userId: string;
@ -70,11 +70,12 @@ function clean(str: string) {
.trim();
}
function formatText(str: string, user: string, channel: string, displayName: string) {
function formatText(str: string, user: string, channel: string, displayName: string, nickname: string) {
return str
.replaceAll("{{USER}}", clean(user) || (user ? "Someone" : ""))
.replaceAll("{{CHANNEL}}", clean(channel) || "channel")
.replaceAll("{{DISPLAY_NAME}}", clean(displayName) || (displayName ? "Someone" : ""));
.replaceAll("{{DISPLAY_NAME}}", clean(displayName) || (displayName ? "Someone" : ""))
.replaceAll("{{NICKNAME}}", clean(nickname) || (nickname ? "Someone" : ""));
}
/*
@ -145,8 +146,9 @@ function updateStatuses(type: string, { deaf, mute, selfDeaf, selfMute, userId,
function playSample(tempSettings: any, type: string) {
const settings = Object.assign({}, Settings.plugins.VcNarrator, tempSettings);
const currentUser = UserStore.getCurrentUser();
const myGuildId = SelectedGuildStore.getGuildId();
speak(formatText(settings[type + "Message"], currentUser.username, "general", (currentUser as any).globalName ?? currentUser.username), settings);
speak(formatText(settings[type + "Message"], currentUser.username, "general", (currentUser as any).globalName ?? currentUser.username, GuildMemberStore.getNick(myGuildId, currentUser.id) ?? currentUser.username), settings);
}
export default definePlugin({
@ -156,6 +158,7 @@ export default definePlugin({
flux: {
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
const myGuildId = SelectedGuildStore.getGuildId();
const myChanId = SelectedChannelStore.getVoiceChannelId();
const myId = UserStore.getCurrentUser().id;
@ -175,9 +178,10 @@ export default definePlugin({
const template = Settings.plugins.VcNarrator[type + "Message"];
const user = isMe && !Settings.plugins.VcNarrator.sayOwnName ? "" : UserStore.getUser(userId).username;
const displayName = user && ((UserStore.getUser(userId) as any).globalName ?? user);
const nickname = user && (GuildMemberStore.getNick(myGuildId, userId) ?? user);
const channel = ChannelStore.getChannel(id).name;
speak(formatText(template, user, channel, displayName));
speak(formatText(template, user, channel, displayName, nickname));
// updateStatuses(type, state, isMe);
}
@ -189,7 +193,7 @@ export default definePlugin({
if (!s) return;
const event = s.mute || s.selfMute ? "unmute" : "mute";
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, ""));
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
},
AUDIO_TOGGLE_SELF_DEAF() {
@ -198,7 +202,7 @@ export default definePlugin({
if (!s) return;
const event = s.deaf || s.selfDeaf ? "undeafen" : "deafen";
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, ""));
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
}
},
@ -315,8 +319,8 @@ export default definePlugin({
You can customise the spoken messages below. You can disable specific messages by setting them to nothing
</Forms.FormText>
<Forms.FormText>
The special placeholders <code>{"{{USER}}"}</code>, <code>{"{{DISPLAY_NAME}}"}</code> and <code>{"{{CHANNEL}}"}</code>{" "}
will be replaced with the user's name (nothing if it's yourself), the user's display name and the channel's name respectively
The special placeholders <code>{"{{USER}}"}</code>, <code>{"{{DISPLAY_NAME}}"}</code>, <code>{"{{NICKNAME}}"}</code> and <code>{"{{CHANNEL}}"}</code>{" "}
will be replaced with the user's name (nothing if it's yourself), the user's display name, the user's nickname on current server and the channel's name respectively
</Forms.FormText>
{hasEnglishVoices && (
<>

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { filters, findByCode, mapMangledModuleLazy } from "@webpack";
import { filters, findByCode, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react";
import { LazyComponent } from "./react";
@ -132,12 +132,7 @@ export const ModalContent = LazyComponent(() => Modals.ModalContent);
export const ModalFooter = LazyComponent(() => Modals.ModalFooter);
export const ModalCloseButton = LazyComponent(() => Modals.ModalCloseButton);
const ModalAPI = mapMangledModuleLazy("onCloseRequest:null!=", {
openModal: filters.byCode("onCloseRequest:null!="),
closeModal: filters.byCode("onCloseCallback&&"),
openModalLazy: m => m?.length === 1 && filters.byCode(".apply(this,arguments)")(m),
closeAllModals: filters.byCode(".value.key,")
});
const ModalAPI = findByPropsLazy("openModalLazy");
/**
* Wait for the render promise to resolve, then open a modal with it.

View file

@ -16,10 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { proxyLazy } from "@utils/lazy";
import type * as Stores from "discord-types/stores";
// eslint-disable-next-line path-alias/no-relative
import { filters, findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "../webpack";
import { filters, findByCode, findByProps, findByPropsLazy, mapMangledModuleLazy } from "../webpack";
import { waitForStore } from "./internal";
import * as t from "./types/stores";
@ -83,7 +84,14 @@ export const useStateFromStores: <T>(
idk?: any,
isEqual?: (old: T, newer: T) => boolean
) => T
= findByCodeLazy("useStateFromStores");
// FIXME: hack to support old stable and new canary
= proxyLazy(() => {
try {
return findByProps("useStateFromStores").useStateFromStores;
} catch {
return findByCode('("useStateFromStores")');
}
});
waitForStore("DraftStore", s => DraftStore = s);
waitForStore("UserStore", s => UserStore = s);

View file

@ -16,10 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { proxyLazy } from "@utils/lazy";
import type { User } from "discord-types/general";
// eslint-disable-next-line path-alias/no-relative
import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack";
import { _resolveReady, filters, find, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack";
import type * as t from "./types/utils";
export let FluxDispatcher: t.FluxDispatcher;
@ -126,4 +127,11 @@ waitFor("parseTopic", m => Parser = m);
export let SettingsRouter: any;
waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint");
// FIXME: hack to support old stable and new canary
export const PermissionsBits: t.PermissionsBits = proxyLazy(() => {
try {
return find(m => m.Permissions?.ADMINISTRATOR).Permissions;
} catch {
return find(m => typeof m.ADMINISTRATOR === "bigint");
}
});

View file

@ -36,9 +36,8 @@ if (window[WEBPACK_CHUNK]) {
Object.defineProperty(window, WEBPACK_CHUNK, {
get: () => webpackChunk,
set: v => {
if (v?.push !== Array.prototype.push) {
if (v?.push !== Array.prototype.push && _initWebpack(v)) {
logger.info(`Patching ${WEBPACK_CHUNK}.push`);
_initWebpack(v);
patchPush();
// @ts-ignore
delete window[WEBPACK_CHUNK];
@ -85,10 +84,9 @@ function patchPush() {
logger.error("Error in patched chunk", err);
return void originalMod(module, exports, require);
}
// There are (at the time of writing) 11 modules exporting the window
// Make these non enumerable to improve webpack search performance
if (module.exports === window) {
if (exports === window) {
Object.defineProperty(require.c, id, {
value: require.c[id],
enumerable: false,

View file

@ -62,9 +62,50 @@ export type CallbackFn = (mod: any, id: number) => void;
export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
if (cache !== void 0) throw "no.";
wreq = instance.push([[Symbol("Vencord")], {}, r => r]);
instance.push([[Symbol("Vencord")], {}, r => wreq = r]);
if (!wreq) return false;
cache = wreq.c;
instance.pop();
for (const id in cache) {
const { exports } = cache[id];
if (!exports) continue;
const numberId = Number(id);
for (const callback of listeners) {
try {
callback(exports, numberId);
} catch (err) {
logger.error("Error in webpack listener", err);
}
}
for (const [filter, callback] of subscriptions) {
try {
if (filter(exports)) {
subscriptions.delete(filter);
callback(exports, numberId);
} else if (typeof exports === "object") {
if (exports.default && filter(exports.default)) {
subscriptions.delete(filter);
callback(exports.default, numberId);
}
for (const nested in exports) if (nested.length <= 3) {
if (exports[nested] && filter(exports[nested])) {
subscriptions.delete(filter);
callback(exports[nested], numberId);
}
}
}
} catch (err) {
logger.error("Error while firing callback for webpack chunk", err);
}
}
}
return true;
}
if (IS_DEV && IS_DISCORD_DESKTOP) {