Merge branch 'immediate-finds' into immediate-finds-modules-proxy

This commit is contained in:
Nuckyz 2024-06-13 00:03:39 -03:00
commit bb22355a57
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9
22 changed files with 353 additions and 354 deletions

View file

@ -40,7 +40,7 @@ import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextI
import Plugins from "~plugins"; import Plugins from "~plugins";
// Avoid circular dependency // Avoid circular dependency
const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("../../plugins")); const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("../../plugins")) as typeof import("../../plugins");
const cl = classNameFactory("vc-plugins-"); const cl = classNameFactory("vc-plugins-");
const logger = new Logger("PluginSettings", "#a6d189"); const logger = new Logger("PluginSettings", "#a6d189");

View file

@ -250,7 +250,7 @@ function ThemesTab() {
Edit QuickCSS Edit QuickCSS
</Button> </Button>
{Vencord.Settings.plugins.ClientTheme.enabled && ( {Vencord.Plugins.isPluginEnabled("ClientTheme") && (
<Button <Button
onClick={() => openModal(modalProps => ( onClick={() => openModal(modalProps => (
<PluginModal <PluginModal

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import BackupAndRestoreTab from "@components/VencordSettings/BackupAndRestoreTab"; import BackupAndRestoreTab from "@components/VencordSettings/BackupAndRestoreTab";
import CloudTab from "@components/VencordSettings/CloudTab"; import CloudTab from "@components/VencordSettings/CloudTab";
import PatchHelperTab from "@components/VencordSettings/PatchHelperTab"; import PatchHelperTab from "@components/VencordSettings/PatchHelperTab";
@ -33,11 +33,27 @@ import gitHash from "~git-hash";
type SectionType = "HEADER" | "DIVIDER" | "CUSTOM"; type SectionType = "HEADER" | "DIVIDER" | "CUSTOM";
type SectionTypes = Record<SectionType, SectionType>; type SectionTypes = Record<SectionType, SectionType>;
const settings = definePluginSettings({
settingsLocation: {
type: OptionType.SELECT,
description: "Where to put the Vencord settings section",
options: [
{ label: "At the very top", value: "top" },
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
{ label: "Below the Nitro section", value: "belowNitro" },
{ label: "Above Activity Settings", value: "aboveActivity" },
{ label: "Below Activity Settings", value: "belowActivity" },
{ label: "At the very bottom", value: "bottom" },
]
}
});
export default definePlugin({ export default definePlugin({
name: "Settings", name: "Settings",
description: "Adds Settings UI and debug info", description: "Adds Settings UI and debug info",
authors: [Devs.Ven, Devs.Megu], authors: [Devs.Ven, Devs.Megu],
required: true, required: true,
settings,
patches: [ patches: [
{ {
@ -63,7 +79,7 @@ export default definePlugin({
noWarn: true, noWarn: true,
replacement: { replacement: {
get match() { get match() {
switch (Settings.plugins.Settings.settingsLocation) { switch (settings.store.settingsLocation) {
case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS/; 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 "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 "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS/;
@ -158,12 +174,12 @@ export default definePlugin({
].filter(Boolean); ].filter(Boolean);
}, },
isRightSpot({ header, settings }: { header?: string; settings?: string[]; }) { isRightSpot({ header, settingsChilds }: { header?: string; settingsChilds?: string[]; }) {
const firstChild = settings?.[0]; const firstChild = settingsChilds?.[0];
// lowest two elements... sanity backup // lowest two elements... sanity backup
if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true; if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true;
const { settingsLocation } = Settings.plugins.Settings; const { settingsLocation } = settings.store;
if (settingsLocation === "bottom") return firstChild === "LOGOUT"; if (settingsLocation === "bottom") return firstChild === "LOGOUT";
if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG"; if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG";
@ -203,21 +219,6 @@ export default definePlugin({
}; };
}, },
options: {
settingsLocation: {
type: OptionType.SELECT,
description: "Where to put the Vencord settings section",
options: [
{ label: "At the very top", value: "top" },
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
{ label: "Below the Nitro section", value: "belowNitro" },
{ label: "Above Activity Settings", value: "aboveActivity" },
{ label: "Below Activity Settings", value: "belowActivity" },
{ label: "At the very bottom", value: "bottom" },
]
},
},
get electronVersion() { get electronVersion() {
return VencordNative.native.getVersions().electron || window.armcord?.electron || null; return VencordNative.native.getVersions().electron || window.armcord?.electron || null;
}, },

View file

@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
@ -28,16 +29,17 @@ export default definePlugin({
find: "BAN_CONFIRM_TITLE.", find: "BAN_CONFIRM_TITLE.",
replacement: { replacement: {
match: /src:\i\("\d+"\)/g, match: /src:\i\("\d+"\)/g,
replace: "src: Vencord.Settings.plugins.BANger.source" replace: "src: $self.settings.store.source"
} }
} }
], ],
options: {
settings: definePluginSettings({
source: { source: {
description: "Source to replace ban GIF with (Video or Gif)", description: "Source to replace ban GIF with (Video or Gif)",
type: OptionType.STRING, type: OptionType.STRING,
default: "https://i.imgur.com/wp5q52C.mp4", default: "https://i.imgur.com/wp5q52C.mp4",
restartNeeded: true, restartNeeded: true,
} }
} })
}); });

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { canonicalizeMatch } from "@utils/patches"; import { canonicalizeMatch } from "@utils/patches";
@ -25,10 +25,26 @@ import { findByProps } from "@webpack";
const UserPopoutSectionCssClasses = findByProps("section", "lastSection"); const UserPopoutSectionCssClasses = findByProps("section", "lastSection");
const settings = definePluginSettings({
hide: {
type: OptionType.BOOLEAN,
description: "Hide notes",
default: false,
restartNeeded: true
},
noSpellCheck: {
type: OptionType.BOOLEAN,
description: "Disable spellcheck in notes",
disabled: () => settings.store.hide,
default: false
}
});
export default definePlugin({ export default definePlugin({
name: "BetterNotesBox", name: "BetterNotesBox",
description: "Hide notes or disable spellcheck (Configure in settings!!)", description: "Hide notes or disable spellcheck (Configure in settings!!)",
authors: [Devs.Ven], authors: [Devs.Ven],
settings,
patches: [ patches: [
{ {
@ -36,7 +52,7 @@ export default definePlugin({
all: true, all: true,
// Some modules match the find but the replacement is returned untouched // Some modules match the find but the replacement is returned untouched
noWarn: true, noWarn: true,
predicate: () => Vencord.Settings.plugins.BetterNotesBox.hide, predicate: () => settings.store.hide,
replacement: { replacement: {
match: /hideNote:.+?(?=([,}].*?\)))/g, match: /hideNote:.+?(?=([,}].*?\)))/g,
replace: (m, rest) => { replace: (m, rest) => {
@ -54,7 +70,7 @@ export default definePlugin({
find: "Messages.NOTE_PLACEHOLDER", find: "Messages.NOTE_PLACEHOLDER",
replacement: { replacement: {
match: /\.NOTE_PLACEHOLDER,/, match: /\.NOTE_PLACEHOLDER,/,
replace: "$&spellCheck:!Vencord.Settings.plugins.BetterNotesBox.noSpellCheck," replace: "$&spellCheck:!$self.settings.store.noSpellCheck,"
} }
}, },
{ {
@ -66,21 +82,6 @@ export default definePlugin({
} }
], ],
options: {
hide: {
type: OptionType.BOOLEAN,
description: "Hide notes",
default: false,
restartNeeded: true
},
noSpellCheck: {
type: OptionType.BOOLEAN,
description: "Disable spellcheck in notes",
disabled: () => Settings.plugins.BetterNotesBox.hide,
default: false
}
},
patchPadding: ErrorBoundary.wrap(({ lastSection }) => { patchPadding: ErrorBoundary.wrap(({ lastSection }) => {
if (!lastSection) return null; if (!lastSection) return null;
return ( return (

View file

@ -16,16 +16,32 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { Clipboard, Toasts } from "@webpack/common"; import { Clipboard, Toasts } from "@webpack/common";
const settings = definePluginSettings({
bothStyles: {
type: OptionType.BOOLEAN,
description: "Show both role dot and coloured names",
restartNeeded: true,
default: false,
},
copyRoleColorInProfilePopout: {
type: OptionType.BOOLEAN,
description: "Allow click on role dot in profile popout to copy role color",
restartNeeded: true,
default: false
}
});
export default definePlugin({ export default definePlugin({
name: "BetterRoleDot", name: "BetterRoleDot",
authors: [Devs.Ven, Devs.AutumnVN], authors: [Devs.Ven, Devs.AutumnVN],
description: description:
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously", "Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
settings,
patches: [ patches: [
{ {
@ -39,7 +55,7 @@ export default definePlugin({
find: '"dot"===', find: '"dot"===',
all: true, all: true,
noWarn: true, noWarn: true,
predicate: () => Settings.plugins.BetterRoleDot.bothStyles, predicate: () => settings.store.bothStyles,
replacement: { replacement: {
match: /"(?:username|dot)"===\i(?!\.\i)/g, match: /"(?:username|dot)"===\i(?!\.\i)/g,
replace: "true", replace: "true",
@ -49,7 +65,7 @@ export default definePlugin({
{ {
find: ".ADD_ROLE_A11Y_LABEL", find: ".ADD_ROLE_A11Y_LABEL",
all: true, all: true,
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
noWarn: true, noWarn: true,
replacement: { replacement: {
match: /"dot"===\i/, match: /"dot"===\i/,
@ -59,7 +75,7 @@ export default definePlugin({
{ {
find: ".roleVerifiedIcon", find: ".roleVerifiedIcon",
all: true, all: true,
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
noWarn: true, noWarn: true,
replacement: { replacement: {
match: /"dot"===\i/, match: /"dot"===\i/,
@ -68,21 +84,6 @@ export default definePlugin({
} }
], ],
options: {
bothStyles: {
type: OptionType.BOOLEAN,
description: "Show both role dot and coloured names",
restartNeeded: true,
default: false,
},
copyRoleColorInProfilePopout: {
type: OptionType.BOOLEAN,
description: "Allow click on role dot in profile popout to copy role color",
restartNeeded: true,
default: false
}
},
copyToClipBoard(color: string) { copyToClipBoard(color: string) {
Clipboard.copy(color); Clipboard.copy(color);
Toasts.show({ Toasts.show({

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
@ -26,7 +26,7 @@ function setCss() {
style.textContent = ` style.textContent = `
.vc-nsfw-img [class^=imageWrapper] img, .vc-nsfw-img [class^=imageWrapper] img,
.vc-nsfw-img [class^=wrapperPaused] video { .vc-nsfw-img [class^=wrapperPaused] video {
filter: blur(${Settings.plugins.BlurNSFW.blurAmount}px); filter: blur(${settings.store.blurAmount}px);
transition: filter 0.2s; transition: filter 0.2s;
} }
.vc-nsfw-img [class^=imageWrapper]:hover img, .vc-nsfw-img [class^=imageWrapper]:hover img,
@ -36,10 +36,20 @@ function setCss() {
`; `;
} }
const settings = definePluginSettings({
blurAmount: {
type: OptionType.NUMBER,
description: "Blur Amount",
default: 10,
onChange: setCss
}
});
export default definePlugin({ export default definePlugin({
name: "BlurNSFW", name: "BlurNSFW",
description: "Blur attachments in NSFW channels until hovered", description: "Blur attachments in NSFW channels until hovered",
authors: [Devs.Ven], authors: [Devs.Ven],
settings,
patches: [ patches: [
{ {
@ -51,15 +61,6 @@ export default definePlugin({
} }
], ],
options: {
blurAmount: {
type: OptionType.NUMBER,
description: "Blur Amount",
default: 10,
onChange: setCss
}
},
start() { start() {
style = document.createElement("style"); style = document.createElement("style");
style.id = "VcBlurNsfw"; style.id = "VcBlurNsfw";

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { useTimer } from "@utils/react"; import { useTimer } from "@utils/react";
@ -25,7 +25,7 @@ import { React } from "@webpack/common";
function formatDuration(ms: number) { function formatDuration(ms: number) {
// here be dragons (moment fucking sucks) // here be dragons (moment fucking sucks)
const human = Settings.plugins.CallTimer.format === "human"; const human = settings.store.format === "human";
const format = (n: number) => human ? n : n.toString().padStart(2, "0"); const format = (n: number) => human ? n : n.toString().padStart(2, "0");
const unit = (s: string) => human ? s : ""; const unit = (s: string) => human ? s : "";
@ -46,32 +46,33 @@ function formatDuration(ms: number) {
return res; return res;
} }
const settings = definePluginSettings({
format: {
type: OptionType.SELECT,
description: "The timer format. This can be any valid moment.js format",
options: [
{
label: "30d 23:00:42",
value: "stopwatch",
default: true
},
{
label: "30d 23h 00m 42s",
value: "human"
}
]
}
});
export default definePlugin({ export default definePlugin({
name: "CallTimer", name: "CallTimer",
description: "Adds a timer to vcs", description: "Adds a timer to vcs",
authors: [Devs.Ven], authors: [Devs.Ven],
settings,
startTime: 0, startTime: 0,
interval: void 0 as NodeJS.Timeout | undefined, interval: void 0 as NodeJS.Timeout | undefined,
options: {
format: {
type: OptionType.SELECT,
description: "The timer format. This can be any valid moment.js format",
options: [
{
label: "30d 23:00:42",
value: "stopwatch",
default: true
},
{
label: "30d 23h 00m 42s",
value: "human"
}
]
}
},
patches: [{ patches: [{
find: "renderConnectionStatus(){", find: "renderConnectionStatus(){",
replacement: { replacement: {

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { definePluginSettings, Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { ErrorCard } from "@components/ErrorCard"; import { ErrorCard } from "@components/ErrorCard";
import { Link } from "@components/Link"; import { Link } from "@components/Link";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
@ -258,7 +258,7 @@ const settings = definePluginSettings({
function onChange() { function onChange() {
setRpc(true); setRpc(true);
if (Settings.plugins.CustomRPC.enabled) setRpc(); if (Vencord.Plugins.isPluginEnabled("CustomRPC")) setRpc();
} }
function isStreamLinkDisabled() { function isStreamLinkDisabled() {

View file

@ -21,20 +21,25 @@ import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByProps, findStore } from "@webpack"; import { findByProps, findStore } from "@webpack";
import { ChannelStore, FluxDispatcher, GuildStore, RelationshipStore, SnowflakeUtils, UserStore } from "@webpack/common"; import { ChannelStore, FluxDispatcher, GuildStore, RelationshipStore, SnowflakeUtils, UserStore } from "@webpack/common";
import { Settings } from "Vencord";
const UserAffinitiesStore = findStore("UserAffinitiesStore"); const UserAffinitiesStore = findStore("UserAffinitiesStore");
const { FriendsSections } = findByProps("FriendsSections"); const { FriendsSections } = findByProps("FriendsSections");
interface UserAffinity { const settings = definePluginSettings({
user_id: string; sortByAffinity: {
affinity: number; type: OptionType.BOOLEAN,
} default: true,
description: "Whether to sort implicit relationships by their affinity to you.",
restartNeeded: true
}
});
export default definePlugin({ export default definePlugin({
name: "ImplicitRelationships", name: "ImplicitRelationships",
description: "Shows your implicit relationships in the Friends tab.", description: "Shows your implicit relationships in the Friends tab.",
authors: [Devs.Dolfies], authors: [Devs.Dolfies],
settings,
patches: [ patches: [
// Counts header // Counts header
{ {
@ -81,7 +86,7 @@ export default definePlugin({
{ {
find: "getRelationshipCounts(){", find: "getRelationshipCounts(){",
replacement: { replacement: {
predicate: () => Settings.plugins.ImplicitRelationships.sortByAffinity, predicate: () => settings.store.sortByAffinity,
match: /\}\)\.sortBy\((.+?)\)\.value\(\)/, match: /\}\)\.sortBy\((.+?)\)\.value\(\)/,
replace: "}).sortBy(row => $self.wrapSort(($1), row)).value()" replace: "}).sortBy(row => $self.wrapSort(($1), row)).value()"
} }
@ -110,16 +115,6 @@ export default definePlugin({
}, },
} }
], ],
settings: definePluginSettings(
{
sortByAffinity: {
type: OptionType.BOOLEAN,
default: true,
description: "Whether to sort implicit relationships by their affinity to you.",
restartNeeded: true
},
}
),
wrapSort(comparator: Function, row: any) { wrapSort(comparator: Function, row: any) {
return row.type === 5 return row.type === 5

View file

@ -20,7 +20,7 @@ import "./messageLogger.css";
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { updateMessage } from "@api/MessageUpdater"; import { updateMessage } from "@api/MessageUpdater";
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles"; import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
@ -41,7 +41,7 @@ interface MLMessage extends Message {
const styles = findByProps("edited", "communicationDisabled", "isSystemMessage"); const styles = findByProps("edited", "communicationDisabled", "isSystemMessage");
function addDeleteStyle() { function addDeleteStyle() {
if (Settings.plugins.MessageLogger.deleteStyle === "text") { if (settings.store.deleteStyle === "text") {
enableStyle(textStyle); enableStyle(textStyle);
disableStyle(overlayStyle); disableStyle(overlayStyle);
} else { } else {
@ -125,11 +125,60 @@ const patchChannelContextMenu: NavContextMenuPatchCallback = (children, { channe
); );
}; };
const settings = definePluginSettings({
deleteStyle: {
type: OptionType.SELECT,
description: "The style of deleted messages",
default: "text",
options: [
{ label: "Red text", value: "text", default: true },
{ label: "Red overlay", value: "overlay" }
],
onChange: () => addDeleteStyle()
},
logDeletes: {
type: OptionType.BOOLEAN,
description: "Whether to log deleted messages",
default: true,
},
logEdits: {
type: OptionType.BOOLEAN,
description: "Whether to log edited messages",
default: true,
},
ignoreBots: {
type: OptionType.BOOLEAN,
description: "Whether to ignore messages by bots",
default: false
},
ignoreSelf: {
type: OptionType.BOOLEAN,
description: "Whether to ignore messages by yourself",
default: false
},
ignoreUsers: {
type: OptionType.STRING,
description: "Comma-separated list of user IDs to ignore",
default: ""
},
ignoreChannels: {
type: OptionType.STRING,
description: "Comma-separated list of channel IDs to ignore",
default: ""
},
ignoreGuilds: {
type: OptionType.STRING,
description: "Comma-separated list of guild IDs to ignore",
default: ""
},
});
export default definePlugin({ export default definePlugin({
name: "MessageLogger", name: "MessageLogger",
description: "Temporarily logs deleted and edited messages.", description: "Temporarily logs deleted and edited messages.",
authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux], authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux],
dependencies: ["MessageUpdaterAPI"], dependencies: ["MessageUpdaterAPI"],
settings,
contextMenus: { contextMenus: {
"message": patchMessageContextMenu, "message": patchMessageContextMenu,
@ -175,54 +224,6 @@ export default definePlugin({
}; };
}, },
options: {
deleteStyle: {
type: OptionType.SELECT,
description: "The style of deleted messages",
default: "text",
options: [
{ label: "Red text", value: "text", default: true },
{ label: "Red overlay", value: "overlay" }
],
onChange: () => addDeleteStyle()
},
logDeletes: {
type: OptionType.BOOLEAN,
description: "Whether to log deleted messages",
default: true,
},
logEdits: {
type: OptionType.BOOLEAN,
description: "Whether to log edited messages",
default: true,
},
ignoreBots: {
type: OptionType.BOOLEAN,
description: "Whether to ignore messages by bots",
default: false
},
ignoreSelf: {
type: OptionType.BOOLEAN,
description: "Whether to ignore messages by yourself",
default: false
},
ignoreUsers: {
type: OptionType.STRING,
description: "Comma-separated list of user IDs to ignore",
default: ""
},
ignoreChannels: {
type: OptionType.STRING,
description: "Comma-separated list of channel IDs to ignore",
default: ""
},
ignoreGuilds: {
type: OptionType.STRING,
description: "Comma-separated list of guild IDs to ignore",
default: ""
},
},
handleDelete(cache: any, data: { ids: string[], id: string; mlDeleted?: boolean; }, isBulk: boolean) { handleDelete(cache: any, data: { ids: string[], id: string; mlDeleted?: boolean; }, isBulk: boolean) {
try { try {
if (cache == null || (!isBulk && !cache.has(data.id))) return cache; if (cache == null || (!isBulk && !cache.has(data.id))) return cache;
@ -257,7 +258,7 @@ export default definePlugin({
}, },
shouldIgnore(message: any, isEdit = false) { shouldIgnore(message: any, isEdit = false) {
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds, logEdits, logDeletes } = Settings.plugins.MessageLogger; const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds, logEdits, logDeletes } = settings.store;
const myId = UserStore.getCurrentUser().id; const myId = UserStore.getCurrentUser().id;
return ignoreBots && message.author?.bot || return ignoreBots && message.author?.bot ||

View file

@ -18,7 +18,7 @@
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "@api/Commands"; import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "@api/Commands";
import * as DataStore from "@api/DataStore"; import * as DataStore from "@api/DataStore";
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
@ -60,7 +60,7 @@ function createTagCommand(tag: Tag) {
return { content: `/${tag.name}` }; return { content: `/${tag.name}` };
} }
if (Settings.plugins.MessageTags.clyde) sendBotMessage(ctx.channel.id, { if (settings.store.clyde) sendBotMessage(ctx.channel.id, {
content: `${EMOTE} The tag **${tag.name}** has been sent!` content: `${EMOTE} The tag **${tag.name}** has been sent!`
}); });
return { content: tag.message.replaceAll("\\n", "\n") }; return { content: tag.message.replaceAll("\\n", "\n") };
@ -69,19 +69,21 @@ function createTagCommand(tag: Tag) {
}, "CustomTags"); }, "CustomTags");
} }
const settings = definePluginSettings({
clyde: {
name: "Clyde message on send",
description: "If enabled, clyde will send you an ephemeral message when a tag was used.",
type: OptionType.BOOLEAN,
default: true
}
});
export default definePlugin({ export default definePlugin({
name: "MessageTags", name: "MessageTags",
description: "Allows you to save messages and to use them with a simple command.", description: "Allows you to save messages and to use them with a simple command.",
authors: [Devs.Luna], authors: [Devs.Luna],
options: { settings,
clyde: {
name: "Clyde message on send",
description: "If enabled, clyde will send you an ephemeral message when a tag was used.",
type: OptionType.BOOLEAN,
default: true
}
},
dependencies: ["CommandsAPI"], dependencies: ["CommandsAPI"],
async start() { async start() {

View file

@ -16,17 +16,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByProps } from "@webpack"; import { findByProps } from "@webpack";
const RelationshipStore = findByProps("getRelationships", "isBlocked"); const RelationshipStore = findByProps("getRelationships", "isBlocked");
const settings = definePluginSettings({
ignoreBlockedMessages: {
description: "Completely ignores (recent) incoming messages from blocked users (locally).",
type: OptionType.BOOLEAN,
default: false,
restartNeeded: true,
},
});
export default definePlugin({ export default definePlugin({
name: "NoBlockedMessages", name: "NoBlockedMessages",
description: "Hides all blocked messages from chat completely.", description: "Hides all blocked messages from chat completely.",
authors: [Devs.rushii, Devs.Samu], authors: [Devs.rushii, Devs.Samu],
settings,
patches: [ patches: [
{ {
find: "Messages.BLOCKED_MESSAGES_HIDE", find: "Messages.BLOCKED_MESSAGES_HIDE",
@ -42,7 +53,7 @@ export default definePlugin({
'"displayName","ReadStateStore")' '"displayName","ReadStateStore")'
].map(find => ({ ].map(find => ({
find, find,
predicate: () => Settings.plugins.NoBlockedMessages.ignoreBlockedMessages === true, predicate: () => settings.store.ignoreBlockedMessages === true,
replacement: [ replacement: [
{ {
match: /(?<=MESSAGE_CREATE:function\((\i)\){)/, match: /(?<=MESSAGE_CREATE:function\((\i)\){)/,
@ -51,14 +62,6 @@ export default definePlugin({
] ]
})) }))
], ],
options: {
ignoreBlockedMessages: {
description: "Completely ignores (recent) incoming messages from blocked users (locally).",
type: OptionType.BOOLEAN,
default: false,
restartNeeded: true,
},
},
isBlocked: message => isBlocked: message =>
RelationshipStore.isBlocked(message.author.id) RelationshipStore.isBlocked(message.author.id)
}); });

View file

@ -19,7 +19,7 @@
import { addBadge, BadgePosition, ProfileBadge, removeBadge } from "@api/Badges"; import { addBadge, BadgePosition, ProfileBadge, removeBadge } from "@api/Badges";
import { addDecorator, removeDecorator } from "@api/MemberListDecorators"; import { addDecorator, removeDecorator } from "@api/MemberListDecorators";
import { addDecoration, removeDecoration } from "@api/MessageDecorations"; import { addDecoration, removeDecoration } from "@api/MessageDecorations";
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
@ -162,27 +162,34 @@ const indicatorLocations = {
} }
}; };
const settings = definePluginSettings({
...Object.fromEntries(
Object.entries(indicatorLocations).map(([key, value]) => {
return [key, {
type: OptionType.BOOLEAN,
description: `Show indicators ${value.description.toLowerCase()}`,
// onChange doesn't give any way to know which setting was changed, so restart required
restartNeeded: true,
default: true
}];
})
) as Record<"list" | "badges" | "messages", { type: OptionType.BOOLEAN; description: string; restartNeeded: boolean; default: boolean; }>,
colorMobileIndicator: {
type: OptionType.BOOLEAN,
description: "Whether to make the mobile indicator match the color of the user status.",
default: true,
restartNeeded: true
}
});
export default definePlugin({ export default definePlugin({
name: "PlatformIndicators", name: "PlatformIndicators",
description: "Adds platform indicators (Desktop, Mobile, Web...) to users", description: "Adds platform indicators (Desktop, Mobile, Web...) to users",
authors: [Devs.kemo, Devs.TheSun, Devs.Nuckyz, Devs.Ven], authors: [Devs.kemo, Devs.TheSun, Devs.Nuckyz, Devs.Ven],
dependencies: ["MessageDecorationsAPI", "MemberListDecoratorsAPI"], dependencies: ["MessageDecorationsAPI", "MemberListDecoratorsAPI"],
settings,
start() { start() {
const settings = Settings.plugins.PlatformIndicators;
const { displayMode } = settings;
// transfer settings from the old ones, which had a select menu instead of booleans
if (displayMode) {
if (displayMode !== "both") settings[displayMode] = true;
else {
settings.list = true;
settings.badges = true;
}
settings.messages = true;
delete settings.displayMode;
}
Object.entries(indicatorLocations).forEach(([key, value]) => { Object.entries(indicatorLocations).forEach(([key, value]) => {
if (settings[key]) value.onEnable(); if (settings[key]) value.onEnable();
}); });
@ -197,7 +204,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: ".Masks.STATUS_ONLINE_MOBILE", find: ".Masks.STATUS_ONLINE_MOBILE",
predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, predicate: () => settings.store.colorMobileIndicator,
replacement: [ replacement: [
{ {
// Return the STATUS_ONLINE_MOBILE mask if the user is on mobile, no matter the status // Return the STATUS_ONLINE_MOBILE mask if the user is on mobile, no matter the status
@ -213,7 +220,7 @@ export default definePlugin({
}, },
{ {
find: ".AVATAR_STATUS_MOBILE_16;", find: ".AVATAR_STATUS_MOBILE_16;",
predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, predicate: () => settings.store.colorMobileIndicator,
replacement: [ replacement: [
{ {
// Return the AVATAR_STATUS_MOBILE size mask if the user is on mobile, no matter the status // Return the AVATAR_STATUS_MOBILE size mask if the user is on mobile, no matter the status
@ -234,32 +241,12 @@ export default definePlugin({
}, },
{ {
find: "}isMobileOnline(", find: "}isMobileOnline(",
predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, predicate: () => settings.store.colorMobileIndicator,
replacement: { replacement: {
// Make isMobileOnline return true no matter what is the user status // Make isMobileOnline return true no matter what is the user status
match: /(?<=\i\[\i\.\i\.MOBILE\])===\i\.\i\.ONLINE/, match: /(?<=\i\[\i\.\i\.MOBILE\])===\i\.\i\.ONLINE/,
replace: "!= null" replace: "!= null"
} }
} }
], ]
options: {
...Object.fromEntries(
Object.entries(indicatorLocations).map(([key, value]) => {
return [key, {
type: OptionType.BOOLEAN,
description: `Show indicators ${value.description.toLowerCase()}`,
// onChange doesn't give any way to know which setting was changed, so restart required
restartNeeded: true,
default: true
}];
})
),
colorMobileIndicator: {
type: OptionType.BOOLEAN,
description: "Whether to make the mobile indicator match the color of the user status.",
default: true,
restartNeeded: true
}
}
}); });

View file

@ -16,7 +16,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings";
import { debounce } from "@shared/debounce"; import { debounce } from "@shared/debounce";
import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent"; import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent";
import { getCurrentChannel } from "@utils/discord"; import { getCurrentChannel } from "@utils/discord";
@ -147,11 +146,11 @@ async function bulkFetchPronouns(ids: string[]): Promise<PronounsResponse> {
} }
} }
export function extractPronouns(pronounSet?: { [locale: string]: PronounCode[] }): string { export function extractPronouns(pronounSet?: { [locale: string]: PronounCode[]; }): string {
if (!pronounSet || !pronounSet.en) return PronounMapping.unspecified; if (!pronounSet || !pronounSet.en) return PronounMapping.unspecified;
// PronounDB returns an empty set instead of {sets: {en: ["unspecified"]}}. // PronounDB returns an empty set instead of {sets: {en: ["unspecified"]}}.
const pronouns = pronounSet.en; const pronouns = pronounSet.en;
const { pronounsFormat } = Settings.plugins.PronounDB as { pronounsFormat: PronounsFormat, enabled: boolean; }; const { pronounsFormat } = settings.store;
if (pronouns.length === 1) { if (pronouns.length === 1) {
// For capitalized pronouns or special codes (any, ask, avoid), we always return the normal (capitalized) string // For capitalized pronouns or special codes (any, ask, avoid), we always return the normal (capitalized) string

View file

@ -17,7 +17,7 @@
*/ */
import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList"; import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList";
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { useForceUpdater } from "@utils/react"; import { useForceUpdater } from "@utils/react";
@ -89,26 +89,27 @@ function handleGuildUpdate() {
forceUpdateGuildCount?.(); forceUpdateGuildCount?.();
} }
const settings = definePluginSettings({
mode: {
description: "mode",
type: OptionType.SELECT,
options: [
{ label: "Only online friend count", value: IndicatorType.FRIEND, default: true },
{ label: "Only server count", value: IndicatorType.SERVER },
{ label: "Both server and online friend counts", value: IndicatorType.BOTH },
]
}
});
export default definePlugin({ export default definePlugin({
name: "ServerListIndicators", name: "ServerListIndicators",
description: "Add online friend count or server count in the server list", description: "Add online friend count or server count in the server list",
authors: [Devs.dzshn], authors: [Devs.dzshn],
dependencies: ["ServerListAPI"], dependencies: ["ServerListAPI"],
settings,
options: {
mode: {
description: "mode",
type: OptionType.SELECT,
options: [
{ label: "Only online friend count", value: IndicatorType.FRIEND, default: true },
{ label: "Only server count", value: IndicatorType.SERVER },
{ label: "Both server and online friend counts", value: IndicatorType.BOTH },
]
}
},
renderIndicator: () => { renderIndicator: () => {
const { mode } = Settings.plugins.ServerListIndicators; const { mode } = settings.store;
return <ErrorBoundary noop> return <ErrorBoundary noop>
<div style={{ marginBottom: "4px" }}> <div style={{ marginBottom: "4px" }}>
{!!(mode & IndicatorType.FRIEND) && <FriendsIndicator />} {!!(mode & IndicatorType.FRIEND) && <FriendsIndicator />}

View file

@ -16,7 +16,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { formatDuration } from "@utils/text"; import { formatDuration } from "@utils/text";
import { findByProps, findComponentByCode } from "@webpack"; import { findByProps, findComponentByCode } from "@webpack";
@ -157,7 +156,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
}); });
} }
if (Settings.plugins.PermissionsViewer.enabled) { if (Vencord.Plugins.isPluginEnabled("PermissionsViewer")) {
setPermissions(sortPermissionOverwrites(Object.values(permissionOverwrites).map(overwrite => ({ setPermissions(sortPermissionOverwrites(Object.values(permissionOverwrites).map(overwrite => ({
type: overwrite.type as PermissionType, type: overwrite.type as PermissionType,
id: overwrite.id, id: overwrite.id,
@ -274,7 +273,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
} }
<div className="shc-lock-screen-allowed-users-and-roles-container"> <div className="shc-lock-screen-allowed-users-and-roles-container">
<div className="shc-lock-screen-allowed-users-and-roles-container-title"> <div className="shc-lock-screen-allowed-users-and-roles-container-title">
{Settings.plugins.PermissionsViewer.enabled && ( {Vencord.Plugins.isPluginEnabled("PermissionsViewer") && (
<Tooltip text="Permission Details"> <Tooltip text="Permission Details">
{({ onMouseLeave, onMouseEnter }) => ( {({ onMouseLeave, onMouseEnter }) => (
<button <button

View file

@ -16,10 +16,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { proxyLazy } from "@utils/lazy";
import { findByProps, webpackDependantLazy } from "@webpack"; import { findByProps, webpackDependantLazy } from "@webpack";
import { Flux, FluxDispatcher } from "@webpack/common"; import { Flux, FluxDispatcher } from "@webpack/common";
// Avoid circular dependency
const { settings } = proxyLazy(() => require(".")) as typeof import(".");
export interface Track { export interface Track {
id: string; id: string;
name: string; name: string;
@ -88,7 +91,7 @@ export const SpotifyStore = webpackDependantLazy(() => {
public isSettingPosition = false; public isSettingPosition = false;
public openExternal(path: string) { public openExternal(path: string) {
const url = Settings.plugins.SpotifyControls.useSpotifyUris || Vencord.Plugins.isPluginEnabled("OpenInApp") const url = settings.store.useSpotifyUris || Vencord.Plugins.isPluginEnabled("OpenInApp")
? "spotify:" + path.replaceAll("/", (_, idx) => idx === 0 ? "" : ":") ? "spotify:" + path.replaceAll("/", (_, idx) => idx === 0 ? "" : ":")
: "https://open.spotify.com" + path; : "https://open.spotify.com" + path;

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles"; import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
@ -29,23 +29,26 @@ function toggleHoverControls(value: boolean) {
(value ? enableStyle : disableStyle)(hoverOnlyStyle); (value ? enableStyle : disableStyle)(hoverOnlyStyle);
} }
export const settings = definePluginSettings({
hoverControls: {
description: "Show controls on hover",
type: OptionType.BOOLEAN,
default: false,
onChange: v => toggleHoverControls(v)
},
useSpotifyUris: {
type: OptionType.BOOLEAN,
description: "Open Spotify URIs instead of Spotify URLs. Will only work if you have Spotify installed and might not work on all platforms",
default: false
}
});
export default definePlugin({ export default definePlugin({
name: "SpotifyControls", name: "SpotifyControls",
description: "Adds a Spotify player above the account panel", description: "Adds a Spotify player above the account panel",
authors: [Devs.Ven, Devs.afn, Devs.KraXen72, Devs.Av32000], authors: [Devs.Ven, Devs.afn, Devs.KraXen72, Devs.Av32000],
options: { settings,
hoverControls: {
description: "Show controls on hover",
type: OptionType.BOOLEAN,
default: false,
onChange: v => toggleHoverControls(v)
},
useSpotifyUris: {
type: OptionType.BOOLEAN,
description: "Open Spotify URIs instead of Spotify URLs. Will only work if you have Spotify installed and might not work on all platforms",
default: false
}
},
patches: [ patches: [
{ {
find: "showTaglessAccountPanel:", find: "showTaglessAccountPanel:",
@ -87,7 +90,7 @@ export default definePlugin({
} }
], ],
start: () => toggleHoverControls(Settings.plugins.SpotifyControls.hoverControls), start: () => toggleHoverControls(settings.store.hoverControls),
PanelWrapper({ VencordOriginal, ...props }) { PanelWrapper({ VencordOriginal, ...props }) {
return ( return (

View file

@ -18,7 +18,7 @@
import "./style.css"; import "./style.css";
import { definePluginSettings, Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
@ -87,7 +87,7 @@ function TypingIndicator({ channelId }: { channelId: string; }) {
break; break;
} }
default: { default: {
tooltipText = Settings.plugins.TypingTweaks.enabled tooltipText = Vencord.Plugins.isPluginEnabled("TypingTweaks")
? buildSeveralUsers({ a: getDisplayName(guildId, typingUsersArray[0]), b: getDisplayName(guildId, typingUsersArray[1]), count: typingUsersArray.length - 2 }) ? buildSeveralUsers({ a: getDisplayName(guildId, typingUsersArray[0]), b: getDisplayName(guildId, typingUsersArray[1]), count: typingUsersArray.length - 2 })
: i18n.Messages.SEVERAL_USERS_TYPING; : i18n.Messages.SEVERAL_USERS_TYPING;
break; break;

View file

@ -16,13 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { ErrorCard } from "@components/ErrorCard"; import { ErrorCard } from "@components/ErrorCard";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { proxyLazy } from "@utils/lazy";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { wordsToTitle } from "@utils/text"; import { wordsToTitle } from "@utils/text";
import definePlugin, { OptionType, PluginOptionsItem, ReporterTestable } from "@utils/types"; import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
import { findByProps } from "@webpack"; import { findByProps } from "@webpack";
import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common"; import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common";
@ -42,25 +43,25 @@ const VoiceStateStore = findByProps("getVoiceStatesForChannel", "getCurrentClien
// Filtering out events is not as simple as just dropping duplicates, as otherwise mute, unmute, mute would // Filtering out events is not as simple as just dropping duplicates, as otherwise mute, unmute, mute would
// not say the second mute, which would lead you to believe they're unmuted // not say the second mute, which would lead you to believe they're unmuted
function speak(text: string, settings: any = Settings.plugins.VcNarrator) { function speak(text: string, mergedSettings: typeof settings.store = settings.store) {
if (!text) return; if (!text) return;
const speech = new SpeechSynthesisUtterance(text); const speech = new SpeechSynthesisUtterance(text);
let voice = speechSynthesis.getVoices().find(v => v.voiceURI === settings.voice); let voice = speechSynthesis.getVoices().find(v => v.voiceURI === mergedSettings.voice);
if (!voice) { if (!voice) {
new Logger("VcNarrator").error(`Voice "${settings.voice}" not found. Resetting to default.`); new Logger("VcNarrator").error(`Voice "${mergedSettings.voice}" not found. Resetting to default.`);
voice = speechSynthesis.getVoices().find(v => v.default); voice = speechSynthesis.getVoices().find(v => v.default);
settings.voice = voice?.voiceURI; mergedSettings.voice = voice?.voiceURI as string;
if (!voice) return; // This should never happen if (!voice) return; // This should never happen
} }
speech.voice = voice!; speech.voice = voice;
speech.volume = settings.volume; speech.volume = mergedSettings.volume;
speech.rate = settings.rate; speech.rate = mergedSettings.rate;
speechSynthesis.speak(speech); speechSynthesis.speak(speech);
} }
function clean(str: string) { function clean(str: string) {
const replacer = Settings.plugins.VcNarrator.latinOnly const replacer = settings.store.latinOnly
? /[^\p{Script=Latin}\p{Number}\p{Punctuation}\s]/gu ? /[^\p{Script=Latin}\p{Number}\p{Punctuation}\s]/gu
: /[^\p{Letter}\p{Number}\p{Punctuation}\s]/gu; : /[^\p{Letter}\p{Number}\p{Punctuation}\s]/gu;
@ -143,19 +144,91 @@ function updateStatuses(type: string, { deaf, mute, selfDeaf, selfMute, userId,
} }
*/ */
function playSample(tempSettings: any, type: string) { function playSample(tempSettings: typeof settings.store, type: string) {
const settings = Object.assign({}, Settings.plugins.VcNarrator, tempSettings); const mergedSettings = Object.assign({}, settings.store, tempSettings);
const currentUser = UserStore.getCurrentUser(); const currentUser = UserStore.getCurrentUser();
const myGuildId = SelectedGuildStore.getGuildId(); const myGuildId = SelectedGuildStore.getGuildId();
speak(formatText(settings[type + "Message"], currentUser.username, "general", (currentUser as any).globalName ?? currentUser.username, GuildMemberStore.getNick(myGuildId, currentUser.id) ?? currentUser.username), settings); speak(formatText(mergedSettings[type + "Message"], currentUser.username, "general", (currentUser as any).globalName ?? currentUser.username, GuildMemberStore.getNick(myGuildId, currentUser.id) ?? currentUser.username), mergedSettings);
} }
const settings = definePluginSettings({
voice: {
type: OptionType.SELECT,
description: "Narrator Voice",
options: proxyLazy(() => window.speechSynthesis?.getVoices().map(v => ({
label: v.name,
value: v.voiceURI,
default: v.default
})) ?? [])
},
volume: {
type: OptionType.SLIDER,
description: "Narrator Volume",
default: 1,
markers: [0, 0.25, 0.5, 0.75, 1],
stickToMarkers: false
},
rate: {
type: OptionType.SLIDER,
description: "Narrator Speed",
default: 1,
markers: [0.1, 0.5, 1, 2, 5, 10],
stickToMarkers: false
},
sayOwnName: {
description: "Say own name",
type: OptionType.BOOLEAN,
default: false
},
latinOnly: {
description: "Strip non latin characters from names before saying them",
type: OptionType.BOOLEAN,
default: false
},
joinMessage: {
type: OptionType.STRING,
description: "Join Message",
default: "{{USER}} joined"
},
leaveMessage: {
type: OptionType.STRING,
description: "Leave Message",
default: "{{USER}} left"
},
moveMessage: {
type: OptionType.STRING,
description: "Move Message",
default: "{{USER}} moved to {{CHANNEL}}"
},
muteMessage: {
type: OptionType.STRING,
description: "Mute Message (only self for now)",
default: "{{USER}} Muted"
},
unmuteMessage: {
type: OptionType.STRING,
description: "Unmute Message (only self for now)",
default: "{{USER}} unmuted"
},
deafenMessage: {
type: OptionType.STRING,
description: "Deafen Message (only self for now)",
default: "{{USER}} deafened"
},
undeafenMessage: {
type: OptionType.STRING,
description: "Undeafen Message (only self for now)",
default: "{{USER}} undeafened"
}
});
export default definePlugin({ export default definePlugin({
name: "VcNarrator", name: "VcNarrator",
description: "Announces when users join, leave, or move voice channels via narrator", description: "Announces when users join, leave, or move voice channels via narrator",
authors: [Devs.Ven], authors: [Devs.Ven],
reporterTestable: ReporterTestable.None, reporterTestable: ReporterTestable.None,
settings,
flux: { flux: {
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) { VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
@ -176,8 +249,8 @@ export default definePlugin({
const [type, id] = getTypeAndChannelId(state, isMe); const [type, id] = getTypeAndChannelId(state, isMe);
if (!type) continue; if (!type) continue;
const template = Settings.plugins.VcNarrator[type + "Message"]; const template = settings.store[type + "Message"];
const user = isMe && !Settings.plugins.VcNarrator.sayOwnName ? "" : UserStore.getUser(userId).username; const user = isMe && !settings.store.sayOwnName ? "" : UserStore.getUser(userId).username;
const displayName = user && ((UserStore.getUser(userId) as any).globalName ?? user); const displayName = user && ((UserStore.getUser(userId) as any).globalName ?? user);
const nickname = user && (GuildMemberStore.getNick(myGuildId, userId) ?? user); const nickname = user && (GuildMemberStore.getNick(myGuildId, userId) ?? user);
const channel = ChannelStore.getChannel(id).name; const channel = ChannelStore.getChannel(id).name;
@ -194,7 +267,7 @@ export default definePlugin({
if (!s) return; if (!s) return;
const event = s.mute || s.selfMute ? "unmute" : "mute"; const event = s.mute || s.selfMute ? "unmute" : "mute";
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", "")); speak(formatText(settings.store[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
}, },
AUDIO_TOGGLE_SELF_DEAF() { AUDIO_TOGGLE_SELF_DEAF() {
@ -203,7 +276,7 @@ export default definePlugin({
if (!s) return; if (!s) return;
const event = s.deaf || s.selfDeaf ? "undeafen" : "deafen"; const event = s.deaf || s.selfDeaf ? "undeafen" : "deafen";
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", "")); speak(formatText(settings.store[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
} }
}, },
@ -217,81 +290,6 @@ export default definePlugin({
}, },
optionsCache: null as Record<string, PluginOptionsItem> | null,
get options() {
return this.optionsCache ??= {
voice: {
type: OptionType.SELECT,
description: "Narrator Voice",
options: window.speechSynthesis?.getVoices().map(v => ({
label: v.name,
value: v.voiceURI,
default: v.default
})) ?? []
},
volume: {
type: OptionType.SLIDER,
description: "Narrator Volume",
default: 1,
markers: [0, 0.25, 0.5, 0.75, 1],
stickToMarkers: false
},
rate: {
type: OptionType.SLIDER,
description: "Narrator Speed",
default: 1,
markers: [0.1, 0.5, 1, 2, 5, 10],
stickToMarkers: false
},
sayOwnName: {
description: "Say own name",
type: OptionType.BOOLEAN,
default: false
},
latinOnly: {
description: "Strip non latin characters from names before saying them",
type: OptionType.BOOLEAN,
default: false
},
joinMessage: {
type: OptionType.STRING,
description: "Join Message",
default: "{{USER}} joined"
},
leaveMessage: {
type: OptionType.STRING,
description: "Leave Message",
default: "{{USER}} left"
},
moveMessage: {
type: OptionType.STRING,
description: "Move Message",
default: "{{USER}} moved to {{CHANNEL}}"
},
muteMessage: {
type: OptionType.STRING,
description: "Mute Message (only self for now)",
default: "{{USER}} Muted"
},
unmuteMessage: {
type: OptionType.STRING,
description: "Unmute Message (only self for now)",
default: "{{USER}} unmuted"
},
deafenMessage: {
type: OptionType.STRING,
description: "Deafen Message (only self for now)",
default: "{{USER}} deafened"
},
undeafenMessage: {
type: OptionType.STRING,
description: "Undeafen Message (only self for now)",
default: "{{USER}} undeafened"
}
};
},
settingsAboutComponent({ tempSettings: s }) { settingsAboutComponent({ tempSettings: s }) {
const [hasVoices, hasEnglishVoices] = useMemo(() => { const [hasVoices, hasEnglishVoices] = useMemo(() => {
const voices = speechSynthesis.getVoices(); const voices = speechSynthesis.getVoices();
@ -299,7 +297,7 @@ export default definePlugin({
}, []); }, []);
const types = useMemo( const types = useMemo(
() => Object.keys(Vencord.Plugins.plugins.VcNarrator.options!).filter(k => k.endsWith("Message")).map(k => k.slice(0, -7)), () => Object.keys(settings.def).filter(k => k.endsWith("Message")).map(k => k.slice(0, -7)),
[], [],
); );
@ -335,7 +333,7 @@ export default definePlugin({
className={"vc-narrator-buttons"} className={"vc-narrator-buttons"}
> >
{types.map(t => ( {types.map(t => (
<Button key={t} onClick={() => playSample(s, t)}> <Button key={t} onClick={() => playSample(s as typeof settings.store, t)}>
{wordsToTitle([t])} {wordsToTitle([t])}
</Button> </Button>
))} ))}

View file

@ -124,6 +124,7 @@ export type Permissions = "ADD_REACTIONS"
| "USE_APPLICATION_COMMANDS" | "USE_APPLICATION_COMMANDS"
| "USE_CLYDE_AI" | "USE_CLYDE_AI"
| "USE_EMBEDDED_ACTIVITIES" | "USE_EMBEDDED_ACTIVITIES"
| "USE_EXTERNAL_APPS"
| "USE_EXTERNAL_EMOJIS" | "USE_EXTERNAL_EMOJIS"
| "USE_EXTERNAL_SOUNDS" | "USE_EXTERNAL_SOUNDS"
| "USE_EXTERNAL_STICKERS" | "USE_EXTERNAL_STICKERS"