diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx
index 9e2980e7e..013e32d7d 100644
--- a/src/components/VencordSettings/PatchHelperTab.tsx
+++ b/src/components/VencordSettings/PatchHelperTab.tsx
@@ -16,7 +16,6 @@
* along with this program. If not, see .
*/
-import { CheckedTextInput } from "@components/CheckedTextInput";
import { CodeBlock } from "@components/CodeBlock";
import { debounce } from "@shared/debounce";
import { Margins } from "@utils/margins";
@@ -47,7 +46,7 @@ const findCandidates = debounce(function ({ find, setModule, setError }) {
interface ReplacementComponentProps {
module: [id: number, factory: Function];
- match: string | RegExp;
+ match: string;
replacement: string | ReplaceFn;
setReplacementError(error: any): void;
}
@@ -58,7 +57,13 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
const [patchedCode, matchResult, diff] = React.useMemo(() => {
const src: string = fact.toString().replaceAll("\n", "");
- const canonicalMatch = canonicalizeMatch(match);
+
+ try {
+ new RegExp(match);
+ } catch (e) {
+ return ["", [], []];
+ }
+ const canonicalMatch = canonicalizeMatch(new RegExp(match));
try {
const canonicalReplace = canonicalizeReplace(replacement, "YourPlugin");
var patched = src.replace(canonicalMatch, canonicalReplace as string);
@@ -286,6 +291,7 @@ function PatchHelper() {
const [module, setModule] = React.useState<[number, Function]>();
const [findError, setFindError] = React.useState();
+ const [matchError, setMatchError] = React.useState();
const code = React.useMemo(() => {
return `
@@ -322,12 +328,17 @@ function PatchHelper() {
}
function onMatchChange(v: string) {
+ setMatchError(void 0);
+ setMatch(v);
+ }
+
+ function onMatchBlur() {
try {
- new RegExp(v);
- setFindError(void 0);
- setMatch(v);
+ new RegExp(match);
+ setMatchError(void 0);
+ setMatch(match);
} catch (e: any) {
- setFindError((e as Error).message);
+ setMatchError((e as Error).message);
}
}
@@ -351,16 +362,12 @@ function PatchHelper() {
/>
match
- {
- try {
- return (new RegExp(v), true);
- } catch (e) {
- return (e as Error).message;
- }
- }}
+ onBlur={onMatchBlur}
+ error={matchError}
/>
@@ -374,7 +381,7 @@ function PatchHelper() {
{module && (
diff --git a/src/plugins/customidle/README.md b/src/plugins/customidle/README.md
new file mode 100644
index 000000000..63bf87d89
--- /dev/null
+++ b/src/plugins/customidle/README.md
@@ -0,0 +1,5 @@
+# CustomIdle
+
+Lets you change the time until your status gets automatically set to idle. You can also prevent idling altogether.
+
+![Plugin Configuration](https://github.com/Vendicated/Vencord/assets/45801973/4e5259b2-18e0-42e5-b69f-efc672ce1e0b)
diff --git a/src/plugins/customidle/index.ts b/src/plugins/customidle/index.ts
new file mode 100644
index 000000000..a59bbcb01
--- /dev/null
+++ b/src/plugins/customidle/index.ts
@@ -0,0 +1,94 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { Notices } from "@api/index";
+import { definePluginSettings } from "@api/Settings";
+import { makeRange } from "@components/PluginSettings/components";
+import { Devs } from "@utils/constants";
+import definePlugin, { OptionType } from "@utils/types";
+import { FluxDispatcher } from "@webpack/common";
+
+const settings = definePluginSettings({
+ idleTimeout: {
+ description: "Minutes before Discord goes idle (0 to disable auto-idle)",
+ type: OptionType.SLIDER,
+ markers: makeRange(0, 60, 5),
+ default: 10,
+ stickToMarkers: false,
+ restartNeeded: true // Because of the setInterval patch
+ },
+ remainInIdle: {
+ description: "When you come back to Discord, remain idle until you confirm you want to go online",
+ type: OptionType.BOOLEAN,
+ default: true
+ }
+});
+
+export default definePlugin({
+ name: "CustomIdle",
+ description: "Allows you to set the time before Discord goes idle (or disable auto-idle)",
+ authors: [Devs.newwares],
+ settings,
+ patches: [
+ {
+ find: "IDLE_DURATION:function(){return",
+ replacement: {
+ match: /(IDLE_DURATION:function\(\){return )\i/,
+ replace: "$1$self.getIdleTimeout()"
+ }
+ },
+ {
+ find: 'type:"IDLE",idle:',
+ replacement: [
+ {
+ match: /Math\.min\((\i\.AfkTimeout\.getSetting\(\)\*\i\.default\.Millis\.SECOND),\i\.IDLE_DURATION\)/,
+ replace: "$1" // Decouple idle from afk (phone notifications will remain at user setting or 10 min maximum)
+ },
+ {
+ match: /\i\.default\.dispatch\({type:"IDLE",idle:!1}\)/,
+ replace: "$self.handleOnline()"
+ },
+ {
+ match: /(setInterval\(\i,\.25\*)\i\.IDLE_DURATION/,
+ replace: "$1$self.getIntervalDelay()" // For web installs
+ }
+ ]
+ }
+ ],
+
+ getIntervalDelay() {
+ return Math.min(6e5, this.getIdleTimeout());
+ },
+
+ handleOnline() {
+ if (!settings.store.remainInIdle) {
+ FluxDispatcher.dispatch({
+ type: "IDLE",
+ idle: false
+ });
+ return;
+ }
+
+ const backOnlineMessage = "Welcome back! Click the button to go online. Click the X to stay idle until reload.";
+ if (
+ Notices.currentNotice?.[1] === backOnlineMessage ||
+ Notices.noticesQueue.some(([, noticeMessage]) => noticeMessage === backOnlineMessage)
+ ) return;
+
+ Notices.showNotice(backOnlineMessage, "Exit idle", () => {
+ Notices.popNotice();
+ FluxDispatcher.dispatch({
+ type: "IDLE",
+ idle: false
+ });
+ });
+ },
+
+ getIdleTimeout() { // milliseconds, default is 6e5
+ const { idleTimeout } = settings.store;
+ return idleTimeout === 0 ? Infinity : idleTimeout * 60000;
+ }
+});
diff --git a/src/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx
index 1a1d2fae3..869319097 100644
--- a/src/plugins/moreUserTags/index.tsx
+++ b/src/plugins/moreUserTags/index.tsx
@@ -352,6 +352,15 @@ export default definePlugin({
if (location === "chat" && !settings.tagSettings[tag.name].showInChat) continue;
if (location === "not-chat" && !settings.tagSettings[tag.name].showInNotChat) continue;
+ // If the owner tag is disabled, and the user is the owner of the guild,
+ // avoid adding other tags because the owner will always match the condition for them
+ if (
+ tag.name !== "OWNER" &&
+ GuildStore.getGuild(channel?.guild_id)?.ownerId === user.id &&
+ (location === "chat" && !settings.tagSettings.OWNER.showInChat) ||
+ (location === "not-chat" && !settings.tagSettings.OWNER.showInNotChat)
+ ) continue;
+
if (
tag.permissions?.some(perm => perms.includes(perm)) ||
(tag.condition?.(message!, user, channel))
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 974758e3a..44d13b54c 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -422,6 +422,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
name: "Av32000",
id: 593436735380127770n,
},
+ Noxillio: {
+ name: "Noxillio",
+ id: 138616536502894592n,
+ },
Kyuuhachi: {
name: "Kyuuhachi",
id: 236588665420251137n,