feat(dispatcher): support regex commands

This commit is contained in:
teidesu 2021-06-19 20:53:51 +03:00
parent 2682392d26
commit fbb5d7005f

View file

@ -798,11 +798,14 @@ export namespace filters {
} }
/** /**
* Filter messages that match a given regular expression. * Filter messages that call the given command(s)..
* *
* When a command matches, the match array is stored in a * When a command matches, the match array is stored in a
* type-safe extension field `.commmand` of the {@link Message} object. * type-safe extension field `.commmand` of the {@link Message} object.
* First element is the command itself, then the arguments * First element is the command itself, then the arguments.
*
* If the matched command was a RegExp, the first element is the full
* command, then the groups from the command regex, then the arguments.
* *
* @param commands Command(s) the filter should look for (w/out prefix) * @param commands Command(s) the filter should look for (w/out prefix)
* @param prefixes * @param prefixes
@ -811,20 +814,27 @@ export namespace filters {
* @param caseSensitive * @param caseSensitive
*/ */
export const command = ( export const command = (
commands: MaybeArray<string>, commands: MaybeArray<string | RegExp>,
prefixes: MaybeArray<string> | null = '/', prefixes: MaybeArray<string> | null = '/',
caseSensitive = false caseSensitive = false
): UpdateFilter<Message, { command: string[] }> => { ): UpdateFilter<Message, { command: string[] }> => {
if (typeof commands === 'string') commands = [commands] if (!Array.isArray(commands)) commands = [commands]
commands = commands.map((i) => i.toLowerCase())
commands = commands.map((i) =>
typeof i === 'string' ? i.toLowerCase() : i
)
const argumentsRe = /(["'])(.*?)(?<!\\)\1|(\S+)/g const argumentsRe = /(["'])(.*?)(?<!\\)\1|(\S+)/g
const unescapeRe = /\\(['"])/ const unescapeRe = /\\(['"])/
const commandsRe: Record<string, RegExp> = {} const commandsRe: RegExp[] = []
commands.forEach((cmd) => { commands.forEach((cmd) => {
commandsRe[cmd] = new RegExp( if (typeof cmd !== 'string') cmd = cmd.source
`^${cmd}(?:\\s|$|@([a-zA-Z0-9_]+?bot)(?:\\s|$))`,
caseSensitive ? '' : 'i' commandsRe.push(
new RegExp(
`^(${cmd})(?:\\s|$|@([a-zA-Z0-9_]+?bot)(?:\\s|$))`,
caseSensitive ? '' : 'i'
)
) )
}) })
@ -836,11 +846,12 @@ export namespace filters {
if (!msg.text.startsWith(pref)) continue if (!msg.text.startsWith(pref)) continue
const withoutPrefix = msg.text.slice(pref.length) const withoutPrefix = msg.text.slice(pref.length)
for (const cmd of commands) { for (const regex of commandsRe) {
const m = withoutPrefix.match(commandsRe[cmd]) const m = withoutPrefix.match(regex)
if (!m) continue if (!m) continue
if (m[1] && msg.client['_isBot']) { const lastGroup = m[m.length - 1]
if (lastGroup && msg.client['_isBot']) {
// check bot username // check bot username
if (!msg.client['_botUsername']) { if (!msg.client['_botUsername']) {
// need to fetch it first // need to fetch it first
@ -851,13 +862,14 @@ export namespace filters {
}) })
} }
if (m[1] !== msg.client['_botUsername']) return false if (lastGroup !== msg.client['_botUsername']) return false
} }
const match = [cmd] const match = m.slice(1, -1)
// we use .replace to iterate over global regex, not to replace the text // we use .replace to iterate over global regex, not to replace the text
withoutPrefix withoutPrefix
.slice(cmd.length) .slice(m[0].length)
.replace(argumentsRe, ($0, $1, $2, $3) => { .replace(argumentsRe, ($0, $1, $2, $3) => {
match.push( match.push(
($2 || $3 || '').replace(unescapeRe, '$1') ($2 || $3 || '').replace(unescapeRe, '$1')