this repo has no description

Merge pull request #164 from moonlight-mod/commands

Commands library

Changed files
+260
packages
core-extensions
src
commands
types
src
coreExtensions
+76
packages/core-extensions/src/commands/index.ts
···
···
+
import { Patch, ExtensionWebpackModule } from "@moonlight-mod/types";
+
import { APPLICATION_ID } from "@moonlight-mod/types/coreExtensions/commands";
+
+
export const patches: Patch[] = [
+
{
+
find: ".fI5MTU)", // COMMAND_SECTION_BUILT_IN_NAME
+
replace: [
+
// inject commands
+
{
+
match: /return (\i)=\i/,
+
replacement: (orig, commands) =>
+
`${commands}=[...${commands},...require("commands_commands").default._getCommands()];${orig}`
+
},
+
+
// section
+
{
+
match: /(?<=\i={)(?=\[\i\.\i\.BUILT_IN]:{id:\i\.\i\.BUILT_IN,type:(\i.\i\.BUILT_IN))/,
+
replacement: (_, type) =>
+
`"${APPLICATION_ID}":{id:"${APPLICATION_ID}",type:${type},get name(){return "moonlight"}},`
+
}
+
]
+
},
+
+
// index our section
+
{
+
find: '"ApplicationCommandIndexStore"',
+
replace: {
+
match: /(?<=let \i=(\i)\((\i\.\i)\[\i\.\i\.BUILT_IN\],(\i),!0,!0,(\i)\);)null!=(\i)&&(\i)\.push\(\i\)/,
+
replacement: (_, createSection, sections, deny, props, section, commands) =>
+
`null!=${section}&&(${section}.data=${section}.data.filter(c=>c.applicationId=="-1"));
+
null!=${section}&&${commands}.push(${section});
+
const moonlightCommands=${createSection}(${sections}["${APPLICATION_ID}"],${deny},!0,!0,${props});
+
null!=moonlightCommands&&(moonlightCommands.data=moonlightCommands.data.filter(c=>c.applicationId=="${APPLICATION_ID}"));
+
null!=moonlightCommands&&${commands}.push(moonlightCommands)`
+
}
+
},
+
+
// grab legacy commands (needed for adding actions that act like sed/plus reacting)
+
{
+
find: "={tts:{action:",
+
replace: {
+
match: /Object\.setPrototypeOf\((\i),null\)/,
+
replacement: (_, legacyCommands) => `require("commands_commands")._getLegacyCommands(${legacyCommands})`
+
}
+
},
+
+
// add icon
+
{
+
find: ",hasSpaceTerminator:",
+
replace: {
+
match: /(\i)\.type===/,
+
replacement: (orig, section) => `${section}.id!=="${APPLICATION_ID}"&&${orig}`
+
}
+
},
+
{
+
find: ".icon,bot:null===",
+
replace: {
+
match: /(\.useMemo\(\(\)=>{)(if\((\i)\.type)/,
+
replacement: (_, before, after, section) => `${before}
+
if (${section}.id==="${APPLICATION_ID}") return "https://moonlight-mod.github.io/favicon.png";
+
${after}`
+
}
+
},
+
// fix icon sizing because they expect built in to be 24 and others to be 32
+
{
+
find: ".builtInSeparator}):null]",
+
replace: {
+
match: /(\i)\.type===\i\.\i\.BUILT_IN/,
+
replacement: (orig, section) => `${section}.id!=="${APPLICATION_ID}"&&${orig}`
+
}
+
}
+
];
+
+
export const webpackModules: Record<string, ExtensionWebpackModule> = {
+
commands: {}
+
};
+11
packages/core-extensions/src/commands/manifest.json
···
···
+
{
+
"$schema": "https://moonlight-mod.github.io/manifest.schema.json",
+
"id": "commands",
+
"apiLevel": 2,
+
"meta": {
+
"name": "Commands",
+
"tagline": "A library to add commands",
+
"authors": ["Cynosphere", "NotNite"],
+
"tags": ["library"]
+
}
+
}
+55
packages/core-extensions/src/commands/webpackModules/commands.ts
···
···
+
import {
+
APPLICATION_ID,
+
Commands,
+
LegacyCommand,
+
RegisteredCommand
+
} from "@moonlight-mod/types/coreExtensions/commands";
+
+
type LegacyCommands = Record<string, LegacyCommand>;
+
let legacyCommands: LegacyCommands | undefined;
+
let queuedLegacyCommands: Record<string, LegacyCommand> | null = {};
+
+
const registeredCommands: RegisteredCommand[] = [];
+
+
export function _getLegacyCommands(commands: LegacyCommands) {
+
legacyCommands = commands;
+
if (queuedLegacyCommands != null) {
+
for (const [key, value] of Object.entries(queuedLegacyCommands)) {
+
legacyCommands[key] = value;
+
}
+
queuedLegacyCommands = null;
+
}
+
}
+
+
export const commands: Commands = {
+
registerCommand(command) {
+
const registered: RegisteredCommand = {
+
...command,
+
untranslatedName: command.id,
+
displayName: command.id,
+
applicationId: APPLICATION_ID,
+
untranslatedDescription: command.description,
+
displayDescription: command.description,
+
options: command.options.map((o) => ({
+
...o,
+
displayName: o.name,
+
displayDescription: o.description
+
}))
+
};
+
registeredCommands.push(registered);
+
},
+
+
registerLegacyCommand(id, command) {
+
if (!legacyCommands) {
+
queuedLegacyCommands![id] = command;
+
} else {
+
legacyCommands[id] = command;
+
}
+
},
+
+
_getCommands() {
+
return [...registeredCommands];
+
}
+
};
+
+
export default commands;
+118
packages/types/src/coreExtensions/commands.ts
···
···
+
export const APPLICATION_ID = "-3";
+
+
export enum CommandType {
+
CHAT = 1,
+
MESSAGE = 3,
+
PRIMARY_ENTRY_POINT = 4,
+
USER = 2
+
}
+
+
export enum InputType {
+
BOT = 3,
+
BUILT_IN = 0,
+
BUILT_IN_INTEGRATION = 2,
+
BUILT_IN_TEXT = 1,
+
PLACEHOLDER = 4
+
}
+
+
export enum OptionType {
+
ATTACHMENT = 11,
+
BOOLEAN = 5,
+
CHANNEL = 7,
+
INTEGER = 4,
+
MENTIONABLE = 9,
+
NUMBER = 10,
+
ROLE = 8,
+
STRING = 3,
+
SUB_COMMAND = 1,
+
SUB_COMMAND_GROUP = 2,
+
USER = 6
+
}
+
+
export type RegisteredCommandOption = {
+
name: string;
+
displayName: string;
+
type: OptionType;
+
description: string;
+
displayDescription: string;
+
};
+
+
export type MoonlightCommandOption = {
+
name: string;
+
type: OptionType;
+
description: string;
+
};
+
+
// TODO: types
+
export type CommandPredicateState = {
+
channel: any;
+
guild: any;
+
};
+
+
export type RegisteredCommand = {
+
id: string;
+
untranslatedName: string;
+
displayName: string;
+
type: CommandType;
+
inputType: InputType;
+
applicationId: string; // set to -3!
+
untranslatedDescription: string;
+
displayDescription: string;
+
options: RegisteredCommandOption[];
+
predicate?: (state: CommandPredicateState) => boolean;
+
execute: (options: CommandOption[]) => void;
+
};
+
+
export type MoonlightCommand = {
+
id: string;
+
description: string;
+
type: CommandType;
+
inputType: InputType;
+
options: MoonlightCommandOption[];
+
predicate?: (state: CommandPredicateState) => boolean;
+
execute: (options: CommandOption[]) => void;
+
};
+
+
export type CommandOption = {
+
name: string;
+
} & ( // TODO: more of these
+
| {
+
type: Exclude<OptionType, OptionType.STRING>;
+
value: any;
+
}
+
| {
+
type: OptionType.STRING;
+
value: string;
+
}
+
);
+
+
export type Commands = {
+
/**
+
* Register a command in the internal slash command system
+
*/
+
registerCommand: (command: MoonlightCommand) => void;
+
+
/**
+
* Register a legacy command that works via regex
+
*/
+
registerLegacyCommand: (id: string, command: LegacyCommand) => void;
+
+
/**
+
* @private
+
*/
+
_getCommands: () => RegisteredCommand[];
+
};
+
+
export type LegacyContext = {
+
channel: any;
+
isEdit: boolean;
+
};
+
+
export type LegacyReturn = {
+
content: string;
+
};
+
+
export type LegacyCommand = {
+
match?: RegExp;
+
action: (content: string, context: LegacyContext) => LegacyReturn;
+
};