mirror of
https://github.com/Vendicated/Vencord
synced 2024-08-26 03:37:47 -04:00
Improve SupportHelper
- improve update check when entering support channel - add "Run Snippet" button to venbot messages with codeblock - add "Send /vencord-debug" button to messages that contain /vencord-debug - add "Update Now" button to messages by venbot and in #known-issues that contain "update" - add some common issues like RPC disabled / NoRPC enabled to /vencord-debug - split plugin list into separate /vencord-plugins command to reduce size & avoid >2000 chars errors
This commit is contained in:
parent
7dc1d4c498
commit
b9392c3be2
3 changed files with 233 additions and 73 deletions
|
@ -16,24 +16,33 @@
|
||||||
* 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 { addAccessory } from "@api/MessageAccessories";
|
||||||
|
import { getUserSettingLazy } from "@api/UserSettings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
|
import { Flex } from "@components/Flex";
|
||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab";
|
import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab";
|
||||||
import { Devs, SUPPORT_CHANNEL_ID } from "@utils/constants";
|
import { Devs, SUPPORT_CHANNEL_ID } from "@utils/constants";
|
||||||
|
import { sendMessage } from "@utils/discord";
|
||||||
|
import { Logger } from "@utils/Logger";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { isPluginDev } from "@utils/misc";
|
import { isPluginDev, tryOrElse } from "@utils/misc";
|
||||||
import { relaunch } from "@utils/native";
|
import { relaunch } from "@utils/native";
|
||||||
|
import { onlyOnce } from "@utils/onlyOnce";
|
||||||
import { makeCodeblock } from "@utils/text";
|
import { makeCodeblock } from "@utils/text";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { isOutdated, update } from "@utils/updater";
|
import { checkForUpdates, isOutdated, update } from "@utils/updater";
|
||||||
import { Alerts, Card, ChannelStore, Forms, GuildMemberStore, NavigationRouter, Parser, RelationshipStore, UserStore } from "@webpack/common";
|
import { Alerts, Button, Card, ChannelStore, Forms, GuildMemberStore, Parser, RelationshipStore, showToast, Toasts, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
import gitHash from "~git-hash";
|
import gitHash from "~git-hash";
|
||||||
import plugins from "~plugins";
|
import plugins, { PluginMeta } from "~plugins";
|
||||||
|
|
||||||
import settings from "./settings";
|
import settings from "./settings";
|
||||||
|
|
||||||
const VENCORD_GUILD_ID = "1015060230222131221";
|
const VENCORD_GUILD_ID = "1015060230222131221";
|
||||||
|
const VENBOT_USER_ID = "1017176847865352332";
|
||||||
|
const KNOWN_ISSUES_CHANNEL_ID = "1222936386626129920";
|
||||||
|
const CodeBlockRe = /```js\n(.+?)```/s;
|
||||||
|
|
||||||
const AllowedChannelIds = [
|
const AllowedChannelIds = [
|
||||||
SUPPORT_CHANNEL_ID,
|
SUPPORT_CHANNEL_ID,
|
||||||
|
@ -47,12 +56,88 @@ const TrustedRolesIds = [
|
||||||
"1042507929485586532", // donor
|
"1042507929485586532", // donor
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const AsyncFunction = async function () { }.constructor;
|
||||||
|
|
||||||
|
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
||||||
|
|
||||||
|
async function forceUpdate() {
|
||||||
|
const outdated = await checkForUpdates();
|
||||||
|
if (outdated) {
|
||||||
|
await update();
|
||||||
|
relaunch();
|
||||||
|
}
|
||||||
|
|
||||||
|
return outdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateDebugInfoMessage() {
|
||||||
|
const { RELEASE_CHANNEL } = window.GLOBAL_ENV;
|
||||||
|
|
||||||
|
const client = (() => {
|
||||||
|
if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`;
|
||||||
|
if (IS_VESKTOP) return `Vesktop v${VesktopNative.app.getVersion()}`;
|
||||||
|
if ("armcord" in window) return `ArmCord v${window.armcord.version}`;
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
const name = typeof unsafeWindow !== "undefined" ? "UserScript" : "Web";
|
||||||
|
return `${name} (${navigator.userAgent})`;
|
||||||
|
})();
|
||||||
|
|
||||||
|
const info = {
|
||||||
|
Vencord:
|
||||||
|
`v${VERSION} • [${gitHash}](<https://github.com/Vendicated/Vencord/commit/${gitHash}>)` +
|
||||||
|
`${settings.additionalInfo} - ${Intl.DateTimeFormat("en-GB", { dateStyle: "medium" }).format(BUILD_TIMESTAMP)}`,
|
||||||
|
Client: `${RELEASE_CHANNEL} ~ ${client}`,
|
||||||
|
Platform: window.navigator.platform
|
||||||
|
};
|
||||||
|
|
||||||
|
if (IS_DISCORD_DESKTOP) {
|
||||||
|
info["Last Crash Reason"] = (await tryOrElse(() => DiscordNative.processUtils.getLastCrash(), undefined))?.rendererCrashReason ?? "N/A";
|
||||||
|
}
|
||||||
|
|
||||||
|
const commonIssues = {
|
||||||
|
"NoRPC enabled": Vencord.Plugins.isPluginEnabled("NoRPC"),
|
||||||
|
"Activity Sharing disabled": tryOrElse(() => !ShowCurrentGame.getSetting(), false),
|
||||||
|
"Vencord DevBuild": !IS_STANDALONE,
|
||||||
|
"Has UserPlugins": Object.values(PluginMeta).some(m => m.userPlugin),
|
||||||
|
"More than two weeks out of date": BUILD_TIMESTAMP < Date.now() - 12096e5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let content = `>>> ${Object.entries(info).map(([k, v]) => `**${k}**: ${v}`).join("\n")}`;
|
||||||
|
content += "\n" + Object.entries(commonIssues)
|
||||||
|
.filter(([, v]) => v).map(([k]) => `⚠️ ${k}`)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
return content.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function generatePluginList() {
|
||||||
|
const isApiPlugin = (plugin: string) => plugin.endsWith("API") || plugins[plugin].required;
|
||||||
|
|
||||||
|
const enabledPlugins = Object.keys(plugins)
|
||||||
|
.filter(p => Vencord.Plugins.isPluginEnabled(p) && !isApiPlugin(p));
|
||||||
|
|
||||||
|
const enabledStockPlugins = enabledPlugins.filter(p => !PluginMeta[p].userPlugin);
|
||||||
|
const enabledUserPlugins = enabledPlugins.filter(p => PluginMeta[p].userPlugin);
|
||||||
|
|
||||||
|
|
||||||
|
let content = `**Enabled Plugins (${enabledStockPlugins.length}):**\n${makeCodeblock(enabledStockPlugins.join(", "))}`;
|
||||||
|
|
||||||
|
if (enabledUserPlugins.length) {
|
||||||
|
content += `**Enabled UserPlugins (${enabledUserPlugins.length}):**\n${makeCodeblock(enabledUserPlugins.join(", "))}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkForUpdatesOnce = onlyOnce(checkForUpdates);
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "SupportHelper",
|
name: "SupportHelper",
|
||||||
required: true,
|
required: true,
|
||||||
description: "Helps us provide support to you",
|
description: "Helps us provide support to you",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven],
|
||||||
dependencies: ["CommandsAPI"],
|
dependencies: ["CommandsAPI", "UserSettingsAPI"],
|
||||||
|
|
||||||
patches: [{
|
patches: [{
|
||||||
find: ".BEGINNING_DM.format",
|
find: ".BEGINNING_DM.format",
|
||||||
|
@ -62,51 +147,20 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
|
||||||
commands: [{
|
commands: [
|
||||||
name: "vencord-debug",
|
{
|
||||||
description: "Send Vencord Debug info",
|
name: "vencord-debug",
|
||||||
predicate: ctx => isPluginDev(UserStore.getCurrentUser()?.id) || AllowedChannelIds.includes(ctx.channel.id),
|
description: "Send Vencord debug info",
|
||||||
async execute() {
|
predicate: ctx => isPluginDev(UserStore.getCurrentUser()?.id) || AllowedChannelIds.includes(ctx.channel.id),
|
||||||
const { RELEASE_CHANNEL } = window.GLOBAL_ENV;
|
execute: async () => ({ content: await generateDebugInfoMessage() })
|
||||||
|
},
|
||||||
const client = (() => {
|
{
|
||||||
if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`;
|
name: "vencord-plugins",
|
||||||
if (IS_VESKTOP) return `Vesktop v${VesktopNative.app.getVersion()}`;
|
description: "Send Vencord plugin list",
|
||||||
if ("armcord" in window) return `ArmCord v${window.armcord.version}`;
|
predicate: ctx => isPluginDev(UserStore.getCurrentUser()?.id) || AllowedChannelIds.includes(ctx.channel.id),
|
||||||
|
execute: () => ({ content: generatePluginList() })
|
||||||
// @ts-expect-error
|
|
||||||
const name = typeof unsafeWindow !== "undefined" ? "UserScript" : "Web";
|
|
||||||
return `${name} (${navigator.userAgent})`;
|
|
||||||
})();
|
|
||||||
|
|
||||||
const isApiPlugin = (plugin: string) => plugin.endsWith("API") || plugins[plugin].required;
|
|
||||||
|
|
||||||
const enabledPlugins = Object.keys(plugins).filter(p => Vencord.Plugins.isPluginEnabled(p) && !isApiPlugin(p));
|
|
||||||
|
|
||||||
const info = {
|
|
||||||
Vencord:
|
|
||||||
`v${VERSION} • [${gitHash}](<https://github.com/Vendicated/Vencord/commit/${gitHash}>)` +
|
|
||||||
`${settings.additionalInfo} - ${Intl.DateTimeFormat("en-GB", { dateStyle: "medium" }).format(BUILD_TIMESTAMP)}`,
|
|
||||||
Client: `${RELEASE_CHANNEL} ~ ${client}`,
|
|
||||||
Platform: window.navigator.platform
|
|
||||||
};
|
|
||||||
|
|
||||||
if (IS_DISCORD_DESKTOP) {
|
|
||||||
info["Last Crash Reason"] = (await DiscordNative.processUtils.getLastCrash())?.rendererCrashReason ?? "N/A";
|
|
||||||
}
|
|
||||||
|
|
||||||
const debugInfo = `
|
|
||||||
>>> ${Object.entries(info).map(([k, v]) => `**${k}**: ${v}`).join("\n")}
|
|
||||||
|
|
||||||
Enabled Plugins (${enabledPlugins.length}):
|
|
||||||
${makeCodeblock(enabledPlugins.join(", "))}
|
|
||||||
`;
|
|
||||||
|
|
||||||
return {
|
|
||||||
content: debugInfo.trim().replaceAll("```\n", "```")
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}],
|
],
|
||||||
|
|
||||||
flux: {
|
flux: {
|
||||||
async CHANNEL_SELECT({ channelId }) {
|
async CHANNEL_SELECT({ channelId }) {
|
||||||
|
@ -115,24 +169,25 @@ ${makeCodeblock(enabledPlugins.join(", "))}
|
||||||
const selfId = UserStore.getCurrentUser()?.id;
|
const selfId = UserStore.getCurrentUser()?.id;
|
||||||
if (!selfId || isPluginDev(selfId)) return;
|
if (!selfId || isPluginDev(selfId)) return;
|
||||||
|
|
||||||
if (isOutdated) {
|
if (!IS_UPDATER_DISABLED) {
|
||||||
return Alerts.show({
|
await checkForUpdatesOnce().catch(() => { });
|
||||||
title: "Hold on!",
|
|
||||||
body: <div>
|
if (isOutdated) {
|
||||||
<Forms.FormText>You are using an outdated version of Vencord! Chances are, your issue is already fixed.</Forms.FormText>
|
return Alerts.show({
|
||||||
<Forms.FormText className={Margins.top8}>
|
title: "Hold on!",
|
||||||
Please first update before asking for support!
|
body: <div>
|
||||||
</Forms.FormText>
|
<Forms.FormText>You are using an outdated version of Vencord! Chances are, your issue is already fixed.</Forms.FormText>
|
||||||
</div>,
|
<Forms.FormText className={Margins.top8}>
|
||||||
onCancel: () => openUpdaterModal!(),
|
Please first update before asking for support!
|
||||||
cancelText: "View Updates",
|
</Forms.FormText>
|
||||||
confirmText: "Update & Restart Now",
|
</div>,
|
||||||
async onConfirm() {
|
onCancel: () => openUpdaterModal!(),
|
||||||
await update();
|
cancelText: "View Updates",
|
||||||
relaunch();
|
confirmText: "Update & Restart Now",
|
||||||
},
|
onConfirm: forceUpdate,
|
||||||
secondaryConfirmText: "I know what I'm doing or I can't update"
|
secondaryConfirmText: "I know what I'm doing or I can't update"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore outdated type
|
// @ts-ignore outdated type
|
||||||
|
@ -148,8 +203,7 @@ ${makeCodeblock(enabledPlugins.join(", "))}
|
||||||
Please either switch to an <Link href="https://vencord.dev/download">officially supported version of Vencord</Link>, or
|
Please either switch to an <Link href="https://vencord.dev/download">officially supported version of Vencord</Link>, or
|
||||||
contact your package maintainer for support instead.
|
contact your package maintainer for support instead.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
</div>,
|
</div>
|
||||||
onCloseCallback: () => setTimeout(() => NavigationRouter.back(), 50)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,8 +217,7 @@ ${makeCodeblock(enabledPlugins.join(", "))}
|
||||||
Please either switch to an <Link href="https://vencord.dev/download">officially supported version of Vencord</Link>, or
|
Please either switch to an <Link href="https://vencord.dev/download">officially supported version of Vencord</Link>, or
|
||||||
contact your package maintainer for support instead.
|
contact your package maintainer for support instead.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
</div>,
|
</div>
|
||||||
onCloseCallback: () => setTimeout(() => NavigationRouter.back(), 50)
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,7 +225,7 @@ ${makeCodeblock(enabledPlugins.join(", "))}
|
||||||
|
|
||||||
ContributorDmWarningCard: ErrorBoundary.wrap(({ userId }) => {
|
ContributorDmWarningCard: ErrorBoundary.wrap(({ userId }) => {
|
||||||
if (!isPluginDev(userId)) return null;
|
if (!isPluginDev(userId)) return null;
|
||||||
if (RelationshipStore.isFriend(userId)) return null;
|
if (RelationshipStore.isFriend(userId) || isPluginDev(UserStore.getCurrentUser()?.id)) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={`vc-plugins-restart-card ${Margins.top8}`}>
|
<Card className={`vc-plugins-restart-card ${Margins.top8}`}>
|
||||||
|
@ -182,5 +235,86 @@ ${makeCodeblock(enabledPlugins.join(", "))}
|
||||||
{!ChannelStore.getChannel(SUPPORT_CHANNEL_ID) && " (Click the link to join)"}
|
{!ChannelStore.getChannel(SUPPORT_CHANNEL_ID) && " (Click the link to join)"}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}, { noop: true })
|
}, { noop: true }),
|
||||||
|
|
||||||
|
start() {
|
||||||
|
addAccessory("vencord-debug", props => {
|
||||||
|
const buttons = [] as JSX.Element[];
|
||||||
|
|
||||||
|
const shouldAddUpdateButton =
|
||||||
|
!IS_UPDATER_DISABLED
|
||||||
|
&& (
|
||||||
|
(props.channel.id === KNOWN_ISSUES_CHANNEL_ID) ||
|
||||||
|
(props.channel.id === SUPPORT_CHANNEL_ID && props.message.author.id === VENBOT_USER_ID)
|
||||||
|
)
|
||||||
|
&& props.message.content?.includes("update");
|
||||||
|
|
||||||
|
if (shouldAddUpdateButton) {
|
||||||
|
buttons.push(
|
||||||
|
<Button
|
||||||
|
key="vc-update"
|
||||||
|
color={Button.Colors.GREEN}
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
if (await forceUpdate())
|
||||||
|
showToast("Success! Restarting...", Toasts.Type.SUCCESS);
|
||||||
|
else
|
||||||
|
showToast("Already up to date!", Toasts.Type.MESSAGE);
|
||||||
|
} catch (e) {
|
||||||
|
new Logger(this.name).error("Error while updating:", e);
|
||||||
|
showToast("Failed to update :(", Toasts.Type.FAILURE);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Update Now
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.channel.id === SUPPORT_CHANNEL_ID) {
|
||||||
|
if (props.message.content.includes("/vencord-debug") || props.message.content.includes("/vencord-plugins")) {
|
||||||
|
buttons.push(
|
||||||
|
<Button
|
||||||
|
key="vc-dbg"
|
||||||
|
onClick={async () => sendMessage(props.channel.id, { content: await generateDebugInfoMessage() })}
|
||||||
|
>
|
||||||
|
Run /vencord-debug
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
key="vc-plg-list"
|
||||||
|
onClick={async () => sendMessage(props.channel.id, { content: generatePluginList() })}
|
||||||
|
>
|
||||||
|
Run /vencord-plugins
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.message.author.id === VENBOT_USER_ID) {
|
||||||
|
const match = CodeBlockRe.exec(props.message.content || props.message.embeds[0]?.rawDescription || "");
|
||||||
|
if (match) {
|
||||||
|
buttons.push(
|
||||||
|
<Button
|
||||||
|
key="vc-run-snippet"
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
await AsyncFunction(match[1])();
|
||||||
|
showToast("Success!", Toasts.Type.SUCCESS);
|
||||||
|
} catch (e) {
|
||||||
|
new Logger(this.name).error("Error while running snippet:", e);
|
||||||
|
showToast("Failed to run snippet :(", Toasts.Type.FAILURE);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Run Snippet
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttons.length
|
||||||
|
? <Flex>{buttons}</Flex>
|
||||||
|
: null;
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -99,3 +99,14 @@ export const isPluginDev = (id: string) => Object.hasOwn(DevsById, id);
|
||||||
export function pluralise(amount: number, singular: string, plural = singular + "s") {
|
export function pluralise(amount: number, singular: string, plural = singular + "s") {
|
||||||
return amount === 1 ? `${amount} ${singular}` : `${amount} ${plural}`;
|
return amount === 1 ? `${amount} ${singular}` : `${amount} ${plural}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function tryOrElse<T>(func: () => T, fallback: T): T {
|
||||||
|
try {
|
||||||
|
const res = func();
|
||||||
|
return res instanceof Promise
|
||||||
|
? res.catch(() => fallback) as T
|
||||||
|
: res;
|
||||||
|
} catch {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
|
@ -131,3 +131,18 @@ export function makeCodeblock(text: string, language?: string) {
|
||||||
const chars = "```";
|
const chars = "```";
|
||||||
return `${chars}${language || ""}\n${text.replaceAll("```", "\\`\\`\\`")}\n${chars}`;
|
return `${chars}${language || ""}\n${text.replaceAll("```", "\\`\\`\\`")}\n${chars}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function stripIndent(strings: TemplateStringsArray, ...values: any[]) {
|
||||||
|
const string = String.raw({ raw: strings }, ...values);
|
||||||
|
|
||||||
|
const match = string.match(/^[ \t]*(?=\S)/gm);
|
||||||
|
if (!match) return string.trim();
|
||||||
|
|
||||||
|
const minIndent = match.reduce((r, a) => Math.min(r, a.length), Infinity);
|
||||||
|
return string.replace(new RegExp(`^[ \\t]{${minIndent}}`, "gm"), "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ZWSP = "\u200b";
|
||||||
|
export function toInlineCode(s: string) {
|
||||||
|
return "``" + ZWSP + s.replaceAll("`", ZWSP + "`" + ZWSP) + ZWSP + "``";
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue