482 lines
18 KiB
TypeScript
482 lines
18 KiB
TypeScript
import core from './core';
|
|
import {Character, Conversation, userStatuses} from './interfaces';
|
|
import l from './localize';
|
|
import ChannelConversation = Conversation.ChannelConversation;
|
|
import PrivateConversation = Conversation.PrivateConversation;
|
|
|
|
export const enum ParamType {
|
|
String, Number, Character, Enum
|
|
}
|
|
|
|
const defaultDelimiters: {[key: number]: string | undefined} = {[ParamType.Character]: ',', [ParamType.String]: ''};
|
|
|
|
export function isAction(this: void, text: string): boolean {
|
|
return /^\/me\b/i.test(text);
|
|
}
|
|
|
|
export function isWarn(this: void, text: string): boolean {
|
|
return /^\/warn\b/i.test(text);
|
|
}
|
|
|
|
export function isCommand(this: void, text: string): boolean {
|
|
return text.charAt(0) === '/' && !isAction(text) && !isWarn(text);
|
|
}
|
|
|
|
export function parse(this: void | never, input: string, context: CommandContext): ((this: Conversation) => void) | string {
|
|
const commandEnd = input.indexOf(' ');
|
|
const name = input.substring(1, commandEnd !== -1 ? commandEnd : undefined).toLowerCase();
|
|
const command = commands[name];
|
|
if(command === undefined) return l('commands.unknown');
|
|
const args = `${commandEnd !== -1 ? input.substr(commandEnd + 1) : ''}`;
|
|
if(command.context !== undefined && (command.context & context) === 0) return l('commands.badContext');
|
|
|
|
let index = 0;
|
|
const values: (string | number)[] = [];
|
|
|
|
if(command.params !== undefined)
|
|
for(let i = 0; i < command.params.length; ++i) {
|
|
while(args[index] === ' ') ++index;
|
|
const param = command.params[i];
|
|
if(index === -1) {
|
|
if(param.optional !== undefined) continue;
|
|
return l('commands.tooFewParams');
|
|
}
|
|
let delimiter = param.delimiter !== undefined ? param.delimiter : defaultDelimiters[param.type];
|
|
if(delimiter === undefined) delimiter = ' ';
|
|
const endIndex = delimiter.length > 0 ? args.indexOf(delimiter, index) : args.length;
|
|
const value = args.substring(index, endIndex !== -1 ? endIndex : undefined);
|
|
if(value.length === 0) {
|
|
if(param.optional !== undefined) continue;
|
|
return l('commands.tooFewParams');
|
|
}
|
|
values[i] = value;
|
|
switch(param.type) {
|
|
case ParamType.String:
|
|
if(i === command.params.length - 1) values[i] = args.substr(index);
|
|
break;
|
|
case ParamType.Enum:
|
|
if((param.options !== undefined ? param.options : []).indexOf(value) === -1)
|
|
return l('commands.invalidParam', l(`commands.${name}.param${i}`));
|
|
break;
|
|
case ParamType.Number:
|
|
const num = parseInt(value, 10);
|
|
if(isNaN(num))
|
|
return l('commands.invalidParam', l(`commands.${name}.param${i}`));
|
|
values[i] = num;
|
|
break;
|
|
case ParamType.Character:
|
|
if(value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') {
|
|
values[i] = value.substring(1, value.length - 1);
|
|
break;
|
|
}
|
|
const char = core.characters.get(value);
|
|
if(char.status === 'offline') return l('commands.invalidCharacter');
|
|
}
|
|
index = endIndex === -1 ? args.length : endIndex + 1;
|
|
}
|
|
return function(this: Conversation): void {
|
|
command.exec(this, ...values);
|
|
};
|
|
}
|
|
|
|
export const enum CommandContext {
|
|
Console = 1 << 0,
|
|
Channel = 1 << 1,
|
|
Private = 1 << 2
|
|
}
|
|
|
|
export enum Permission {
|
|
RoomOp = -1,
|
|
RoomOwner = -2,
|
|
ChannelMod = 4,
|
|
ChatOp = 2,
|
|
Admin = 1
|
|
}
|
|
|
|
export interface Command {
|
|
readonly context?: CommandContext, //default implicit Console | Channel | Private
|
|
readonly permission?: Permission
|
|
readonly documented?: false, //default true
|
|
readonly params?: {
|
|
readonly type: ParamType
|
|
readonly options?: ReadonlyArray<string>, //default undefined
|
|
readonly optional?: true, //default false
|
|
readonly delimiter?: string, //default ' ' (',' for type: Character)
|
|
validator?(data: string | number): boolean //default undefined
|
|
}[]
|
|
exec(context: Conversation, ...params: (string | number | undefined)[]): void
|
|
}
|
|
|
|
const commands: { readonly [key: string]: Command | undefined } = {
|
|
me: {
|
|
exec: () => "stub",
|
|
context: CommandContext.Channel | CommandContext.Private,
|
|
params: [{ type: ParamType.String }],
|
|
},
|
|
reward: {
|
|
exec: (_, character: string) =>
|
|
core.connection.send("RWD", { character }),
|
|
permission: Permission.Admin,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
greports: {
|
|
permission: Permission.ChannelMod,
|
|
exec: () => core.connection.send("PCR"),
|
|
},
|
|
join: {
|
|
exec: (_, channel: string) => core.connection.send("JCH", { channel }),
|
|
params: [{ type: ParamType.String }],
|
|
},
|
|
close: {
|
|
exec: (conv: PrivateConversation | ChannelConversation) => conv.close(),
|
|
context: CommandContext.Private | CommandContext.Channel,
|
|
},
|
|
priv: {
|
|
exec: (_, character: string) =>
|
|
core.conversations
|
|
.getPrivate(core.characters.get(character))
|
|
.show(),
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
uptime: {
|
|
exec: () => core.connection.send("UPT"),
|
|
},
|
|
clear: {
|
|
exec: (conv: Conversation) => conv.clear(),
|
|
},
|
|
status: {
|
|
exec: (_, status: Character.Status, statusmsg: string = "") =>
|
|
core.connection.send("STA", { status, statusmsg }),
|
|
params: [
|
|
{ type: ParamType.Enum, options: userStatuses },
|
|
{ type: ParamType.String, optional: true },
|
|
],
|
|
},
|
|
roll: {
|
|
exec: (
|
|
conv: ChannelConversation | PrivateConversation,
|
|
dice: string
|
|
) => {
|
|
if (dice.toLocaleLowerCase() === "inf") {
|
|
conv.infoText = "Inf took many lives during its reign. Thankfully, you have been spared.";
|
|
return;
|
|
}
|
|
else if (Conversation.isChannel(conv))
|
|
core.connection.send("RLL", { channel: conv.channel.id, dice });
|
|
else
|
|
core.connection.send("RLL", {
|
|
recipient: conv.character.name,
|
|
dice,
|
|
});
|
|
},
|
|
context: CommandContext.Channel | CommandContext.Private,
|
|
params: [{ type: ParamType.String }],
|
|
},
|
|
bottle: {
|
|
exec: (conv: ChannelConversation | PrivateConversation) => {
|
|
if (Conversation.isChannel(conv))
|
|
core.connection.send("RLL", {
|
|
channel: conv.channel.id,
|
|
dice: "bottle",
|
|
});
|
|
else
|
|
core.connection.send("RLL", {
|
|
recipient: conv.character.name,
|
|
dice: "bottle",
|
|
});
|
|
},
|
|
context: CommandContext.Channel | CommandContext.Private,
|
|
},
|
|
warn: {
|
|
exec: () => "stub",
|
|
permission: Permission.RoomOp,
|
|
context: CommandContext.Channel,
|
|
params: [{ type: ParamType.String }],
|
|
},
|
|
kick: {
|
|
exec: (conv: ChannelConversation, character: string) =>
|
|
core.connection.send("CKU", {
|
|
channel: conv.channel.id,
|
|
character,
|
|
}),
|
|
permission: Permission.RoomOp,
|
|
context: CommandContext.Channel,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
ban: {
|
|
exec: (conv: ChannelConversation, character: string) =>
|
|
core.connection.send("CBU", {
|
|
channel: conv.channel.id,
|
|
character,
|
|
}),
|
|
permission: Permission.RoomOp,
|
|
context: CommandContext.Channel,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
unban: {
|
|
exec: (conv: ChannelConversation, character: string) =>
|
|
core.connection.send("CUB", {
|
|
channel: conv.channel.id,
|
|
character,
|
|
}),
|
|
permission: Permission.RoomOp,
|
|
context: CommandContext.Channel,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
banlist: {
|
|
exec: (conv: ChannelConversation) =>
|
|
core.connection.send("CBL", { channel: conv.channel.id }),
|
|
permission: Permission.RoomOp,
|
|
context: CommandContext.Channel,
|
|
},
|
|
timeout: {
|
|
exec: (conv: ChannelConversation, character: string, length: number) =>
|
|
core.connection.send("CTU", {
|
|
channel: conv.channel.id,
|
|
character,
|
|
length,
|
|
}),
|
|
permission: Permission.RoomOp,
|
|
context: CommandContext.Channel,
|
|
params: [
|
|
{ type: ParamType.Character, delimiter: "," },
|
|
{ type: ParamType.Number, validator: (x) => x >= 1 },
|
|
],
|
|
},
|
|
gkick: {
|
|
exec: (_, character: string) =>
|
|
core.connection.send("KIK", { character }),
|
|
permission: Permission.ChatOp,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
gban: {
|
|
exec: (_, character: string) =>
|
|
core.connection.send("ACB", { character }),
|
|
permission: Permission.ChatOp,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
gunban: {
|
|
exec: (_, character: string) =>
|
|
core.connection.send("UNB", { character }),
|
|
permission: Permission.ChatOp,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
gtimeout: {
|
|
exec: (_, character: string, time: number, reason: string) =>
|
|
core.connection.send("TMO", { character, time, reason }),
|
|
permission: Permission.ChatOp,
|
|
params: [
|
|
{ type: ParamType.Character, delimiter: "," },
|
|
{ type: ParamType.Number, validator: (x) => x >= 1 },
|
|
{ type: ParamType.String },
|
|
],
|
|
},
|
|
setowner: {
|
|
exec: (conv: ChannelConversation, character: string) =>
|
|
core.connection.send("CSO", {
|
|
channel: conv.channel.id,
|
|
character,
|
|
}),
|
|
permission: Permission.RoomOwner,
|
|
context: CommandContext.Channel,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
ignore: {
|
|
exec: (_, character: string) =>
|
|
core.connection.send("IGN", { action: "add", character }),
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
unignore: {
|
|
exec: (_, character: string) =>
|
|
core.connection.send("IGN", { action: "delete", character }),
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
ignorelist: {
|
|
exec: (conv: Conversation) =>
|
|
(conv.infoText = l(
|
|
"chat.ignoreList",
|
|
core.characters.ignoreList.join(", ")
|
|
)),
|
|
},
|
|
makeroom: {
|
|
exec: (_, channel: string) => core.connection.send("CCR", { channel }),
|
|
params: [{ type: ParamType.String }],
|
|
},
|
|
gop: {
|
|
exec: (_, character: string) =>
|
|
core.connection.send("AOP", { character }),
|
|
permission: Permission.Admin,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
gdeop: {
|
|
exec: (_, character: string) =>
|
|
core.connection.send("DOP", { character }),
|
|
permission: Permission.Admin,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
op: {
|
|
exec: (conv: ChannelConversation, character: string) =>
|
|
core.connection.send("COA", {
|
|
channel: conv.channel.id,
|
|
character,
|
|
}),
|
|
permission: Permission.RoomOwner,
|
|
context: CommandContext.Channel,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
deop: {
|
|
exec: (conv: ChannelConversation, character: string) =>
|
|
core.connection.send("COR", {
|
|
channel: conv.channel.id,
|
|
character,
|
|
}),
|
|
permission: Permission.RoomOwner,
|
|
context: CommandContext.Channel,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
scop: {
|
|
exec: (_, character: string) =>
|
|
core.connection.send("SCP", { action: "add", character }),
|
|
permission: Permission.Admin,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
scdeop: {
|
|
exec: (_, character: string) =>
|
|
core.connection.send("SCP", { action: "remove", character }),
|
|
permission: Permission.Admin,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
oplist: {
|
|
exec: (conv: ChannelConversation) =>
|
|
core.connection.send("COL", { channel: conv.channel.id }),
|
|
context: CommandContext.Channel,
|
|
},
|
|
invite: {
|
|
exec: (conv: ChannelConversation, character: string) =>
|
|
core.connection.send("CIU", {
|
|
channel: conv.channel.id,
|
|
character,
|
|
}),
|
|
permission: Permission.RoomOp,
|
|
context: CommandContext.Channel,
|
|
params: [{ type: ParamType.Character }],
|
|
},
|
|
closeroom: {
|
|
exec: (conv: ChannelConversation) =>
|
|
core.connection.send("RST", {
|
|
channel: conv.channel.id,
|
|
status: "private",
|
|
}),
|
|
permission: Permission.RoomOwner,
|
|
context: CommandContext.Channel,
|
|
},
|
|
openroom: {
|
|
exec: (conv: ChannelConversation) =>
|
|
core.connection.send("RST", {
|
|
channel: conv.channel.id,
|
|
status: "public",
|
|
}),
|
|
permission: Permission.RoomOwner,
|
|
context: CommandContext.Channel,
|
|
},
|
|
setmode: {
|
|
exec: (conv: ChannelConversation, mode: "ads" | "chat" | "both") =>
|
|
core.connection.send("RMO", { channel: conv.channel.id, mode }),
|
|
permission: Permission.RoomOwner,
|
|
context: CommandContext.Channel,
|
|
params: [{ type: ParamType.Enum, options: ["ads", "chat", "both"] }],
|
|
},
|
|
setdescription: {
|
|
exec: (conv: ChannelConversation, description: string) =>
|
|
core.connection.send("CDS", {
|
|
channel: conv.channel.id,
|
|
description,
|
|
}),
|
|
permission: Permission.RoomOp,
|
|
context: CommandContext.Channel,
|
|
params: [{ type: ParamType.String }],
|
|
},
|
|
code: {
|
|
exec: (conv: ChannelConversation) => {
|
|
const active = <HTMLElement>document.activeElement;
|
|
const elm = document.createElement("textarea");
|
|
elm.value = `[session=${conv.channel.name}]${conv.channel.id}[/session]`;
|
|
document.body.appendChild(elm);
|
|
elm.select();
|
|
document.execCommand("copy");
|
|
document.body.removeChild(elm);
|
|
active.focus();
|
|
conv.infoText = l("commands.code.success");
|
|
},
|
|
permission: Permission.RoomOwner,
|
|
context: CommandContext.Channel,
|
|
},
|
|
killchannel: {
|
|
exec: (conv: ChannelConversation) =>
|
|
core.connection.send("KIC", { channel: conv.channel.id }),
|
|
permission: Permission.RoomOwner,
|
|
context: CommandContext.Channel,
|
|
},
|
|
createchannel: {
|
|
exec: (_, channel: string) => core.connection.send("CRC", { channel }),
|
|
permission: Permission.ChatOp,
|
|
params: [{ type: ParamType.String }],
|
|
},
|
|
broadcast: {
|
|
exec: (_, message: string) => core.connection.send("BRO", { message }),
|
|
permission: Permission.Admin,
|
|
params: [{ type: ParamType.String }],
|
|
},
|
|
reloadconfig: {
|
|
exec: (_, save?: "save") =>
|
|
core.connection.send(
|
|
"RLD",
|
|
save !== undefined ? { save } : undefined
|
|
),
|
|
permission: Permission.Admin,
|
|
params: [{ type: ParamType.Enum, options: ["save"], optional: true }],
|
|
},
|
|
xyzzy: {
|
|
exec: (_, command: string, arg: string) =>
|
|
core.connection.send("ZZZ", { command, arg }),
|
|
permission: Permission.Admin,
|
|
params: [
|
|
{ type: ParamType.String, delimiter: " " },
|
|
{ type: ParamType.String },
|
|
],
|
|
},
|
|
elf: {
|
|
exec: (conv: Conversation) =>
|
|
(conv.infoText = elf[Math.floor(Math.random() * elf.length)]),
|
|
documented: false,
|
|
},
|
|
};
|
|
|
|
const elf = [ //Ceran is to be thanked for most of these.
|
|
`Now no one can say there's "not enough Elf." It's a well-kept secret, but elves love headpets. You should try it sometime.`,
|
|
`Your task for the day: provide a soft cushion in a sunny area for maximum elf comfort, earn +2 Bliss pts.`,
|
|
`Your task for the day: pet an elf at least (3) times, receive Good Karma.`,
|
|
`Your task for the day: make dinner for an elf, receive +3 Luck pts.`,
|
|
`The reason that elves' ears are so long is so that they can better hear your sins. Now that's an Elf Fact!`,
|
|
`A "straight" elf is basically an oxymoron.`,
|
|
`Don't forget to water your elf today!`,
|
|
`Please don't let your elf eat out of the trash can.`,
|
|
`Elves are not allowed to have soda after 12pm, but they will anyway.`,
|
|
`Pet an elf on the ears (4) times. Any more and the elf will bite. Now that's an Elf Fact!`,
|
|
`There are two kinds of people in the world. People who like elves. And people with questionable taste.`,
|
|
`Love yourself, pet an elf!!!`,
|
|
`Your task for the day: leave out snacks for your local elves, they'll help dispel vermin!`,
|
|
`An elf in your home will discourage the presence of predators! Or summon them. I forget which.`,
|
|
`If you crack open an elf, there's just a smaller elf within. It's just smaller and smaller elves all the way down.`,
|
|
`In case of an emergency, an elf's ass can be used as a flotation device. Now that's an Elf Fact!`,
|
|
`Running low on inventory space? An elf can be used to slot additional cylindrical artifacts! Now that's an Elf Fact!`,
|
|
`The average elf can consume half their body weight in cum. Now that's an Elf Fact!`,
|
|
`Your task for the day: subjugate yourself to your elven overlords in order to achieve righteous bliss.`,
|
|
`Your task for the day: Rub an elf's tummy. Be wary of them bear-trapping your arm as the forces of darkness consume their soul.`,
|
|
`Listen, that elfroot you found in my sock drawer is PURELY medicinal!`,
|
|
`What are elves? We just don't know.`,
|
|
`As long as you pet an elf's head, they will be content. What will happen if you stop? No one's ever come back to tell the tale.`,
|
|
`Elves are very helpful creatures. Just ask them to carry something for you, and they will. Especially eggs.`
|
|
];
|
|
|
|
export default commands; |