Allow 100+ plugin users to export list & fix DataStore export errors (#767)
* Add idle auto restart plugin * Add idle auto restart plugin * Add idle auto restart plugin * Add idle auto restart plugin update * Add idle auto restart plugin update * Add idle auto restart plugin update * Replace SelectedChannelStore with VoiceStateStore * updates * feat: Allow 100+ plugin users to export list as file & fix DataStore export errors - Support Helper: Allow users with 100+ plugins to send plugin list as .txt file attachment instead of blocking them - Backup & Restore: Fix 'Failed to read large IndexedDB value' error by only loading DataStore when needed - Export Plugins/CSS now skip DataStore entirely, preventing crashes on large datastores * Delete src/equicordplugins/idleAutoRestart directory * fix lint * Update supportHelper.tsx * Update offline.ts --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com>
This commit is contained in:
parent
2cfd13d640
commit
5eefbb1d85
2 changed files with 124 additions and 24 deletions
|
|
@ -108,11 +108,27 @@ export async function importSettings(data: string, type: BackupType = "all", clo
|
|||
export async function exportSettings({ syncDataStore = true, type = "all", minify }: { syncDataStore?: boolean; type?: BackupType; minify?: boolean; }) {
|
||||
const settings = VencordNative.settings.get();
|
||||
const quickCss = await VencordNative.quickCss.get();
|
||||
const dataStore = syncDataStore ? await DataStore.entries() : undefined;
|
||||
let dataStore: any;
|
||||
|
||||
if (syncDataStore) {
|
||||
try {
|
||||
dataStore = await DataStore.entries();
|
||||
} catch (err) {
|
||||
logger.error("Failed to read DataStore entries:", err);
|
||||
|
||||
if (type === "all") {
|
||||
logger.warn("Skipping DataStore in backup due to size. Export DataStore separately if needed.");
|
||||
toast(Toasts.Type.MESSAGE, "DataStore too large - exported without it. Use 'Export DataStore' separately if needed.");
|
||||
dataStore = undefined;
|
||||
} else if (type === "datastore") {
|
||||
throw new Error("DataStore is too large to export. Please clear some plugin data and try again.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case "all": {
|
||||
return JSON.stringify({ settings, quickCss, ...(syncDataStore && { dataStore }) }, null, minify ? undefined : 4);
|
||||
return JSON.stringify({ settings, quickCss, ...(dataStore && { dataStore }) }, null, minify ? undefined : 4);
|
||||
}
|
||||
case "plugins": {
|
||||
return JSON.stringify({ settings }, null, minify ? undefined : 4);
|
||||
|
|
@ -127,14 +143,21 @@ export async function exportSettings({ syncDataStore = true, type = "all", minif
|
|||
}
|
||||
|
||||
export async function downloadSettingsBackup(type: BackupType = "all", { minify }: { minify?: boolean; } = {}) {
|
||||
const backup = await exportSettings({ minify, type });
|
||||
const filename = `equicord-${type}-backup-${moment().format("YYYY-MM-DD")}.json`;
|
||||
const data = new TextEncoder().encode(backup);
|
||||
try {
|
||||
const syncDataStore = type === "all" || type === "datastore";
|
||||
const backup = await exportSettings({ minify, type, syncDataStore });
|
||||
const filename = `equicord-${type}-backup-${moment().format("YYYY-MM-DD")}.json`;
|
||||
const data = new TextEncoder().encode(backup);
|
||||
|
||||
if (IS_DISCORD_DESKTOP) {
|
||||
DiscordNative.fileManager.saveWithDialog(data, filename);
|
||||
} else {
|
||||
saveFile(new File([data], filename, { type: "application/json" }));
|
||||
if (IS_DISCORD_DESKTOP) {
|
||||
DiscordNative.fileManager.saveWithDialog(data, filename);
|
||||
} else {
|
||||
saveFile(new File([data], filename, { type: "application/json" }));
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error("Failed to export settings:", err);
|
||||
toast(Toasts.Type.FAILURE, "Failed to export settings, check console");
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ import { onlyOnce } from "@utils/onlyOnce";
|
|||
import { makeCodeblock } from "@utils/text";
|
||||
import definePlugin from "@utils/types";
|
||||
import { checkForUpdates, isOutdated, update } from "@utils/updater";
|
||||
import { Alerts, Button, ChannelStore, GuildMemberStore, Parser, PermissionsBits, PermissionStore, RelationshipStore, SelectedChannelStore, showToast, Toasts, UserStore } from "@webpack/common";
|
||||
import { CloudUploadPlatform } from "@vencord/discord-types/enums";
|
||||
import { Alerts, Button, ChannelStore, CloudUploader, Constants, GuildMemberStore, Parser, PermissionsBits, PermissionStore, RelationshipStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, UserStore } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
import plugins, { PluginMeta } from "~plugins";
|
||||
|
|
@ -185,6 +186,36 @@ async function generateDebugInfoMessage() {
|
|||
return content.trim();
|
||||
}
|
||||
|
||||
async function uploadPluginListFile(channelId: string, fileContent: string, filename: string) {
|
||||
const file = new File([fileContent], filename, { type: "text/plain" });
|
||||
const upload = new CloudUploader({ file, platform: CloudUploadPlatform.WEB }, channelId);
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
upload.on("complete", () => {
|
||||
RestAPI.post({
|
||||
url: Constants.Endpoints.MESSAGES(channelId),
|
||||
body: {
|
||||
flags: 0,
|
||||
channel_id: channelId,
|
||||
content: `⚠️ Plugin list attached as file due to high plugin count (${fileContent.split("\n").filter(l => l.startsWith(" -")).length} plugins enabled)`,
|
||||
nonce: SnowflakeUtils.fromTimestamp(Date.now()),
|
||||
sticker_ids: [],
|
||||
type: 0,
|
||||
attachments: [{
|
||||
id: "0",
|
||||
filename: upload.filename,
|
||||
uploaded_filename: upload.uploadedFilename,
|
||||
}],
|
||||
}
|
||||
}).then(() => resolve()).catch(reject);
|
||||
});
|
||||
|
||||
upload.on("error", () => reject(new Error("Failed to upload file")));
|
||||
|
||||
upload.upload();
|
||||
});
|
||||
}
|
||||
|
||||
function generatePluginList() {
|
||||
const isApiPlugin = (plugin: string) => plugin.endsWith("API") || plugins[plugin].required;
|
||||
|
||||
|
|
@ -194,29 +225,54 @@ function generatePluginList() {
|
|||
const enabledStockPlugins = enabledPlugins.filter(p => !PluginMeta[p].userPlugin).sort();
|
||||
const enabledUserPlugins = enabledPlugins.filter(p => PluginMeta[p].userPlugin).sort();
|
||||
|
||||
let content = `**Enabled Plugins (${enabledStockPlugins.length}):**\n${makeCodeblock(enabledStockPlugins.join(", "))}`;
|
||||
|
||||
if (enabledUserPlugins.length) {
|
||||
content += `**Enabled UserPlugins (${enabledUserPlugins.length}):**\n${makeCodeblock(enabledUserPlugins.join(", "))}`;
|
||||
}
|
||||
|
||||
const user = UserStore.getCurrentUser();
|
||||
|
||||
if (enabledPlugins.length > 100 && !isAnyPluginDev(user.id)) {
|
||||
Alerts.show({
|
||||
title: "You are attempting to get support!",
|
||||
title: "Warning: High Plugin Count",
|
||||
body: <div>
|
||||
<div style={{ display: "flex", justifyContent: "center", marginBottom: "1rem" }}>
|
||||
<img src="https://media.tenor.com/QtGqjwBpRzwAAAAi/wumpus-dancing.gif" />
|
||||
</div>
|
||||
<Paragraph>Before you ask for help,</Paragraph>
|
||||
<Paragraph>We do not handle support for users who use 100+ plugins</Paragraph>
|
||||
<Paragraph>issue could be plugin confliction</Paragraph>
|
||||
<Paragraph>try removing some plugins and see if it fixes!</Paragraph>
|
||||
<Paragraph>You have more than 100 plugins enabled.</Paragraph>
|
||||
<Paragraph>Due to the sheer amount of plugins, you may not receive support.</Paragraph>
|
||||
<Paragraph>Your issue is likely caused by plugin conflicts.</Paragraph>
|
||||
<Paragraph>Please consider disabling some plugins to troubleshoot.</Paragraph>
|
||||
<Paragraph className={Margins.top8}>Your plugin list will be sent as a text file.</Paragraph>
|
||||
</div>
|
||||
});
|
||||
|
||||
return `${user.username} has more than 100 plugins enabled, please reduce the number of enabled plugins to get support.`;
|
||||
const fileContent = [
|
||||
`Enabled Stock Plugins (${enabledStockPlugins.length}):`,
|
||||
...enabledStockPlugins.map(p => ` - ${p}`),
|
||||
"",
|
||||
];
|
||||
|
||||
if (enabledUserPlugins.length) {
|
||||
fileContent.push(
|
||||
`Enabled User Plugins (${enabledUserPlugins.length}):`,
|
||||
...enabledUserPlugins.map(p => ` - ${p}`),
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
fileContent.push(
|
||||
"---",
|
||||
`Total Enabled Plugins: ${enabledPlugins.length}`,
|
||||
"Warning: Due to the high number of enabled plugins, support may be limited."
|
||||
);
|
||||
|
||||
return {
|
||||
uploadFile: true,
|
||||
fileContent: fileContent.join("\n"),
|
||||
filename: `${user.username}-plugins.txt`
|
||||
};
|
||||
}
|
||||
|
||||
let content = `**Enabled Plugins (${enabledStockPlugins.length}):**\n${makeCodeblock(enabledStockPlugins.join(", "))}`;
|
||||
|
||||
if (enabledUserPlugins.length) {
|
||||
content += `**Enabled UserPlugins (${enabledUserPlugins.length}):**\n${makeCodeblock(enabledUserPlugins.join(", "))}`;
|
||||
}
|
||||
|
||||
return content;
|
||||
|
|
@ -258,9 +314,22 @@ export default definePlugin({
|
|||
description: "Send Equicord plugin list",
|
||||
// @ts-ignore
|
||||
predicate: ctx => isAnyPluginDev(UserStore.getCurrentUser()?.id) || isEquicordGuild(ctx?.guild?.id, true),
|
||||
execute: () => {
|
||||
execute: async () => {
|
||||
const channelId = SelectedChannelStore.getChannelId();
|
||||
const pluginList = generatePluginList();
|
||||
return { content: typeof pluginList === "string" ? pluginList : "Unable to generate plugin list." };
|
||||
|
||||
if (typeof pluginList === "string") {
|
||||
return { content: pluginList };
|
||||
} else if (pluginList && typeof pluginList === "object" && pluginList.uploadFile) {
|
||||
try {
|
||||
await uploadPluginListFile(channelId, pluginList.fileContent, pluginList.filename);
|
||||
return { content: "" }; // Empty return since file was already sent
|
||||
} catch (e) {
|
||||
new Logger("SupportHelper").error("Failed to upload plugin list:", e);
|
||||
return { content: "Failed to upload plugin list file. Please try again." };
|
||||
}
|
||||
}
|
||||
return { content: "Unable to generate plugin list." };
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -380,6 +449,14 @@ export default definePlugin({
|
|||
const pluginList = generatePluginList();
|
||||
if (typeof pluginList === "string") {
|
||||
sendMessage(props.channel.id, { content: pluginList });
|
||||
} else if (pluginList && typeof pluginList === "object" && pluginList.uploadFile) {
|
||||
try {
|
||||
await uploadPluginListFile(props.channel.id, pluginList.fileContent, pluginList.filename);
|
||||
showToast("Plugin list uploaded successfully!", Toasts.Type.SUCCESS);
|
||||
} catch (e) {
|
||||
new Logger("SupportHelper").error("Failed to upload plugin list:", e);
|
||||
showToast("Failed to upload plugin list", Toasts.Type.FAILURE);
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue