Skip to content

Instantly share code, notes, and snippets.

@y21
Last active November 4, 2019 12:28
Show Gist options
  • Save y21/a7cc433e1a0f6b64b9b1931710b5b56d to your computer and use it in GitHub Desktop.
Save y21/a7cc433e1a0f6b64b9b1931710b5b56d to your computer and use it in GitHub Desktop.
Pylon Client
interface ClientOptions {
owner: string;
description: string;
prefix: string;
commands?: Array<Command>;
};
interface CommandOptions {
name: string;
enabled?: boolean;
flags: Array<Flag>;
run: (message: MessageData) => any;
};
interface MessageData {
message: discord.Message;
args: Array<string>;
flags: Array<MatchedFlag>;
};
interface MatchedFlag {
name: string;
value?: string;
};
interface FlagOptions {
name: string;
description: string;
};
class Client {
public owner: string;
public description: string;
public prefix: string;
public wrapper: any;
public commands: Array<Command>;
/*
* Constructs a client
* @param {any} wrapper The 'discord' namespace
* @param {ClientOptions} options Options to use for this instance
*/
constructor(wrapper: any, options: ClientOptions) {
this.wrapper = wrapper;
this.owner = options.owner;
this.description = options.description;
this.prefix = options.prefix;
this.commands = options.commands || [];
discord.registerEventHandler(discord.Event.MESSAGE_CREATE, (message: discord.Message): void => {
const [command, ...args] = message.content.split(" ");
const commandInst: Command | undefined = this.commands.find(c => this.prefix + c.name === command);
// Command not found
if (!commandInst) return;
// Parse flags
const flags: Array<MatchedFlag> = Flag.parse(args.join(" "));
commandInst.run({ args: args.map((k: string, i: number) => {
if (Flag.hasFlag(k)) {
return "";
} else if (Flag.hasFlag(args[i - 1])) {
return "";
} else return k;
}), flags, message });
});
}
/**
* Prevents everyone/here/role/user mentions by adding an invisible character in mention
* @param {string} src The string that should be escaped
*/
static escapeMentions(src: string): string {
return src.replace(/<@[!&](\d+)>/g, "<@\u200b$1>").replace(/@(here|everyone)/g, "@\u200b$1");
}
/**
* Returns the string in a codeblock
* @param {string} src The string that should be placed inside of a codeblock
* @param {string} lang The language of the returned codeblock
*/
static codeblock(src: string, lang?: string): string {
return "```" + (lang || "") + "\n" + src + "\n```";
}
};
class Command {
public name: string;
public enabled: boolean;
public flags: Array<Flag>;
public run: (message: MessageData) => any;
constructor(options: CommandOptions) {
this.name = options.name;
this.enabled = options.enabled || true;
this.run = options.run;
this.flags = options.flags;
}
};
class Flag {
public name: string;
public description: string;
public static FLAG_REGEX = /--(\w+)(?: )?(\w+)?/;
constructor(options: FlagOptions) {
this.name = options.name;
this.description = options.description;
}
static parse(src: string): Array<MatchedFlag> {
return (src.match(new RegExp(Flag.FLAG_REGEX, "g")) || []).map(m => {
// Assert since there is definitely going to be matches
const match: RegExpMatchArray = <RegExpMatchArray>(m.match(Flag.FLAG_REGEX));
return {
name: match[1],
value: match[2]
};
});
}
static eraseFlags(src: string): string {
return src.replace(new RegExp(Flag.FLAG_REGEX, "g"), "");
}
static hasFlag(src: string): boolean {
return Flag.FLAG_REGEX.test(src);
}
};
const client: Client = new Client(discord, {
owner: "312715611413413889",
description: "Epic Pylon Bot",
prefix: "!"
});
/**
* Example command:
* !say hello @here --lang js
*
* ```js
* hello @here
* ```
* Due to calling Client.escapeMentions before sending the message, this would not mention "@here" even if it had permissions to do so,
* since Client.escapeMentions adds '\u200b' between @ and the ID (or here/everyone) in mentions
*/
client.commands.push(<Command>{
name: "say",
flags: [
{
name: "lang",
description: "The codeblock language for this response"
}
],
run: (data: MessageData) => {
const { message, args, flags } = data;
const language: MatchedFlag | undefined = flags.find(f => f.name === "lang");
client.wrapper.sendMessage(
message.channel_id,
Client.codeblock(
Client.escapeMentions(args.join(" ")),
(language || {}).value
)
);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment