diff --git a/src/equicordplugins/mediaDownloader.desktop/index.tsx b/src/equicordplugins/mediaDownloader.desktop/index.tsx index f7bce69f..7f74361b 100644 --- a/src/equicordplugins/mediaDownloader.desktop/index.tsx +++ b/src/equicordplugins/mediaDownloader.desktop/index.tsx @@ -200,45 +200,50 @@ export default definePlugin({ inputType: ApplicationCommandInputType.BUILT_IN, name: "download", description: "Download and send videos, audio or gifs.", - options: [{ - name: "url", - description: "The URL of any video supported by yt-dlp.", - required: true, - type: ApplicationCommandOptionType.STRING - }, { - name: "format", - description: "Whether to download a video or audio.", - type: ApplicationCommandOptionType.STRING, - choices: [ - { name: "Video", value: "video", label: "Video" }, - { name: "Audio", value: "audio", label: "Audio" }, - { name: "GIF", value: "gif", label: "GIF" } - ], - required: false, - }, { - name: "gif_quality", - type: ApplicationCommandOptionType.INTEGER, - description: "The quality level when using GIF. Try lowering this number if the GIF is too large.", - required: false, - choices: [ - { name: "5", value: "5", label: "5" }, - { name: "4", value: "4", label: "4" }, - { name: "3", value: "3", label: "3" }, - { name: "2", value: "2", label: "2" }, - { name: "1", value: "1", label: "1" } - ] - }, { - name: "yt-dlp_args", - description: "Additional arguments to pass to yt-dlp. These will take precedence over arguments set in the settings. This may overwrite default plugin arguments such format selection. Note: if modifying the output, ensure the filename starts with `download`.", - required: false, - type: ApplicationCommandOptionType.STRING - }, { - name: "ffmpeg_args", - description: "Additional arguments to pass to ffmpeg. These will take precedence over arguments set in the settings. This may overwrite default plugin arguments such as auto-scaling. Note: if modifying the output, ensure the filename starts with `remux`.", - required: false, - type: ApplicationCommandOptionType.STRING - }], - + options: [ + { + name: "url", + description: "The URL of any video supported by yt-dlp.", + required: true, + type: ApplicationCommandOptionType.STRING + }, + { + name: "format", + description: "Whether to download a video or audio.", + type: ApplicationCommandOptionType.STRING, + choices: [ + { name: "Video", value: "video", label: "Video" }, + { name: "Audio", value: "audio", label: "Audio" }, + { name: "GIF", value: "gif", label: "GIF" } + ], + required: false, + }, + { + name: "gif_quality", + type: ApplicationCommandOptionType.INTEGER, + description: "The quality level when using GIF. Try lowering this number if the GIF is too large.", + required: false, + choices: [ + { name: "5", value: "5", label: "5" }, + { name: "4", value: "4", label: "4" }, + { name: "3", value: "3", label: "3" }, + { name: "2", value: "2", label: "2" }, + { name: "1", value: "1", label: "1" } + ] + }, + { + name: "yt-dlp_args", + description: "Additional arguments to pass to yt-dlp. These will take precedence over arguments set in the settings. This may overwrite default plugin arguments such format selection. Note: if modifying the output, ensure the filename starts with `download`.", + required: false, + type: ApplicationCommandOptionType.STRING + }, + { + name: "ffmpeg_args", + description: "Additional arguments to pass to ffmpeg. These will take precedence over arguments set in the settings. This may overwrite default plugin arguments such as auto-scaling. Note: if modifying the output, ensure the filename starts with `remux`.", + required: false, + type: ApplicationCommandOptionType.STRING + } + ], execute: async (args, ctx) => { if (!await Native.isYtdlpAvailable()) return openDependencyModal(); if (!await Native.isFfmpegAvailable() && settings.store.showFfmpegWarning) sendFfmpegWarning(ctx.channel.id); diff --git a/src/equicordplugins/moreCommands/index.ts b/src/equicordplugins/moreCommands/index.ts index 4046b08e..251ec23e 100644 --- a/src/equicordplugins/moreCommands/index.ts +++ b/src/equicordplugins/moreCommands/index.ts @@ -31,7 +31,7 @@ function mock(input: string): string { export default definePlugin({ name: "MoreCommands", - description: "echo, lenny, mock", + description: "Echo, Lenny, Mock, and More", authors: [Devs.Arjix, Devs.echo, Devs.Samu, EquicordDevs.zyqunix], commands: [ { @@ -62,112 +62,308 @@ export default definePlugin({ }), }, { - name: "toLowerCase", - description: "all text will be lowercase", - options: [ - { - name: "text", - description: "text to lowercase", - type: ApplicationCommandOptionType.STRING, - required: true - } - ], - execute: opts => { - const input = opts.find(o => o.name === "text")?.value as string; - const content = input.toLowerCase(); - return { content: content }; + name: "wordcount", + description: "Counts the number of words in a message", + options: [RequiredMessageOption], + inputType: ApplicationCommandInputType.BOT, + execute: (opts, ctx) => { + const message = findOption(opts, "message", ""); + const wordCount = message.trim().split(/\s+/).length; + sendBotMessage(ctx.channel.id, { + content: `The message contains ${wordCount} words.` + }); }, }, { - name: "toUpperCase", - description: "ALL TEXT WILL BE UPPERCASE", - options: [ - { - name: "text", - description: "TEXT TO UPPERCASE", - type: ApplicationCommandOptionType.STRING, - required: true - } - ], - execute: opts => { - const input = opts.find(o => o.name === "text")?.value as string; - const content = input.toUpperCase(); - return { content: content }; + name: "ping", + description: "Pings the bot to check if it's responding", + options: [], + inputType: ApplicationCommandInputType.BOT, + execute: (opts, ctx) => { + sendBotMessage(ctx.channel.id, { + content: "Pong!" + }); }, }, { - name: "toLocaleLowerCase", - description: "all text will be locale lowercase", - options: [ - { - name: "text", - description: "text to lowercase", - type: ApplicationCommandOptionType.STRING, - required: true - } - ], + name: "rolldice", + description: "Roll a die with the specified number of sides", + options: [RequiredMessageOption], execute: opts => { - const input = opts.find(o => o.name === "text")?.value as string; - const content = input.toLocaleLowerCase(); - return { content: content }; + const sides = parseInt(findOption(opts, "message", "6")); + const roll = Math.floor(Math.random() * sides) + 1; + return { + content: `You rolled a ${roll}!` + }; }, }, { - name: "toLocaleUpperCase", - description: "ALL TEXT WILL BE LOCALE UPPERCASE", - options: [ - { - name: "text", - description: "TEXT TO UPPERCASE", - type: ApplicationCommandOptionType.STRING, - required: true - } - ], - execute: opts => { - const input = opts.find(o => o.name === "text")?.value as string; - const content = input.toLocaleUpperCase(); - return { content: content }; + name: "flipcoin", + description: "Flips a coin and returns heads or tails", + options: [], + execute: (opts, ctx) => { + const flip = Math.random() < 0.5 ? "Heads" : "Tails"; + return { + content: `The coin landed on: ${flip}` + }; }, }, { - name: "normalize", - description: "Returns Unicode Normalization Form of string", - options: [ - { - name: "text", - description: "Text to normalize", - type: ApplicationCommandOptionType.STRING, - required: true - } - ], + name: "ask", + description: "Ask a yes/no question and get an answer", + options: [RequiredMessageOption], execute: opts => { - const input = opts.find(o => o.name === "text")?.value as string; - const content = input.normalize(); - return { content: content }; + const question = findOption(opts, "message", ""); + const responses = ["Yes", "No", "Maybe", "Ask again later", "Definitely not", "It is certain"]; + const response = responses[Math.floor(Math.random() * responses.length)]; + return { + content: `${question} - ${response}` + }; }, }, { - name: "repeat", - description: "Repeats the string count times", + name: "randomanimal", + description: "Get a random cat picture", options: [ { - name: "text", - description: "Text to repeat", + name: "animal", + description: "pick your animal", type: ApplicationCommandOptionType.STRING, + required: true, + choices: [ + { name: "cat", value: "cat", label: "cat" }, + { name: "dog", value: "dog", label: "dog" }, + ] + } + ], + execute: (opts, ctx) => { + return (async () => { + const animal = findOption(opts, "animal") as string; + let url; + if (animal === "cat") { + url = "https://api.thecatapi.com/v1/images/search"; + } else if (animal === "dog") { + url = "https://api.thedogapi.com/v1/images/search"; + } + try { + const response = await fetch(url); + if (!response.ok) throw new Error(`Failed to fetch ${animal} image`); + const data = await response.json(); + return { + content: data[0].url + }; + } catch (err) { + sendBotMessage(ctx.channel.id, { + content: "Sorry, couldn't fetch a cat picture right now 😿" + }); + } + })(); + }, + }, + { + name: "randomnumber", + description: "Generates a random number between two values", + options: [ + { + name: "min", + description: "Minimum value", + type: ApplicationCommandOptionType.INTEGER, required: true }, { - name: "count", - description: "Amount of repetitions", + name: "max", + description: "Maximum value", type: ApplicationCommandOptionType.INTEGER, required: true } ], execute: opts => { - const text = opts.find(o => o.name === "text")?.value as string; - const count = (opts.find(o => o.name === "count")?.value ?? 1) as number; - const content = text.repeat(count); - return { content: content }; + const min = parseInt(findOption(opts, "min", "0")); + const max = parseInt(findOption(opts, "max", "100")); + const number = Math.floor(Math.random() * (max - min + 1)) + min; + return { + content: `Random number between ${min} and ${max}: ${number}` + }; + } + }, + { + name: "countdown", + description: "Starts a countdown from a specified number", + options: [ + { + name: "number", + description: "Number to countdown from (max 10)", + type: ApplicationCommandOptionType.INTEGER, + required: true + } + ], + inputType: ApplicationCommandInputType.BOT, + execute: async (opts, ctx) => { + const number = Math.min(parseInt(findOption(opts, "number", "5")), 10); + if (isNaN(number) || number < 1) { + sendBotMessage(ctx.channel.id, { + content: "Please provide a valid number between 1 and 10!" + }); + return; + } + sendBotMessage(ctx.channel.id, { + content: `Starting countdown from ${number}...` + }); + for (let i = number; i >= 0; i--) { + await new Promise(resolve => setTimeout(resolve, 1000)); + sendBotMessage(ctx.channel.id, { + content: i === 0 ? "🎉 Go! 🎉" : `${i}...` + }); + } + }, + }, + { + name: "choose", + description: "Randomly chooses from provided options", + options: [ + { + name: "choices", + description: "Comma-separated list of choices", + type: ApplicationCommandOptionType.STRING, + required: true + } + ], + execute: opts => { + const choices = findOption(opts, "choices", "").split(",").map(c => c.trim()); + const choice = choices[Math.floor(Math.random() * choices.length)]; + return { + content: `I choose: ${choice}` + }; + } + }, + { + name: "systeminfo", + description: "Shows system information", + options: [], + execute: async (opts, ctx) => { + try { + const { userAgent, hardwareConcurrency, onLine, languages } = navigator; + const { width, height, colorDepth } = window.screen; + const { deviceMemory, connection }: { deviceMemory: any, connection: any; } = navigator as any; + const platform = userAgent.includes("Windows") ? "Windows" : + userAgent.includes("Mac") ? "MacOS" : + userAgent.includes("Linux") ? "Linux" : "Unknown"; + const isMobile = /Mobile|Android|iPhone/i.test(userAgent); + const deviceType = isMobile ? "Mobile" : "Desktop"; + const browserInfo = userAgent.match(/(?:chrome|firefox|safari|edge|opr)\/?\s*(\d+)/i)?.[0] || "Unknown"; + const networkInfo = connection ? `${connection.effectiveType || "Unknown"}` : "Unknown"; + const info = [ + `> **Platform**: ${platform}`, + `> **Device Type**: ${deviceType}`, + `> **Browser**: ${browserInfo}`, + `> **CPU Cores**: ${hardwareConcurrency || "N/A"}`, + `> **Memory**: ${deviceMemory ? `${deviceMemory}GB` : "N/A"}`, + `> **Screen**: ${width}x${height} (${colorDepth}bit)`, + `> **Languages**: ${languages?.join(", ")}`, + `> **Network**: ${networkInfo} (${onLine ? "Online" : "Offline"})` + ].join("\n"); + return { content: info }; + } catch (err) { + sendBotMessage(ctx.channel.id, { content: "Failed to fetch system information" }); + } + }, + }, + { + name: "getUptime", + description: "Returns the system uptime", + execute: async (opts, ctx) => { + const uptime = performance.now() / 1000; + const uptimeInfo = `> **System Uptime**: ${Math.floor(uptime / 60)} minutes`; + return { content: uptimeInfo }; + }, + }, + { + name: "getTime", + description: "Returns the current server time", + execute: async (opts, ctx) => { + const currentTime = new Date().toLocaleString(); + return { content: `> **Current Time**: ${currentTime}` }; + }, + }, + { + name: "getLocation", + description: "Returns the user's approximate location based on IP", + execute: async (opts, ctx) => { + try { + const response = await fetch("https://ipapi.co/json/"); + const data = await response.json(); + const locationInfo = `> **Country**: ${data.country_name}\n> **Region**: ${data.region}\n> **City**: ${data.city}`; + return { content: locationInfo }; + } catch (err) { + sendBotMessage(ctx.channel.id, { content: "Failed to fetch location information" }); + } + }, + }, + { + name: "transform", + description: "Transform your text with the specified option", + options: [ + { + name: "text", + description: "TEXT TO UPPERCASE", + type: ApplicationCommandOptionType.STRING, + required: true + }, + { + name: "transformation", + description: "transformation to apply to your text", + type: ApplicationCommandOptionType.STRING, + required: true, + choices: [ + { name: "toLowerCase", value: "toLowerCase", label: "toLowerCase" }, + { name: "toUpperCase", value: "toUpperCase", label: "toUpperCase" }, + { name: "toLocaleLowerCase", value: "toLocaleLowerCase", label: "toLocaleLowerCase" }, + { name: "toLocaleUpperCase", value: "toLocaleUpperCase", label: "toLocaleUpperCase" }, + { name: "reverse", value: "reverse", label: "reverse" }, + { name: "stay the same", value: "same", label: "stay the same" } + ] + }, + { + name: "repeat", + description: "how many times to repeat", + type: ApplicationCommandOptionType.INTEGER, + required: false + }, + { + name: "normalize", + description: "which normailze option to use", + type: ApplicationCommandOptionType.INTEGER, + required: false, + choices: [ + { name: "NFC", value: "NFC", label: "NFC" }, + { name: "NFD", value: "NFD", label: "NFD" }, + { name: "NFKC", value: "NFKC", label: "NFKC" }, + { name: "NFKD", value: "NFKD", label: "NFKD" } + ] + }, + ], + execute: opts => { + const transform = findOption(opts, "transformation") as string; + const repeat = findOption(opts, "repeat") as number | undefined; + const normalize = findOption(opts, "normalize") as string | undefined; + let text = findOption(opts, "text") as string; + + if (transform && text) { + if (transform === "same") { + if (normalize) text = text.normalize(normalize); + return { + content: text.repeat(repeat ?? 1) + }; + } else { + const method = (text as any)[transform]; + const transformed = method.call(text); + if (normalize) text = text.normalize(normalize); + if (transform === "reverse") text = text.split("").reverse().join(""); + return { + content: transformed.repeat(repeat ?? 1) + }; + } + } }, }, ] diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 3e671ae8..545505f9 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -21,7 +21,7 @@ import "./style.css"; import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack"; +import { findByPropsLazy, findStoreLazy } from "@webpack"; import { FluxDispatcher } from "@webpack/common"; import { ReactNode } from "react"; @@ -35,7 +35,6 @@ enum FolderIconDisplay { export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore"); const SortedGuildStore = findStoreLazy("SortedGuildStore"); -const GuildsTree = findLazy(m => m.prototype?.moveNextTo); const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand"); let lastGuildId = null as string | null;