Skip to content
This repository has been archived by the owner on Feb 6, 2024. It is now read-only.

[Commands] Init core commands #92

Open
wants to merge 7 commits into
base: rewrite
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/def.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ interface VendettaObject {
invites: PropIntellisense<"acceptInviteAndTransitionToInviteChannel">;
commands: PropIntellisense<"getBuiltInCommands">;
navigation: PropIntellisense<"pushLazy">;
messageUtil: PropIntellisense<"sendBotMessage">;
navigationStack: PropIntellisense<"createStackNavigator">;
NavigationNative: PropIntellisense<"NavigationContainer">;
// You may ask: "Why not just install Flux's types?"
Expand Down
40 changes: 40 additions & 0 deletions src/lib/commands/debug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ApplicationCommand, ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType } from "@types";
import { getDebugInfo } from "@lib/debug";
import { messageUtil } from "@metro/common";

export default <ApplicationCommand>{
name: "debug",
displayName: "debug",
description: "Send Vendetta debug info.",
displayDescription: "Send Vendetta debug info.",
options: [
{
name: "ephemeral",
displayName: "ephemeral",
type: ApplicationCommandOptionType.BOOLEAN,
description: "Send debug info ephemerally.",
displayDescription: "Send debug info ephemerally.",
}
],
applicationId: "-1",
inputType: ApplicationCommandInputType.BUILT_IN_TEXT,
type: ApplicationCommandType.CHAT,
execute([ephemeral], ctx) {
const info = getDebugInfo();
const content = [
"**__Vendetta Debug Info__**",
`> Vendetta: ${info.vendetta.version} (${info.vendetta.loader})`,
`> Discord: ${info.discord.version} (${info.discord.build})`,
`> React: ${info.react.version} (RN ${info.react.nativeVersion})`,
`> Hermes: ${info.hermes.version} (bcv${info.hermes.bytecodeVersion})`,
`> System: ${info.os.name} ${info.os.version} (SDK ${info.os.sdk})`,
`> Device: ${info.device.model} (${info.device.codename})`,
].join("\n");

if (ephemeral?.value) {
messageUtil.sendBotMessage(ctx.channel.id, content);
} else {
return { content };
}
}
}
49 changes: 49 additions & 0 deletions src/lib/commands/eval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { findByProps } from "@metro/filters";
import { messageUtil } from "@metro/common";
import { ApplicationCommand, ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType } from "@types";

const util = findByProps("inspect");
const AsyncFunction = (async () => void 0).constructor;

const ZERO_WIDTH_SPACE_CHARACTER = "\u200B";

function wrapInJSCodeblock(resString: string) {
return "```js\n" + resString.replaceAll("`", "`" + ZERO_WIDTH_SPACE_CHARACTER) + "\n```";
}

export default <ApplicationCommand>{
name: "eval",
displayName: "eval",
description: "Evaluate JavaScript code.",
displayDescription: "Evaluate JavaScript code.",
options: [
{
name: "code",
displayName: "code",
type: ApplicationCommandOptionType.STRING,
description: "The code to evaluate.",
displayDescription: "The code to evaluate.",
required: true
},
{
name: "async",
displayName: "async",
type: ApplicationCommandOptionType.BOOLEAN,
description: "Whether to support 'await' in code. Must explicitly return for result (default: false)",
displayDescription: "Whether to support 'await' in code. Must explicitly return for result (default: false)"
}
],
applicationId: "-1",
inputType: ApplicationCommandInputType.BUILT_IN_TEXT,
type: ApplicationCommandType.CHAT,
async execute([code, async], ctx) {
try {
const res = util.inspect(async?.value ? await AsyncFunction(code.value)() : eval?.(code.value));
const trimmedRes = res.length > 2000 ? res.slice(0, 2000) + "..." : res;

messageUtil.sendBotMessage(ctx.channel.id, wrapInJSCodeblock(trimmedRes));
} catch (err: any) {
messageUtil.sendBotMessage(ctx.channel.id, wrapInJSCodeblock(err?.stack ?? err));
}
}
}
7 changes: 7 additions & 0 deletions src/lib/commands.ts → src/lib/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ import { ApplicationCommand, ApplicationCommandType } from "@types";
import { commands as commandsModule } from "@metro/common";
import { after } from "@lib/patcher";

import evalCommand from "@lib/commands/eval";
import debugCommand from "@lib/commands/debug";
import pluginCommand from "@lib/commands/plugins";

let commands: ApplicationCommand[] = [];

export function patchCommands() {
const unpatch = after("getBuiltInCommands", commandsModule, ([type], res: ApplicationCommand[]) => {
if (type === ApplicationCommandType.CHAT) return res.concat(commands);
});

// Register core commands
[evalCommand, debugCommand, pluginCommand].forEach(registerCommand);

return () => {
commands = [];
unpatch();
Expand Down
45 changes: 45 additions & 0 deletions src/lib/commands/plugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ApplicationCommand, ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType, Plugin } from "@types";
import { messageUtil } from "@metro/common";
import { plugins as pluginStorage } from "../plugins";

export default <ApplicationCommand>{
name: "plugins",
displayName: "plugins",
description: "Send list of installed plugins.",
displayDescription: "Send list of installed plugins.",
options: [
{
name: "ephemeral",
displayName: "ephemeral",
type: ApplicationCommandOptionType.BOOLEAN,
description: "Send plugins list ephemerally.",
displayDescription: "Send plugins list ephemerally.",
}
],
inputType: ApplicationCommandInputType.BUILT_IN_TEXT,
type: ApplicationCommandType.CHAT,
execute([ephemeral], ctx) {
const plugins = Object.values(pluginStorage).sort((a, b) => a.manifest.name.localeCompare(b.manifest.name));

const enabled = plugins.filter(p => p.enabled).map(p => p.manifest.name);
const disabled = plugins.filter(p => !p.enabled).map(p => p.manifest.name);

const content = [
`**__Installed Plugins (${plugins.length})__**`,
...(enabled.length > 0 ? [
`Enabled (${enabled.length}):`,
"> " + enabled.join(", "),
] : []),
...(disabled.length > 0 ? [
`Disabled (${disabled.length}):`,
"> " + disabled.join(", "),
] : []),
].join("\n");

if (ephemeral?.value) {
messageUtil.sendBotMessage(ctx.channel.id, content);
} else {
return { content };
}
}
}
1 change: 1 addition & 0 deletions src/lib/metro/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const assets = findByProps("registerAsset");
export const invites = findByProps("acceptInviteAndTransitionToInviteChannel");
export const commands = findByProps("getBuiltInCommands");
export const navigation = findByProps("pushLazy");
export const messageUtil = findByProps("sendBotMessage");
export const navigationStack = findByProps("createStackNavigator");
export const NavigationNative = findByProps("NavigationContainer");

Expand Down